diff --git a/__tests__/AnonymousVC.spec.ts b/__tests__/AnonymousVC.spec.ts new file mode 100644 index 0000000..1bb4c04 --- /dev/null +++ b/__tests__/AnonymousVC.spec.ts @@ -0,0 +1,119 @@ +/* + * Copyright 2020 - MATTR Limited + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + exampleBls12381KeyPair, + customLoader, + testAnonymousVcDocument, + testRevealAnonymousVcDocument, + testNestedRevealDocument, + testNestedRevealFullDocument, + testNestedAnonymousVcDocument +} from "./__fixtures__"; + +import jsigs from "jsonld-signatures"; +import { + Bls12381G2KeyPair, + BbsBlsSignatureProof2020, + BbsBlsSignature2020, + deriveProof +} from "../src/index"; +import { getProofs } from "../src/utilities"; + +const key = new Bls12381G2KeyPair(exampleBls12381KeyPair); + +const signDeriveVerify = async (vc: any, reveal: any, subject: any) => { + // Issuer issues VC + const signedVc = await jsigs.sign(vc, { + suite: new BbsBlsSignature2020({ key }), + purpose: new jsigs.purposes.AssertionProofPurpose(), + documentLoader: customLoader + }); + expect(signedVc).toBeDefined(); + + // Holder verifies VC + const verifiedVc = await jsigs.verify(signedVc, { + suite: new BbsBlsSignature2020(), + purpose: new jsigs.purposes.AssertionProofPurpose(), + documentLoader: customLoader + }); + expect(verifiedVc.verified).toBeTruthy(); + + // Holder derives Proof + const derivedProof = await deriveProof(signedVc, reveal, { + suite: new BbsBlsSignatureProof2020(), + documentLoader: customLoader + }); + expect(derivedProof.credentialSubject).toEqual(subject); + + // Verifier verifies proof + const { document, proofs } = await getProofs({ + document: derivedProof, + proofType: BbsBlsSignatureProof2020.proofType, + documentLoader: customLoader + }); + const suite = new BbsBlsSignatureProof2020(); + const result = await suite.verifyProof({ + document, + proof: proofs[0], + documentLoader: customLoader, + purpose: new jsigs.purposes.AssertionProofPurpose() + }); + expect(result.verified).toBeTruthy(); +}; + +describe("anonymous verifiable credentials with blank node identifiers", () => { + it("should sign, derive proof, and verify proof on anonymous verifiable credential", async () => { + await signDeriveVerify( + testAnonymousVcDocument, + testRevealAnonymousVcDocument, + { + id: "urn:bnid:_:c14n1", + type: ["Person", "PermanentResident"], + commuterClassification: "C1" + } + ); + }); + + it("should sign, derive proof, and verify proof on anonymous nested and partially revealed verifiable credential", async () => { + await signDeriveVerify( + testNestedAnonymousVcDocument, + testNestedRevealDocument, + { + id: "urn:bnid:_:c14n2", + degree: { + id: "urn:bnid:_:c14n1", + type: "BachelorDegree", + name: "Bachelor of Science and Arts" + } + } + ); + }); + + it("should sign, derive proof, and verify proof on anonymous nested and fully revealed verifiable credential", async () => { + await signDeriveVerify( + testNestedAnonymousVcDocument, + testNestedRevealFullDocument, + { + id: "urn:bnid:_:c14n2", + degree: { + id: "urn:bnid:_:c14n1", + type: "BachelorDegree", + name: "Bachelor of Science and Arts", + degreeType: "Underwater Basket Weaving" + }, + college: "Contoso University" + } + ); + }); +}); diff --git a/__tests__/__fixtures__/data/test_anonymous_vc.json b/__tests__/__fixtures__/data/test_anonymous_vc.json new file mode 100644 index 0000000..5bfcefb --- /dev/null +++ b/__tests__/__fixtures__/data/test_anonymous_vc.json @@ -0,0 +1,27 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/citizenship/v1", + "https://w3id.org/security/v3-unstable" + ], + "type": ["VerifiableCredential", "PermanentResidentCard"], + "issuer": "did:example:489398593", + "identifier": "83627465", + "name": "Permanent Resident Card", + "description": "Government of Example Permanent Resident Card.", + "issuanceDate": "2019-12-03T12:19:52Z", + "expirationDate": "2029-12-03T12:19:52Z", + "credentialSubject": { + "type": ["PermanentResident", "Person"], + "givenName": "JOHN", + "familyName": "SMITH", + "gender": "Male", + "image": "data:image/png;base64,iVBORw0KGgokJggg==", + "residentSince": "2015-01-01", + "lprCategory": "C09", + "lprNumber": "999-999-999", + "commuterClassification": "C1", + "birthCountry": "Bahamas", + "birthDate": "1958-07-17" + } +} diff --git a/__tests__/__fixtures__/data/test_anonymous_vc_reveal_document.json b/__tests__/__fixtures__/data/test_anonymous_vc_reveal_document.json new file mode 100644 index 0000000..8d7cc3c --- /dev/null +++ b/__tests__/__fixtures__/data/test_anonymous_vc_reveal_document.json @@ -0,0 +1,17 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/citizenship/v1", + "https://w3id.org/security/v3-unstable" + ], + "type": ["VerifiableCredential", "PermanentResidentCard"], + "@explicit": true, + "issuer": {}, + "name": {}, + "description": {}, + "credentialSubject": { + "type": ["PermanentResident", "Person"], + "@explicit": true, + "commuterClassification": {} + } +} diff --git a/__tests__/__fixtures__/data/test_nested_anonymous_vc_document.json b/__tests__/__fixtures__/data/test_nested_anonymous_vc_document.json new file mode 100644 index 0000000..ec15d2c --- /dev/null +++ b/__tests__/__fixtures__/data/test_nested_anonymous_vc_document.json @@ -0,0 +1,18 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/2018/credentials/examples/v1", + "https://www.w3id.org/security/v3-unstable" + ], + "type": ["VerifiableCredential", "UniversityDegreeCredential"], + "issuer": "did:example:489398593", + "issuanceDate": "2020-03-10T04:24:12.164Z", + "credentialSubject": { + "degree": { + "type": "BachelorDegree", + "name": "Bachelor of Science and Arts", + "degreeType": "Underwater Basket Weaving" + }, + "college": "Contoso University" + } +} diff --git a/__tests__/__fixtures__/index.ts b/__tests__/__fixtures__/index.ts index d05de4d..96e3de0 100644 --- a/__tests__/__fixtures__/index.ts +++ b/__tests__/__fixtures__/index.ts @@ -36,6 +36,9 @@ import testNestedVcDocument from "./data/test_nested_vc_document.json"; import testSignedNestedVcDocument from "./data/test_signed_nested_vc_document.json"; import testProofNestedVcDocument from "./data/test_proof_nested_vc_document.json"; import testPartialProofNestedVcDocument from "./data/test_partial_proof_nested_vc_document.json"; +import testAnonymousVcDocument from "./data/test_anonymous_vc.json"; +import testRevealAnonymousVcDocument from "./data/test_anonymous_vc_reveal_document.json"; +import testNestedAnonymousVcDocument from "./data/test_nested_anonymous_vc_document.json"; export { exampleBls12381KeyPair, @@ -61,5 +64,8 @@ export { testSignedNestedVcDocument, testProofNestedVcDocument, testPartialProofNestedVcDocument, - customLoader + customLoader, + testAnonymousVcDocument, + testRevealAnonymousVcDocument, + testNestedAnonymousVcDocument }; diff --git a/src/BbsBlsSignatureProof2020.ts b/src/BbsBlsSignatureProof2020.ts index 8ef7b7f..c5df29d 100644 --- a/src/BbsBlsSignatureProof2020.ts +++ b/src/BbsBlsSignatureProof2020.ts @@ -126,19 +126,8 @@ export class BbsBlsSignatureProof2020 extends suites.LinkedDataProof { // Transform any blank node identifiers for the input // document statements into actual node identifiers // e.g _:c14n0 => urn:bnid:_:c14n0 - const transformedInputDocumentStatements = documentStatements.map( - element => { - if (element.includes("_:c14n")) { - const prefixIndex = element.indexOf("_:c14n"); - const spaceIndex = element.indexOf(" ", prefixIndex); - return element.replace( - element.substring(prefixIndex, spaceIndex), - `` - ); - } - - return element; - } + const transformedInputDocumentStatements = documentStatements.map(element => + element.replace(/(_:c14n[0-9]+)/g, "") ); //Transform the resulting RDF statements back into JSON-LD @@ -263,18 +252,9 @@ export class BbsBlsSignatureProof2020 extends suites.LinkedDataProof { // Transform the blank node identifier placeholders for the document statements // back into actual blank node identifiers - const transformedDocumentStatements = documentStatements.map(element => { - const prefixString = "", prefixIndex); - return element.replace( - element.substring(prefixIndex, closingIndex + 1), - element.substring(prefixIndex + prefixString.length, closingIndex) - ); - } - return element; - }); + const transformedDocumentStatements = documentStatements.map(element => + element.replace(//g, "$1") + ); // Combine all the statements to be verified const statementsToVerify: Uint8Array[] = proofStatements