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

1 lines • 23.5 kB
{"version":3,"file":"X509Service.mjs","names":[],"sources":["../../../src/modules/x509/X509Service.ts"],"sourcesContent":["import * as x509 from '@peculiar/x509'\nimport { AgentContext } from '../../agent'\nimport { CredoWebCrypto } from '../../crypto/webcrypto'\nimport { injectable } from '../../plugins'\nimport { CertificateSigningRequest } from './CertificateSigningRequest'\nimport { X509ExtensionIdentifier } from './utils'\nimport { validateCriticalExtensionsForChain } from './utils/criticalExtensions'\nimport { X509Certificate, X509KeyUsage } from './X509Certificate'\nimport { X509CertificateRevocationList } from './X509CertificateRevocationList'\nimport { X509Error, X509ValidationError } from './X509Error'\nimport { X509ModuleConfig } from './X509ModuleConfig'\nimport { X509RevocationService } from './X509RevocationService'\nimport type {\n X509CreateCertificateOptions,\n X509CreateCertificateRevocationListOptions,\n X509CreateCertificateSigningRequestOptions,\n X509GetLeafCertificateOptions,\n X509ParseCertificateOptions,\n X509ParseCertificateSigningRequestOptions,\n X509ValidateCertificateChainOptions,\n} from './X509ServiceOptions'\nimport { X509RevocationCheckMode } from './X509ValidationOptions'\nimport type { X509CertificateSingleValidationResult, X509ValidationResult } from './X509ValidationResult'\n\n@injectable()\n// biome-ignore lint/complexity/noStaticOnlyClass: no explanation\nexport class X509Service {\n /**\n *\n * Validate a chain of X.509 certificates according to RFC 5280\n *\n * This function requires a list of base64 encoded certificates and, optionally, a certificate that should be found in the chain.\n * If no certificate is provided, it will just assume the leaf certificate\n *\n * The leaf certificate should be the 0th index and the root the last\n *\n * The Issuer of the certificate is found with the following algorithm:\n * - Check if there is an AuthorityKeyIdentifierExtension\n * - Go through all the other certificates and see if the SubjectKeyIdentifier is equal to the AuthorityKeyIdentifier\n * - If they are equal, the certificate signature is verified and returned as the issuer\n *\n * Validation checks performed:\n * - Chain validation (structure, signatures, validity, trust anchor)\n * - Basic constraints are valid for CA certificates\n * - Key usage of issuing CA certificates includes `keyCertSign` (when the extension is present)\n * - Path length constraints are satisfied\n * - All critical extensions are understood and valid\n * - Certificates have not been revoked (CRL) - if enabled\n *\n * Limitations (RFC 5280 features not enforced here):\n * - Revocation is checked via CRL only; OCSP is not supported.\n * - Name Constraints (§4.2.1.10), Policy Constraints (§4.2.1.11), and Certificate Policies\n * (§4.2.1.4) are not enforced. A certificate carrying one of these as a *critical* extension is\n * rejected (unrecognized critical extension); a non-critical occurrence is ignored.\n * - Extended Key Usage is parsed but not enforced.\n * - Key Usage is only enforced when the extension is present (it is optional; absent means\n * unrestricted).\n * - Subject/Issuer Alternative Names only surface the URI and DNS GeneralName forms.\n */\n public static async validateCertificateChain(\n agentContext: AgentContext,\n {\n certificateChain,\n certificate = certificateChain[0],\n verificationDate = new Date(),\n trustedCertificates,\n allowNonRootTrustedCertificate = true,\n }: X509ValidateCertificateChainOptions\n ) {\n const validations: X509ValidationResult['validations'] = {}\n const config = agentContext.dependencyManager.resolve(X509ModuleConfig)\n\n let parsedChain: X509Certificate[]\n\n // Phase 1: Chain validation (structure + signatures + validity + trust)\n try {\n if (certificateChain.length === 0) {\n throw new X509Error('Certificate chain is empty')\n }\n const webCrypto = new CredoWebCrypto(agentContext)\n\n let parsedLeafCertificate: x509.X509Certificate\n let certificatesToBuildChain: x509.X509Certificate[]\n try {\n parsedLeafCertificate = new x509.X509Certificate(certificate)\n certificatesToBuildChain = [...certificateChain, ...(trustedCertificates ?? [])].map(\n (c) => new x509.X509Certificate(c)\n )\n } catch (error) {\n throw new X509Error('Error during parsing of x509 certificate', { cause: error })\n }\n\n const certificateChainBuilder = new x509.X509ChainBuilder({\n certificates: certificatesToBuildChain,\n })\n\n const chain = await certificateChainBuilder.build(parsedLeafCertificate, webCrypto)\n\n // The chain is reversed here as the `x5c` header (the expected input),\n // has the leaf certificate as the first entry, while the `x509` library expects this as the last\n parsedChain = chain.map((c) => X509Certificate.fromRawCertificate(new Uint8Array(c.rawData))).reverse()\n\n // We allow longer parsed chain, in case the root cert was not part of the chain, but in the\n // list of trusted certificates\n if (parsedChain.length < certificateChain.length) {\n throw new X509Error('Could not parse the full chain. Likely due to incorrect ordering')\n }\n\n let previousCertificate: X509Certificate | undefined\n\n if (trustedCertificates) {\n const parsedTrustedCertificates = trustedCertificates.map((trustedCertificate) =>\n X509Certificate.fromEncodedCertificate(trustedCertificate)\n )\n\n const trustedCertificateIndex = parsedChain.findIndex((cert) =>\n parsedTrustedCertificates.some((tCert) => cert.equal(tCert))\n )\n\n if (trustedCertificateIndex === -1) {\n throw new X509Error('No trusted certificate was found while validating the X.509 chain')\n }\n\n if (trustedCertificateIndex > 0) {\n // When we trust a certificate other than the first certificate in the provided chain we keep a reference to the\n // previous certificate as we need the key of this certificate to verify the first certificate in the chain as\n // it's not self-signed.\n previousCertificate = parsedChain[trustedCertificateIndex - 1]\n\n // Pop everything off before the index of the trusted certificate (those are more root) as it is not relevant for validation\n parsedChain = parsedChain.slice(trustedCertificateIndex)\n }\n }\n\n // Verify signatures and validity for all certificates in the chain\n for (let i = 0; i < parsedChain.length; i++) {\n const cert = parsedChain[i]\n const publicJwk = previousCertificate ? previousCertificate.publicJwk : undefined\n\n // When a trusted certificate is the first in the chain and we don't have its parent certificate's public key,\n // we may need to skip signature verification based on configuration.\n //\n // Scenario: For ISO 18013-5 mDL, the root cert MUST NOT be in the chain. If the intermediate/signer\n // certificate is directly trusted, we can't verify its signature without the root certificate.\n // See also https://github.com/openid/OpenID4VCI/issues/62\n //\n // If allowNonRootTrustedCertificate is false, we require the signature to be verifiable.\n const skipSignatureVerification = i === 0 && trustedCertificates && !publicJwk && allowNonRootTrustedCertificate\n\n // If we don't allow non-root trusted certificates and can't verify the signature, throw an error\n if (i === 0 && trustedCertificates && !publicJwk && !allowNonRootTrustedCertificate) {\n throw new X509Error(\n 'Unable to verify the certificate chain. A trusted non-self-signed certificate is the first certificate in the chain, ' +\n 'but no parent certificate was found to verify its signature. Either provide the parent certificate in trustedCertificates, ' +\n 'or set allowNonRootTrustedCertificate to true to allow trusting intermediate certificates without signature verification.'\n )\n }\n\n await cert.verify(\n {\n publicJwk,\n verificationDate,\n skipSignatureVerification,\n },\n webCrypto\n )\n previousCertificate = cert\n }\n\n validations.chain = { isValid: true }\n } catch (error) {\n const x509Error = error instanceof X509Error ? error : new X509Error('Chain validation failed', { cause: error })\n validations.chain = { isValid: false, error: x509Error }\n throw new X509ValidationError(x509Error.message, { isValid: false, validations, error: x509Error })\n }\n\n // Phase 2: Basic constraints validation for CA certificates\n validations.basicConstraints = X509Service.validateBasicConstraintsForChain(parsedChain)\n if (!validations.basicConstraints.isValid) {\n throw new X509ValidationError(\n validations.basicConstraints.error?.message ?? 'Basic constraints validation failed',\n { isValid: false, validations, error: validations.basicConstraints.error }\n )\n }\n\n // Phase 2.5: Key usage validation for issuing CA certificates\n validations.keyUsage = X509Service.validateKeyUsageForChain(parsedChain)\n if (!validations.keyUsage.isValid) {\n throw new X509ValidationError(validations.keyUsage.error?.message ?? 'Key usage validation failed', {\n isValid: false,\n validations,\n error: validations.keyUsage.error,\n })\n }\n\n // Phase 3: Path length constraint validation\n validations.pathLength = X509Service.validatePathLengthConstraints(parsedChain)\n if (!validations.pathLength.isValid) {\n throw new X509ValidationError(\n validations.pathLength.error?.message ?? 'Path length constraint validation failed',\n { isValid: false, validations, error: validations.pathLength.error }\n )\n }\n\n // Phase 4: Critical extension validation\n const peculiarChain = parsedChain.map((cert) => new x509.X509Certificate(cert.rawCertificate))\n validations.criticalExtensions = validateCriticalExtensionsForChain(peculiarChain)\n if (!validations.criticalExtensions.isValid) {\n throw new X509ValidationError(\n validations.criticalExtensions.error?.message ?? 'Critical extension validation failed',\n { isValid: false, validations, error: validations.criticalExtensions.error }\n )\n }\n\n // Phase 5: Revocation checking (if enabled)\n if (config.revocationCheck && config.revocationCheck.mode !== X509RevocationCheckMode.Disabled) {\n validations.revocationStatus = await X509RevocationService.checkCertificateChainRevocation(\n agentContext,\n parsedChain,\n config,\n verificationDate\n )\n if (!validations.revocationStatus.isValid) {\n throw new X509ValidationError(validations.revocationStatus.error?.message ?? 'Revocation check failed', {\n isValid: false,\n validations,\n error: validations.revocationStatus.error,\n })\n }\n } else {\n validations.revocationStatus = { isValid: true, details: 'Revocation checking disabled' }\n }\n\n return parsedChain\n }\n\n /**\n *\n * Parses a base64-encoded X.509 certificate into a {@link X509Certificate}\n *\n */\n public static parseCertificate(\n _agentContext: AgentContext,\n { encodedCertificate }: X509ParseCertificateOptions\n ): X509Certificate {\n const certificate = X509Certificate.fromEncodedCertificate(encodedCertificate)\n\n return certificate\n }\n\n public static getLeafCertificate(\n _agentContext: AgentContext,\n { certificateChain }: X509GetLeafCertificateOptions\n ): X509Certificate {\n if (certificateChain.length === 0) throw new X509Error('Certificate chain is empty')\n\n const certificate = X509Certificate.fromEncodedCertificate(certificateChain[0])\n\n return certificate\n }\n\n public static async createCertificate(agentContext: AgentContext, options: X509CreateCertificateOptions) {\n const webCrypto = new CredoWebCrypto(agentContext)\n\n const certificate = await X509Certificate.create(options, webCrypto)\n\n return certificate\n }\n\n public static async createCertificateSigningRequest(\n agentContext: AgentContext,\n options: X509CreateCertificateSigningRequestOptions\n ) {\n const webCrypto = new CredoWebCrypto(agentContext)\n\n const csr = await CertificateSigningRequest.create(options, webCrypto)\n\n return csr\n }\n\n /**\n * Creates and signs a X.509 Certificate Revocation List (CRL).\n *\n * Supported CRL extensions: CRL Number (§5.2.3), Delta CRL Indicator (§5.2.4), Authority Key\n * Identifier (§5.2.1) and Issuing Distribution Point (§5.2.5), plus the per-entry\n * `certificateIssuer` extension (§5.3.3) for indirect CRLs. Other CRL extensions are not produced.\n *\n * Note: issuer/subject names built from a structured object only support the `CN`, `C`, `OU` and\n * `ST` distinguished-name attributes; pass a raw DN string to use any other attribute.\n */\n public static async createCertificateRevocationList(\n agentContext: AgentContext,\n options: X509CreateCertificateRevocationListOptions\n ) {\n const webCrypto = new CredoWebCrypto(agentContext)\n\n const crl = await X509CertificateRevocationList.create(options, webCrypto)\n\n return crl\n }\n\n public static parseCertificateSigningRequest({\n encodedCertificateSigningRequest,\n }: X509ParseCertificateSigningRequestOptions) {\n const csr = CertificateSigningRequest.fromEncodedCertificateRequest(encodedCertificateSigningRequest)\n\n return csr\n }\n\n /**\n * Validates path length constraints for a certificate chain\n * Per RFC 5280 Section 4.2.1.9\n */\n private static validatePathLengthConstraints(\n certificateChain: X509Certificate[]\n ): X509CertificateSingleValidationResult {\n let currentPathLength = 0\n\n // The chain is ordered root-first (index 0) to leaf (last index). Iterate from the leaf upward,\n // counting the intermediate CAs encountered so each CA's pathLenConstraint can be checked against\n // the number of CAs that follow it toward the leaf.\n for (let i = certificateChain.length - 1; i >= 0; i--) {\n const cert = certificateChain[i]\n const peculiarCert = new x509.X509Certificate(cert.rawCertificate)\n const basicConstraintsExt = peculiarCert.getExtension(X509ExtensionIdentifier.BasicConstraints)\n\n if (!basicConstraintsExt) {\n // Leaf certificates don't need basicConstraints\n if (i === 0) continue\n // But CA certificates should have it\n continue\n }\n\n const basicConstraints = new x509.BasicConstraintsExtension(basicConstraintsExt.rawData)\n\n // Skip leaf certificates (not CA)\n if (!basicConstraints.ca) {\n continue\n }\n\n // Check if pathLength constraint is violated\n if (basicConstraints.pathLength !== undefined) {\n const remainingCAsInChain = currentPathLength\n if (remainingCAsInChain > basicConstraints.pathLength) {\n return {\n isValid: false,\n error: new X509Error(\n `Path length constraint violated. Certificate '${cert.subject}' has pathLength=${basicConstraints.pathLength} ` +\n `but ${remainingCAsInChain} intermediate CA(s) follow it in the chain.`\n ),\n details: `pathLength=${basicConstraints.pathLength}, actual=${remainingCAsInChain}`,\n }\n }\n }\n\n currentPathLength++\n }\n\n return { isValid: true }\n }\n\n /**\n * Validates basic constraints for CA certificates in the chain\n * Per RFC 5280 Section 4.2.1.9\n */\n private static validateBasicConstraintsForChain(\n certificateChain: X509Certificate[]\n ): X509CertificateSingleValidationResult {\n // Check all certificates except the last one (which is typically the end-entity/leaf)\n // In a properly ordered chain, CA certificates will be before the leaf\n for (let i = 0; i < certificateChain.length - 1; i++) {\n const cert = certificateChain[i]\n const peculiarCert = new x509.X509Certificate(cert.rawCertificate)\n const basicConstraintsExt = peculiarCert.getExtension(X509ExtensionIdentifier.BasicConstraints)\n\n if (!basicConstraintsExt) {\n return {\n isValid: false,\n error: new X509Error(\n `Certificate '${cert.subject}' is used as a CA but does not have a basicConstraints extension`\n ),\n }\n }\n\n const basicConstraints = new x509.BasicConstraintsExtension(basicConstraintsExt.rawData)\n\n // Intermediate and root CAs MUST have ca=true\n if (!basicConstraints.ca) {\n return {\n isValid: false,\n error: new X509Error(\n `Certificate '${cert.subject}' is used as a CA but does not have basicConstraints with ca=true`\n ),\n }\n }\n }\n\n return { isValid: true }\n }\n\n /**\n * Validates the Key Usage extension of the issuing CA certificates in the chain.\n * Per RFC 5280 §4.2.1.3 / §6.1.4(n): a certificate used to sign other certificates MUST, when it\n * asserts a Key Usage extension, include the `keyCertSign` bit.\n *\n * Key Usage is optional in a certificate; when the extension is absent, all usages are permitted\n * and no constraint is enforced. Only the issuing certificates (every certificate except the leaf)\n * are checked.\n */\n private static validateKeyUsageForChain(certificateChain: X509Certificate[]): X509CertificateSingleValidationResult {\n // Every certificate except the last one (the leaf/end-entity) issues the next certificate in the\n // chain, mirroring the set checked by validateBasicConstraintsForChain.\n for (let i = 0; i < certificateChain.length - 1; i++) {\n const cert = certificateChain[i]\n const keyUsage = cert.keyUsage\n\n // An empty result means no Key Usage extension is present, so the key is unrestricted and there\n // is nothing to enforce (a well-formed Key Usage extension always asserts at least one bit).\n if (keyUsage && keyUsage.length > 0 && !keyUsage.includes(X509KeyUsage.KeyCertSign)) {\n return {\n isValid: false,\n error: new X509Error(\n `Certificate '${cert.subject}' is used as a CA but its key usage does not include keyCertSign`\n ),\n details: 'Missing keyCertSign key usage',\n }\n }\n }\n\n return { isValid: true }\n }\n}\n"],"mappings":"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"}