From 40452b25fb36f9ad3cbab703c38eb5633cc6a4f8 Mon Sep 17 00:00:00 2001 From: Sam Hellawell Date: Thu, 28 Sep 2023 23:23:20 +0100 Subject: [PATCH 01/10] Update anoncreds JSON-LD contexts to support bounds property in proof Signed-off-by: Sam Hellawell --- src/utils/vc/contexts/dock-bbs-v1.json | 4 ++++ src/utils/vc/contexts/dock-bbs23-v1.json | 4 ++++ src/utils/vc/contexts/dock-ps-v1.json | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/src/utils/vc/contexts/dock-bbs-v1.json b/src/utils/vc/contexts/dock-bbs-v1.json index c369a1bfd..8e0a328e6 100644 --- a/src/utils/vc/contexts/dock-bbs-v1.json +++ b/src/utils/vc/contexts/dock-bbs-v1.json @@ -48,6 +48,10 @@ }, "domain": "https://ld.dock.io/security#domain", "context": "https://ld.dock.io/security#context", + "bounds": { + "@id": "https://ld.dock.io/security#bounds", + "@context": {"@vocab": "https://ld.dock.io/security/bounds"} + }, "sigType": "https://ld.dock.io/security#sigType", "boundedPseudonyms": "https://ld.dock.io/security#boundedPseudonyms", "unboundedPseudonyms": "https://ld.dock.io/security#unboundedPseudonyms", diff --git a/src/utils/vc/contexts/dock-bbs23-v1.json b/src/utils/vc/contexts/dock-bbs23-v1.json index 335325ec5..e412645b9 100644 --- a/src/utils/vc/contexts/dock-bbs23-v1.json +++ b/src/utils/vc/contexts/dock-bbs23-v1.json @@ -48,6 +48,10 @@ }, "domain": "https://ld.dock.io/security#domain", "context": "https://ld.dock.io/security#context", + "bounds": { + "@id": "https://ld.dock.io/security#bounds", + "@context": {"@vocab": "https://ld.dock.io/security/bounds"} + }, "sigType": "https://ld.dock.io/security#sigType", "boundedPseudonyms": "https://ld.dock.io/security#boundedPseudonyms", "unboundedPseudonyms": "https://ld.dock.io/security#unboundedPseudonyms", diff --git a/src/utils/vc/contexts/dock-ps-v1.json b/src/utils/vc/contexts/dock-ps-v1.json index 1b476f6fb..9213787ff 100644 --- a/src/utils/vc/contexts/dock-ps-v1.json +++ b/src/utils/vc/contexts/dock-ps-v1.json @@ -48,6 +48,10 @@ }, "domain": "https://ld.dock.io/security#domain", "context": "https://ld.dock.io/security#context", + "bounds": { + "@id": "https://ld.dock.io/security#bounds", + "@context": {"@vocab": "https://ld.dock.io/security/bounds"} + }, "sigType": "https://ld.dock.io/security#sigType", "boundedPseudonyms": "https://ld.dock.io/security#boundedPseudonyms", "unboundedPseudonyms": "https://ld.dock.io/security#unboundedPseudonyms", From f4394672ef1299a1dd5c78239390f93e9369d324 Mon Sep 17 00:00:00 2001 From: Sam Hellawell Date: Thu, 28 Sep 2023 23:23:38 +0100 Subject: [PATCH 02/10] Pass anoncreds verify params to suite through verifyCredential Signed-off-by: Sam Hellawell --- src/utils/vc/credentials.js | 22 ++++++++++++++----- .../crypto/common/DockCryptoSignatureProof.js | 14 ++++++++++-- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/utils/vc/credentials.js b/src/utils/vc/credentials.js index f5028066e..feb334e49 100644 --- a/src/utils/vc/credentials.js +++ b/src/utils/vc/credentials.js @@ -233,6 +233,12 @@ export async function verifyCredential( controller = null, suite = [], verifyDates = true, + + // Anoncreds params + predicateParams = null, + accumulatorPublicKeys = null, + circomOutputs = null, + blindedAttributesCircomOutputs = null, } = {}, ) { if (documentLoader && resolver) { @@ -321,17 +327,21 @@ export async function verifyCredential( return { verified }; } + // Specify certain parameters for anoncreds + const anoncredsParams = { + accumulatorPublicKeys, predicateParams, circomOutputs, blindedAttributesCircomOutputs, + }; const fullSuite = [ new Ed25519Signature2018(), new EcdsaSepc256k1Signature2019(), new Sr25519Signature2020(), - new Bls12381BBSSignatureDock2022(), - new Bls12381BBSSignatureProofDock2022(), - new Bls12381BBSSignatureDock2023(), - new Bls12381BBSSignatureProofDock2023(), - new Bls12381PSSignatureDock2023(), - new Bls12381PSSignatureProofDock2023(), new JsonWebSignature2020(), + new Bls12381BBSSignatureDock2022(anoncredsParams), + new Bls12381BBSSignatureProofDock2022(anoncredsParams), + new Bls12381BBSSignatureDock2023(anoncredsParams), + new Bls12381BBSSignatureProofDock2023(anoncredsParams), + new Bls12381PSSignatureDock2023(anoncredsParams), + new Bls12381PSSignatureProofDock2023(anoncredsParams), ...suite, ]; diff --git a/src/utils/vc/crypto/common/DockCryptoSignatureProof.js b/src/utils/vc/crypto/common/DockCryptoSignatureProof.js index 9bce751f8..1f773a514 100644 --- a/src/utils/vc/crypto/common/DockCryptoSignatureProof.js +++ b/src/utils/vc/crypto/common/DockCryptoSignatureProof.js @@ -46,6 +46,10 @@ export default withExtendedStaticProperties( }; this.verificationMethod = verificationMethod; + this.accumulatorPublicKeys = options.accumulatorPublicKeys; + this.predicateParams = options.predicateParams; + this.circomOutputs = options.circomOutputs; + this.blindedAttributesCircomOutputs = options.blindedAttributesCircomOutputs; } async verifyProof({ @@ -70,7 +74,12 @@ export default withExtendedStaticProperties( return new this.constructor.Signature.KeyPair.PublicKey(pkRaw); }); - if (!recreatedPres.verify(pks)) { + const { + accumulatorPublicKeys, predicateParams, + circomOutputs, blindedAttributesCircomOutputs, + } = this; + + if (!recreatedPres.verify(pks, accumulatorPublicKeys, predicateParams, circomOutputs, blindedAttributesCircomOutputs)) { throw new Error('Invalid signature'); } @@ -96,7 +105,7 @@ export default withExtendedStaticProperties( } = document; return { - version: '0.1.0', + version: '0.1.0', // TODO: should this be retrieved from the doc somehow? nonce: proof.nonce, context: proof.context, spec: { @@ -104,6 +113,7 @@ export default withExtendedStaticProperties( { sigType: proof.sigType, version: proof.version, + bounds: proof.bounds, schema: JSON.stringify(credentialSchema), revealedAttributes: { proof: { From 30c2f781432b2b01e1f9d31e9680f87a72845b28 Mon Sep 17 00:00:00 2001 From: Sam Hellawell Date: Thu, 28 Sep 2023 23:24:22 +0100 Subject: [PATCH 03/10] Set derived presentation bounds property, pass anoncreds verify options Signed-off-by: Sam Hellawell --- src/presentation.js | 1 + src/utils/vc/presentations.js | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/presentation.js b/src/presentation.js index 002564149..6d2d74869 100644 --- a/src/presentation.js +++ b/src/presentation.js @@ -206,6 +206,7 @@ export default class Presentation { unboundedPseudonyms: presentation.spec.unboundedPseudonyms, version: credential.version, sigType: credential.sigType, + bounds: credential.bounds, }, }; }); diff --git a/src/utils/vc/presentations.js b/src/utils/vc/presentations.js index 53189fa8b..99477bd61 100644 --- a/src/utils/vc/presentations.js +++ b/src/utils/vc/presentations.js @@ -265,6 +265,10 @@ export function isAnoncreds(presentation) { export async function verifyAnoncreds(presentation, options = {}) { const documentLoader = options.documentLoader || defaultDocumentLoader(options.resolver); + const { + predicateParams, accumulatorPublicKeys, + circomOutputs, blindedAttributesCircomOutputs, + } = options; const keyDocuments = await Promise.all( presentation.spec.credentials.map((c, idx) => { @@ -315,5 +319,5 @@ export async function verifyAnoncreds(presentation, options = {}) { } }); - return recreatedPres.verify(pks); + return recreatedPres.verify(pks, accumulatorPublicKeys, predicateParams, circomOutputs, blindedAttributesCircomOutputs); } From a482b6bd4e38eac645ed2f8ce38eb9c5fb04147d Mon Sep 17 00:00:00 2001 From: Sam Hellawell Date: Thu, 28 Sep 2023 23:25:02 +0100 Subject: [PATCH 04/10] Bounds check presentation and derived credentials tests --- .../anoncreds/derived-credentials.test.js | 113 +++++++++++++++++- .../anoncreds/presentation.test.js | 76 +++++++++++- 2 files changed, 186 insertions(+), 3 deletions(-) diff --git a/tests/integration/anoncreds/derived-credentials.test.js b/tests/integration/anoncreds/derived-credentials.test.js index 11649ee29..3f303e085 100644 --- a/tests/integration/anoncreds/derived-credentials.test.js +++ b/tests/integration/anoncreds/derived-credentials.test.js @@ -1,6 +1,7 @@ import { randomAsHex } from '@polkadot/util-crypto'; import { stringToU8a, u8aToHex } from '@polkadot/util'; -import { initializeWasm } from '@docknetwork/crypto-wasm-ts'; +import { error } from 'console'; +import { initializeWasm, BoundCheckSnarkSetup } from '@docknetwork/crypto-wasm-ts'; import { DockAPI } from '../../../src'; import { FullNodeEndpoint, @@ -156,7 +157,7 @@ for (const { ); }, 30000); - async function createAndVerifyPresentation(credentials) { + async function createAndVerifyPresentation(credentials, verifyOptions = {}) { const holderKey = getKeyDoc( holder3DID, dock.keyring.addFromUri(holder3KeySeed, null, 'sr25519'), @@ -202,6 +203,7 @@ for (const { challenge: chal, domain, resolver, + ...verifyOptions, }); expect(result.verified).toBe(true); @@ -305,6 +307,113 @@ for (const { await createAndVerifyPresentation(credentials); }, 30000); + test('Holder creates a derived verifiable credential from a credential with range proofs', async () => { + const provingKeyId = 'provingKeyId'; + const pk = BoundCheckSnarkSetup(); + const provingKey = pk.decompress(); + + const issuerKey = getKeyDoc(did1, keypair, keypair.type, keypair.id); + const unsignedCred = { + ...credentialJSON, + issuer: did1, + }; + + const presentationOptions = { + nonce: stringToU8a('noncetest'), + context: 'my context', + }; + + // Create W3C credential + const credential = await issueCredential(issuerKey, unsignedCred); + + // Begin to derive a credential from the above issued one + const presentationInstance = new Presentation(); + const idx = await presentationInstance.addCredentialToPresent( + credential, + { resolver }, + ); + + // NOTE: revealing subject type because of JSON-LD processing for this certain credential + // you may not always need to do this depending on your JSON-LD contexts + await presentationInstance.addAttributeToReveal(idx, [ + 'credentialSubject.type.0', + ]); + await presentationInstance.addAttributeToReveal(idx, [ + 'credentialSubject.type.1', + ]); + + // Enforce LPR number to be between values aswell as revealed + // NOTE: unlike other tests, we cannot "reveal" this value and enforce bounds at the same time! + presentationInstance.presBuilder.enforceBounds( + idx, + 'credentialSubject.lprNumber', + 1233, + 1235, + provingKeyId, + provingKey, + ); + + // Enforce issuance date to be between values + // NOTE: we dont need to set proving key value here (must still set ID though!) as its done above, should pass undefined + presentationInstance.presBuilder.enforceBounds( + idx, + 'issuanceDate', + new Date('2019-10-01'), + new Date('2020-01-01'), + provingKeyId, + undefined, + ); + + // Derive a W3C Verifiable Credential JSON from the above presentation + const credentials = await presentationInstance.deriveCredentials( + presentationOptions, + ); + expect(credentials.length).toEqual(1); + expect(credentials[0].proof).toBeDefined(); + expect(credentials[0].proof.bounds).toBeDefined(); + expect(credentials[0].proof.bounds).toEqual({ + issuanceDate: { + min: 1569888000000, + max: 1577836800000, + paramId: 'provingKeyId', + protocol: 'LegoGroth16', + }, + credentialSubject: { + lprNumber: { + min: 1233, + max: 1235, + paramId: 'provingKeyId', + protocol: 'LegoGroth16', + }, + }, + }); + expect(credentials[0]).toHaveProperty('credentialSubject'); + expect(credentials[0].credentialSubject).toMatchObject( + expect.objectContaining({ + type: unsignedCred.credentialSubject.type, + }), + ); + + const reconstructedPres = convertToPresentation(credentials[0]); + expect(reconstructedPres.proof).toBeDefined(); + expect(reconstructedPres.spec.credentials[0].bounds).toEqual(credentials[0].proof.bounds); + + // Setup predicate params with the verifying key for range proofs + const predicateParams = new Map(); + predicateParams.set(provingKeyId, pk.getVerifyingKey()); + + // Try to verify the derived credential alone + const credentialResult = await verifyCredential(credentials[0], { + resolver, + predicateParams, + }); + expect(credentialResult.error).toBe(undefined); + expect(credentialResult.verified).toBe(true); + + // Create a VP and verify it from this credential + await createAndVerifyPresentation(credentials, { predicateParams }); + }, 30000); + afterAll(async () => { await dock.disconnect(); }, 10000); diff --git a/tests/integration/anoncreds/presentation.test.js b/tests/integration/anoncreds/presentation.test.js index cf5262624..073181543 100644 --- a/tests/integration/anoncreds/presentation.test.js +++ b/tests/integration/anoncreds/presentation.test.js @@ -1,7 +1,7 @@ import { randomAsHex } from '@polkadot/util-crypto'; import { u8aToHex, stringToU8a } from '@polkadot/util'; import b58 from 'bs58'; -import { initializeWasm } from '@docknetwork/crypto-wasm-ts'; +import { BoundCheckSnarkSetup, initializeWasm } from '@docknetwork/crypto-wasm-ts'; import { DockAPI } from '../../../src'; import { FullNodeEndpoint, @@ -132,6 +132,80 @@ for (const { keypair.id = publicKey[1].id; }, 30000); + test('expect to range proofs', async () => { + const provingKeyId = 'provingKeyId'; + const pk = BoundCheckSnarkSetup(); + const provingKey = pk.decompress(); + const presentationInstance = new Presentation(); + const issuerKey = getKeyDoc(did1, keypair, keypair.type, keypair.id); + const unsignedCred = { + ...credentialJSON, + issuer: did1, + }; + + const credential = await issueCredential(issuerKey, unsignedCred); + + const idx = await presentationInstance.addCredentialToPresent( + credential, + { resolver }, + ); + + await presentationInstance.addAttributeToReveal(idx, [ + 'credentialSubject.lprNumber', + ]); + + // Enforce issuance date to be between values + presentationInstance.presBuilder.enforceBounds( + idx, + 'issuanceDate', + new Date('2019-10-01'), + new Date('2020-01-01'), + provingKeyId, + provingKey, + ); + + const presentation = await presentationInstance.createPresentation(); + + expect(presentation.spec.credentials[0].bounds).toBeDefined(); + expect(presentation.spec.credentials[0].bounds).toEqual({ + issuanceDate: { + min: 1569888000000, + max: 1577836800000, + paramId: 'provingKeyId', + protocol: 'LegoGroth16', + }, + }); + + expect( + presentation.spec.credentials[0].revealedAttributes, + ).toHaveProperty('credentialSubject'); + expect( + presentation.spec.credentials[0].revealedAttributes.credentialSubject, + ).toHaveProperty('lprNumber', 1234); + + // Ensure verificationMethod & type is revealed always + expect( + presentation.spec.credentials[0].revealedAttributes.proof, + ).toBeDefined(); + expect( + presentation.spec.credentials[0].revealedAttributes.proof, + ).toHaveProperty( + 'verificationMethod', + credential.proof.verificationMethod, + ); + expect( + presentation.spec.credentials[0].revealedAttributes.proof, + ).toHaveProperty('type', credential.proof.type); + + // Setup predicate params with the verifying key for range proofs + const predicateParams = new Map(); + predicateParams.set(provingKeyId, pk.getVerifyingKey()); + + // Verify the presentation, note that we must pass predicateParams with the verification key + const { verified } = await verifyPresentation(presentation, { resolver, predicateParams }); + expect(verified).toEqual(true); + }, 30000); + test('expect to reveal specified attributes', async () => { const presentationInstance = new Presentation(); const issuerKey = getKeyDoc(did1, keypair, keypair.type, keypair.id); From b476b253f19e8d714ab70b88028e8880fa2bfe70 Mon Sep 17 00:00:00 2001 From: Sam Hellawell Date: Thu, 28 Sep 2023 23:25:19 +0100 Subject: [PATCH 05/10] Version bump Signed-off-by: Sam Hellawell --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d719615b6..590c4c46d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@docknetwork/sdk", - "version": "6.5.0", + "version": "6.5.1", "main": "index.js", "license": "MIT", "repository": { From 7c7640f887a80f31b30365146278464d577afe50 Mon Sep 17 00:00:00 2001 From: Sam Hellawell Date: Thu, 28 Sep 2023 23:45:03 +0100 Subject: [PATCH 06/10] log potential error Signed-off-by: Sam Hellawell --- tests/integration/anoncreds/derived-credentials.test.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/integration/anoncreds/derived-credentials.test.js b/tests/integration/anoncreds/derived-credentials.test.js index 3f303e085..77edd570d 100644 --- a/tests/integration/anoncreds/derived-credentials.test.js +++ b/tests/integration/anoncreds/derived-credentials.test.js @@ -407,6 +407,9 @@ for (const { resolver, predicateParams, }); + if (credentialResult.error) { + console.log('credentialResult.error', JSON.stringify(credentialResult.error, null, 2)); + } expect(credentialResult.error).toBe(undefined); expect(credentialResult.verified).toBe(true); From be2dd12f6f8d3534ac37fa0c01e153c7c03a4710 Mon Sep 17 00:00:00 2001 From: Sam Hellawell Date: Fri, 29 Sep 2023 00:03:38 +0100 Subject: [PATCH 07/10] try increase test timeout Signed-off-by: Sam Hellawell --- tests/integration/anoncreds/derived-credentials.test.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/integration/anoncreds/derived-credentials.test.js b/tests/integration/anoncreds/derived-credentials.test.js index 77edd570d..181764b24 100644 --- a/tests/integration/anoncreds/derived-credentials.test.js +++ b/tests/integration/anoncreds/derived-credentials.test.js @@ -1,6 +1,5 @@ import { randomAsHex } from '@polkadot/util-crypto'; import { stringToU8a, u8aToHex } from '@polkadot/util'; -import { error } from 'console'; import { initializeWasm, BoundCheckSnarkSetup } from '@docknetwork/crypto-wasm-ts'; import { DockAPI } from '../../../src'; import { @@ -415,7 +414,7 @@ for (const { // Create a VP and verify it from this credential await createAndVerifyPresentation(credentials, { predicateParams }); - }, 30000); + }, 60000); afterAll(async () => { await dock.disconnect(); From 8963de9654199edff7bc6ae83e42ef606f08d21d Mon Sep 17 00:00:00 2001 From: Sam Hellawell Date: Fri, 29 Sep 2023 00:05:47 +0100 Subject: [PATCH 08/10] use node 18 for integration tests Signed-off-by: Sam Hellawell --- .github/workflows/integrations-tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/integrations-tests.yml b/.github/workflows/integrations-tests.yml index 530a38c93..8a68f8def 100644 --- a/.github/workflows/integrations-tests.yml +++ b/.github/workflows/integrations-tests.yml @@ -15,7 +15,7 @@ jobs: run_daemon: true - uses: actions/setup-node@v1 with: - node-version: '16.x' + node-version: '18.x' - run: yarn install --frozen-lockfile --ignore-scripts - run: yarn build - run: yarn test-with-node @@ -32,7 +32,7 @@ jobs: run_daemon: true - uses: actions/setup-node@v1 with: - node-version: '16.x' + node-version: '18.x' - run: yarn install --frozen-lockfile --ignore-scripts - run: yarn build - run: yarn test-with-node @@ -49,7 +49,7 @@ jobs: run_daemon: true - uses: actions/setup-node@v1 with: - node-version: '16.x' + node-version: '18.x' - run: yarn install --frozen-lockfile --ignore-scripts - run: yarn build - run: yarn test-with-node From 24e127078f01bc1debefc88c6dcdc9ffe10429d4 Mon Sep 17 00:00:00 2001 From: Sam Hellawell Date: Fri, 29 Sep 2023 00:17:28 +0100 Subject: [PATCH 09/10] increase range proof test time Signed-off-by: Sam Hellawell --- tests/integration/anoncreds/presentation.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/anoncreds/presentation.test.js b/tests/integration/anoncreds/presentation.test.js index 073181543..336eb4e42 100644 --- a/tests/integration/anoncreds/presentation.test.js +++ b/tests/integration/anoncreds/presentation.test.js @@ -204,7 +204,7 @@ for (const { // Verify the presentation, note that we must pass predicateParams with the verification key const { verified } = await verifyPresentation(presentation, { resolver, predicateParams }); expect(verified).toEqual(true); - }, 30000); + }, 60000); test('expect to reveal specified attributes', async () => { const presentationInstance = new Presentation(); From 14aa2cb45c605d8bc13c69b6cca0f4e6e5e987a4 Mon Sep 17 00:00:00 2001 From: Sam Hellawell Date: Fri, 29 Sep 2023 00:39:20 +0100 Subject: [PATCH 10/10] revert node version change Signed-off-by: Sam Hellawell --- .github/workflows/integrations-tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/integrations-tests.yml b/.github/workflows/integrations-tests.yml index 8a68f8def..530a38c93 100644 --- a/.github/workflows/integrations-tests.yml +++ b/.github/workflows/integrations-tests.yml @@ -15,7 +15,7 @@ jobs: run_daemon: true - uses: actions/setup-node@v1 with: - node-version: '18.x' + node-version: '16.x' - run: yarn install --frozen-lockfile --ignore-scripts - run: yarn build - run: yarn test-with-node @@ -32,7 +32,7 @@ jobs: run_daemon: true - uses: actions/setup-node@v1 with: - node-version: '18.x' + node-version: '16.x' - run: yarn install --frozen-lockfile --ignore-scripts - run: yarn build - run: yarn test-with-node @@ -49,7 +49,7 @@ jobs: run_daemon: true - uses: actions/setup-node@v1 with: - node-version: '18.x' + node-version: '16.x' - run: yarn install --frozen-lockfile --ignore-scripts - run: yarn build - run: yarn test-with-node