@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
1 lines • 18.7 kB
Source Map (JSON)
{"version":3,"file":"JwsService.mjs","names":[],"sources":["../../src/crypto/JwsService.ts"],"sourcesContent":["import type { AgentContext } from '../agent'\nimport { CredoError } from '../error'\nimport {\n assertJwkAsymmetric,\n asymmetricPublicJwkMatches,\n getJwkHumanDescription,\n KeyManagementApi,\n KeyManagementError,\n type KnownJwaSignatureAlgorithm,\n PublicJwk,\n} from '../modules/kms'\nimport { isKnownJwaSignatureAlgorithm } from '../modules/kms/jwk/jwa'\nimport { convertLegacyTrustedCertificates } from '../modules/x509/utils/convertLegacyTrustedCertificates'\nimport { X509ModuleConfig, type X509VerificationTrustedCertificates } from '../modules/x509/X509ModuleConfig'\nimport { X509Service } from './../modules/x509/X509Service'\nimport { type EncodedX509Certificate } from '../modules/x509/X509ServiceOptions'\nimport { injectable } from '../plugins'\nimport { isJsonObject } from '../types'\nimport { JsonEncoder, TypedArrayEncoder } from '../utils'\nimport type { JwsSigner, JwsSignerWithJwk } from './JwsSigner'\nimport type {\n Jws,\n JwsDetachedFormat,\n JwsFlattenedFormat,\n JwsGeneralFormat,\n JwsProtectedHeaderOptions,\n} from './JwsTypes'\nimport { JWS_COMPACT_FORMAT_MATCHER } from './JwsTypes'\nimport { JwtPayload } from './jose/jwt'\n\n@injectable()\nexport class JwsService {\n private async createJwsBase(agentContext: AgentContext, options: CreateJwsBaseOptions) {\n const { jwk, alg, x5c } = options.protectedHeaderOptions\n\n const kms = agentContext.dependencyManager.resolve(KeyManagementApi)\n\n const key = await kms.getPublicKey({ keyId: options.keyId })\n assertJwkAsymmetric(key)\n\n const publicJwk = PublicJwk.fromPublicJwk(key)\n\n // Make sure the options.x5c and x5c from protectedHeader are the same.\n if (x5c) {\n const certificate = X509Service.getLeafCertificate(agentContext, {\n certificateChain: x5c,\n })\n\n if (!asymmetricPublicJwkMatches(certificate.publicJwk.toJson(), key)) {\n throw new CredoError('Protected header x5c does not match key for signing.')\n }\n }\n\n const jwkInstance = jwk instanceof PublicJwk ? jwk : jwk ? PublicJwk.fromUnknown(jwk) : undefined\n // Make sure the options.key and jwk from protectedHeader are the same.\n if (jwkInstance && !asymmetricPublicJwkMatches(jwkInstance.toJson(), key)) {\n throw new CredoError('Protected header JWK does not match key for signing.')\n }\n\n // Validate the options.key used for signing against the jws options\n if (!publicJwk.supportedSignatureAlgorithms.includes(alg)) {\n throw new CredoError(\n `alg '${alg}' is not a valid JWA signature algorithm for this jwk with ${publicJwk.jwkTypeHumanDescription}. Supported algorithms are ${publicJwk.supportedSignatureAlgorithms.join(\n ', '\n )}`\n )\n }\n\n const payload =\n options.payload instanceof JwtPayload ? JsonEncoder.toUint8Array(options.payload.toJson()) : options.payload\n\n const base64Payload = TypedArrayEncoder.toBase64Url(payload)\n const base64UrlProtectedHeader = JsonEncoder.toBase64Url(this.buildProtected(options.protectedHeaderOptions))\n\n const signResult = await kms.sign({\n algorithm: alg,\n data: TypedArrayEncoder.fromUtf8String(`${base64UrlProtectedHeader}.${base64Payload}`),\n keyId: options.keyId,\n })\n const signature = TypedArrayEncoder.toBase64Url(signResult.signature)\n\n return {\n base64Payload,\n base64UrlProtectedHeader,\n signature,\n }\n }\n\n public async createJws(\n agentContext: AgentContext,\n { payload, keyId, header, protectedHeaderOptions }: CreateJwsOptions\n ): Promise<JwsGeneralFormat> {\n const { base64UrlProtectedHeader, signature, base64Payload } = await this.createJwsBase(agentContext, {\n payload,\n keyId,\n protectedHeaderOptions,\n })\n\n return {\n protected: base64UrlProtectedHeader,\n signature,\n header,\n payload: base64Payload,\n }\n }\n\n /**\n * @see {@link https://www.rfc-editor.org/rfc/rfc7515#section-3.1}\n * */\n public async createJwsCompact(\n agentContext: AgentContext,\n { payload, keyId, protectedHeaderOptions }: CreateCompactJwsOptions\n ): Promise<string> {\n const { base64Payload, base64UrlProtectedHeader, signature } = await this.createJwsBase(agentContext, {\n payload,\n keyId,\n protectedHeaderOptions,\n })\n return `${base64UrlProtectedHeader}.${base64Payload}.${signature}`\n }\n\n /**\n * Verify a JWS\n */\n public async verifyJws(\n agentContext: AgentContext,\n {\n jws,\n resolveJwsSigner,\n trustedCertificates,\n jwsSigner: expectedJwsSigner,\n allowedJwsSignerMethods = ['did', 'jwk', 'x5c'],\n }: VerifyJwsOptions\n ): Promise<VerifyJwsResult> {\n let signatures: JwsDetachedFormat[] = []\n let payload: string\n\n if (expectedJwsSigner && !allowedJwsSignerMethods.includes(expectedJwsSigner.method)) {\n throw new CredoError(\n `jwsSigner provided with method '${\n expectedJwsSigner.method\n }', but allowed jws signer methods are ${allowedJwsSignerMethods.join(', ')}.`\n )\n }\n\n if (typeof jws === 'string') {\n if (!JWS_COMPACT_FORMAT_MATCHER.test(jws)) throw new CredoError(`Invalid JWS compact format for value '${jws}'.`)\n\n const [protectedHeader, _payload, signature] = jws.split('.')\n\n payload = _payload\n signatures.push({\n header: {},\n protected: protectedHeader,\n signature,\n })\n } else if ('signatures' in jws) {\n signatures = jws.signatures\n payload = jws.payload\n } else {\n signatures.push(jws)\n payload = jws.payload\n }\n\n if (signatures.length === 0) {\n throw new CredoError('Unable to verify JWS, no signatures present in JWS.')\n }\n\n const jwsFlattened = {\n signatures,\n payload,\n } satisfies JwsFlattenedFormat\n\n const jwsSigners: JwsSignerWithJwk[] = []\n for (const jws of signatures) {\n const protectedJson = JsonEncoder.fromBase64Url(jws.protected)\n\n if (!isJsonObject(protectedJson)) {\n throw new CredoError('Unable to verify JWS, protected header is not a valid JSON object.')\n }\n\n if (!protectedJson.alg || typeof protectedJson.alg !== 'string') {\n throw new CredoError('Unable to verify JWS, protected header alg is not provided or not a string.')\n }\n\n const jwsSigner =\n expectedJwsSigner ??\n (await this.jwsSignerFromJws(agentContext, {\n jws,\n payload,\n protectedHeader: {\n ...protectedJson,\n alg: protectedJson.alg,\n },\n allowedJwsSignerMethods,\n resolveJwsSigner,\n }))\n\n await this.verifyJwsSigner(agentContext, {\n jwsSigner,\n trustedCertificates,\n })\n\n if (!jwsSigner.jwk.supportedSignatureAlgorithms.includes(protectedJson.alg as KnownJwaSignatureAlgorithm)) {\n throw new CredoError(\n `alg '${protectedJson.alg}' is not a valid JWA signature algorithm for this jwk ${getJwkHumanDescription(jwsSigner.jwk.toJson())}. Supported algorithms are ${jwsSigner.jwk.supportedSignatureAlgorithms.join(', ')}`\n )\n }\n\n const data = TypedArrayEncoder.fromUtf8String(`${jws.protected}.${payload}`)\n const signature = TypedArrayEncoder.fromBase64Url(jws.signature)\n jwsSigners.push(jwsSigner)\n\n const kms = agentContext.dependencyManager.resolve(KeyManagementApi)\n\n try {\n const { verified } = await kms.verify({\n key: {\n publicJwk: jwsSigner.jwk.toJson(),\n },\n data,\n signature,\n algorithm: protectedJson.alg as KnownJwaSignatureAlgorithm,\n })\n\n if (!verified) {\n return {\n isValid: false,\n jwsSigners: [],\n jws: jwsFlattened,\n }\n }\n } catch (error) {\n // WalletError probably means signature verification failed. Would be useful to add\n // more specific error type in kms.verify method\n if (error instanceof KeyManagementError) {\n return {\n isValid: false,\n jwsSigners: [],\n jws: jwsFlattened,\n }\n }\n\n throw error\n }\n }\n\n return { isValid: true, jwsSigners, jws: jwsFlattened }\n }\n\n private buildProtected(options: JwsProtectedHeaderOptions) {\n return {\n ...options,\n alg: options.alg,\n jwk: options.jwk instanceof PublicJwk ? options.jwk.toJson() : options.jwk,\n kid: options.kid,\n }\n }\n\n private async verifyJwsSigner(\n agentContext: AgentContext,\n options: {\n jwsSigner: JwsSignerWithJwk\n trustedCertificates?: EncodedX509Certificate[] | X509VerificationTrustedCertificates[]\n }\n ) {\n const { jwsSigner } = options\n const x509ModuleConfig = agentContext.dependencyManager.resolve(X509ModuleConfig)\n\n if (jwsSigner.method === 'x5c') {\n const trustedCertificatesFromConfig = x509ModuleConfig.trustedCertificates ?? []\n const trustedCertificates = options.trustedCertificates ?? trustedCertificatesFromConfig\n if (trustedCertificates.length === 0) {\n throw new CredoError(\n `trustedCertificates is required when the JWS protected header contains an 'x5c' property.`\n )\n }\n\n const convertedTrustedCertificates = convertLegacyTrustedCertificates(trustedCertificates)\n\n await X509Service.validateCertificateChain(agentContext, {\n certificateChain: jwsSigner.x5c,\n trustedCertificates: convertedTrustedCertificates.flatMap(({ issuance }) => issuance),\n })\n }\n }\n\n private async jwsSignerFromJws(\n agentContext: AgentContext,\n options: {\n jws: JwsDetachedFormat\n allowedJwsSignerMethods: JwsSigner['method'][]\n protectedHeader: { alg: string; [key: string]: unknown }\n payload: string\n resolveJwsSigner?: JwsSignerResolver\n }\n ): Promise<JwsSignerWithJwk> {\n const { protectedHeader, resolveJwsSigner, jws, payload, allowedJwsSignerMethods } = options\n\n const alg = protectedHeader.alg\n if (!isKnownJwaSignatureAlgorithm(alg)) {\n throw new CredoError(`Unsupported JWA signature algorithm '${protectedHeader.alg}'`)\n }\n\n if (protectedHeader.x5c && allowedJwsSignerMethods.includes('x5c')) {\n if (\n !Array.isArray(protectedHeader.x5c) ||\n protectedHeader.x5c.some((certificate) => typeof certificate !== 'string')\n ) {\n throw new CredoError('x5c header is not a valid JSON array of strings.')\n }\n\n const certificate = X509Service.getLeafCertificate(agentContext, {\n certificateChain: protectedHeader.x5c,\n })\n return {\n method: 'x5c',\n jwk: certificate.publicJwk,\n x5c: protectedHeader.x5c,\n }\n }\n\n // Jwk\n if (protectedHeader.jwk && allowedJwsSignerMethods.includes('jwk')) {\n if (!isJsonObject(protectedHeader.jwk)) throw new CredoError('JWK is not a valid JSON object.')\n\n const protectedJwk = PublicJwk.fromUnknown(protectedHeader.jwk)\n\n return {\n method: 'jwk',\n jwk: protectedJwk,\n }\n }\n\n if (!resolveJwsSigner) {\n throw new CredoError(`resolveJwsSigner is required for resolving jws signers other than 'jwk' and 'x5c'.`)\n }\n\n try {\n const jwsSigner = await resolveJwsSigner({\n jws,\n protectedHeader: {\n ...protectedHeader,\n alg,\n },\n payload,\n })\n\n if (!allowedJwsSignerMethods.includes(jwsSigner.method)) {\n throw new CredoError(\n `resolveJwsSigner returned jws signer with method '${\n jwsSigner.method\n }', but allowed jws signer methods are ${allowedJwsSignerMethods.join(', ')}.`\n )\n }\n\n return jwsSigner\n } catch (error) {\n throw new CredoError(`Error when resolving jws signer for jws in resolveJwsSigner. ${error.message}`, {\n cause: error,\n })\n }\n }\n}\n\nexport interface CreateJwsOptions {\n payload: Uint8Array | JwtPayload\n keyId: string\n header: Record<string, unknown>\n protectedHeaderOptions: JwsProtectedHeaderOptions\n}\n\ntype CreateJwsBaseOptions = Omit<CreateJwsOptions, 'header'>\ntype CreateCompactJwsOptions = Omit<CreateJwsOptions, 'header'>\n\nexport interface VerifyJwsOptions {\n jws: Jws\n\n /**\n * The expected signer of the JWS. If provided the signer won't be dynamically\n * detected based on the values in the JWS.\n */\n jwsSigner?: JwsSignerWithJwk\n\n /**\n * Allowed jws signer methods when dynamically inferring the jws signer method.\n */\n allowedJwsSignerMethods?: JwsSigner['method'][]\n\n /*\n * Method that should return the JWS signer was used\n * to sign the JWS.\n *\n * This method is called by the JWS Service when it could not determine the public key.\n *\n * Currently the JWS Service can only determine the public key if the JWS protected header\n * contains a `jwk` or `x5c` property. In all other cases, it's up to the caller to resolve the public\n * key based on the JWS.\n *\n * A common use case is the `kid` property in the JWS protected header. Or determining the key\n * base on the `iss` property in the JWT payload.\n */\n resolveJwsSigner?: JwsSignerResolver\n\n trustedCertificates?: EncodedX509Certificate[] | X509VerificationTrustedCertificates[]\n}\n\nexport type JwsSignerResolver = (options: {\n jws: JwsDetachedFormat\n payload: string\n protectedHeader: {\n alg: KnownJwaSignatureAlgorithm\n jwk?: string\n kid?: string\n [key: string]: unknown\n }\n}) => Promise<JwsSignerWithJwk> | JwsSignerWithJwk\n\nexport interface VerifyJwsResult {\n isValid: boolean\n jwsSigners: JwsSignerWithJwk[]\n\n jws: JwsFlattenedFormat\n}\n"],"mappings":"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"}