Skip to content

Commit

Permalink
Fix empty objects in credential throws error
Browse files Browse the repository at this point in the history
Signed-off-by: Samuel Hellawell <[email protected]>
  • Loading branch information
cykoder committed Apr 4, 2023
1 parent f6e26c3 commit ff3741d
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 15 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@docknetwork/crypto-wasm-ts",
"version": "0.31.2",
"version": "0.31.3",
"description": "Typescript abstractions over Dock's Rust crypto library's WASM wrapper",
"homepage": "https://github.com/docknetwork/crypto-wasm-ts",
"main": "lib/index.js",
Expand Down
30 changes: 19 additions & 11 deletions src/anonymous-credentials/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,7 @@ export class CredentialSchema extends Versioned {

static validateGeneric(schema: object, ignoreKeys: Set<string> = new Set()) {
const [names, values] = this.flattenSchemaObj(schema);

for (let i = 0; i < names.length; i++) {
if (ignoreKeys.has(names[i])) {
continue;
Expand Down Expand Up @@ -644,16 +645,16 @@ export class CredentialSchema extends Versioned {
properties: {
id: {
type: 'string'
},
},
}
}
},
proof: {
type: 'object',
properties: {
type: {
type: 'string'
},
},
}
}
}
}
};
Expand Down Expand Up @@ -1101,13 +1102,20 @@ export class CredentialSchema extends Versioned {
} else if (schemaProps[key]['type'] == 'object' && typ == 'object') {
const schemaKeys = new Set([...Object.keys(schemaProps[key]['properties'])]);
const valKeys = new Set([...Object.keys(value)]);
for (const vk of valKeys) {
CredentialSchema.generateFromCredential(value, schemaProps[key]['properties']);
}
// Delete extra keys not in cred
for (const sk of schemaKeys) {
if (value[sk] === undefined) {
delete schemaKeys[sk];

// If empty object, skip it here otherwise causes problems downstream
if (schemaKeys.size === 0) {
delete schemaProps[key];
} else {
for (const vk of valKeys) {
// TODO: why this loop? seems useless?
CredentialSchema.generateFromCredential(value, schemaProps[key]['properties']);
}
// Delete extra keys not in cred
for (const sk of schemaKeys) {
if (value[sk] === undefined) {
delete schemaKeys[sk];
}
}
}
} else {
Expand Down
4 changes: 3 additions & 1 deletion src/anonymous-credentials/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { ValueType, ValueTypes } from './schema';
import { Encoder } from '../bbs-plus';
import { SetupParam, Statement, WitnessEqualityMetaStatement } from '../composite-proof';
import { SetupParamsTracker } from './setup-params-tracker';
import { isEmptyObject } from '../util';

export function dockAccumulatorParams(): AccumulatorParams {
return Accumulator.generateParams(ACCUMULATOR_PARAMS_LABEL_BYTES);
Expand All @@ -56,7 +57,8 @@ export function dockSaverEncryptionGensUncompressed(): SaverEncryptionGensUncomp
export function flattenTill2ndLastKey(obj: object): [string[], object[]] {
const flattened = {};
const temp = flatten(obj) as object;
for (const k of Object.keys(temp)) {
const tempKeys = Object.keys(temp).filter((key) => typeof temp[key] !== 'object' || !isEmptyObject(temp[key]));
for (const k of tempKeys) {
// taken from https://stackoverflow.com/a/5555607
const pos = k.lastIndexOf('.');
const name = k.substring(0, pos);
Expand Down
10 changes: 8 additions & 2 deletions src/bbs-plus/encoder.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { SignatureG1 } from './signature';
import { flattenObjectToKeyValuesList, isPositiveInteger } from '../util';
import { flattenObjectToKeyValuesList, isEmptyObject, isPositiveInteger } from '../util';

/**
* A function that encodes the input to field element bytes
Expand Down Expand Up @@ -63,7 +63,13 @@ export class Encoder {
* @param strict - If set to false and no appropriate encoder is found but the value is a bytearray, it will encode it using the built-in mechanism
*/
encodeMessageObject(messages: object, strict = false): [string[], Uint8Array[]] {
const [names, values] = flattenObjectToKeyValuesList(messages);
let [names, values] = flattenObjectToKeyValuesList(messages);

// Filter keys to remove empty objects
// this is done because schema generation removes empty objects + nothing to encode
names = names.filter((k, i) => typeof values[i] !== 'object' || !isEmptyObject(values[i]));
values = values.filter((k, i) => typeof values[i] !== 'object' || !isEmptyObject(values[i]));

const encoded: Uint8Array[] = [];
for (let i = 0; i < names.length; i++) {
encoded.push(this.encodeMessage(names[i], values[i], strict));
Expand Down
13 changes: 13 additions & 0 deletions tests/anonymous-credentials/credential.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -871,4 +871,17 @@ describe('Credential signing and verification', () => {
checkResult(recreatedCred.verify(pk));
}
});

it('for credential with auto-generated schema and empty objects', () => {
const builder = new CredentialBuilder();
builder.schema = new CredentialSchema(CredentialSchema.essential());
builder.subject = {
fname: 'John',
emptyObject: {},
lname: 'Smith',
};

const cred = builder.sign(sk, undefined, {requireSameFieldsAsSchema: false});
checkResult(cred.verify(pk));
});
});

0 comments on commit ff3741d

Please sign in to comment.