UNPKG

@credo-ts/core

Version:

<p align="center"> <br /> <img alt="Credo Logo" src="https://github.com/openwallet-foundation/credo-ts/blob/c7886cb8377ceb8ee4efe8d264211e561a75072d/images/credo-logo.png" height="250px" /> </p> <h1 align="center"><b>Credo - Core</b></h1

182 lines (181 loc) • 7.71 kB
import { CredoError } from "../../../../error/CredoError.mjs"; import "../../../../error/index.mjs"; import { TypedArrayEncoder } from "../../../../utils/TypedArrayEncoder.mjs"; import { JsonEncoder } from "../../../../utils/JsonEncoder.mjs"; import "../../../../utils/index.mjs"; import { isKnownJwaSignatureAlgorithm } from "../../../kms/jwk/jwa.mjs"; import { PublicJwk } from "../../../kms/jwk/PublicJwk.mjs"; import "../../../kms/index.mjs"; import { suites } from "../adapters/jsonld-signatures-adapter.mjs"; //#region src/modules/vc/linked-data-proofs/signature-suites/JwsLinkedDataSignature.ts /*! * Copyright (c) 2020-2021 Digital Bazaar, Inc. All rights reserved. */ const LinkedDataSignature = suites.LinkedDataSignature; var JwsLinkedDataSignature = class extends LinkedDataSignature { /** * @param options - Options hashmap. * @param options.type - Provided by subclass. * @param options.alg - JWS alg provided by subclass. * @param [options.LDKeyClass] - Provided by subclass or subclass * overrides `getVerificationMethod`. * * Either a `key` OR at least one of `signer`/`verifier` is required. * * @param [options.key] - An optional key object (containing an * `id` property, and either `signer` or `verifier`, depending on the * intended operation. Useful for when the application is managing keys * itself (when using a KMS, you never have access to the private key, * and so should use the `signer` param instead). * * Advanced optional parameters and overrides. * * @param [options.proof] - A JSON-LD document with options to use * for the `proof` node. Any other custom fields can be provided here * using a context different from `security-v2`. * @param [options.date] - Signing date to use if not passed. * @param options.contextUrl - JSON-LD context url that corresponds * to this signature suite. Used for enforcing suite context during the * `sign()` operation. * @param [options.useNativeCanonize] - Whether to use a native * canonize algorithm. */ constructor(options) { super({ type: options.type, LDKeyClass: options.LDKeyClass, contextUrl: options.contextUrl, key: options.key, signer: void 0, verifier: void 0, proof: options.proof, date: options.date, useNativeCanonize: options.useNativeCanonize }); this.alg = options.algorithm; } /** * @param options - Options hashmap. * @param options.verifyData - The data to sign. * @param options.proof - A JSON-LD document with options to use * for the `proof` node. Any other custom fields can be provided here * using a context different from `security-v2`. * * @returns The proof containing the signature value. */ async sign(options) { if (!(this.signer && typeof this.signer.sign === "function")) throw new Error("A signer API has not been specified."); if (this.key?.publicJwk instanceof PublicJwk) { if (!isKnownJwaSignatureAlgorithm(this.alg)) throw new CredoError(`Unsupported JWA signature algorithm '${this.alg}' for signature suite ${this.type}`); this.key.publicJwk.alg = this.alg; } const header = { alg: this.alg, b64: false, crit: ["b64"] }; const encodedHeader = JsonEncoder.toBase64Url(header); const data = _createJws({ encodedHeader, verifyData: options.verifyData }); const signature = await this.signer.sign({ data }); const encodedSignature = TypedArrayEncoder.toBase64Url(signature); options.proof.jws = `${encodedHeader}..${encodedSignature}`; return options.proof; } /** * @param options - Options hashmap. * @param options.verifyData - The data to verify. * @param options.verificationMethod - A verification method. * @param options.proof - The proof to be verified. * * @returns Resolves with the verification result. */ async verifySignature(options) { if (!(options.proof.jws && typeof options.proof.jws === "string" && options.proof.jws.includes("."))) throw new TypeError("The proof does not include a valid \"jws\" property."); const [encodedHeader, , encodedSignature] = options.proof.jws.split("."); let header; try { header = JsonEncoder.fromBase64Url(encodedHeader); } catch (e) { throw new Error(`Could not parse JWS header; ${e}`); } if (!(header && typeof header === "object")) throw new Error("Invalid JWS header."); if (!(header.alg === this.alg && header.b64 === false && Array.isArray(header.crit) && header.crit.length === 1 && header.crit[0] === "b64" && Object.keys(header).length === 3)) throw new Error(`Invalid JWS header parameters for ${this.type}.`); const signature = TypedArrayEncoder.fromBase64Url(encodedSignature); const data = _createJws({ encodedHeader, verifyData: options.verifyData }); let { verifier } = this; if (!verifier) { const key = await this.LDKeyClass.from(options.verificationMethod); if (key.publicJwk instanceof PublicJwk) { if (!isKnownJwaSignatureAlgorithm(header.alg)) throw new CredoError(`Unsupported JWA signature algorithm '${header.alg}' for signature suite ${this.type}`); key.publicJwk.alg = header.alg; } verifier = key.verifier(); } return verifier.verify({ data, signature }); } async getVerificationMethod(options) { if (this.key) return this.key.export({ publicKey: true }); let { verificationMethod } = options.proof; if (typeof verificationMethod === "object" && verificationMethod !== null) verificationMethod = verificationMethod.id; if (!verificationMethod) throw new Error("No \"verificationMethod\" found in proof."); if (!options.documentLoader) throw new CredoError("Missing custom document loader. This is required for resolving verification methods."); const { document } = await options.documentLoader(verificationMethod); verificationMethod = typeof document === "string" ? JSON.parse(document) : document; await this.assertVerificationMethod(verificationMethod); return verificationMethod; } /** * Checks whether a given proof exists in the document. * * @param options - Options hashmap. * @param options.proof - A proof. * @param options.document - A JSON-LD document. * @param options.purpose - A jsonld-signatures ProofPurpose * instance (e.g. AssertionProofPurpose, AuthenticationProofPurpose, etc). * @param options.documentLoader - A secure document loader (it is * recommended to use one that provides static known documents, instead of * fetching from the web) for returning contexts, controller documents, * keys, and other relevant URLs needed for the proof. * * @returns Whether a match for the proof was found. */ async matchProof(options) { if (!await super.matchProof({ proof: options.proof, document: options.document, purpose: options.purpose, documentLoader: options.documentLoader })) return false; if (!this.key) return true; const { verificationMethod } = options.proof; if (typeof verificationMethod === "object") return verificationMethod.id === this.key.id; return verificationMethod === this.key.id; } }; /** * Creates the bytes ready for signing. * * @param {object} options - Options hashmap. * @param {string} options.encodedHeader - A base64url encoded JWT header. * @param {Uint8Array} options.verifyData - Payload to sign/verify. * @returns {Uint8Array} A combined byte array for signing. */ function _createJws(options) { const encodedHeaderBytes = TypedArrayEncoder.fromUtf8String(`${options.encodedHeader}.`); const data = new Uint8Array(encodedHeaderBytes.length + options.verifyData.length); data.set(encodedHeaderBytes, 0); data.set(options.verifyData, encodedHeaderBytes.length); return data; } //#endregion export { JwsLinkedDataSignature }; //# sourceMappingURL=JwsLinkedDataSignature.mjs.map