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

231 lines (227 loc) • 10.7 kB
import { CredoError } from "../../../error/CredoError.mjs"; import "../../../error/index.mjs"; import { asArray } from "../../../utils/array.mjs"; import { MessageValidator } from "../../../utils/MessageValidator.mjs"; import { JsonTransformer } from "../../../utils/JsonTransformer.mjs"; import { nowInSeconds } from "../../../utils/timestamp.mjs"; import "../../../utils/index.mjs"; import { JwtPayload } from "../../../crypto/jose/jwt/JwtPayload.mjs"; import { injectable } from "../../../plugins/index.mjs"; import { __decorateMetadata } from "../../../_virtual/_@oxc-project_runtime@0.112.0/helpers/decorateMetadata.mjs"; import { __decorate } from "../../../_virtual/_@oxc-project_runtime@0.112.0/helpers/decorate.mjs"; import { JwsService } from "../../../crypto/JwsService.mjs"; import { getPublicJwkFromVerificationMethod } from "../../dids/domain/key-type/keyDidMapping.mjs"; import { W3cV2JwtVerifiableCredential } from "./W3cV2JwtVerifiableCredential.mjs"; import "../../../crypto/index.mjs"; import { extractKeyFromHolderBinding } from "../../sd-jwt-vc/utils.mjs"; import { extractHolderFromPresentationCredentials, getVerificationMethodForJwt, validateAndResolveVerificationMethod } from "../v2-jwt-utils.mjs"; import { W3cV2JwtVerifiablePresentation } from "./W3cV2JwtVerifiablePresentation.mjs"; //#region src/modules/vc/jwt-vc/W3cV2JwtCredentialService.ts var _ref; let W3cV2JwtCredentialService = class W3cV2JwtCredentialService { constructor(jwsService) { this.jwsService = jwsService; } /** * Signs a credential */ async signCredential(agentContext, options) { MessageValidator.validateSync(options.credential); const jwtPayload = new JwtPayload({ additionalClaims: JsonTransformer.toJSON(options.credential) }); jwtPayload.iat = nowInSeconds(); jwtPayload.additionalClaims.cnf = options.holder ? (await extractKeyFromHolderBinding(agentContext, options.holder)).cnf : void 0; const publicJwk = await validateAndResolveVerificationMethod(agentContext, options.verificationMethod, ["assertionMethod"]); const jwt = await this.jwsService.createJwsCompact(agentContext, { payload: jwtPayload, keyId: publicJwk.keyId, protectedHeaderOptions: { typ: "vc+jwt", alg: options.alg, kid: options.verificationMethod } }); return W3cV2JwtVerifiableCredential.fromCompact(jwt); } /** * Verifies the signature(s) of a credential * * @param credential the credential to be verified * @returns the verification result */ async verifyCredential(agentContext, options) { const validationResults = { isValid: false, validations: {} }; try { let credential; try { if (options.credential instanceof W3cV2JwtVerifiableCredential) options.credential.validate(); credential = options.credential instanceof W3cV2JwtVerifiableCredential ? options.credential : W3cV2JwtVerifiableCredential.fromCompact(options.credential); credential.jwt.payload.validate({ skewSeconds: agentContext.config.validitySkewSeconds }); validationResults.validations.dataModel = { isValid: true }; } catch (error) { validationResults.validations.dataModel = { isValid: false, error }; return validationResults; } const issuerVerificationMethod = await getVerificationMethodForJwt(agentContext, credential, ["assertionMethod"]); const issuerPublicKey = getPublicJwkFromVerificationMethod(issuerVerificationMethod); let signatureResult; try { signatureResult = await this.jwsService.verifyJws(agentContext, { jws: credential.jwt.serializedJwt, jwsSigner: { method: "did", jwk: issuerPublicKey, didUrl: issuerVerificationMethod.id } }); if (!signatureResult.isValid) validationResults.validations.signature = { isValid: false, error: new CredoError("Invalid JWS signature") }; else validationResults.validations.signature = { isValid: true }; } catch (error) { validationResults.validations.signature = { isValid: false, error }; } if (credential.resolvedCredential.issuerId !== issuerVerificationMethod.controller) validationResults.validations.issuerIsSigner = { isValid: false, error: new CredoError(`Credential is signed using verification method ${issuerVerificationMethod.id}, while the issuer of the credential is '${credential.resolvedCredential.issuerId}'`) }; else validationResults.validations.issuerIsSigner = { isValid: true }; if (!signatureResult?.jwsSigners.some((jwsSigner) => jwsSigner.jwk.fingerprint === issuerPublicKey.fingerprint)) validationResults.validations.issuerIsSigner = { isValid: false, error: new CredoError("Credential is not signed by the issuer of the credential") }; else validationResults.validations.issuerIsSigner = { isValid: true }; validationResults.isValid = Object.values(validationResults.validations).every((v) => v.isValid); return validationResults; } catch (error) { validationResults.error = error; return validationResults; } } /** * Signs a presentation including the credentials it includes * * @param presentation the presentation to be signed * @returns the signed presentation */ async signPresentation(agentContext, options) { MessageValidator.validateSync(options.presentation); const jwtPayload = new JwtPayload({ additionalClaims: JsonTransformer.toJSON(options.presentation) }); jwtPayload.additionalClaims.nonce = options.challenge; jwtPayload.aud = options.domain; const holder = await extractHolderFromPresentationCredentials(agentContext, options.presentation); const jwt = await this.jwsService.createJwsCompact(agentContext, { payload: jwtPayload, keyId: holder.publicJwk.keyId, protectedHeaderOptions: { typ: "vp+jwt", alg: holder.alg, kid: holder?.cnf?.kid } }); return W3cV2JwtVerifiablePresentation.fromCompact(jwt); } /** * Verifies a presentation including the credentials it includes * * @param presentation the presentation to be verified * @returns the verification result */ async verifyPresentation(agentContext, options) { const validationResults = { isValid: false, validations: {} }; try { let presentation; try { if (options.presentation instanceof W3cV2JwtVerifiablePresentation) options.presentation.validate(); presentation = options.presentation instanceof W3cV2JwtVerifiablePresentation ? options.presentation : W3cV2JwtVerifiablePresentation.fromCompact(options.presentation); presentation.jwt.payload.validate({ skewSeconds: agentContext.config.validitySkewSeconds }); if (options.challenge !== presentation.jwt.payload.additionalClaims.nonce) throw new CredoError(`JWT payload 'nonce' does not match challenge '${options.challenge}'`); const audArray = asArray(presentation.jwt.payload.aud); if (options.domain && !audArray.includes(options.domain)) throw new CredoError(`JWT payload 'aud' does not include domain '${options.domain}'`); validationResults.validations.dataModel = { isValid: true }; } catch (error) { validationResults.validations.dataModel = { isValid: false, error }; return validationResults; } const proverVerificationMethod = await getVerificationMethodForJwt(agentContext, presentation, ["authentication"]); const proverPublicKey = getPublicJwkFromVerificationMethod(proverVerificationMethod); let signatureResult; try { signatureResult = await this.jwsService.verifyJws(agentContext, { jws: presentation.jwt.serializedJwt, allowedJwsSignerMethods: ["did"], jwsSigner: { method: "did", didUrl: proverVerificationMethod.id, jwk: proverPublicKey }, trustedCertificates: [] }); if (!signatureResult.isValid) validationResults.validations.presentationSignature = { isValid: false, error: new CredoError("Invalid JWS signature on presentation") }; else validationResults.validations.presentationSignature = { isValid: true }; } catch (error) { validationResults.validations.presentationSignature = { isValid: false, error }; } if (presentation.resolvedPresentation.holderId && proverVerificationMethod.controller !== presentation.resolvedPresentation.holderId) validationResults.validations.holderIsSigner = { isValid: false, error: new CredoError(`Presentation is signed using verification method ${proverVerificationMethod.id}, while the holder of the presentation is '${presentation.resolvedPresentation.holderId}'`) }; else validationResults.validations.holderIsSigner = { isValid: true }; const credentials = asArray(presentation.resolvedPresentation.verifiableCredential); validationResults.validations.credentials = await Promise.all(credentials.map(async (credential) => { if (!(credential.envelopedCredential instanceof W3cV2JwtVerifiableCredential)) return { isValid: false, error: new CredoError("Credential is not of format JWT. Presentations in JWT format can only contain credentials in JWT format."), validations: {} }; const credentialResult = await this.verifyCredential(agentContext, { credential: credential.envelopedCredential }); let credentialSubjectAuthentication; const credentialSubjectIds = credential.resolvedCredential.credentialSubjectIds; const presentationAuthenticatesCredentialSubject = credentialSubjectIds.some((subjectId) => proverVerificationMethod.controller === subjectId); if (credentialSubjectIds.length > 0 && !presentationAuthenticatesCredentialSubject) credentialSubjectAuthentication = { isValid: false, error: new CredoError("Credential has one or more credentialSubject ids, but presentation does not authenticate credential subject") }; else credentialSubjectAuthentication = { isValid: true }; return { ...credentialResult, isValid: credentialResult.isValid && credentialSubjectAuthentication.isValid, validations: { ...credentialResult.validations, credentialSubjectAuthentication } }; })); validationResults.isValid = Object.values(validationResults.validations).every((v) => Array.isArray(v) ? v.every((vv) => vv.isValid) : v.isValid); return validationResults; } catch (error) { validationResults.error = error; return validationResults; } } }; W3cV2JwtCredentialService = __decorate([injectable(), __decorateMetadata("design:paramtypes", [typeof (_ref = typeof JwsService !== "undefined" && JwsService) === "function" ? _ref : Object])], W3cV2JwtCredentialService); //#endregion export { W3cV2JwtCredentialService }; //# sourceMappingURL=W3cV2JwtCredentialService.mjs.map