@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 • 21.8 kB
Source Map (JSON)
{"version":3,"file":"X509CertificateRevocationList.mjs","names":["AsnIssuingDistributionPoint"],"sources":["../../../src/modules/x509/X509CertificateRevocationList.ts"],"sourcesContent":["import { AsnParser } from '@peculiar/asn1-schema'\nimport { IssuingDistributionPoint as AsnIssuingDistributionPoint, BaseCRLNumber, CRLNumber } from '@peculiar/asn1-x509'\nimport * as x509 from '@peculiar/x509'\nimport {\n CredoWebCrypto,\n CredoWebCryptoKey,\n jwaAlgorithmToKeySignParams,\n publicJwkToCryptoKeyAlgorithm,\n} from '../../crypto/webcrypto'\nimport {\n convertName,\n createAuthorityKeyIdentifierExtension,\n createCrlEntryCertificateIssuerExtension,\n createCrlNumberExtension,\n createDeltaCrlIndicatorExtension,\n createIssuingDistributionPointExtension,\n X509CrlExtensionIdentifier,\n x509SignatureAlgorithmToJwa,\n} from './utils'\nimport { normalizeSerialNumber } from './utils/serialNumber'\nimport { X509Certificate, X509KeyUsage } from './X509Certificate'\nimport type { X509RevocationReason } from './X509CrlDistributionPoint'\nimport { X509Error } from './X509Error'\nimport type { X509CreateCertificateRevocationListOptions } from './X509ServiceOptions'\n\n/**\n * Reason a certificate was revoked, as carried in a CRL entry's `reasonCode` extension\n * (RFC 5280 §5.3.1 `CRLReason`).\n */\nexport enum X509CertificateRevocationListEntryReason {\n Unused = 0,\n KeyCompromise = 1,\n CACompromise = 2,\n AffiliationChanged = 3,\n Superseded = 4,\n CessationOfOperation = 5,\n CertificateHold = 6,\n RemoveFromCrl = 8,\n PrivilegeWithdrawn = 9,\n AACompromise = 10,\n}\n\nexport interface X509CertificateRevocationListEntry {\n serialNumber: string\n revocationDate: Date\n reason?: X509CertificateRevocationListEntryReason\n}\n\n/**\n * Parsed Issuing Distribution Point CRL extension (RFC 5280 §5.2.5), describing the scope of a CRL.\n */\nexport interface X509IssuingDistributionPoint {\n /** Distribution point name as a list of URIs (other GeneralName forms are ignored). */\n fullName: string[]\n /** Whether the CRL only covers end-entity certificates. */\n onlyContainsUserCerts: boolean\n /** Whether the CRL only covers CA certificates. */\n onlyContainsCACerts: boolean\n /** The revocation reasons this CRL is limited to (reasonFlags bits), if scoped. */\n onlySomeReasons?: X509RevocationReason[]\n /** Whether this is an indirect CRL (entries may carry a `certificateIssuer`). */\n indirectCRL: boolean\n /** Whether the CRL only covers attribute certificates. */\n onlyContainsAttributeCerts: boolean\n}\n\n/**\n * Byte-for-byte equality of two `Uint8Array`s.\n */\nfunction uint8ArraysEqual(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false\n }\n return true\n}\n\n/**\n * Wrapper class for X.509 Certificate Revocation List (CRL)\n * Provides a clean interface over the @peculiar/x509 CRL implementation\n */\nexport class X509CertificateRevocationList {\n private crl: x509.X509Crl\n\n private constructor(crl: x509.X509Crl) {\n this.crl = crl\n }\n\n /**\n * Parse a CRL from raw bytes\n */\n public static fromRaw(rawCrl: Uint8Array): X509CertificateRevocationList {\n try {\n const crl = new x509.X509Crl(rawCrl)\n return new X509CertificateRevocationList(crl)\n } catch (error) {\n throw new X509Error('Failed to parse CRL', { cause: error instanceof Error ? error : undefined })\n }\n }\n\n /**\n * Parse a CRL from PEM or base64 encoded string\n */\n public static fromEncoded(encodedCrl: string): X509CertificateRevocationList {\n try {\n const crl = new x509.X509Crl(encodedCrl)\n return new X509CertificateRevocationList(crl)\n } catch (error) {\n throw new X509Error('Failed to parse encoded CRL', { cause: error instanceof Error ? error : undefined })\n }\n }\n\n /**\n * Create and sign a new CRL.\n *\n * Supports the CRL Number, Authority Key Identifier and Issuing Distribution Point extensions, as\n * well as indirect CRLs (per-entry `certificateIssuer`). See `options.extensions` / entry options.\n *\n * NOTE: indirect CRLs and CRLs whose Issuing Distribution Point scope does not cover the\n * certificate being checked are rejected during revocation checking (delta CRLs are likewise not\n * processed), so such CRLs are not treated as authoritative proof that a certificate is unrevoked.\n */\n public static async create(\n options: X509CreateCertificateRevocationListOptions,\n webCrypto: CredoWebCrypto\n ): Promise<X509CertificateRevocationList> {\n const signingKey = new CredoWebCryptoKey(\n options.authorityKey,\n publicJwkToCryptoKeyAlgorithm(options.authorityKey),\n false,\n 'private',\n ['sign']\n )\n\n const entries = options.entries?.map((entry) => {\n const serialNumber = entry.serialNumber ?? entry.certificate?.data.serialNumber\n if (!serialNumber) {\n throw new X509Error('A CRL entry must provide either a serialNumber or a certificate')\n }\n\n return {\n serialNumber,\n revocationDate: entry.revocationDate,\n // X509CrlEntryReason mirrors @peculiar/x509's X509CrlReason (RFC 5280 CRLReason) values.\n reason: entry.reason === undefined ? undefined : (entry.reason as number as x509.X509CrlReason),\n // For indirect CRLs, the certificateIssuer is carried as a (critical) CRL entry extension.\n extensions: entry.issuer === undefined ? undefined : [createCrlEntryCertificateIssuerExtension(entry.issuer)],\n } satisfies x509.X509CrlEntryParams\n })\n\n const extensions: Array<x509.Extension | undefined> = [\n createCrlNumberExtension(options.extensions?.crlNumber),\n createDeltaCrlIndicatorExtension(options.extensions?.deltaCrlIndicator),\n createAuthorityKeyIdentifierExtension(options.extensions?.authorityKeyIdentifier, {\n publicJwk: options.authorityKey,\n }),\n createIssuingDistributionPointExtension(options.extensions?.issuingDistributionPoint),\n ]\n\n const crl = await x509.X509CrlGenerator.create(\n {\n issuer: convertName(options.issuer),\n thisUpdate: options.validity?.thisUpdate,\n nextUpdate: options.validity?.nextUpdate,\n signingKey,\n signingAlgorithm: jwaAlgorithmToKeySignParams(options.authorityKey.signatureAlgorithm),\n entries,\n extensions: extensions.filter((extension): extension is x509.Extension => extension !== undefined),\n },\n webCrypto\n )\n\n return new X509CertificateRevocationList(crl)\n }\n\n /**\n * Get the raw CRL data\n */\n public get rawCertificateRevocationList(): Uint8Array {\n return new Uint8Array(this.crl.rawData)\n }\n\n /**\n * Get the issuer of the CRL\n */\n public get issuer(): string {\n return this.crl.issuer\n }\n\n /**\n * Get the date this CRL was issued\n */\n public get thisUpdate(): Date {\n return this.crl.thisUpdate\n }\n\n /**\n * Get the date when the next CRL will be published\n */\n public get nextUpdate(): Date | undefined {\n return this.crl.nextUpdate\n }\n\n /**\n * Check if the CRL has expired (past nextUpdate date)\n */\n public isExpired(date = new Date()): boolean {\n return this.nextUpdate ? date > this.nextUpdate : false\n }\n\n /**\n * Check if the CRL is not yet valid (before its thisUpdate date).\n */\n public isNotYetValid(date = new Date()): boolean {\n return date < this.thisUpdate\n }\n\n private getMatchingExtensions<T = { critical: boolean }>(objectIdentifier: string): Array<T> {\n return this.crl.extensions.filter((e) => e.type === objectIdentifier) as Array<T>\n }\n\n /**\n * CRL Number (RFC 5280 §5.2.3): the monotonically increasing sequence number of this CRL.\n */\n public get crlNumber(): number | undefined {\n const extensions = this.getMatchingExtensions<x509.Extension>(X509CrlExtensionIdentifier.CrlNumber)\n\n if (extensions.length > 1) {\n throw new X509Error('Multiple CRL Number extensions are not allowed')\n }\n\n const extension = extensions[0]\n if (!extension) return undefined\n\n return AsnParser.parse(extension.value, CRLNumber).value\n }\n\n /**\n * Delta CRL Indicator (RFC 5280 §5.2.4): when present, this CRL is a delta CRL and the value is\n * the CRL Number of the base (complete) CRL it is relative to. `undefined` for a complete CRL.\n */\n public get deltaCrlIndicator(): number | undefined {\n const extensions = this.getMatchingExtensions<x509.Extension>(X509CrlExtensionIdentifier.DeltaCrlIndicator)\n\n if (extensions.length > 1) {\n throw new X509Error('Multiple Delta CRL Indicator extensions are not allowed')\n }\n\n const extension = extensions[0]\n if (!extension) return undefined\n\n return AsnParser.parse(extension.value, BaseCRLNumber).value\n }\n\n /**\n * Issuing Distribution Point (RFC 5280 §5.2.5), describing the scope of this CRL.\n *\n * NOTE: only the URI form of the distribution point name is surfaced; other GeneralName forms and\n * `nameRelativeToCRLIssuer` are ignored.\n */\n public get issuingDistributionPoint(): X509IssuingDistributionPoint | undefined {\n const extensions = this.getMatchingExtensions<x509.Extension>(X509CrlExtensionIdentifier.IssuingDistributionPoint)\n\n if (extensions.length > 1) {\n throw new X509Error('Multiple Issuing Distribution Point extensions are not allowed')\n }\n\n const extension = extensions[0]\n if (!extension) return undefined\n\n const idp = AsnParser.parse(extension.value, AsnIssuingDistributionPoint)\n\n const fullName: string[] = []\n if (idp.distributionPoint?.fullName) {\n for (const generalName of idp.distributionPoint.fullName) {\n if (generalName.uniformResourceIdentifier) {\n fullName.push(generalName.uniformResourceIdentifier)\n }\n }\n }\n\n let onlySomeReasons: X509RevocationReason[] | undefined\n if (idp.onlySomeReasons) {\n // The ASN.1 BIT STRING uses bit `i` for revocation reason code `i` (e.g. keyCompromise = bit 1).\n const reasonBits = idp.onlySomeReasons.toNumber()\n onlySomeReasons = []\n for (let i = 0; i <= 8; i++) {\n if ((reasonBits & (1 << i)) !== 0) {\n onlySomeReasons.push(i)\n }\n }\n }\n\n return {\n fullName,\n onlyContainsUserCerts: idp.onlyContainsUserCerts,\n onlyContainsCACerts: idp.onlyContainsCACerts,\n onlySomeReasons,\n indirectCRL: idp.indirectCRL,\n onlyContainsAttributeCerts: idp.onlyContainsAttributeCerts,\n }\n }\n\n /**\n * The object identifiers of all critical extensions on this CRL.\n */\n public get criticalExtensionIds(): string[] {\n return this.crl.extensions.filter((extension) => extension.critical).map((extension) => extension.type)\n }\n\n /**\n * Whether the extension with the given id is marked critical. Throws if the extension is absent.\n */\n public isExtensionCritical(id: X509CrlExtensionIdentifier | string): boolean {\n const extensions = this.getMatchingExtensions(id)\n if (extensions.length === 0) {\n throw new X509Error(`extension with id '${id}' is not found`)\n }\n\n return !!extensions[0].critical\n }\n\n /**\n * Verify this CRL with the issuer's certificate.\n *\n * NOTE: the CRL summary cache fast path in `X509RevocationService` reproduces the\n * issuer-certificate-dependent checks below (public key, subject name, cRLSign key usage). If a\n * new check depending on the issuer certificate is added here, update that gate in tandem.\n */\n public async verify(\n {\n issuerCertificate,\n verificationDate: now = new Date(),\n }: {\n issuerCertificate: X509Certificate\n verificationDate?: Date\n },\n webCrypto: CredoWebCrypto\n ): Promise<{ isValid: boolean; error?: Error }> {\n // 1. Verify the CRL signature with the issuer's public key.\n try {\n const publicJwk = issuerCertificate.publicJwk\n const cryptoKeyAlgorithm = publicJwkToCryptoKeyAlgorithm(publicJwk, {\n alg: publicJwk.kty === 'RSA' ? x509SignatureAlgorithmToJwa(this.crl.signatureAlgorithm) : undefined,\n })\n const publicCryptoKey = new CredoWebCryptoKey(publicJwk, cryptoKeyAlgorithm, true, 'public', ['verify'])\n\n const isValid = await this.crl.verify({ publicKey: publicCryptoKey }, webCrypto)\n\n if (!isValid) {\n return { isValid: false, error: new X509Error('CRL signature verification failed') }\n }\n } catch (error) {\n return {\n isValid: false,\n error: new X509Error('CRL signature verification failed', {\n cause: error instanceof Error ? error : undefined,\n }),\n }\n }\n\n // 2. The signature only proves the key matches; this binds the issuer name too. Compare the\n // DER-encoded distinguished names (not their string forms) for an exact, encoding-stable match.\n const crlIssuerNameBytes = new Uint8Array(this.crl.issuerName.toArrayBuffer())\n if (!uint8ArraysEqual(crlIssuerNameBytes, issuerCertificate.subjectNameBytes)) {\n return {\n isValid: false,\n error: new X509Error(\n `CRL issuer '${this.issuer}' does not match certificate issuer '${issuerCertificate.subject}'`\n ),\n }\n }\n\n // 3. RFC 5280 §6.3.3(f): the issuer certificate must be authorized to sign CRLs. Key Usage is\n // optional; when present, it MUST include the cRLSign bit. An empty result means the extension is\n // absent, so all usages are permitted (a well-formed Key Usage always asserts at least one bit).\n const issuerKeyUsage = issuerCertificate.keyUsage\n if (issuerKeyUsage && issuerKeyUsage.length > 0 && !issuerKeyUsage.includes(X509KeyUsage.CrlSign)) {\n return {\n isValid: false,\n error: new X509Error(\n `CRL issuer '${issuerCertificate.subject}' is not authorized to sign CRLs (key usage does not include cRLSign)`\n ),\n }\n }\n\n // 4. Check the CRL validity window.\n if (this.isNotYetValid(now)) {\n return {\n isValid: false,\n error: new X509Error(`CRL is not yet valid (thisUpdate: ${this.thisUpdate.toISOString()})`),\n }\n }\n\n if (this.isExpired(now)) {\n return {\n isValid: false,\n error: new X509Error(`CRL has expired (nextUpdate: ${this.nextUpdate?.toISOString() ?? 'unknown'})`),\n }\n }\n\n return { isValid: true }\n }\n\n /**\n * Check if a certificate is revoked in this CRL.\n *\n * We compare serial numbers ourselves (normalizing case and leading zeros) instead of\n * delegating to `@peculiar/x509`'s `X509Crl.findRevoked`. The latter eagerly resolves the\n * global crypto provider (even though it isn't needed for a serial number comparison), which\n * throws when no provider has been registered globally.\n */\n public findRevoked(certificate: X509Certificate): X509CertificateRevocationListEntry | null {\n const target = normalizeSerialNumber(certificate.data.serialNumber)\n\n for (const entry of this.crl.entries) {\n if (normalizeSerialNumber(entry.serialNumber) === target) {\n return {\n serialNumber: entry.serialNumber,\n revocationDate: entry.revocationDate,\n reason: entry.reason as unknown as X509CertificateRevocationListEntryReason | undefined,\n }\n }\n }\n\n return null\n }\n\n /**\n * Get all revoked certificates in this CRL\n */\n public get revokedCertificates(): X509CertificateRevocationListEntry[] {\n const revoked: X509CertificateRevocationListEntry[] = []\n\n for (const entry of this.crl.entries) {\n revoked.push({\n serialNumber: entry.serialNumber,\n revocationDate: entry.revocationDate,\n reason: entry.reason as unknown as X509CertificateRevocationListEntryReason | undefined,\n })\n }\n\n return revoked\n }\n\n /**\n * Get the number of revoked certificates in this CRL\n */\n public get revokedCount(): number {\n return this.crl.entries.length\n }\n\n /**\n * @param format the format to export to, defaults to `pem`\n */\n public toString(format?: 'asn' | 'pem' | 'hex' | 'base64' | 'text' | 'base64url') {\n return this.crl.toString(format ?? 'pem')\n }\n\n public equal(crl: X509CertificateRevocationList) {\n const parsedOther = new x509.X509Crl(crl.rawCertificateRevocationList)\n\n return this.crl.equal(parsedOther)\n }\n}\n"],"mappings":"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"}