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

298 lines (297 loc) • 14.5 kB
import { CredoError } from "../../../error/CredoError.mjs"; import { RecordNotFoundError } from "../../../error/RecordNotFoundError.mjs"; import "../../../error/index.mjs"; import { asArray } from "../../../utils/array.mjs"; import { JsonTransformer } from "../../../utils/JsonTransformer.mjs"; import "../../../utils/index.mjs"; import { injectable } from "../../../plugins/index.mjs"; import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/decorateMetadata.mjs"; import __decorate from "../../../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/decorate.mjs"; import { parseDid } from "../../dids/domain/parse.mjs"; import { DidsApi } from "../../dids/DidsApi.mjs"; import "../../dids/index.mjs"; import { TrustedIssuerContext } from "../../../agent/TrustedIssuerContext.mjs"; import "../anoncreds-w3c-credential/IAnonCredsW3cCredentialService.mjs"; import "../anoncreds-w3c-credential/index.mjs"; import { W3cJsonLdVerifiableCredential } from "./models/W3cJsonLdVerifiableCredential.mjs"; import { validateCredentialSubjectAuthentication, w3cDate } from "../util.mjs"; import jsonld_default from "../jsonld/jsonld.mjs"; import { JsonLdModuleConfig } from "../jsonld/JsonLdModuleConfig.mjs"; import vc_adapter_default from "./adapters/vc-adapter.mjs"; import "../jsonld/index.mjs"; import { W3cJsonLdVerifiablePresentation } from "./models/W3cJsonLdVerifiablePresentation.mjs"; import { assertOnlyW3cJsonLdVerifiableCredentials } from "./proof-ops/jsonldUtil.mjs"; import { SignatureSuiteRegistry } from "./SignatureSuiteRegistry.mjs"; import { createKmsKeyPairClass } from "../../../crypto/KmsKeyPair.mjs"; import { W3cJsonLdCredentialSigningNotSupportedError } from "./W3cJsonLdCredentialSigningNotSupportedError.mjs"; //#region src/modules/vc/linked-data-proofs/W3cJsonLdCredentialService.ts let W3cJsonLdCredentialService = class W3cJsonLdCredentialService { constructor(signatureSuiteRegistry, jsonLdModuleConfig) { this.signatureSuiteRegistry = signatureSuiteRegistry; this.jsonLdModuleConfig = jsonLdModuleConfig; } /** * Signs a credential */ async signCredential(agentContext, options) { const { suite } = await this.prepareSigningSuite(agentContext, { proofType: options.proofType, verificationMethodId: options.verificationMethod, allowedPurposes: options.proofPurpose?.term ? [options.proofPurpose.term] : ["assertionMethod"], date: options.created ?? w3cDate(), controller: options.credential.issuerId }); try { const result = await vc_adapter_default.issue({ credential: JsonTransformer.toJSON(options.credential), suite, purpose: options.proofPurpose, documentLoader: this.jsonLdModuleConfig.documentLoader(agentContext) }); return JsonTransformer.fromJSON(result, W3cJsonLdVerifiableCredential); } catch (error) { throw new CredoError(`Error issuing W3C JSON-LD VC. ${error.message}`, { cause: error }); } } /** * Validate that the agent can prepare to sign a credential with the given proof type and verification method. */ async assertCanSignCredential(agentContext, options) { await this.prepareSigningSuite(agentContext, { proofType: options.proofType, verificationMethodId: options.verificationMethod, allowedPurposes: ["assertionMethod"], controller: options.controller }); } /** * Verifies the signature(s) of a credential * * @param credential the credential to be verified * @returns the verification result */ async verifyCredential(agentContext, options) { try { this.assertNoAnonCredsW3cCredentialProof(options.credential.proof); const verifyCredentialStatus = options.verifyCredentialStatus ?? true; await this.ensureCredentialIssuerTrusted(agentContext, options.credential, options.trustedIssuers); const suites = this.getSignatureSuitesForCredential(agentContext, options.credential); const verifyOptions = { credential: JsonTransformer.toJSON(options.credential), suite: suites, documentLoader: this.jsonLdModuleConfig.documentLoader(agentContext), checkStatus: ({ credential }) => { if (verifyCredentialStatus && "credentialStatus" in credential) throw new CredoError("Verifying credential status for JSON-LD credentials is currently not supported"); return { verified: true }; } }; if (options.proofPurpose) verifyOptions.purpose = options.proofPurpose; const result = await vc_adapter_default.verifyCredential(verifyOptions); const { verified: isValid, ...remainingResult } = result; if (!isValid) agentContext.config.logger.debug(`Credential verification failed: ${result.error?.message}`, { stack: result.error?.stack }); return { isValid, validations: { vcJs: { isValid, ...remainingResult } }, error: result.error }; } catch (error) { return { isValid: false, validations: {}, error }; } } /** * Signs a presentation including the credentials it includes * * @param presentation the presentation to be signed * @returns the signed presentation */ async signPresentation(agentContext, options) { const { suite } = await this.prepareSigningSuite(agentContext, { proofType: options.proofType, verificationMethodId: options.verificationMethod, allowedPurposes: options.proofPurpose?.term ? [options.proofPurpose.term] : ["authentication"], date: (/* @__PURE__ */ new Date()).toISOString() }); const signOptions = { presentation: JsonTransformer.toJSON(options.presentation), suite, challenge: options.challenge, domain: options.domain, documentLoader: this.jsonLdModuleConfig.documentLoader(agentContext) }; if (options.proofPurpose) signOptions.purpose = options.proofPurpose; const result = await vc_adapter_default.signPresentation(signOptions); return JsonTransformer.fromJSON(result, W3cJsonLdVerifiablePresentation); } /** * Verifies a presentation including the credentials it includes * * @param presentation the presentation to be verified * @returns the verification result */ async verifyPresentation(agentContext, options) { try { this.assertNoAnonCredsW3cCredentialProof(options.presentation.proof); const WalletKeyPair = createKmsKeyPairClass(agentContext); let proofs = options.presentation.proof; if (!Array.isArray(proofs)) proofs = [proofs]; if (options.purpose) proofs = proofs.filter((proof) => proof.proofPurpose === options.purpose.term); const presentationSuites = proofs.map((proof) => { const SuiteClass = this.signatureSuiteRegistry.getByProofType(proof.type).suiteClass; return new SuiteClass({ LDKeyClass: WalletKeyPair, proof: { verificationMethod: proof.verificationMethod }, date: proof.created, useNativeCanonize: false }); }); const credentials = asArray(options.presentation.verifiableCredential); assertOnlyW3cJsonLdVerifiableCredentials(credentials); for (const credential of credentials) { this.assertNoAnonCredsW3cCredentialProof(credential.proof); await this.ensureCredentialIssuerTrusted(agentContext, credential, options.trustedIssuers); } const credentialSuites = credentials.map((credential) => this.getSignatureSuitesForCredential(agentContext, credential)); const allSuites = presentationSuites.concat(...credentialSuites); const verifyOptions = { presentation: JsonTransformer.toJSON(options.presentation), suite: allSuites, challenge: options.challenge, domain: options.domain, documentLoader: this.jsonLdModuleConfig.documentLoader(agentContext) }; if (options.purpose) verifyOptions.presentationPurpose = options.purpose; const result = await vc_adapter_default.verify(verifyOptions); const { verified: isValid, ...remainingResult } = result; const holderProofPurpose = options.purpose?.term ?? "authentication"; const holderVerificationMethods = asArray(options.presentation.proof).filter((proof) => proof.proofPurpose === holderProofPurpose).map((proof) => proof.verificationMethod); const credentialValidations = credentials.map((credential) => { const credentialSubjectIds = credential.credentialSubjectIds; const credentialSubjectAuthentication = credentialSubjectIds.length === 0 || holderVerificationMethods.some((verificationMethod) => validateCredentialSubjectAuthentication(credentialSubjectIds, verificationMethod).isValid) ? { isValid: true } : { isValid: false, error: new CredoError("Presentation does not authenticate the credentialSubject of one or more included credentials") }; return { isValid: credentialSubjectAuthentication.isValid, validations: { credentialSubjectAuthentication } }; }); return { isValid: isValid && credentialValidations.every((credential) => credential.isValid), validations: { vcJs: { isValid, ...remainingResult }, credentials: credentialValidations }, error: result.error ?? credentialValidations.find((credential) => !credential.isValid)?.validations.credentialSubjectAuthentication.error }; } catch (error) { return { isValid: false, validations: {}, error }; } } getVerificationMethodTypesByProofType(proofType) { return this.getSigningSuiteInfo(proofType).verificationMethodTypes; } async getExpandedTypesForCredential(agentContext, credential) { const expandedTypes = (await jsonld_default.expand(JsonTransformer.toJSON(credential), { documentLoader: this.jsonLdModuleConfig.documentLoader(agentContext) }))[0]["@type"]; return asArray(expandedTypes); } async ensureCredentialIssuerTrusted(agentContext, credential, trustedIssuers) { const issuerId = credential.issuerId; if (!issuerId.startsWith("did:")) return; const issuerDid = parseDid(issuerId).did; for (const proof of asArray(credential.proof)) { const verificationMethod = proof.verificationMethod; if (!verificationMethod.startsWith("did:") || parseDid(verificationMethod).did !== issuerDid) throw new CredoError(`The proof verification method '${verificationMethod}' does not match the credential issuer did '${issuerDid}'.`); await TrustedIssuerContext.ensureTrustedSigner(agentContext, { signer: { method: "did", didUrl: verificationMethod }, verification: { type: "credential", credential } }, trustedIssuers); } } getSigningSuiteInfo(proofType) { if (!this.signatureSuiteRegistry.supportedProofTypes.includes(proofType)) throw new W3cJsonLdCredentialSigningNotSupportedError(`The requested proofType ${proofType} is not supported`, "unsupported-proof-type"); return this.signatureSuiteRegistry.getByProofType(proofType); } async prepareSigningSuite(agentContext, options) { const suiteInfo = this.getSigningSuiteInfo(options.proofType); const dids = agentContext.resolve(DidsApi); let resolved; try { resolved = await dids.resolveVerificationMethodFromCreatedDidRecord(options.verificationMethodId, options.allowedPurposes); } catch (error) { if (error instanceof RecordNotFoundError) throw new W3cJsonLdCredentialSigningNotSupportedError(error.message, "verification-method-not-controlled", error); if (error instanceof CredoError && error.message === `Unable to locate verification method with id '${options.verificationMethodId}' in purposes ${options.allowedPurposes}`) throw new W3cJsonLdCredentialSigningNotSupportedError(error.message, "verification-method-not-found", error); throw error; } const { verificationMethod: nativeVerificationMethod, publicJwk } = resolved; if (!suiteInfo.verificationMethodTypes.includes(nativeVerificationMethod.type)) throw new W3cJsonLdCredentialSigningNotSupportedError(`Unsupported verification method type '${nativeVerificationMethod.type}' for proof type '${options.proofType}'. Supported types are: ${suiteInfo.verificationMethodTypes.join(", ")}`, "unsupported-verification-method-type"); const loaderResult = await this.jsonLdModuleConfig.documentLoader(agentContext)(options.verificationMethodId); const framedVerificationMethod = typeof loaderResult.document === "string" ? JSON.parse(loaderResult.document) : loaderResult.document; const WalletKeyPair = createKmsKeyPairClass(agentContext); const SuiteClass = suiteInfo.suiteClass; await new SuiteClass({ LDKeyClass: WalletKeyPair, proof: { verificationMethod: options.verificationMethodId }, useNativeCanonize: false, date: options.date ?? w3cDate() }).assertVerificationMethod(framedVerificationMethod); if (!this.signatureSuiteRegistry.getAllByPublicJwkType(publicJwk).some(({ suiteClass }) => suiteClass === suiteInfo.suiteClass)) throw new W3cJsonLdCredentialSigningNotSupportedError("The key type of the verification method does not match the suite", "unsupported-key-type"); const keyPair = new WalletKeyPair({ controller: options.controller ?? framedVerificationMethod.controller ?? nativeVerificationMethod.controller, id: options.verificationMethodId, publicJwk }); return { suite: new SuiteClass({ key: keyPair, LDKeyClass: WalletKeyPair, proof: { verificationMethod: options.verificationMethodId }, useNativeCanonize: false, date: options.date ?? w3cDate() }), suiteInfo, keyPair, WalletKeyPair }; } getSignatureSuitesForCredential(agentContext, credential) { const WalletKeyPair = createKmsKeyPairClass(agentContext); let proofs = credential.proof; if (!Array.isArray(proofs)) proofs = [proofs]; return proofs.map((proof) => { const SuiteClass = this.signatureSuiteRegistry.getByProofType(proof.type).suiteClass; return new SuiteClass({ LDKeyClass: WalletKeyPair, proof: { verificationMethod: proof.verificationMethod }, date: proof.created, useNativeCanonize: false }); }); } assertNoAnonCredsW3cCredentialProof(proofsInput) { if (asArray(proofsInput).some((proof) => proof.type === "DataIntegrityProof" && "cryptosuite" in proof && proof.cryptosuite === "anoncreds-2023")) throw new CredoError("W3C credential proof with cryptosuite anoncreds-2023 must be verified through the anoncreds W3C credential path, not the generic linked-data verifier"); } }; W3cJsonLdCredentialService = __decorate([injectable(), __decorateMetadata("design:paramtypes", [typeof SignatureSuiteRegistry === "undefined" ? Object : SignatureSuiteRegistry, typeof JsonLdModuleConfig === "undefined" ? Object : JsonLdModuleConfig])], W3cJsonLdCredentialService); //#endregion export { W3cJsonLdCredentialService }; //# sourceMappingURL=W3cJsonLdCredentialService.mjs.map