import jsonld from "jsonld" ; import { suites , SECURITY_CONTEXT_URL } from "jsonld-signatures" ; import { SignatureSuiteOptions , CreateProofOptions , CanonizeOptions , CreateVerifyDataOptions , VerifyProofOptions , VerifySignatureOptions , SuiteSignOptions } from "./types" ; import { w3cDate } from "./utilities" ; import { Bls12381G2KeyPair } from "@mattrglobal/bls12381-key-pair" ; /**
 * A BBS+ signature suite for use with BLS12-381 key pairs
 */ export class BbsBlsSignature2020 extends suites . LinkedDataProof { param options { SignatureSuiteOptions } constructor ( options = { } ) { const { verificationMethod , signer , key , date , useNativeCanonize , LDKeyClass } = options ; if ( verificationMethod !== undefined && typeof verificationMethod !== "string" ) { throw new TypeError ( '"verificationMethod" must be a URL string.' ) ; } super ( { type : "sec:BbsBlsSignature2020" } ) ; this . proof = { "@context" : [ { sec : "https://w3id.org/security#" , proof : { "@id" : "sec:proof" , "@type" : "@id" , "@container" : "@graph" } } , "https://w3id.org/security/bbs/v1" ] , type : "BbsBlsSignature2020" } ; this . LDKeyClass = LDKeyClass ?? Bls12381G2KeyPair ; this . signer = signer ; this . verificationMethod = verificationMethod ; this . proofSignatureKey = "proofValue" ; if ( key ) { if ( verificationMethod === undefined ) { this . verificationMethod = key . id ; } this . key = key ; if ( typeof key . signer === "function" ) { this . signer = key . signer ( ) ; } if ( typeof key . verifier === "function" ) { this . verifier = key . verifier ( ) ; } } if ( date ) { this . date = new Date ( date ) ; if ( isNaN ( this . date ) ) { throw TypeError ( `"date" "${ date }" is not a valid date.` ) ; } } this . useNativeCanonize = useNativeCanonize ; } param options { CreateProofOptions } returns { Promise < object > } async createProof ( options ) { const { document , purpose , documentLoader , expansionMap , compactProof } = options ; let proof ; if ( this . proof ) { proof = await jsonld . compact ( this . proof , SECURITY_CONTEXT_URL , { documentLoader , expansionMap , compactToRelative : false } ) ; } else { proof = { "@context" : SECURITY_CONTEXT_URL } ; } proof . type = this . type ; let date = this . date ; if ( proof . created === undefined && date === undefined ) { date = new Date ( ) ; } if ( date !== undefined && typeof date !== "string" ) { date = w3cDate ( date ) ; } if ( date !== undefined ) { proof . created = date ; } if ( this . verificationMethod !== undefined ) { proof . verificationMethod = this . verificationMethod ; } proof = await purpose . update ( proof , { document , suite : this , documentLoader , expansionMap } ) ; const verifyData = ( await this . createVerifyData ( { document , proof , documentLoader , expansionMap , compactProof } ) ) . map ( item => new Uint8Array ( Buffer . from ( item ) ) ) ; proof = await this . sign ( { verifyData , document , proof , documentLoader , expansionMap } ) ; return proof ; } param options { object } returns { Promise < { object } > } async verifyProof ( options ) { const { proof , document , documentLoader , expansionMap , purpose } = options ; try { const verifyData = ( await this . createVerifyData ( { document , proof , documentLoader , expansionMap , compactProof : false } ) ) . map ( item => new Uint8Array ( Buffer . from ( item ) ) ) ; const verificationMethod = await this . getVerificationMethod ( { proof , document , documentLoader , expansionMap } ) ; const verified = await this . verifySignature ( { verifyData , verificationMethod , document , proof , documentLoader , expansionMap } ) ; if ( ! verified ) { throw new Error ( "Invalid signature." ) ; } const { valid , error } = await purpose . validate ( proof , { document , suite : this , verificationMethod , documentLoader , expansionMap } ) ; if ( ! valid ) { throw error ; } return { verified : true } ; } 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 [ this . proofSignatureKey ] ; 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" : SECURITY_CONTEXT_URL , "@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 ; } param options { SuiteSignOptions } returns { Promise < { object } > } async sign ( options ) { const { verifyData , proof } = options ; if ( ! ( this . signer && typeof this . signer . sign === "function" ) ) { throw new Error ( "A signer API with sign function has not been specified." ) ; } const proofValue = await this . signer . sign ( { data : verifyData } ) ; proof [ this . proofSignatureKey ] = Buffer . from ( proofValue ) . toString ( "base64" ) ; return proof ; } param verifyData { VerifySignatureOptions } returns { Promise < boolean > } async verifySignature ( options ) { const { verificationMethod , verifyData , proof } = options ; let { verifier } = this ; if ( ! verifier ) { const key = await this . LDKeyClass . from ( verificationMethod ) ; verifier = key . verifier ( key , this . alg , this . type ) ; } return await verifier . verify ( { data : verifyData , signature : new Uint8Array ( Buffer . from ( proof [ this . proofSignatureKey ] as string , "base64" ) ) } ) ; } static proofType = [ "BbsBlsSignature2020" , "sec:BbsBlsSignature2020" , "https://w3id.org/security#BbsBlsSignature2020" ] ; }