import jsonld from "jsonld" ; import { suites , SECURITY_CONTEXT_URL } from "jsonld-signatures" ; import { blsCreateProof , blsVerifyProof } from "@mattrglobal/bbs-signatures" ; import { DeriveProofOptions , DidDocumentPublicKey , VerifyProofOptions , CreateVerifyDataOptions , CanonizeOptions } from "./types" ; import { BbsBlsSignature2020 } from "./BbsBlsSignature2020" ; import { randomBytes } from "@stablelib/random" ; import { VerifyProofResult } from "./types/VerifyProofResult" ; import { Bls12381G2KeyPair } from "@mattrglobal/bls12381-key-pair" ; export class BbsBlsSignatureProof2020 extends suites . LinkedDataProof { constructor ( { useNativeCanonize , key , LDKeyClass } = { } ) { super ( { type : "sec:BbsBlsSignatureProof2020" } ) ; this . proof = { "@context" : [ { sec : "https://w3id.org/security#" , proof : { "@id" : "sec:proof" , "@type" : "@id" , "@container" : "@graph" } } , "https://w3id.org/security/bbs/v1" ] , type : "BbsBlsSignatureProof2020" } ; this . mappedDerivedProofType = "https://w3id.org/security#BbsBlsSignature2020" ; this . supportedDeriveProofType = BbsBlsSignatureProof2020 . supportedDerivedProofType ; this . LDKeyClass = LDKeyClass ?? Bls12381G2KeyPair ; this . proofSignatureKey = "proofValue" ; this . key = key ; this . useNativeCanonize = useNativeCanonize ; } param options { object } returns { Promise < object > } async deriveProof ( options ) { const { document , proof , revealDocument , documentLoader , expansionMap , skipProofCompaction } = options ; let { nonce } = options ; if ( ! BbsBlsSignatureProof2020 . supportedDerivedProofType . includes ( proof . type ) ) { throw new TypeError ( `proof document proof incompatible, expected proof types of ${ JSON . stringify ( BbsBlsSignatureProof2020 . supportedDerivedProofType ) } received ${ proof . type }` ) ; } const signature = Buffer . from ( proof [ this . proofSignatureKey ] , "base64" ) ; const suite = new BbsBlsSignature2020 ( ) ; let derivedProof ; if ( this . proof ) { derivedProof = await jsonld . compact ( this . proof , SECURITY_CONTEXT_URL , { documentLoader , expansionMap , compactToRelative : false } ) ; } else { derivedProof = { "@context" : SECURITY_CONTEXT_URL } ; } derivedProof . type = this . type ; const documentStatements = await suite . createVerifyDocumentData ( document , { documentLoader , expansionMap , compactProof : ! skipProofCompaction } ) ; const proofStatements = await suite . createVerifyProofData ( proof , { documentLoader , expansionMap , compactProof : ! skipProofCompaction } ) ; const transformedInputDocumentStatements = documentStatements . map ( element => element . replace ( /(_:c14n[0-9]+)/g , "<urn:bnid:$1>" ) ) ; const compactInputProofDocument = await jsonld . fromRDF ( transformedInputDocumentStatements . join ( "\n" ) ) ; const revealDocumentResult = await jsonld . frame ( compactInputProofDocument , revealDocument , { documentLoader } ) ; const revealDocumentStatements = await suite . createVerifyDocumentData ( revealDocumentResult , { documentLoader , expansionMap } ) ; const numberOfProofStatements = proofStatements . length ; const proofRevealIndicies = Array . from ( Array ( numberOfProofStatements ) . keys ( ) ) ; const documentRevealIndicies = revealDocumentStatements . map ( key => transformedInputDocumentStatements . indexOf ( key ) + numberOfProofStatements ) ; if ( documentRevealIndicies . length !== revealDocumentStatements . length ) { throw new Error ( "Some statements in the reveal document not found in original proof" ) ; } const revealIndicies = proofRevealIndicies . concat ( documentRevealIndicies ) ; if ( ! nonce ) { nonce = await randomBytes ( 50 ) ; } derivedProof . nonce = Buffer . from ( nonce ) . toString ( "base64" ) ; const allInputStatements = proofStatements . concat ( documentStatements ) . map ( item => new Uint8Array ( Buffer . from ( item ) ) ) ; const verificationMethod = await this . getVerificationMethod ( { proof , document , documentLoader , expansionMap } ) ; const key = verificationMethod . publicKeyJwk ? await this . LDKeyClass . fromJwk ( verificationMethod ) : await this . LDKeyClass . from ( verificationMethod ) ; const outputProof = await blsCreateProof ( { signature : new Uint8Array ( signature ) , publicKey : new Uint8Array ( key . publicKeyBuffer ) , messages : allInputStatements , nonce : nonce , revealed : revealIndicies } ) ; derivedProof . proofValue = Buffer . from ( outputProof ) . toString ( "base64" ) ; derivedProof . verificationMethod = proof . verificationMethod ; derivedProof . proofPurpose = proof . proofPurpose ; derivedProof . created = proof . created ; return { document : { ... revealDocumentResult } , proof : derivedProof } ; } param options { object } returns { Promise < { object } > } async verifyProof ( options ) { const { document , documentLoader , expansionMap , purpose } = options ; const { proof } = options ; try { proof . type = this . mappedDerivedProofType ; const proofStatements = await this . createVerifyProofData ( proof , { documentLoader , expansionMap } ) ; const documentStatements = await this . createVerifyProofData ( document , { documentLoader , expansionMap } ) ; const transformedDocumentStatements = documentStatements . map ( element => element . replace ( /<urn:bnid:(_:c14n[0-9]+)>/g , "$1" ) ) ; const statementsToVerify = proofStatements . concat ( transformedDocumentStatements ) . map ( item => new Uint8Array ( Buffer . from ( item ) ) ) ; const verificationMethod = await this . getVerificationMethod ( { proof , document , documentLoader , expansionMap } ) ; const key = verificationMethod . publicKeyJwk ? await this . LDKeyClass . fromJwk ( verificationMethod ) : await this . LDKeyClass . from ( verificationMethod ) ; const verified = await blsVerifyProof ( { proof : new Uint8Array ( Buffer . from ( proof . proofValue , "base64" ) ) , publicKey : new Uint8Array ( key . publicKeyBuffer ) , messages : statementsToVerify , nonce : new Uint8Array ( Buffer . from ( proof . nonce as string , "base64" ) ) } ) ; const { valid , error } = await purpose . validate ( proof , { document , suite : this , verificationMethod , documentLoader , expansionMap } ) ; if ( ! valid ) { throw error ; } return verified ; } catch ( error ) { return { verified : false , error } ; } } async canonize ( input , options ) { const { documentLoader , expansionMap , skipExpansion } = options ; return jsonld . canonize ( input , { algorithm : "URDNA2015" , format : "application/n-quads" , documentLoader , expansionMap , skipExpansion , useNative : this . useNativeCanonize } ) ; } async canonizeProof ( proof , options ) { const { documentLoader , expansionMap } = options ; proof = { ... proof } ; delete proof . nonce ; delete proof . proofValue ; return this . canonize ( proof , { documentLoader , expansionMap , skipExpansion : false } ) ; } param document { CreateVerifyDataOptions } returns { Promise < { async createVerifyData ( options ) { const { proof , document , documentLoader , expansionMap } = options ; const proofStatements = await this . createVerifyProofData ( proof , { documentLoader , expansionMap } ) ; const documentStatements = await this . createVerifyDocumentData ( document , { documentLoader , expansionMap } ) ; return proofStatements . concat ( documentStatements ) ; } param proof param options returns { Promise < { async createVerifyProofData ( proof , { documentLoader , expansionMap } ) { const c14nProofOptions = await this . canonizeProof ( proof , { documentLoader , expansionMap } ) ; return c14nProofOptions . split ( "\n" ) . filter ( _ => _ . length > 0 ) ; } param document param options returns { Promise < { async createVerifyDocumentData ( document , { documentLoader , expansionMap } ) { const c14nDocument = await this . canonize ( document , { documentLoader , expansionMap } ) ; return c14nDocument . split ( "\n" ) . filter ( _ => _ . length > 0 ) ; } param document { object } param proof { object } param documentLoader { function } param expansionMap { function } async getVerificationMethod ( { proof , documentLoader } ) { let { verificationMethod } = proof ; if ( typeof verificationMethod === "object" ) { verificationMethod = verificationMethod . id ; } if ( ! verificationMethod ) { throw new Error ( 'No "verificationMethod" found in proof.' ) ; } const result = await jsonld . frame ( verificationMethod , { "@context" : [ "https://w3id.org/security/v2" , "https://w3id.org/security/suites/jws-2020/v1" ] , "@embed" : "@always" , id : verificationMethod } , { documentLoader , compactToRelative : false , expandContext : SECURITY_CONTEXT_URL } ) ; if ( ! result ) { throw new Error ( `Verification method ${ verificationMethod } not found.` ) ; } if ( result . revoked !== undefined ) { throw new Error ( "The verification method has been revoked." ) ; } return result ; } static proofType = [ "BbsBlsSignatureProof2020" , "sec:BbsBlsSignatureProof2020" , "https://w3id.org/security#BbsBlsSignatureProof2020" ] ; static supportedDerivedProofType = [ "BbsBlsSignature2020" , "sec:BbsBlsSignature2020" , "https://w3id.org/security#BbsBlsSignature2020" ] ; }