From f40249441fb9e936fc79044bc367ef81790a91cf Mon Sep 17 00:00:00 2001 From: Kyle Den Hartog Date: Mon, 5 Oct 2020 17:11:12 +1300 Subject: [PATCH] feat: adds support for providing a proofDocument with multiple proofs see issue #79 for details --- __tests__/deriveProof.spec.ts | 81 ++++++++++++++++++++++++++++++++++- src/deriveProof.ts | 58 +++++++++++++++++++++---- 2 files changed, 130 insertions(+), 9 deletions(-) diff --git a/__tests__/deriveProof.spec.ts b/__tests__/deriveProof.spec.ts index e4766b4..64173ea 100644 --- a/__tests__/deriveProof.spec.ts +++ b/__tests__/deriveProof.spec.ts @@ -16,11 +16,16 @@ import { testSignedDocument, customLoader, testSignedVcDocument, - testRevealVcDocument + testRevealVcDocument, + testSignedDocumentMultiProofs, + testSignedDocumentMultiDifProofs, + testSignedDocumentEd25519 } from "./__fixtures__"; import { BbsBlsSignatureProof2020, deriveProof } from "../src/index"; +import jsigs from "jsonld-signatures"; + describe("BbsBlsSignatureProof2020", () => { it("should derive proof", async () => { const result = await deriveProof(testSignedDocument, testRevealDocument, { @@ -49,4 +54,78 @@ describe("BbsBlsSignatureProof2020", () => { ); expect(result).toBeDefined(); }); + + it("should derive proofs from a document featuring multiple supporting proofs", async () => { + const result = await deriveProof( + testSignedDocumentMultiProofs, + testRevealDocument, + { + suite: new BbsBlsSignatureProof2020(), + documentLoader: customLoader + } + ); + expect(result).toBeDefined(); + expect(result.proof.length).toBe(2); + }); + + it("should derive proofs from a document featuring multiple different proofs with at least 1 supporting proof", async () => { + const result = await deriveProof( + testSignedDocumentMultiDifProofs, + testRevealDocument, + { + suite: new BbsBlsSignatureProof2020(), + documentLoader: customLoader + } + ); + expect(result).toBeDefined(); + + // this returns a document with only a single proof so it should be an object rather than an array + expect(Array.isArray(result.proof)).toBe(false); + }); + + it("should derive proofs from multiple proof documents and be able to verify them using jsonld-signatures library", async () => { + const result = await deriveProof( + testSignedDocumentMultiProofs, + testRevealDocument, + { + suite: new BbsBlsSignatureProof2020(), + documentLoader: customLoader, + skipProofCompaction: false + } + ); + + const derivedProofVerified = await jsigs.verify(result, { + suite: new BbsBlsSignatureProof2020(), + purpose: new jsigs.purposes.AssertionProofPurpose(), + documentLoader: customLoader + }); + + expect(result).toBeDefined(); + expect(result.proof.length).toBe(2); + expect(derivedProofVerified.verified).toBeTruthy(); + }); + + it("should throw an error when proofDocument is the wrong type", async () => { + await expect( + deriveProof( + [testSignedDocument, testSignedDocument], + testRevealDocument, + { + suite: new BbsBlsSignatureProof2020(), + documentLoader: customLoader + } + ) + ).rejects.toThrowError("proofDocument should be an object not an array."); + }); + + it("should throw an error when proofDocument doesn't include a BBSBlsSignatureProof2020", async () => { + await expect( + deriveProof(testSignedDocumentEd25519, testRevealDocument, { + suite: new BbsBlsSignatureProof2020(), + documentLoader: customLoader + }) + ).rejects.toThrowError( + "There were not any BBSBlsSignatureProof2020 proofs provided that can be used to derive a proof." + ); + }); }); diff --git a/src/deriveProof.ts b/src/deriveProof.ts index 5d4e6fc..d3e7729 100644 --- a/src/deriveProof.ts +++ b/src/deriveProof.ts @@ -34,6 +34,10 @@ export const deriveProof = async ( throw new TypeError('"options.suite" is required.'); } + if (Array.isArray(proofDocument)) { + throw new TypeError("proofDocument should be an object not an array."); + } + const { proofs, document } = await getProofs({ document: proofDocument, proofType: suite.supportedDeriveProofType, @@ -41,7 +45,14 @@ export const deriveProof = async ( expansionMap }); - const result = await suite.deriveProof({ + if (proofs.length === 0) { + throw new Error( + "There were not any BBSBlsSignatureProof2020 proofs provided that can be used to derive a proof." + ); + } + let derivedProof; + + derivedProof = await suite.deriveProof({ document, proof: proofs[0], revealDocument, @@ -49,21 +60,43 @@ export const deriveProof = async ( expansionMap }); + if (proofs.length > 1) { + // convert the proof property value from object ot array of objects + derivedProof = { ...derivedProof, proof: [derivedProof.proof] }; + + // drop the first proof because it's already been processed + proofs.splice(0, 1); + + // add all the additional proofs to the derivedProof document + for (const proof of proofs) { + const additionalDerivedProofValue = await suite.deriveProof({ + document, + proof, + revealDocument, + documentLoader, + expansionMap + }); + derivedProof.proof.push(additionalDerivedProofValue.proof); + } + } + if (!skipProofCompaction) { /* eslint-disable prefer-const */ let expandedProof: any = { - [SECURITY_PROOF_URL]: { "@graph": result.proof } + [SECURITY_PROOF_URL]: { + "@graph": derivedProof.proof + } }; // account for type-scoped `proof` definition by getting document types - const { types, alias } = await getTypeInfo(result.document, { + const { types, alias } = await getTypeInfo(derivedProof.document, { documentLoader, expansionMap }); expandedProof["@type"] = types; - const ctx = jsonld.getValues(result.document, "@context"); + const ctx = jsonld.getValues(derivedProof.document, "@context"); const compactProof = await jsonld.compact(expandedProof, ctx, { documentLoader, @@ -74,13 +107,22 @@ export const deriveProof = async ( delete compactProof[alias]; delete compactProof["@context"]; + /** + * removes the @included tag when multiple proofs exist because the + * @included tag messes up the canonicalized bytes leading to a bad + * signature that won't verify. + **/ + if (compactProof.proof["@included"]) { + compactProof.proof = compactProof.proof["@included"]; + } + // add proof to document const key = Object.keys(compactProof)[0]; - jsonld.addValue(result.document, key, compactProof[key]); + jsonld.addValue(derivedProof.document, key, compactProof[key]); } else { - delete result.proof["@context"]; - jsonld.addValue(result.document, "proof", result.proof); + delete derivedProof.proof["@context"]; + jsonld.addValue(derivedProof.document, "proof", derivedProof.proof); } - return result.document; + return derivedProof.document; };