diff --git a/package.json b/package.json index c3a9e104..53e393f7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@docknetwork/crypto-wasm-ts", - "version": "0.54.0", + "version": "0.55.0", "description": "Typescript abstractions over Dock's Rust crypto library's WASM wrapper", "homepage": "https://github.com/docknetwork/crypto-wasm-ts", "main": "lib/index.js", diff --git a/src/anonymous-credentials/blinded-credential-request-builder.ts b/src/anonymous-credentials/blinded-credential-request-builder.ts index 1bbc030d..c3c84dae 100644 --- a/src/anonymous-credentials/blinded-credential-request-builder.ts +++ b/src/anonymous-credentials/blinded-credential-request-builder.ts @@ -137,7 +137,7 @@ export abstract class BlindedCredentialRequestBuilder extends Version throw new Error('This method should be implemented by extending class'); } - addCredentialToPresentation(credential: Credential, pk: PublicKey): number { + addCredentialToPresentation(credential: Credential, pk?: PublicKey): number { return this.presentationBuilder.addCredential(credential, pk); } diff --git a/src/anonymous-credentials/blinded-credential-request.ts b/src/anonymous-credentials/blinded-credential-request.ts index 03558198..bbc3af3b 100644 --- a/src/anonymous-credentials/blinded-credential-request.ts +++ b/src/anonymous-credentials/blinded-credential-request.ts @@ -27,7 +27,7 @@ export abstract class BlindedCredentialRequest extends Versioned { } verify( - publicKeys: Map | CredentialVerificationParam[], + publicKeys: Map | CredentialVerificationParam[], accumulatorPublicKeys?: Map, predicateParams?: Map, circomOutputs?: Map, diff --git a/src/anonymous-credentials/delegated-proof.ts b/src/anonymous-credentials/delegated-proof.ts index 891e4728..3a7a6f0d 100644 --- a/src/anonymous-credentials/delegated-proof.ts +++ b/src/anonymous-credentials/delegated-proof.ts @@ -1,8 +1,16 @@ +import b58 from 'bs58'; import { VerifyResult } from 'crypto-wasm-new'; import { AccumulatorSecretKey } from '../accumulator'; import { BDDT16MacSecretKey } from '../bddt16-mac'; import { BDDT16DelegatedProof, VBAccumMembershipDelegatedProof } from '../delegated-proofs'; -import { ID_STR, REV_CHECK_STR, RevocationStatusProtocol, SignatureType, TYPE_STR } from './types-and-consts'; +import { + ID_STR, + MEM_CHECK_KV_STR, + REV_CHECK_STR, + RevocationStatusProtocol, + SignatureType, + TYPE_STR +} from './types-and-consts'; import { Versioned } from './versioned'; export interface IDelegatedCredentialProof { @@ -17,6 +25,9 @@ export interface IDelegatedCredentialStatusProof { proof: VBAccumMembershipDelegatedProof; } +/** + * Delegated proof for a KVAC. It can contain proof for either the credential or the status or both + */ export class DelegatedProof extends Versioned { static VERSION = '0.1.0'; @@ -24,6 +35,9 @@ export class DelegatedProof extends Versioned { readonly status?: IDelegatedCredentialStatusProof; constructor(credential?: IDelegatedCredentialProof, status?: IDelegatedCredentialStatusProof) { + if (credential === undefined && status === undefined) { + throw new Error(`At least one of credential or status must be defined`) + } super(DelegatedProof.VERSION); this.credential = credential; this.status = status; @@ -31,6 +45,7 @@ export class DelegatedProof extends Versioned { verify(credentialSecretKey?: BDDT16MacSecretKey, accumSecretKey?: AccumulatorSecretKey): VerifyResult { const r = { verified: true, error: '' }; + if (this.credential !== undefined) { if (credentialSecretKey === undefined) { throw new Error('Secret key not provided for credential'); @@ -40,15 +55,67 @@ export class DelegatedProof extends Versioned { return rc; } } + if (this.status !== undefined) { if (accumSecretKey === undefined) { throw new Error('Secret key not provided for accumulator'); } + if (this.status[ID_STR] === undefined) { + throw new Error(`${ID_STR} field is required in the delegated proof`); + } + if (this.status[TYPE_STR] !== RevocationStatusProtocol.Vb22 || this.status[REV_CHECK_STR] !== MEM_CHECK_KV_STR) { + throw new Error(`Unexpected values for ${TYPE_STR} and ${REV_CHECK_STR}: ${this.status[TYPE_STR]}, ${this.status[REV_CHECK_STR]}`); + } const rc = this.status.proof.verify(accumSecretKey); if (!rc.verified) { return rc; } } + return r; } + + toJSON(): object { + let d = {}; + if (this.credential !== undefined) { + d['credential'] = { + sigType: this.credential.sigType, + proof: b58.encode(this.credential.proof.bytes) + }; + } + if (this.status !== undefined) { + d['status'] = { + [ID_STR]: this.status[ID_STR], + [TYPE_STR]: this.status[TYPE_STR], + [REV_CHECK_STR]: this.status[REV_CHECK_STR], + proof: b58.encode(this.status.proof.bytes) + }; + } + return d; + } + + static fromJSON(j: object): DelegatedProof { + let credential, status; + if (j['credential'] !== undefined) { + if (j['credential'].sigType === undefined || j['credential'].proof === undefined) { + throw new Error(`Expected fields sigType and proof but found the credential object to be ${j['credential']}`); + } + credential = { + sigType: j['credential'].sigType, + proof: new BDDT16DelegatedProof(b58.decode(j['credential'].proof)) + }; + } + if (j['status'] !== undefined) { + if (j['status'][ID_STR] === undefined || j['status'][TYPE_STR] === undefined || j['status'][REV_CHECK_STR] === undefined || j['status'].proof === undefined) { + throw new Error(`Expected fields ${ID_STR}, ${TYPE_STR}, ${REV_CHECK_STR} and proof but found the status object to be ${j['status']}`); + } + status = { + [ID_STR]: j['status'][ID_STR], + [TYPE_STR]: j['status'][TYPE_STR], + [REV_CHECK_STR]: j['status'][REV_CHECK_STR], + proof: new VBAccumMembershipDelegatedProof(b58.decode(j['status'].proof)) + }; + } + return new DelegatedProof(credential, status) + } } diff --git a/src/anonymous-credentials/presentation-builder.ts b/src/anonymous-credentials/presentation-builder.ts index 68e947f3..80ea0420 100644 --- a/src/anonymous-credentials/presentation-builder.ts +++ b/src/anonymous-credentials/presentation-builder.ts @@ -203,7 +203,7 @@ export class PresentationBuilder extends Versioned { /** * Add a credential to this presentation. This will result in a proof of possession of this credential being created * @param credential - * @param pk + * @param pk - Only certain kinds of credentials need a public key for creating presentation */ addCredential(credential: Credential, pk?: PublicKey): number { // TODO: Accept reference to public keys in case of same key for many credentials diff --git a/src/anonymous-credentials/presentation.ts b/src/anonymous-credentials/presentation.ts index c2d80840..31259b8a 100644 --- a/src/anonymous-credentials/presentation.ts +++ b/src/anonymous-credentials/presentation.ts @@ -146,8 +146,11 @@ export class Presentation extends Versioned { /** * - * @param credentialVerifParams - Array of keys in the order of credentials in the presentation. - * @param accumulatorPublicKeys - Mapping credential index -> accumulator public key + * @param credentialVerifParams - Map of verification parameters for credentials in the presentation. The key of the map + * is the credential index. Can also take array of keys in the order of credentials in the presentation for supporting old API but this will + * be removed in future. The verification param could be a public key or secret key. Certain kinds of credentials don't require + * either for (partial) verification but will require for full verification + * @param accumulatorPublicKeys - Mapping credential index -> accumulator verification parameters. * @param predicateParams - Setup params for various predicates * @param circomOutputs - Values for the outputs variables of the Circom programs used for predicates. They key of the map * is the credential index @@ -156,7 +159,7 @@ export class Presentation extends Versioned { */ verify( // TODO: Accept reference to public keys in case of same key for many credentials - credentialVerifParams: Map | CredentialVerificationParam[], + credentialVerifParams: Map | CredentialVerificationParam[], accumulatorPublicKeys?: Map, predicateParams?: Map, circomOutputs?: Map, @@ -168,6 +171,7 @@ export class Presentation extends Versioned { // processed at 2nd last in the builder than they should be processed at 2nd last here as well. By convention credentials are // processed first, then their statuses (if present) and then any predicates. + // Dealing with old API - convert array to map let credVerifParams = new Map(); if (credentialVerifParams instanceof Map) { credVerifParams = credentialVerifParams; @@ -176,6 +180,7 @@ export class Presentation extends Versioned { credVerifParams.set(i, v); }); } + const statements = new Statements(); const metaStatements = new MetaStatements(); @@ -609,7 +614,7 @@ export class Presentation extends Versioned { } /** - * Get delegated proof for + * Get delegated proofs for credentials and there statuses where applicable. * @returns - The key in the returned map is the credential index */ getDelegatedProofs(): Map { diff --git a/src/bddt16-mac/keys.ts b/src/bddt16-mac/keys.ts index c4a2b1ca..d5588e4d 100644 --- a/src/bddt16-mac/keys.ts +++ b/src/bddt16-mac/keys.ts @@ -35,4 +35,12 @@ export class BDDT16KeypairG1 { const pk = sk.generatePublicKeyG1(params); return new BDDT16KeypairG1(sk, pk); } + + get secretKey(): BDDT16MacSecretKey { + return this.sk; + } + + get publicKey(): BDDT16MacPublicKeyG1 { + return this.pk; + } } diff --git a/tests/anonymous-credentials/blind-issuance.spec.ts b/tests/anonymous-credentials/blind-issuance.spec.ts index 57a028cf..f3c5aebc 100644 --- a/tests/anonymous-credentials/blind-issuance.spec.ts +++ b/tests/anonymous-credentials/blind-issuance.spec.ts @@ -387,7 +387,7 @@ skipIfPS.each([true, false])(`${Scheme} Blind issuance of credentials with withS secret: 'my-secret-that-wont-tell-anyone' }; const reqBuilder = newReqBuilder(schema2, blindedSubject); - expect(reqBuilder.addCredentialToPresentation(credential1, pk1)).toEqual(0); + expect(reqBuilder.addCredentialToPresentation(credential1, isPS() ? pk1 : undefined)).toEqual(0); reqBuilder.markCredentialAttributesRevealed( 0, new Set([ @@ -499,8 +499,8 @@ skipIfPS.each([true, false])(`${Scheme} Blind issuance of credentials with withS ]; const reqBuilder = newReqBuilder(schema3, blindedSubject); - expect(reqBuilder.addCredentialToPresentation(credential1, pk1)).toEqual(0); - expect(reqBuilder.addCredentialToPresentation(credential2, pk2)).toEqual(1); + expect(reqBuilder.addCredentialToPresentation(credential1, isPS() ? pk1 : undefined)).toEqual(0); + expect(reqBuilder.addCredentialToPresentation(credential2, isPS() ? pk2 : undefined)).toEqual(1); reqBuilder.markCredentialAttributesRevealed( 0, diff --git a/tests/anonymous-credentials/delegated-proofs.spec.ts b/tests/anonymous-credentials/delegated-proofs.spec.ts index 383be5fa..e9617812 100644 --- a/tests/anonymous-credentials/delegated-proofs.spec.ts +++ b/tests/anonymous-credentials/delegated-proofs.spec.ts @@ -1,15 +1,20 @@ import { VerifyResult } from 'crypto-wasm-new'; import { AccumulatorPublicKey, - AccumulatorSecretKey, BDDT16Credential, BDDT16CredentialBuilder, CredentialSchema, ID_STR, + AccumulatorSecretKey, BDDT16Credential, BDDT16CredentialBuilder, CredentialSchema, DelegatedProof, ID_STR, initializeWasm, MEM_CHECK_KV_STR, MEM_CHECK_STR, - PositiveAccumulator, PresentationBuilder, REV_CHECK_STR, RevocationStatusProtocol, SignatureType, TYPE_STR, - VBMembershipWitness + PositiveAccumulator, Presentation, + PresentationBuilder, + REV_CHECK_STR, + RevocationStatusProtocol, + SignatureType, + TYPE_STR, + VBMembershipWitness, + BDDT16MacSecretKey } from '../../src'; -import { BDDT16MacSecretKey } from '../../src/bddt16-mac'; -import { Credential, CredentialBuilder, isKvac, PublicKey, Scheme, SecretKey } from '../scheme'; +import { Credential, CredentialBuilder, isKvac, isPS, PublicKey, Scheme, SecretKey } from '../scheme'; import { checkResult } from '../utils'; -import { getExampleSchema, getKeys, setupPrefilledAccum, verifyCred } from './utils'; +import { checkPresentationJson, getExampleSchema, getKeys, setupPrefilledAccum, verifyCred } from './utils'; describe(`Delegated proof verification with BDDT16 MAC and ${Scheme} signatures`, () => { let sk: SecretKey, pk: PublicKey; @@ -21,10 +26,12 @@ describe(`Delegated proof verification with BDDT16 MAC and ${Scheme} signatures` let credential4: BDDT16Credential; let credential5: BDDT16Credential; + // Accumulator where membership is publicly verifiable let accumulator1: PositiveAccumulator; let accumulator1Pk: AccumulatorPublicKey; let accumulator1Witness: VBMembershipWitness; + // Accumulator where membership verification needs secret key let accumulator2: PositiveAccumulator; let accumulator2Sk: AccumulatorSecretKey; let accumulator2Witness: VBMembershipWitness; @@ -124,13 +131,17 @@ describe(`Delegated proof verification with BDDT16 MAC and ${Scheme} signatures` }); it('works', () => { + // Describes a test with 5 credentials. Credentials 1, 2, and 3 are non-KVAC and 4 and 5 is KVAC. + // Status verification of credential 1 and credential 4 requires public key but for credential 2 and credential 5 requires secret key + const builder = new PresentationBuilder(); - expect(builder.addCredential(credential1, pk)).toEqual(0); - expect(builder.addCredential(credential2, pk)).toEqual(1); - expect(builder.addCredential(credential3, pk)).toEqual(2); + expect(builder.addCredential(credential1, isPS() ? pk : undefined)).toEqual(0); + expect(builder.addCredential(credential2, isPS() ? pk : undefined)).toEqual(1); + expect(builder.addCredential(credential3, isPS() ? pk : undefined)).toEqual(2); expect(builder.addCredential(credential4)).toEqual(3); expect(builder.addCredential(credential5)).toEqual(4); + builder.addAccumInfoForCredStatus(0, accumulator1Witness, accumulator1.accumulated, accumulator1Pk, { blockNo: 2010334 }); @@ -155,54 +166,94 @@ describe(`Delegated proof verification with BDDT16 MAC and ${Scheme} signatures` accumPks.set(3, accumulator1Pk); checkResult(pres.verify(pks, accumPks)); + checkPresentationJson(pres, pks, accumPks); + // Check full verification using secret key pks.set(3, skKvac); pks.set(4, skKvac); accumPks.set(1, accumulator2Sk); accumPks.set(4, accumulator2Sk); checkResult(pres.verify(pks, accumPks)); + let recreatedPres = checkPresentationJson(pres, pks, accumPks); + + checkDelegatedProofs(pres); + checkDelegatedProofs(recreatedPres); + + function checkDelegatedProofs(presentation: Presentation) { - const delegatedProofs = pres.getDelegatedProofs(); + /** + * Check if the serialized versions of delegated proofs can be verified + * @param verifyFunc - the function that verifier + * @param delgCredProof + */ + function checkSerialized(verifyFunc, delgCredProof?: DelegatedProof) { + let j = delgCredProof?.toJSON(); + let recreated = DelegatedProof.fromJSON(j as object); + verifyFunc(recreated); + } - expect(delegatedProofs.size).toEqual(isKvac() ? 5 : 3); - if (isKvac()) { - for (let j = 0; j < 3; j++) { - const delgCredProof = delegatedProofs.get(j); + function onlyCredProofAvailable(delgCredProof?: DelegatedProof) { expect(delgCredProof?.credential).toMatchObject({ sigType: SignatureType.Bddt16 }); checkResult(delgCredProof?.credential?.proof.verify(sk) as VerifyResult); } - } - const delgCredProof2 = delegatedProofs.get(1); - if (!isKvac()) { - expect(delgCredProof2?.credential).not.toBeDefined(); - } - expect(delgCredProof2?.status).toMatchObject({ - [ID_STR]: 'dock:accumulator:accumId124', - [TYPE_STR]: RevocationStatusProtocol.Vb22, - [REV_CHECK_STR]: MEM_CHECK_KV_STR - }); - checkResult(delgCredProof2?.status?.proof.verify(accumulator2Sk) as VerifyResult); + function check2(delgCredProof?: DelegatedProof) { + if (!isKvac()) { + expect(delgCredProof?.credential).not.toBeDefined(); + } + expect(delgCredProof?.status).toMatchObject({ + [ID_STR]: 'dock:accumulator:accumId124', + [TYPE_STR]: RevocationStatusProtocol.Vb22, + [REV_CHECK_STR]: MEM_CHECK_KV_STR + }); + checkResult(delgCredProof?.status?.proof.verify(accumulator2Sk) as VerifyResult); + } - const delgCredProof4 = delegatedProofs.get(3); - expect(delgCredProof4?.credential).toMatchObject({ - sigType: SignatureType.Bddt16 - }); - checkResult(delgCredProof4?.credential?.proof.verify(skKvac) as VerifyResult); - expect(delgCredProof4?.status).not.toBeDefined(); + function check3(delgCredProof?: DelegatedProof) { + expect(delgCredProof?.credential).toMatchObject({ + sigType: SignatureType.Bddt16 + }); + checkResult(delgCredProof?.credential?.proof.verify(skKvac) as VerifyResult); + expect(delgCredProof?.status).not.toBeDefined(); + } - const delgCredProof5 = delegatedProofs.get(4); - expect(delgCredProof5?.credential).toMatchObject({ - sigType: SignatureType.Bddt16 - }); - checkResult(delgCredProof5?.credential?.proof.verify(skKvac) as VerifyResult); - expect(delgCredProof5?.status).toMatchObject({ - [ID_STR]: 'dock:accumulator:accumId124', - [TYPE_STR]: RevocationStatusProtocol.Vb22, - [REV_CHECK_STR]: MEM_CHECK_KV_STR - }); - checkResult(delgCredProof5?.status?.proof.verify(accumulator2Sk) as VerifyResult); + function check4(delgCredProof?: DelegatedProof) { + expect(delgCredProof?.credential).toMatchObject({ + sigType: SignatureType.Bddt16 + }); + checkResult(delgCredProof?.credential?.proof.verify(skKvac) as VerifyResult); + expect(delgCredProof?.status).toMatchObject({ + [ID_STR]: 'dock:accumulator:accumId124', + [TYPE_STR]: RevocationStatusProtocol.Vb22, + [REV_CHECK_STR]: MEM_CHECK_KV_STR + }); + checkResult(delgCredProof?.status?.proof.verify(accumulator2Sk) as VerifyResult); + } + + const delegatedProofs = presentation.getDelegatedProofs(); + expect(delegatedProofs.size).toEqual(isKvac() ? 5 : 3); + + if (isKvac()) { + for (let i = 0; i < 3; i++) { + const delgCredProof = delegatedProofs.get(i); + onlyCredProofAvailable(delgCredProof); + checkSerialized(onlyCredProofAvailable, delgCredProof); + } + } + + const delgCredProof2 = delegatedProofs.get(1); + check2(delgCredProof2); + checkSerialized(check2, delgCredProof2); + + const delgCredProof4 = delegatedProofs.get(3); + check3(delgCredProof4); + checkSerialized(check3, delgCredProof4); + + const delgCredProof5 = delegatedProofs.get(4); + check4(delgCredProof5); + checkSerialized(check4, delgCredProof5); + } }) }) \ No newline at end of file diff --git a/tests/anonymous-credentials/presentation-multiple-sig-types.spec.ts b/tests/anonymous-credentials/presentation-multiple-sig-types.spec.ts index d989ea69..9911548c 100644 --- a/tests/anonymous-credentials/presentation-multiple-sig-types.spec.ts +++ b/tests/anonymous-credentials/presentation-multiple-sig-types.spec.ts @@ -144,8 +144,8 @@ describe.each([true, false])( const commKey = new PederCommKey(stringToBytes('test')); const builder = new PresentationBuilder(); - expect(builder.addCredential(credentialBbs, pkBbs)).toEqual(0); - expect(builder.addCredential(credentialBbsPlus, pkBbsPlus)).toEqual(1); + expect(builder.addCredential(credentialBbs)).toEqual(0); + expect(builder.addCredential(credentialBbsPlus)).toEqual(1); expect(builder.addCredential(credentialPs, pkPs)).toEqual(2); expect(builder.addCredential(credentialBddt16)).toEqual(3); @@ -224,6 +224,7 @@ describe.each([true, false])( checkResult(pres.verify(pks, undefined, pp)); checkPresentationJson(pres, pks, undefined, pp); + // For KVAC, set secret key for full verification pks.set(3, skBddt16); checkResult(pres.verify(pks, undefined, pp)); checkPresentationJson(pres, pks, undefined, pp); diff --git a/tests/anonymous-credentials/presentation.spec.ts b/tests/anonymous-credentials/presentation.spec.ts index c8ba0cfa..b5c1154d 100644 --- a/tests/anonymous-credentials/presentation.spec.ts +++ b/tests/anonymous-credentials/presentation.spec.ts @@ -38,7 +38,16 @@ import { VBMembershipWitness, VerifiableEncryptionProtocol } from '../../src'; -import { Credential, CredentialBuilder, isKvac, PresentationBuilder, PublicKey, Scheme, SecretKey } from '../scheme'; +import { + Credential, + CredentialBuilder, + isKvac, + isPS, + PresentationBuilder, + PublicKey, + Scheme, + SecretKey +} from '../scheme'; import { areUint8ArraysEqual, checkResult, @@ -503,7 +512,7 @@ describe.each([true, false])( it('from a flat credential - `credential1`', () => { const builder1 = new PresentationBuilder(); - expect(builder1.addCredential(credential1, pk1)).toEqual(0); + expect(builder1.addCredential(credential1, isPS() ? pk1 : undefined)).toEqual(0); builder1.markAttributesRevealed(0, new Set(['credentialSubject.fname', 'credentialSubject.lname'])); const pres1 = builder1.finalize(); @@ -546,8 +555,8 @@ describe.each([true, false])( expect(base1ForSecretKey).toEqual(encodedBaseForSecretKey); const builder1 = new PresentationBuilder(); - expect(builder1.addCredential(credential1, pk1)).toEqual(0); - expect(builder1.addCredential(credential2, pk2)).toEqual(1); + expect(builder1.addCredential(credential1, isPS() ? pk1 : undefined)).toEqual(0); + expect(builder1.addCredential(credential2, isPS() ? pk2 : undefined)).toEqual(1); attributeNames1.set(0, ['credentialSubject.SSN', 'credentialSubject.email']); attributeNames1.set(1, ['credentialSubject.sensitive.userId']); expect( @@ -602,13 +611,18 @@ describe.each([true, false])( const credential = credBuilder.sign(sk, undefined, { requireSameFieldsAsSchema: false }); verifyCred(credential, pk, sk); const builder7 = new PresentationBuilder(); - expect(builder7.addCredential(credential, pk)).toEqual(0); + expect(builder7.addCredential(credential, isPS() ? pk : undefined)).toEqual(0); const pres7 = builder7.finalize(); expect(pres7.spec.credentials.length).toEqual(1); - checkResult(pres7.verify([pk])); + checkResult(pres7.verify([pk])); checkPresentationJson(pres7, [pk]); + + if (isKvac()) { + checkResult(pres7.verify([sk])); + checkPresentationJson(pres7, [sk]); + } } }); @@ -617,7 +631,7 @@ describe.each([true, false])( const nonce = randomFieldElement(); const builder1 = new PresentationBuilder(); - expect(builder1.addCredential(credential1, pk1)).toEqual(0); + expect(builder1.addCredential(credential1, isPS() ? pk1 : undefined)).toEqual(0); builder1.markAttributesRevealed(0, new Set(['credentialSubject.fname', 'credentialSubject.lname'])); builder1.context = ctx; @@ -634,7 +648,7 @@ describe.each([true, false])( expect(pres.verify([pk1]).verified).toBe(false); const builder2 = new PresentationBuilder(); - expect(builder2.addCredential(credential1, pk1)).toEqual(0); + expect(builder2.addCredential(credential1, isPS() ? pk1 : undefined)).toEqual(0); builder2.markAttributesRevealed(0, new Set(['credentialSubject.fname', 'credentialSubject.lname'])); builder2.context = ctx; @@ -655,7 +669,7 @@ describe.each([true, false])( expect(pres.verify([pk1]).verified).toBe(false); const builder3 = new PresentationBuilder(); - expect(builder3.addCredential(credential1, pk1)).toEqual(0); + expect(builder3.addCredential(credential1, isPS() ? pk1 : undefined)).toEqual(0); builder3.markAttributesRevealed(0, new Set(['credentialSubject.fname', 'credentialSubject.lname'])); builder3.nonce = nonce; @@ -672,7 +686,7 @@ describe.each([true, false])( it('from a nested credential - `credential2`', () => { const builder2 = new PresentationBuilder(); - expect(builder2.addCredential(credential2, pk2)).toEqual(0); + expect(builder2.addCredential(credential2, isPS() ? pk2 : undefined)).toEqual(0); builder2.markAttributesRevealed( 0, new Set([ @@ -706,7 +720,7 @@ describe.each([true, false])( it('from a nested credential with credential status - `credential3`', () => { const builder3 = new PresentationBuilder(); - expect(builder3.addCredential(credential3, pk3)).toEqual(0); + expect(builder3.addCredential(credential3, isPS() ? pk3 : undefined)).toEqual(0); builder3.markAttributesRevealed( 0, new Set([ @@ -752,8 +766,8 @@ describe.each([true, false])( it('from 2 credentials, `credential1` and `credential2`, and prove some attributes equal', () => { const builder4 = new PresentationBuilder(); - expect(builder4.addCredential(credential1, pk1)).toEqual(0); - expect(builder4.addCredential(credential2, pk2)).toEqual(1); + expect(builder4.addCredential(credential1, isPS() ? pk1 : undefined)).toEqual(0); + expect(builder4.addCredential(credential2, isPS() ? pk2 : undefined)).toEqual(1); builder4.markAttributesRevealed(0, new Set(['credentialSubject.fname', 'credentialSubject.lname'])); builder4.markAttributesRevealed( @@ -789,6 +803,12 @@ describe.each([true, false])( if (!isKvac()) { // Public keys in wrong order expect(pres4.verify([pk2, pk1]).verified).toEqual(false); + + // Public keys with wrong indices + const pks = new Map(); + pks.set(0, pk2); + pks.set(1, pk1); + expect(pres4.verify(pks).verified).toEqual(false); } checkResult(pres4.verify([pk1, pk2])); @@ -806,8 +826,8 @@ describe.each([true, false])( it('from 2 credentials, both having credential status', () => { const builder5 = new PresentationBuilder(); - expect(builder5.addCredential(credential3, pk3)).toEqual(0); - expect(builder5.addCredential(credential4, pk4)).toEqual(1); + expect(builder5.addCredential(credential3, isPS() ? pk3 : undefined)).toEqual(0); + expect(builder5.addCredential(credential4, isPS() ? pk4 : undefined)).toEqual(1); builder5.markAttributesRevealed( 0, @@ -885,10 +905,10 @@ describe.each([true, false])( it('from multiple credentials, some having credential status (revocable) and some not', () => { const builder6 = new PresentationBuilder(); - expect(builder6.addCredential(credential1, pk1)).toEqual(0); - expect(builder6.addCredential(credential2, pk2)).toEqual(1); - expect(builder6.addCredential(credential3, pk3)).toEqual(2); - expect(builder6.addCredential(credential4, pk4)).toEqual(3); + expect(builder6.addCredential(credential1, isPS() ? pk1 : undefined)).toEqual(0); + expect(builder6.addCredential(credential2, isPS() ? pk2 : undefined)).toEqual(1); + expect(builder6.addCredential(credential3, isPS() ? pk3 : undefined)).toEqual(2); + expect(builder6.addCredential(credential4, isPS() ? pk4 : undefined)).toEqual(3); builder6.markAttributesRevealed(0, new Set(['credentialSubject.fname', 'credentialSubject.lname'])); builder6.markAttributesRevealed( @@ -975,6 +995,7 @@ describe.each([true, false])( acc.set(2, accumulator3Pk); acc.set(3, accumulator4Pk); checkResult(pres6.verify([pk1, pk2, pk3, pk4], acc)); + checkResult(pres6.verify(new Map([[0, pk1], [1, pk2], [2, pk3], [3, pk4]]), acc)); const presJson = pres6.toJSON(); @@ -989,14 +1010,22 @@ describe.each([true, false])( checkSchemaFromJson(presJson.spec.credentials[3].schema, credential4.schema); checkPresentationJson(pres6, [pk1, pk2, pk3, pk4], acc); + checkPresentationJson(pres6, new Map([[0, pk1], [1, pk2], [2, pk3], [3, pk4]]), acc); + + if (isKvac()) { + checkResult(pres6.verify([sk1, sk2, sk3, sk4], acc)); + checkResult(pres6.verify(new Map([[0, sk1], [1, sk2], [2, sk3], [3, sk4]]), acc)); + checkPresentationJson(pres6, [sk1, sk2, sk3, sk4], acc); + checkPresentationJson(pres6, new Map([[0, sk1], [1, sk2], [2, sk3], [3, sk4]]), acc); + } }); it('from credential `credential1` and proving some attributes inequal to public values', () => { const builder = new PresentationBuilder(); - expect(builder.addCredential(credential1, pk1)).toEqual(0); - expect(builder.addCredential(credential2, pk2)).toEqual(1); - expect(builder.addCredential(credential7, pk1)).toEqual(2); + expect(builder.addCredential(credential1, isPS() ? pk1 : undefined)).toEqual(0); + expect(builder.addCredential(credential2, isPS() ? pk2 : undefined)).toEqual(1); + expect(builder.addCredential(credential7, isPS() ? pk1 : undefined)).toEqual(2); builder.markAttributesEqual([0, 'credentialSubject.SSN'], [1, 'credentialSubject.sensitive.SSN']); @@ -1077,7 +1106,7 @@ describe.each([true, false])( // ------------------- Presentation with 1 credential ----------------------------------------- console.time(`Proof generation over 1 credential and 3 bound-check in total using ${protocol}`); const builder7 = new PresentationBuilder(); - expect(builder7.addCredential(credential1, pk1)).toEqual(0); + expect(builder7.addCredential(credential1, isPS() ? pk1 : undefined)).toEqual(0); builder7.markAttributesRevealed(0, new Set(['credentialSubject.fname', 'credentialSubject.lname'])); @@ -1153,9 +1182,9 @@ describe.each([true, false])( console.time(`Proof generation over 3 credential and 5 bound-check in total using ${protocol}`); const builder8 = new PresentationBuilder(); - expect(builder8.addCredential(credential1, pk1)).toEqual(0); - expect(builder8.addCredential(credential2, pk2)).toEqual(1); - expect(builder8.addCredential(credential3, pk3)).toEqual(2); + expect(builder8.addCredential(credential1, isPS() ? pk1 : undefined)).toEqual(0); + expect(builder8.addCredential(credential2, isPS() ? pk2 : undefined)).toEqual(1); + expect(builder8.addCredential(credential3, isPS() ? pk3 : undefined)).toEqual(2); builder8.markAttributesRevealed(0, new Set(['credentialSubject.fname', 'credentialSubject.lname'])); builder8.markAttributesRevealed( @@ -1284,7 +1313,7 @@ describe.each([true, false])( function checkBoundsOnDates(protocol: BoundCheckProtocol, paramId?: string, provingParams?: BoundCheckParamType, verifyingParams?: BoundCheckParamType) { console.time(`Proof generation over 1 credential and 2 bound-check in total using ${protocol}`); const builder7 = new PresentationBuilder(); - expect(builder7.addCredential(credential7, pk1)).toEqual(0); + expect(builder7.addCredential(credential7, isPS() ? pk1 : undefined)).toEqual(0); builder7.markAttributesRevealed(0, new Set(['credentialSubject.name'])); @@ -1407,7 +1436,7 @@ describe.each([true, false])( const snarkPkId = 'random-3'; const builder9 = new PresentationBuilder(); - expect(builder9.addCredential(credential1, pk1)).toEqual(0); + expect(builder9.addCredential(credential1, isPS() ? pk1 : undefined)).toEqual(0); builder9.markAttributesRevealed(0, new Set(['credentialSubject.fname', 'credentialSubject.lname'])); builder9.verifiablyEncrypt( @@ -1469,9 +1498,9 @@ describe.each([true, false])( const commKeyNew = ckNew.decompress(); const builder10 = new PresentationBuilder(); - expect(builder10.addCredential(credential1, pk1)).toEqual(0); - expect(builder10.addCredential(credential2, pk2)).toEqual(1); - expect(builder10.addCredential(credential3, pk3)).toEqual(2); + expect(builder10.addCredential(credential1, isPS() ? pk1 : undefined)).toEqual(0); + expect(builder10.addCredential(credential2, isPS() ? pk2 : undefined)).toEqual(1); + expect(builder10.addCredential(credential3, isPS() ? pk3 : undefined)).toEqual(2); builder10.markAttributesRevealed(0, new Set(['credentialSubject.fname', 'credentialSubject.lname'])); builder10.markAttributesRevealed( @@ -1602,9 +1631,9 @@ describe.each([true, false])( const commKey = ck.decompress(); const builder11 = new PresentationBuilder(); - expect(builder11.addCredential(credential1, pk1)).toEqual(0); - expect(builder11.addCredential(credential2, pk2)).toEqual(1); - expect(builder11.addCredential(credential3, pk3)).toEqual(2); + expect(builder11.addCredential(credential1, isPS() ? pk1 : undefined)).toEqual(0); + expect(builder11.addCredential(credential2, isPS() ? pk2 : undefined)).toEqual(1); + expect(builder11.addCredential(credential3, isPS() ? pk3 : undefined)).toEqual(2); builder11.markAttributesRevealed(0, new Set(['credentialSubject.fname', 'credentialSubject.lname'])); builder11.markAttributesRevealed( diff --git a/tests/anonymous-credentials/utils.ts b/tests/anonymous-credentials/utils.ts index 65099ff4..9d313000 100644 --- a/tests/anonymous-credentials/utils.ts +++ b/tests/anonymous-credentials/utils.ts @@ -665,10 +665,11 @@ export function getDecodedBoundedPseudonym( * @param predicateParams * @param circomOutputs * @param circomOutputsMultiCred + * @returns The new presentation created from deserializing the serialized presentation */ export function checkPresentationJson( pres: Presentation, - pks: Map | CredentialVerificationParam[], + pks: Map | CredentialVerificationParam[], accumulatorPublicKeys?: Map, predicateParams?: Map, circomOutputs?: Map, @@ -682,6 +683,12 @@ export function checkPresentationJson( return recreatedPres; } +/** + * Function used to write serialized params, keys, schemas, credentials and presentations which are later used for testing + * backward compatibility. This is usually called in tests when new versions of these objects are created + * @param obj + * @param fileName + */ export function writeSerializedObject(obj: any, fileName: string) { let objBytes; if (obj instanceof BytearrayWrapper) {