micro509 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +220 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +1 -0
- package/dist/internal/asn1/asn1.js +2 -0
- package/dist/internal/asn1/asn1.js.map +1 -0
- package/dist/internal/asn1/der.js +2 -0
- package/dist/internal/asn1/der.js.map +1 -0
- package/dist/internal/asn1/oids.js +2 -0
- package/dist/internal/asn1/oids.js.map +1 -0
- package/dist/internal/crypto/algorithm-names.js +2 -0
- package/dist/internal/crypto/algorithm-names.js.map +1 -0
- package/dist/internal/crypto/ecdsa.js +2 -0
- package/dist/internal/crypto/ecdsa.js.map +1 -0
- package/dist/internal/crypto/hash.js +2 -0
- package/dist/internal/crypto/hash.js.map +1 -0
- package/dist/internal/crypto/pbes2.d.ts +23 -0
- package/dist/internal/crypto/pbes2.js +2 -0
- package/dist/internal/crypto/pbes2.js.map +1 -0
- package/dist/internal/crypto/rsa-pss.js +2 -0
- package/dist/internal/crypto/rsa-pss.js.map +1 -0
- package/dist/internal/crypto/sig-verify.js +2 -0
- package/dist/internal/crypto/sig-verify.js.map +1 -0
- package/dist/internal/crypto/signing.d.ts +16 -0
- package/dist/internal/crypto/signing.js +2 -0
- package/dist/internal/crypto/signing.js.map +1 -0
- package/dist/internal/crypto/webcrypto.js +2 -0
- package/dist/internal/crypto/webcrypto.js.map +1 -0
- package/dist/internal/shared/base64.js +2 -0
- package/dist/internal/shared/base64.js.map +1 -0
- package/dist/internal/shared/dn.js +2 -0
- package/dist/internal/shared/dn.js.map +1 -0
- package/dist/internal/shared/ip.js +2 -0
- package/dist/internal/shared/ip.js.map +1 -0
- package/dist/internal/verify/name-constraints-engine.js +2 -0
- package/dist/internal/verify/name-constraints-engine.js.map +1 -0
- package/dist/internal/verify/policy-engine.js +2 -0
- package/dist/internal/verify/policy-engine.js.map +1 -0
- package/dist/internal/verify/verify-path.js +2 -0
- package/dist/internal/verify/verify-path.js.map +1 -0
- package/dist/internal/x509/extension-bits.d.ts +18 -0
- package/dist/internal/x509/extension-bits.js +2 -0
- package/dist/internal/x509/extension-bits.js.map +1 -0
- package/dist/internal/x509/extension-registry.js +2 -0
- package/dist/internal/x509/extension-registry.js.map +1 -0
- package/dist/internal/x509/name-fields.js +2 -0
- package/dist/internal/x509/name-fields.js.map +1 -0
- package/dist/keys/keys.d.ts +431 -0
- package/dist/keys/keys.js +5 -0
- package/dist/keys/keys.js.map +1 -0
- package/dist/keys.d.ts +3 -0
- package/dist/keys.js +1 -0
- package/dist/pem/pem.d.ts +56 -0
- package/dist/pem/pem.js +6 -0
- package/dist/pem/pem.js.map +1 -0
- package/dist/pem.d.ts +2 -0
- package/dist/pem.js +1 -0
- package/dist/pkcs/pfx.d.ts +177 -0
- package/dist/pkcs/pfx.js +2 -0
- package/dist/pkcs/pfx.js.map +1 -0
- package/dist/pkcs/pkcs12-mac.d.ts +41 -0
- package/dist/pkcs/pkcs12-mac.js +2 -0
- package/dist/pkcs/pkcs12-mac.js.map +1 -0
- package/dist/pkcs/pkcs7.d.ts +131 -0
- package/dist/pkcs/pkcs7.js +2 -0
- package/dist/pkcs/pkcs7.js.map +1 -0
- package/dist/pkcs.d.ts +5 -0
- package/dist/pkcs.js +1 -0
- package/dist/result/result.d.ts +68 -0
- package/dist/result/result.js +2 -0
- package/dist/result/result.js.map +1 -0
- package/dist/result.d.ts +2 -0
- package/dist/result.js +1 -0
- package/dist/revocation/chain.d.ts +180 -0
- package/dist/revocation/chain.js +2 -0
- package/dist/revocation/chain.js.map +1 -0
- package/dist/revocation/crl.d.ts +316 -0
- package/dist/revocation/crl.js +2 -0
- package/dist/revocation/crl.js.map +1 -0
- package/dist/revocation/ocsp.d.ts +332 -0
- package/dist/revocation/ocsp.js +2 -0
- package/dist/revocation/ocsp.js.map +1 -0
- package/dist/revocation/revocation.d.ts +168 -0
- package/dist/revocation/revocation.js +2 -0
- package/dist/revocation/revocation.js.map +1 -0
- package/dist/revocation.d.ts +5 -0
- package/dist/revocation.js +1 -0
- package/dist/verify/identity.d.ts +129 -0
- package/dist/verify/identity.js +2 -0
- package/dist/verify/identity.js.map +1 -0
- package/dist/verify/name-constraints.d.ts +18 -0
- package/dist/verify/policy.d.ts +39 -0
- package/dist/verify/verify.d.ts +404 -0
- package/dist/verify/verify.js +2 -0
- package/dist/verify/verify.js.map +1 -0
- package/dist/verify.d.ts +5 -0
- package/dist/verify.js +1 -0
- package/dist/x509/certificate.d.ts +191 -0
- package/dist/x509/certificate.js +2 -0
- package/dist/x509/certificate.js.map +1 -0
- package/dist/x509/csr.d.ts +55 -0
- package/dist/x509/csr.js +2 -0
- package/dist/x509/csr.js.map +1 -0
- package/dist/x509/extensions.d.ts +550 -0
- package/dist/x509/extensions.js +2 -0
- package/dist/x509/extensions.js.map +1 -0
- package/dist/x509/name.d.ts +140 -0
- package/dist/x509/name.js +2 -0
- package/dist/x509/name.js.map +1 -0
- package/dist/x509/parse.d.ts +377 -0
- package/dist/x509/parse.js +2 -0
- package/dist/x509/parse.js.map +1 -0
- package/dist/x509.d.ts +8 -0
- package/dist/x509.js +1 -0
- package/package.json +153 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crl.js","names":[],"sources":["../../src/revocation/crl.ts"],"sourcesContent":["/**\n * Full CRL lifecycle: create, parse, verify, validate, and revocation check.\n *\n * Supports complete and delta CRLs, issuing distribution point scoping,\n * indirect CRL processing, per-entry reason codes, and invalidity dates.\n *\n * @module\n */\n\nimport {\n\tchildrenOf,\n\tdecodeBoolean,\n\tdecodeIntegerNumber,\n\tdecodeObjectIdentifier,\n\tdecodeString,\n\textractBitStringValue,\n\thexToBytes,\n\tparseTime,\n\trequireElement,\n\ttoHex,\n} from '#micro509/internal/asn1/asn1.ts';\nimport type { DerElement } from '#micro509/internal/asn1/der.ts';\nimport {\n\tbitString,\n\tconcatBytes,\n\tDEFAULT_MAX_DER_DEPTH,\n\texplicitContext,\n\tgeneralizedTime,\n\timplicitConstructedContext,\n\timplicitPrimitiveContext,\n\tinteger,\n\tintegerFromNumber,\n\treadElement,\n\treadRootElement,\n\treadSequenceChildren,\n\tsequence,\n\ttime,\n\ttlv,\n} from '#micro509/internal/asn1/der.ts';\nimport { OIDS } from '#micro509/internal/asn1/oids.ts';\nimport { describeSignatureAlgorithm } from '#micro509/internal/crypto/algorithm-names.ts';\nimport { verifySignedDataDetailed } from '#micro509/internal/crypto/sig-verify.ts';\nimport {\n\tencodeAlgorithmIdentifier,\n\tgetSignatureAlgorithm,\n\tsignBytes,\n} from '#micro509/internal/crypto/signing.ts';\nimport { base64Encode } from '#micro509/internal/shared/base64.ts';\nimport {\n\tcompareDistinguishedNames,\n\tcompareNameAttributeValue,\n} from '#micro509/internal/shared/dn.ts';\nimport { decodeIpAddress } from '#micro509/internal/shared/ip.ts';\nimport {\n\tencodeDistributionPointReasonFlagsContent,\n\ttype ParsedBitFlags,\n\tparseDistributionPointReasonFlagsContent,\n} from '#micro509/internal/x509/extension-bits.ts';\nimport { exportSpkiDer } from '#micro509/keys/keys.ts';\nimport { pemDecode, pemEncode } from '#micro509/pem/pem.ts';\nimport type { ErrorResult, Micro509Error } from '#micro509/result/result.ts';\nimport type {\n\tDistributionPoint,\n\tDistributionPointReason,\n\tGeneralName,\n\tIssuingDistributionPoint,\n} from '#micro509/x509/extensions.ts';\nimport {\n\tbuildSubjectKeyIdentifier,\n\tencodeCrlDistributionPoints,\n\tencodeExtension,\n\tencodeSubjectAltName,\n} from '#micro509/x509/extensions.ts';\nimport type { NameFieldKey, NameInput } from '#micro509/x509/name.ts';\nimport {\n\tencodeName,\n\tencodeRelativeDistinguishedName,\n\tnameFieldKeyFromOid,\n} from '#micro509/x509/name.ts';\nimport type {\n\tParsedCertificate,\n\tParsedDistributionPoint,\n\tParsedDistributionPointName,\n\tParsedIssuingDistributionPoint,\n\tParsedName,\n\tParsedNameAttribute,\n\tParsedRelativeDistinguishedName,\n} from '#micro509/x509/parse.ts';\nimport { parseCertificateDer, parseCertificateFromSource } from '#micro509/x509/parse.ts';\n\n/**\n * Single revoked certificate entry for {@linkcode createCertificateRevocationList}.\n */\nexport interface RevokedCertificateInput {\n\t/** DER-encoded certificate serial number to revoke. */\n\treadonly serialNumber: Uint8Array;\n\t/** When the certificate was revoked. Defaults to `thisUpdate` of the CRL. */\n\treadonly revocationDate?: Date;\n\t/** RFC 5280 CRLReason code. Omit for `unspecified`. */\n\treadonly reasonCode?: RevocationReason;\n\t/** When the key or certificate became suspect — may predate `revocationDate`. */\n\treadonly invalidityDate?: Date;\n}\n\n/**\n * RFC 5280 §5.3.1 CRLReason code values.\n *\n * `removeFromCRL` is used in delta CRLs to un-hold a certificate.\n */\nexport type RevocationReason =\n\t| 'unspecified'\n\t| 'keyCompromise'\n\t| 'cACompromise'\n\t| 'affiliationChanged'\n\t| 'superseded'\n\t| 'cessationOfOperation'\n\t| 'certificateHold'\n\t| 'removeFromCRL'\n\t| 'privilegeWithdrawn'\n\t| 'aACompromise';\n\n/**\n * Input for {@linkcode createCertificateRevocationList}.\n */\nexport interface CreateCertificateRevocationListInput {\n\t/** Distinguished name of the CRL issuer (typically the signing CA). */\n\treadonly issuer: NameInput;\n\t/** Private key used to sign the CRL. Algorithm is inferred from the key. */\n\treadonly signerPrivateKey: CryptoKey;\n\t/** Issuer public key — used to embed an Authority Key Identifier extension. */\n\treadonly issuerPublicKey?: CryptoKey;\n\t/** Issuance timestamp. Defaults to `new Date()`. */\n\treadonly thisUpdate?: Date;\n\t/** Planned next issuance. Omit for an open-ended CRL. */\n\treadonly nextUpdate?: Date;\n\t/** Certificates to list as revoked in this CRL. */\n\treadonly revokedCertificates?: readonly RevokedCertificateInput[];\n\t/** Monotonically-increasing CRL sequence number (CRLNumber extension). */\n\treadonly crlNumber?: number;\n\t/** If set, marks this CRL as a delta CRL referencing the given base CRL number. */\n\treadonly baseCrlNumber?: number;\n\t/** Issuing distribution point extension — scopes this CRL to a subset of certificates. */\n\treadonly issuingDistributionPoint?: IssuingDistributionPoint;\n\t/** Freshest CRL distribution points — tells relying parties where to find delta CRLs. */\n\treadonly freshestCrlDistributionPoints?: readonly DistributionPoint[];\n}\n\n/**\n * Encoded CRL in multiple serialisation formats, returned by {@linkcode createCertificateRevocationList}.\n */\nexport interface CertificateRevocationListMaterial {\n\t/** Raw DER bytes of the signed CRL. */\n\treadonly der: Uint8Array;\n\t/** PEM-encoded CRL (`-----BEGIN X509 CRL-----`). */\n\treadonly pem: string;\n\t/** Base64-encoded DER (no PEM armour). */\n\treadonly base64: string;\n}\n\n/**\n * A single revoked-certificate entry decoded from a CRL.\n */\nexport interface ParsedRevokedCertificate {\n\t/** Hex-encoded serial number of the revoked certificate. */\n\treadonly serialNumberHex: string;\n\t/** When the CA declared this certificate revoked. */\n\treadonly revocationDate: Date;\n\t/** RFC 5280 CRLReason, if the entry carries one. */\n\treadonly reasonCode?: RevocationReason;\n\t/** When the key or certificate actually became suspect, if present. */\n\treadonly invalidityDate?: Date;\n\t/** Indirect-CRL certificate issuer override (RFC 5280 §5.3.3). */\n\treadonly certificateIssuer?: readonly GeneralName[];\n}\n\n/**\n * Decoded X.509 CRL, returned by {@linkcode parseCertificateRevocationListDer}\n * and {@linkcode parseCertificateRevocationListPem}.\n */\nexport interface ParsedCertificateRevocationList {\n\t/** Original DER bytes when this object came from {@linkcode parseCertificateRevocationListDer} or PEM parsing. */\n\treadonly der?: Uint8Array;\n\t/** CRL version (1 = v1, 2 = v2 with extensions). */\n\treadonly version: number;\n\t/** DER-encoded TBSCertList — the signed payload for signature verification. */\n\treadonly tbsCertListDer: Uint8Array;\n\t/** Raw signature bytes from the CRL outer wrapper. */\n\treadonly signatureValue: Uint8Array;\n\t/** CRL issuer distinguished name. */\n\treadonly issuer: ParsedName;\n\t/** Start of the CRL validity window. */\n\treadonly thisUpdate: Date;\n\t/** End of the CRL validity window. Absent if the CA does not commit to a schedule. */\n\treadonly nextUpdate?: Date;\n\t/** OID of the algorithm used to sign this CRL. */\n\treadonly signatureAlgorithmOid: string;\n\t/** Human-readable signature algorithm name (e.g. `\"ECDSA with SHA-256\"`). */\n\treadonly signatureAlgorithmName: string;\n\t/** DER-encoded signature algorithm parameters (e.g. DER NULL for RSA PKCS#1 v1.5). */\n\treadonly signatureAlgorithmParametersDer?: Uint8Array;\n\t/** OID of the issuer's public key algorithm, when available. */\n\treadonly issuerPublicKeyAlgorithmOid?: string;\n\t/** OID of the issuer's public key parameters (e.g. named curve), when available. */\n\treadonly issuerPublicKeyParametersOid?: string;\n\t/** Hex-encoded Authority Key Identifier, if the extension is present. */\n\treadonly authorityKeyIdentifier?: string;\n\t/** CRLNumber extension value — monotonically increasing sequence number. */\n\treadonly crlNumber?: number;\n\t/** Delta CRL indicator — present only on delta CRLs, referencing the base CRL number. */\n\treadonly baseCrlNumber?: number;\n\t/** Issuing distribution point extension — scopes this CRL to a certificate subset. */\n\treadonly issuingDistributionPoint?: ParsedIssuingDistributionPoint;\n\t/** Freshest CRL extension — points to delta CRL locations. */\n\treadonly freshestCrlDistributionPoints?: readonly ParsedDistributionPoint[];\n\t/** All revoked certificate entries (empty array if none). */\n\treadonly revokedCertificates: readonly ParsedRevokedCertificate[];\n}\n\n/** RFC 5280 §5.3.1 CRLReason integer codes, keyed by reason name. */\nconst REVOCATION_REASON_CODES: Record<RevocationReason, number> = {\n\tunspecified: 0,\n\tkeyCompromise: 1,\n\tcACompromise: 2,\n\taffiliationChanged: 3,\n\tsuperseded: 4,\n\tcessationOfOperation: 5,\n\tcertificateHold: 6,\n\tremoveFromCRL: 8,\n\tprivilegeWithdrawn: 9,\n\taACompromise: 10,\n};\n\n/** PEM string, DER bytes, or already-parsed CRL. */\nexport type CrlSource = string | Uint8Array | ParsedCertificateRevocationList;\n/** PEM string, DER bytes, or already-parsed certificate. */\nexport type CrlCertificateSource = string | Uint8Array | ParsedCertificate;\n\n/** Failure detail when CRL signature verification fails. */\nexport interface VerifyCertificateRevocationListFailure extends Micro509Error<'signature_invalid'> {\n\t/** Always `false` for failures. */\n\treadonly ok: false;\n}\n\n/**\n * Result of {@linkcode verifyCertificateRevocationList}.\n *\n * On success, `value` is the parsed CRL whose signature has been verified.\n */\nexport type VerifyCertificateRevocationListResult =\n\t| {\n\t\t\treadonly ok: true;\n\t\t\t/** Parsed CRL with a verified signature. */\n\t\t\treadonly value: ParsedCertificateRevocationList;\n\t }\n\t| ErrorResult<'signature_invalid', Record<never, never>, VerifyCertificateRevocationListFailure>;\n\n/**\n * Input for {@linkcode validateCertificateRevocationList}.\n */\nexport interface ValidateCertificateRevocationListInput {\n\t/** The CRL to validate. */\n\treadonly crl: CrlSource;\n\t/** Certificate of the CA that should have signed the CRL. */\n\treadonly issuerCertificate: CrlCertificateSource;\n\t/** Evaluation time for freshness checks. Defaults to `new Date()`. */\n\treadonly at?: Date;\n\t/** Tolerance in milliseconds for clock skew when checking `thisUpdate`/`nextUpdate`. */\n\treadonly clockSkewMs?: number;\n}\n\n/**\n * Failure detail for {@linkcode validateCertificateRevocationList}.\n *\n * Possible codes: `signature_invalid`, `issuer_mismatch`, `stale_crl`, `crl_sign_not_permitted`.\n */\nexport interface ValidateCertificateRevocationListFailure\n\textends Micro509Error<\n\t\t'signature_invalid' | 'issuer_mismatch' | 'stale_crl' | 'crl_sign_not_permitted'\n\t> {\n\t/** Always `false` for failures. */\n\treadonly ok: false;\n}\n\n/**\n * Result of {@linkcode validateCertificateRevocationList}.\n *\n * On success, the CRL has passed signature, issuer, key-usage, and freshness checks.\n */\nexport type ValidateCertificateRevocationListResult =\n\t| {\n\t\t\treadonly ok: true;\n\t\t\t/** Validated and parsed CRL. */\n\t\t\treadonly value: ParsedCertificateRevocationList;\n\t }\n\t| ErrorResult<\n\t\t\t'signature_invalid' | 'issuer_mismatch' | 'stale_crl' | 'crl_sign_not_permitted',\n\t\t\tRecord<never, never>,\n\t\t\tValidateCertificateRevocationListFailure\n\t >;\n\n/**\n * Input for {@linkcode checkCertificateRevocationAgainstCrl}.\n */\nexport interface CheckCertificateRevocationAgainstCrlInput {\n\t/** Certificate whose revocation status to check. */\n\treadonly certificate: CrlCertificateSource;\n\t/** Issuer of `certificate` — also expected signer of the CRL. */\n\treadonly issuerCertificate: CrlCertificateSource;\n\t/** Complete (base) CRL to check against. */\n\treadonly crl: CrlSource;\n\t/** Optional delta CRL for more recent revocation information. */\n\treadonly deltaCrl?: CrlSource;\n\t/** Evaluation time. Defaults to `new Date()`. */\n\treadonly at?: Date;\n\t/** Clock-skew tolerance in milliseconds for freshness checks. */\n\treadonly clockSkewMs?: number;\n}\n\n/** Error codes that {@linkcode checkCertificateRevocationAgainstCrl} may return. */\nexport type CheckCertificateRevocationAgainstCrlErrorCode =\n\t| 'signature_invalid'\n\t| 'issuer_mismatch'\n\t| 'stale_crl'\n\t| 'crl_sign_not_permitted'\n\t| 'non_applicable';\n\n/** Structured reason why a CRL was deemed non-applicable to a given certificate. */\nexport type CrlApplicabilityFailureReason =\n\t| 'certificate_scope_mismatch'\n\t| 'delta_crl_incompatible'\n\t| 'delta_crl_unsupported'\n\t| 'distribution_point_mismatch'\n\t| 'indirect_crl_unsupported'\n\t| 'issuer_mismatch'\n\t| 'reasons_mismatch';\n\n/** Internal result of looking up a serial number in a CRL's revoked entries. */\ntype RevokedCertificateLookupResult =\n\t| {\n\t\t\treadonly ok: true;\n\t\t\t/** Matching revoked entry, if found. */\n\t\t\treadonly entry?: ParsedRevokedCertificate;\n\t }\n\t| ErrorResult<\n\t\t\tCheckCertificateRevocationAgainstCrlErrorCode,\n\t\t\tCheckCertificateRevocationAgainstCrlFailureDetails,\n\t\t\tCheckCertificateRevocationAgainstCrlFailure\n\t >;\n\n/** Structured details attached to a {@linkcode CheckCertificateRevocationAgainstCrlFailure}. */\nexport interface CheckCertificateRevocationAgainstCrlFailureDetails {\n\t/** Why the CRL was non-applicable, when the error code is `non_applicable`. */\n\treadonly reason?: CrlApplicabilityFailureReason;\n}\n\n/** Failure detail for {@linkcode checkCertificateRevocationAgainstCrl}. */\nexport interface CheckCertificateRevocationAgainstCrlFailure\n\textends Micro509Error<\n\t\tCheckCertificateRevocationAgainstCrlErrorCode,\n\t\tCheckCertificateRevocationAgainstCrlFailureDetails\n\t> {\n\t/** Always `false` for failures. */\n\treadonly ok: false;\n}\n\n/** Success value when the certificate is not found in the CRL. */\nexport interface CheckCertificateRevocationAgainstCrlGoodValue {\n\t/** Certificate is not revoked. */\n\treadonly status: 'good';\n\t/** The validated CRL that was checked. */\n\treadonly crl: ParsedCertificateRevocationList;\n}\n\n/** Success value when the certificate is found as revoked in the CRL. */\nexport interface CheckCertificateRevocationAgainstCrlRevokedValue {\n\t/** Certificate is revoked. */\n\treadonly status: 'revoked';\n\t/** The validated CRL that contained the revocation entry. */\n\treadonly crl: ParsedCertificateRevocationList;\n\t/** When the CA declared this certificate revoked. */\n\treadonly revocationDate: Date;\n\t/** CRLReason from the entry, if present. */\n\treadonly reasonCode?: RevocationReason;\n}\n\n/** Discriminated union of `good` and `revoked` outcomes. */\nexport type CheckCertificateRevocationAgainstCrlValue =\n\t| CheckCertificateRevocationAgainstCrlGoodValue\n\t| CheckCertificateRevocationAgainstCrlRevokedValue;\n\n/**\n * Result of {@linkcode checkCertificateRevocationAgainstCrl}.\n *\n * On success `value.status` is `'good'` or `'revoked'`.\n * On failure the CRL could not be validated or was non-applicable.\n */\nexport type CheckCertificateRevocationAgainstCrlResult =\n\t| {\n\t\t\treadonly ok: true;\n\t\t\treadonly value: CheckCertificateRevocationAgainstCrlValue;\n\t }\n\t| ErrorResult<\n\t\t\tCheckCertificateRevocationAgainstCrlErrorCode,\n\t\t\tCheckCertificateRevocationAgainstCrlFailureDetails,\n\t\t\tCheckCertificateRevocationAgainstCrlFailure\n\t >;\n\n/**\n * Signs and encodes an X.509 v2 CRL.\n *\n * Embeds Authority Key Identifier, CRLNumber, delta CRL indicator,\n * issuing distribution point, and freshest-CRL extensions as configured.\n *\n * @example\n * ```ts\n * import { createCertificateRevocationList } from 'micro509';\n *\n * const crl = await createCertificateRevocationList({\n * issuer: { commonName: 'Example CA' },\n * signerPrivateKey: caPrivateKey,\n * issuerPublicKey: caPublicKey,\n * thisUpdate: new Date('2025-01-01'),\n * nextUpdate: new Date('2025-02-01'),\n * crlNumber: 42,\n * revokedCertificates: [\n * { serialNumber: revokedSerial, reasonCode: 'keyCompromise' },\n * ],\n * });\n * // crl.pem, crl.der, crl.base64\n * ```\n */\nexport async function createCertificateRevocationList(\n\tinput: CreateCertificateRevocationListInput,\n): Promise<CertificateRevocationListMaterial> {\n\tconst signatureAlgorithm = getSignatureAlgorithm(input.signerPrivateKey);\n\tconst thisUpdate = input.thisUpdate ?? new Date();\n\tconst nextUpdate = input.nextUpdate;\n\tconst extensions = await buildCrlExtensions(\n\t\tinput.issuerPublicKey,\n\t\tinput.crlNumber,\n\t\tinput.baseCrlNumber,\n\t\tinput.issuingDistributionPoint,\n\t\tinput.freshestCrlDistributionPoints,\n\t);\n\tconst revoked = input.revokedCertificates ?? [];\n\tconst revokedSequence =\n\t\trevoked.length === 0\n\t\t\t? []\n\t\t\t: [sequence(revoked.map((entry) => createRevokedCertificate(entry, thisUpdate)))];\n\tconst tbsCertList = sequence([\n\t\tintegerFromNumber(1),\n\t\tencodeAlgorithmIdentifier(signatureAlgorithm),\n\t\tencodeName(input.issuer),\n\t\ttime(thisUpdate),\n\t\t...(nextUpdate === undefined ? [] : [time(nextUpdate)]),\n\t\t...revokedSequence,\n\t\t...(extensions.length === 0 ? [] : [explicitContext(0, sequence(extensions))]),\n\t]);\n\tconst signatureValue = await signBytes(input.signerPrivateKey, signatureAlgorithm, tbsCertList);\n\tconst der = sequence([\n\t\ttbsCertList,\n\t\tencodeAlgorithmIdentifier(signatureAlgorithm),\n\t\tbitString(signatureValue),\n\t]);\n\treturn {\n\t\tder,\n\t\tpem: pemEncode('X509 CRL', der),\n\t\tbase64: base64Encode(der),\n\t};\n}\n\n/**\n * Decodes a DER-encoded X.509 CRL into a structured {@linkcode ParsedCertificateRevocationList}.\n *\n * Does not verify the signature — call {@linkcode verifyCertificateRevocationList} or\n * {@linkcode validateCertificateRevocationList} for that.\n */\nexport function parseCertificateRevocationListDer(\n\tder: Uint8Array,\n): ParsedCertificateRevocationList {\n\tconst top = readSequenceChildren(der, { maxDepth: DEFAULT_MAX_DER_DEPTH });\n\tif (top.length !== 3) {\n\t\tthrow new Error('Malformed CRL');\n\t}\n\tconst tbsCertList = requireElement(top[0], 'TBSCertList');\n\tconst signatureAlgorithm = requireElement(top[1], 'signatureAlgorithm');\n\tconst signatureValue = requireElement(top[2], 'signatureValue');\n\tconst signedFields = parseSignedCrlFields(\n\t\tder.slice(tbsCertList.start - tbsCertList.headerLength, tbsCertList.end),\n\t);\n\tconst parsedSignatureAlgorithm = parseAlgorithmIdentifier(der, signatureAlgorithm);\n\treturn {\n\t\tder: new Uint8Array(der),\n\t\tversion: signedFields.version,\n\t\ttbsCertListDer: der.slice(tbsCertList.start - tbsCertList.headerLength, tbsCertList.end),\n\t\tsignatureValue: extractBitStringValue(signatureValue),\n\t\tissuer: signedFields.issuer,\n\t\tthisUpdate: signedFields.thisUpdate,\n\t\t...(signedFields.nextUpdate === undefined ? {} : { nextUpdate: signedFields.nextUpdate }),\n\t\tsignatureAlgorithmOid: parsedSignatureAlgorithm.oid,\n\t\tsignatureAlgorithmName: describeSignatureAlgorithm(\n\t\t\tparsedSignatureAlgorithm.oid,\n\t\t\tparsedSignatureAlgorithm.parametersDer,\n\t\t),\n\t\t...(parsedSignatureAlgorithm.parametersDer === undefined\n\t\t\t? {}\n\t\t\t: { signatureAlgorithmParametersDer: parsedSignatureAlgorithm.parametersDer }),\n\t\t...(signedFields.authorityKeyIdentifier === undefined\n\t\t\t? {}\n\t\t\t: { authorityKeyIdentifier: signedFields.authorityKeyIdentifier }),\n\t\t...(signedFields.crlNumber === undefined ? {} : { crlNumber: signedFields.crlNumber }),\n\t\t...(signedFields.baseCrlNumber === undefined\n\t\t\t? {}\n\t\t\t: { baseCrlNumber: signedFields.baseCrlNumber }),\n\t\t...(signedFields.issuingDistributionPoint === undefined\n\t\t\t? {}\n\t\t\t: { issuingDistributionPoint: signedFields.issuingDistributionPoint }),\n\t\t...(signedFields.freshestCrlDistributionPoints === undefined\n\t\t\t? {}\n\t\t\t: { freshestCrlDistributionPoints: signedFields.freshestCrlDistributionPoints }),\n\t\trevokedCertificates: signedFields.revokedCertificates,\n\t};\n}\n\n/**\n * Decodes a PEM-encoded X.509 CRL (`-----BEGIN X509 CRL-----`).\n *\n * @example\n * ```ts\n * import { parseCertificateRevocationListPem } from 'micro509';\n *\n * const crl = parseCertificateRevocationListPem(pemString);\n * console.log(crl.issuer.values.commonName, crl.revokedCertificates.length);\n * ```\n */\nexport function parseCertificateRevocationListPem(pem: string): ParsedCertificateRevocationList {\n\treturn parseCertificateRevocationListDer(pemDecode('X509 CRL', pem));\n}\n\n/**\n * Verifies the CRL signature against the issuer certificate's public key.\n *\n * Does **not** check issuer name match, key-usage, or freshness — use\n * {@linkcode validateCertificateRevocationList} for full validation.\n */\nexport async function verifyCertificateRevocationList(\n\tcrl: string | Uint8Array,\n\tissuerCertificate: string | Uint8Array,\n): Promise<VerifyCertificateRevocationListResult> {\n\tlet parsedCrl: ParsedCertificateRevocationList;\n\tlet issuer: ParsedCertificate;\n\ttry {\n\t\tparsedCrl =\n\t\t\ttypeof crl === 'string'\n\t\t\t\t? parseCertificateRevocationListPem(crl)\n\t\t\t\t: parseCertificateRevocationListDer(new Uint8Array(crl));\n\t\tissuer =\n\t\t\ttypeof issuerCertificate === 'string'\n\t\t\t\t? parseIssuerCertificatePem(issuerCertificate)\n\t\t\t\t: parseIssuerCertificateDer(new Uint8Array(issuerCertificate));\n\t} catch {\n\t\treturn verifyCertificateRevocationListFailureResult(\n\t\t\t'signature_invalid',\n\t\t\t'certificate revocation list or issuer certificate input is malformed',\n\t\t);\n\t}\n\tlet verifiedResult: Awaited<ReturnType<typeof verifySignedDataDetailed>>;\n\ttry {\n\t\tverifiedResult = await verifySignedDataDetailed(\n\t\t\tparsedCrl.signatureAlgorithmOid,\n\t\t\tparsedCrl.signatureAlgorithmParametersDer,\n\t\t\tissuer.publicKeyAlgorithmOid,\n\t\t\tissuer.publicKeyParametersOid,\n\t\t\tissuer.subjectPublicKeyInfoDer,\n\t\t\tparsedCrl.signatureValue,\n\t\t\tparsedCrl.tbsCertListDer,\n\t\t);\n\t} catch {\n\t\treturn verifyCertificateRevocationListFailureResult(\n\t\t\t'signature_invalid',\n\t\t\t'certificate revocation list signature verification failed',\n\t\t);\n\t}\n\tif (!verifiedResult.ok) {\n\t\tif (verifiedResult.code === 'verification_error') {\n\t\t\treturn verifyCertificateRevocationListFailureResult(\n\t\t\t\t'signature_invalid',\n\t\t\t\t'certificate revocation list signature verification failed',\n\t\t\t);\n\t\t}\n\t\treturn verifyCertificateRevocationListFailureResult(\n\t\t\t'signature_invalid',\n\t\t\t'certificate revocation list signature uses unsupported algorithm parameters',\n\t\t);\n\t}\n\treturn verifiedResult.valid\n\t\t? { ok: true, value: parsedCrl }\n\t\t: verifyCertificateRevocationListFailureResult(\n\t\t\t\t'signature_invalid',\n\t\t\t\t'certificate revocation list signature does not verify',\n\t\t\t);\n}\n\n/**\n * Full CRL validation: issuer name match, authority key identifier match,\n * cRLSign key-usage check, signature verification, and `thisUpdate`/`nextUpdate`\n * freshness check (with optional clock-skew tolerance).\n */\nexport async function validateCertificateRevocationList(\n\tinput: ValidateCertificateRevocationListInput,\n): Promise<ValidateCertificateRevocationListResult> {\n\tlet parsedCrl: ParsedCertificateRevocationList;\n\ttry {\n\t\tparsedCrl = normalizeCrl(input.crl);\n\t} catch {\n\t\treturn validateCertificateRevocationListFailureResult(\n\t\t\t'signature_invalid',\n\t\t\t'certificate revocation list signed content is malformed',\n\t\t);\n\t}\n\tlet issuer: ParsedCertificate;\n\ttry {\n\t\tissuer = normalizeCrlCertificate(input.issuerCertificate);\n\t} catch {\n\t\treturn validateCertificateRevocationListFailureResult(\n\t\t\t'signature_invalid',\n\t\t\t'issuer certificate input is malformed',\n\t\t);\n\t}\n\tif (!compareDistinguishedNames(parsedCrl.issuer, issuer.subject)) {\n\t\treturn validateCertificateRevocationListFailureResult(\n\t\t\t'issuer_mismatch',\n\t\t\t'CRL issuer name does not match certificate subject',\n\t\t);\n\t}\n\tif (\n\t\tparsedCrl.authorityKeyIdentifier !== undefined &&\n\t\tissuer.subjectKeyIdentifier !== undefined &&\n\t\tparsedCrl.authorityKeyIdentifier !== issuer.subjectKeyIdentifier\n\t) {\n\t\treturn validateCertificateRevocationListFailureResult(\n\t\t\t'issuer_mismatch',\n\t\t\t'CRL authority key identifier does not match issuer subject key identifier',\n\t\t);\n\t}\n\tif (issuer.keyUsage !== undefined && !issuer.keyUsage.flags.includes('cRLSign')) {\n\t\treturn validateCertificateRevocationListFailureResult(\n\t\t\t'crl_sign_not_permitted',\n\t\t\t'issuer certificate key usage does not permit CRL signing',\n\t\t);\n\t}\n\tlet verifiedResult: Awaited<ReturnType<typeof verifySignedDataDetailed>>;\n\ttry {\n\t\tverifiedResult = await verifySignedDataDetailed(\n\t\t\tparsedCrl.signatureAlgorithmOid,\n\t\t\tparsedCrl.signatureAlgorithmParametersDer,\n\t\t\tissuer.publicKeyAlgorithmOid,\n\t\t\tissuer.publicKeyParametersOid,\n\t\t\tissuer.subjectPublicKeyInfoDer,\n\t\t\tparsedCrl.signatureValue,\n\t\t\tparsedCrl.tbsCertListDer,\n\t\t);\n\t} catch {\n\t\treturn validateCertificateRevocationListFailureResult(\n\t\t\t'signature_invalid',\n\t\t\t'certificate revocation list signature verification failed',\n\t\t);\n\t}\n\tif (!verifiedResult.ok) {\n\t\tif (verifiedResult.code === 'verification_error') {\n\t\t\treturn validateCertificateRevocationListFailureResult(\n\t\t\t\t'signature_invalid',\n\t\t\t\t'certificate revocation list signature verification failed',\n\t\t\t);\n\t\t}\n\t\treturn validateCertificateRevocationListFailureResult(\n\t\t\t'signature_invalid',\n\t\t\t'certificate revocation list signature uses unsupported algorithm parameters',\n\t\t);\n\t}\n\tif (!verifiedResult.valid) {\n\t\treturn validateCertificateRevocationListFailureResult(\n\t\t\t'signature_invalid',\n\t\t\t'certificate revocation list signature does not verify',\n\t\t);\n\t}\n\tconst at = input.at ?? new Date();\n\tconst skew = input.clockSkewMs ?? 0;\n\tif (\n\t\tparsedCrl.thisUpdate.getTime() - skew > at.getTime() ||\n\t\t(parsedCrl.nextUpdate !== undefined && parsedCrl.nextUpdate.getTime() + skew < at.getTime())\n\t) {\n\t\treturn validateCertificateRevocationListFailureResult(\n\t\t\t'stale_crl',\n\t\t\t'CRL is not valid at requested time',\n\t\t);\n\t}\n\treturn { ok: true, value: parsedCrl };\n}\n\n/**\n * End-to-end revocation check: validates the CRL (and optional delta CRL),\n * verifies applicability via distribution-point and scope matching, then\n * resolves the certificate's revocation status.\n *\n * Returns `good` if the serial is absent, `revoked` with date/reason if present,\n * or an error if the CRL cannot be validated or is non-applicable.\n *\n * @example\n * ```ts\n * import { checkCertificateRevocationAgainstCrl } from 'micro509';\n *\n * const result = await checkCertificateRevocationAgainstCrl({\n * certificate: leafPem,\n * issuerCertificate: caPem,\n * crl: crlPem,\n * });\n * if (result.ok && result.value.status === 'revoked') {\n * console.log('revoked on', result.value.revocationDate);\n * }\n * ```\n */\nexport async function checkCertificateRevocationAgainstCrl(\n\tinput: CheckCertificateRevocationAgainstCrlInput,\n): Promise<CheckCertificateRevocationAgainstCrlResult> {\n\tlet certificate: ParsedCertificate;\n\ttry {\n\t\tcertificate = normalizeCrlCertificate(input.certificate);\n\t} catch {\n\t\treturn checkCertificateRevocationAgainstCrlFailureResult(\n\t\t\t'non_applicable',\n\t\t\t'certificate input is malformed',\n\t\t);\n\t}\n\tconst validated = await validateCertificateRevocationList({\n\t\tcrl: input.crl,\n\t\tissuerCertificate: input.issuerCertificate,\n\t\t...(input.at === undefined ? {} : { at: input.at }),\n\t\t...(input.clockSkewMs === undefined ? {} : { clockSkewMs: input.clockSkewMs }),\n\t});\n\tif (!validated.ok) {\n\t\treturn checkCertificateRevocationAgainstCrlFailureResult(validated.code, validated.message);\n\t}\n\tlet validatedDelta: ParsedCertificateRevocationList | undefined;\n\tif (input.deltaCrl !== undefined) {\n\t\tconst deltaValidation = await validateCertificateRevocationList({\n\t\t\tcrl: input.deltaCrl,\n\t\t\tissuerCertificate: input.issuerCertificate,\n\t\t\t...(input.at === undefined ? {} : { at: input.at }),\n\t\t\t...(input.clockSkewMs === undefined ? {} : { clockSkewMs: input.clockSkewMs }),\n\t\t});\n\t\tif (!deltaValidation.ok) {\n\t\t\treturn checkCertificateRevocationAgainstCrlFailureResult(\n\t\t\t\tdeltaValidation.code,\n\t\t\t\tdeltaValidation.message,\n\t\t\t);\n\t\t}\n\t\tconst compatibilityFailure = checkDeltaCrlCompatibility(validated.value, deltaValidation.value);\n\t\tif (compatibilityFailure !== undefined) {\n\t\t\treturn compatibilityFailure;\n\t\t}\n\t\tvalidatedDelta = deltaValidation.value;\n\t}\n\tconst applicabilityFailure = checkCrlApplicability(certificate, validated.value);\n\tif (applicabilityFailure !== undefined) {\n\t\treturn applicabilityFailure;\n\t}\n\tif (validatedDelta !== undefined) {\n\t\tconst deltaApplicabilityFailure = checkCrlApplicability(certificate, validatedDelta, true);\n\t\tif (deltaApplicabilityFailure !== undefined) {\n\t\t\treturn deltaApplicabilityFailure;\n\t\t}\n\t}\n\tconst completeRevoked = findRevokedCertificateEntry(certificate, validated.value);\n\tif (!completeRevoked.ok) {\n\t\treturn completeRevoked;\n\t}\n\tlet deltaRevoked: ParsedRevokedCertificate | undefined;\n\tif (validatedDelta !== undefined) {\n\t\tconst deltaLookup = findRevokedCertificateEntry(certificate, validatedDelta);\n\t\tif (!deltaLookup.ok) {\n\t\t\treturn deltaLookup;\n\t\t}\n\t\tdeltaRevoked = deltaLookup.entry;\n\t}\n\treturn resolveCertificateRevocationStatus(\n\t\tcertificate,\n\t\tinput.at ?? new Date(),\n\t\tvalidated.value,\n\t\tcompleteRevoked.entry,\n\t\tdeltaRevoked,\n\t);\n}\n\n/** Builds a `VerifyCertificateRevocationListFailureResult`. */\nfunction verifyCertificateRevocationListFailureResult(\n\tcode: 'signature_invalid',\n\tmessage: string,\n): ErrorResult<'signature_invalid', Record<never, never>, VerifyCertificateRevocationListFailure> {\n\tconst error: VerifyCertificateRevocationListFailure = {\n\t\tok: false,\n\t\tcode,\n\t\tmessage,\n\t};\n\treturn { ok: false, error, code, message };\n}\n\n/** Builds a `ValidateCertificateRevocationListFailureResult`. */\nfunction validateCertificateRevocationListFailureResult(\n\tcode: 'signature_invalid' | 'issuer_mismatch' | 'stale_crl' | 'crl_sign_not_permitted',\n\tmessage: string,\n): ErrorResult<\n\t'signature_invalid' | 'issuer_mismatch' | 'stale_crl' | 'crl_sign_not_permitted',\n\tRecord<never, never>,\n\tValidateCertificateRevocationListFailure\n> {\n\tconst error: ValidateCertificateRevocationListFailure = {\n\t\tok: false,\n\t\tcode,\n\t\tmessage,\n\t};\n\treturn { ok: false, error, code, message };\n}\n\n/** Builds a `CheckCertificateRevocationAgainstCrlFailureResult`. */\nfunction checkCertificateRevocationAgainstCrlFailureResult(\n\tcode: CheckCertificateRevocationAgainstCrlErrorCode,\n\tmessage: string,\n\tdetails?: CheckCertificateRevocationAgainstCrlFailureDetails,\n): ErrorResult<\n\tCheckCertificateRevocationAgainstCrlErrorCode,\n\tCheckCertificateRevocationAgainstCrlFailureDetails,\n\tCheckCertificateRevocationAgainstCrlFailure\n> {\n\tconst error: CheckCertificateRevocationAgainstCrlFailure = {\n\t\tok: false,\n\t\tcode,\n\t\tmessage,\n\t\t...(details === undefined ? {} : { details }),\n\t};\n\treturn {\n\t\tok: false,\n\t\terror,\n\t\tcode,\n\t\tmessage,\n\t\t...(details === undefined ? {} : { details }),\n\t};\n}\n\n/** Wraps a success value into a `CheckCertificateRevocationAgainstCrlResult`. */\nfunction checkCertificateRevocationAgainstCrlSuccess(\n\tvalue: CheckCertificateRevocationAgainstCrlValue,\n): CheckCertificateRevocationAgainstCrlResult {\n\treturn { ok: true, value };\n}\n\n/**\n * Quick serial-number lookup — returns `true` if the serial appears in the\n * CRL's revoked entries. Does **not** validate the CRL or check applicability.\n */\nexport function isCertificateRevoked(\n\tcertificateSerialNumber: Uint8Array | string,\n\tcrl: ParsedCertificateRevocationList,\n): boolean {\n\tconst serialNumberHex =\n\t\ttypeof certificateSerialNumber === 'string'\n\t\t\t? normalizeHex(certificateSerialNumber)\n\t\t\t: toHex(certificateSerialNumber);\n\treturn crl.revokedCertificates.some(\n\t\t(entry) => normalizeHex(entry.serialNumberHex) === serialNumberHex,\n\t);\n}\n\n/** Searches CRL entries for the certificate's serial, respecting indirect-CRL issuer overrides. */\nfunction findRevokedCertificateEntry(\n\tcertificate: ParsedCertificate,\n\tcrl: ParsedCertificateRevocationList,\n): RevokedCertificateLookupResult {\n\tconst serialNumberHex = normalizeHex(certificate.serialNumberHex);\n\tlet effectiveIssuer: readonly GeneralName[] | undefined;\n\tlet sawUnsupportedIssuer = false;\n\tlet matchedEntry: ParsedRevokedCertificate | undefined;\n\tfor (const entry of crl.revokedCertificates) {\n\t\tif (entry.certificateIssuer !== undefined) {\n\t\t\teffectiveIssuer = entry.certificateIssuer;\n\t\t}\n\t\tif (normalizeHex(entry.serialNumberHex) !== serialNumberHex) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst issuerMatch = matchesRevokedEntryIssuer(certificate, crl, effectiveIssuer);\n\t\tif (issuerMatch === 'match') {\n\t\t\tif (matchedEntry !== undefined) {\n\t\t\t\treturn checkCertificateRevocationAgainstCrlFailureResult(\n\t\t\t\t\t'signature_invalid',\n\t\t\t\t\t'CRL contains multiple revoked entries for certificate',\n\t\t\t\t);\n\t\t\t}\n\t\t\tmatchedEntry = entry;\n\t\t\tcontinue;\n\t\t}\n\t\tif (issuerMatch === 'unsupported') {\n\t\t\tsawUnsupportedIssuer = true;\n\t\t}\n\t}\n\tif (matchedEntry !== undefined) {\n\t\treturn { ok: true, entry: matchedEntry };\n\t}\n\tif (sawUnsupportedIssuer) {\n\t\treturn nonApplicable(\n\t\t\t'indirect_crl_unsupported',\n\t\t\t'indirect CRL entry certificateIssuer must include a directoryName',\n\t\t);\n\t}\n\treturn { ok: true };\n}\n\n/** RFC 5280 §6.3 CRL applicability: scope, distribution-point, reason, and indirect-CRL checks. */\nfunction checkCrlApplicability(\n\tcertificate: ParsedCertificate,\n\tcrl: ParsedCertificateRevocationList,\n\tallowDeltaCrl = false,\n):\n\t| ErrorResult<\n\t\t\tCheckCertificateRevocationAgainstCrlErrorCode,\n\t\t\tCheckCertificateRevocationAgainstCrlFailureDetails,\n\t\t\tCheckCertificateRevocationAgainstCrlFailure\n\t >\n\t| undefined {\n\tif (!allowDeltaCrl && crl.baseCrlNumber !== undefined) {\n\t\treturn nonApplicable(\n\t\t\t'delta_crl_unsupported',\n\t\t\t'a delta CRL cannot be used as the primary complete CRL input',\n\t\t);\n\t}\n\tconst issuingDistributionPoint = crl.issuingDistributionPoint;\n\tconst isIndirectCrl = issuingDistributionPoint?.indirectCrl === true;\n\tif (!isIndirectCrl && !compareDistinguishedNames(certificate.issuer, crl.issuer)) {\n\t\treturn nonApplicable(\n\t\t\t'issuer_mismatch',\n\t\t\t'CRL issuer does not match certificate issuer for direct CRL processing',\n\t\t);\n\t}\n\tif (issuingDistributionPoint?.onlyContainsAttributeCerts === true) {\n\t\treturn nonApplicable(\n\t\t\t'certificate_scope_mismatch',\n\t\t\t'attribute-certificate-only CRLs are not applicable to public-key certificates',\n\t\t);\n\t}\n\tconst isCaCertificate = certificate.basicConstraints?.ca === true;\n\tif (issuingDistributionPoint?.onlyContainsUserCerts === true && isCaCertificate) {\n\t\treturn nonApplicable(\n\t\t\t'certificate_scope_mismatch',\n\t\t\t'CRL only applies to end-entity certificates',\n\t\t);\n\t}\n\tif (issuingDistributionPoint?.onlyContainsCACerts === true && !isCaCertificate) {\n\t\treturn nonApplicable('certificate_scope_mismatch', 'CRL only applies to CA certificates');\n\t}\n\tconst distributionPoints = certificate.crlDistributionPoints ?? [];\n\tif (distributionPoints.length === 0) {\n\t\tif (issuingDistributionPoint?.distributionPoint !== undefined) {\n\t\t\treturn nonApplicable(\n\t\t\t\t'distribution_point_mismatch',\n\t\t\t\t'certificates without CRL distribution points only accept full-scope CRLs',\n\t\t\t);\n\t\t}\n\t\tif (isIndirectCrl && !compareDistinguishedNames(certificate.issuer, crl.issuer)) {\n\t\t\treturn nonApplicable(\n\t\t\t\t'issuer_mismatch',\n\t\t\t\t'indirect CRLs for alternate certificate issuers require matching CRLIssuer distribution points',\n\t\t\t);\n\t\t}\n\t\treturn undefined;\n\t}\n\tlet sawIndirectDistributionPoint = false;\n\tlet sawDistributionMismatch = false;\n\tlet sawIndirectIssuerMismatch = false;\n\tlet sawIndirectIssuerUnsupported = false;\n\tlet sawReasonsMismatch = false;\n\tfor (const distributionPoint of distributionPoints) {\n\t\tif (!isIndirectCrl && distributionPoint.crlIssuer !== undefined) {\n\t\t\tsawIndirectDistributionPoint = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (isIndirectCrl) {\n\t\t\tif (\n\t\t\t\t!compareDistinguishedNames(certificate.issuer, crl.issuer) ||\n\t\t\t\tdistributionPoint.crlIssuer !== undefined\n\t\t\t) {\n\t\t\t\tconst issuerMatch = matchesIndirectCrlIssuer(distributionPoint.crlIssuer, crl);\n\t\t\t\tif (issuerMatch === 'unsupported') {\n\t\t\t\t\tsawIndirectIssuerUnsupported = true;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (!issuerMatch) {\n\t\t\t\t\tsawIndirectIssuerMismatch = true;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (\n\t\t\t!matchesDistributionPointName(\n\t\t\t\tdistributionPoint.distributionPoint,\n\t\t\t\tissuingDistributionPoint?.distributionPoint,\n\t\t\t\tcrl.issuer,\n\t\t\t)\n\t\t) {\n\t\t\tsawDistributionMismatch = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (\n\t\t\t!hasOverlappingReasons(distributionPoint.reasons, issuingDistributionPoint?.onlySomeReasons)\n\t\t) {\n\t\t\tsawReasonsMismatch = true;\n\t\t\tcontinue;\n\t\t}\n\t\treturn undefined;\n\t}\n\tif (sawReasonsMismatch) {\n\t\treturn nonApplicable(\n\t\t\t'reasons_mismatch',\n\t\t\t'certificate distribution point reasons do not overlap the CRL reason scope',\n\t\t);\n\t}\n\tif (sawIndirectIssuerUnsupported) {\n\t\treturn nonApplicable(\n\t\t\t'indirect_crl_unsupported',\n\t\t\t'indirect CRL distribution points must identify the CRL issuer with directoryName',\n\t\t);\n\t}\n\tif (sawIndirectIssuerMismatch) {\n\t\treturn nonApplicable(\n\t\t\t'issuer_mismatch',\n\t\t\t'certificate distribution points do not authorize this indirect CRL issuer',\n\t\t);\n\t}\n\tif (sawDistributionMismatch) {\n\t\treturn nonApplicable(\n\t\t\t'distribution_point_mismatch',\n\t\t\t'certificate distribution points do not match the CRL issuing distribution point',\n\t\t);\n\t}\n\tif (sawIndirectDistributionPoint) {\n\t\treturn nonApplicable(\n\t\t\t'indirect_crl_unsupported',\n\t\t\t'certificate distribution points that name alternate CRL issuers are not supported yet',\n\t\t);\n\t}\n\treturn nonApplicable(\n\t\t'distribution_point_mismatch',\n\t\t'certificate distribution points do not match the CRL scope',\n\t);\n}\n\n/** Verifies that a delta CRL is compatible with the given complete CRL (same issuer, AKI, IDP, and valid numbering). */\nfunction checkDeltaCrlCompatibility(\n\tcompleteCrl: ParsedCertificateRevocationList,\n\tdeltaCrl: ParsedCertificateRevocationList,\n):\n\t| ErrorResult<\n\t\t\tCheckCertificateRevocationAgainstCrlErrorCode,\n\t\t\tCheckCertificateRevocationAgainstCrlFailureDetails,\n\t\t\tCheckCertificateRevocationAgainstCrlFailure\n\t >\n\t| undefined {\n\tif (completeCrl.baseCrlNumber !== undefined) {\n\t\treturn nonApplicable(\n\t\t\t'delta_crl_incompatible',\n\t\t\t'complete CRL input must not itself be a delta CRL',\n\t\t);\n\t}\n\tif (deltaCrl.baseCrlNumber === undefined) {\n\t\treturn nonApplicable(\n\t\t\t'delta_crl_incompatible',\n\t\t\t'delta CRL input must include a delta CRL indicator',\n\t\t);\n\t}\n\tif (!compareDistinguishedNames(completeCrl.issuer, deltaCrl.issuer)) {\n\t\treturn nonApplicable(\n\t\t\t'delta_crl_incompatible',\n\t\t\t'complete and delta CRLs must share the same issuer',\n\t\t);\n\t}\n\tif (completeCrl.authorityKeyIdentifier !== deltaCrl.authorityKeyIdentifier) {\n\t\treturn nonApplicable(\n\t\t\t'delta_crl_incompatible',\n\t\t\t'complete and delta CRLs must share the same authority key identifier',\n\t\t);\n\t}\n\tif (\n\t\t!sameIssuingDistributionPoint(\n\t\t\tcompleteCrl.issuingDistributionPoint,\n\t\t\tdeltaCrl.issuingDistributionPoint,\n\t\t)\n\t) {\n\t\treturn nonApplicable(\n\t\t\t'delta_crl_incompatible',\n\t\t\t'complete and delta CRLs must share the same issuing distribution point scope',\n\t\t);\n\t}\n\tif (completeCrl.crlNumber === undefined || deltaCrl.crlNumber === undefined) {\n\t\treturn nonApplicable(\n\t\t\t'delta_crl_incompatible',\n\t\t\t'complete and delta CRLs must both carry CRL numbers for delta processing',\n\t\t);\n\t}\n\tif (completeCrl.crlNumber < deltaCrl.baseCrlNumber) {\n\t\treturn nonApplicable(\n\t\t\t'delta_crl_incompatible',\n\t\t\t'delta CRL base number must not exceed the complete CRL number',\n\t\t);\n\t}\n\tif (completeCrl.crlNumber >= deltaCrl.crlNumber) {\n\t\treturn nonApplicable(\n\t\t\t'delta_crl_incompatible',\n\t\t\t'delta CRL number must be newer than the complete CRL number',\n\t\t);\n\t}\n\treturn undefined;\n}\n\n/** Checks whether any `crlIssuer` GeneralName matches the CRL's issuer via directoryName DER comparison. */\nfunction matchesIndirectCrlIssuer(\n\tcrlIssuerNames: readonly GeneralName[] | undefined,\n\tcrl: ParsedCertificateRevocationList,\n): boolean | 'unsupported' {\n\tif (crlIssuerNames === undefined) {\n\t\treturn false;\n\t}\n\tlet sawUnsupportedName = false;\n\tfor (const generalName of crlIssuerNames) {\n\t\tif (generalName.type === 'directoryName') {\n\t\t\tif (directoryNameDerHexMatchesParsedName(generalName.derHex, crl.issuer)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tsawUnsupportedName = true;\n\t}\n\treturn sawUnsupportedName ? 'unsupported' : false;\n}\n\n/** Determines whether a revoked entry's effective issuer matches the certificate's issuer. */\nfunction matchesRevokedEntryIssuer(\n\tcertificate: ParsedCertificate,\n\tcrl: ParsedCertificateRevocationList,\n\teffectiveIssuer: readonly GeneralName[] | undefined,\n): 'match' | 'mismatch' | 'unsupported' {\n\tif (effectiveIssuer === undefined) {\n\t\treturn compareDistinguishedNames(certificate.issuer, crl.issuer) ? 'match' : 'mismatch';\n\t}\n\tlet sawUnsupportedName = false;\n\tfor (const generalName of effectiveIssuer) {\n\t\tif (generalName.type === 'directoryName') {\n\t\t\tif (directoryNameDerHexMatchesParsedName(generalName.derHex, certificate.issuer)) {\n\t\t\t\treturn 'match';\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tsawUnsupportedName = true;\n\t}\n\treturn sawUnsupportedName ? 'unsupported' : 'mismatch';\n}\n\n/** Shorthand for building a `non_applicable` failure result with the given reason. */\nfunction nonApplicable(\n\treason: CrlApplicabilityFailureReason,\n\tmessage: string,\n): ErrorResult<\n\tCheckCertificateRevocationAgainstCrlErrorCode,\n\tCheckCertificateRevocationAgainstCrlFailureDetails,\n\tCheckCertificateRevocationAgainstCrlFailure\n> {\n\treturn checkCertificateRevocationAgainstCrlFailureResult('non_applicable', message, { reason });\n}\n\n/** Merges complete and delta CRL entries per RFC 5280 §5.2.4 to produce a final `good`/`revoked` status. */\nfunction resolveCertificateRevocationStatus(\n\tcertificate: ParsedCertificate,\n\tat: Date,\n\tcompleteCrl: ParsedCertificateRevocationList,\n\tcompleteEntry: ParsedRevokedCertificate | undefined,\n\tdeltaEntry: ParsedRevokedCertificate | undefined,\n): CheckCertificateRevocationAgainstCrlResult {\n\tif (deltaEntry !== undefined) {\n\t\tif (deltaEntry.reasonCode === 'removeFromCRL') {\n\t\t\tif (\n\t\t\t\tcompleteEntry?.reasonCode === 'certificateHold' ||\n\t\t\t\tcertificate.notAfter.getTime() < at.getTime()\n\t\t\t) {\n\t\t\t\treturn checkCertificateRevocationAgainstCrlSuccess({\n\t\t\t\t\tstatus: 'good',\n\t\t\t\t\tcrl: completeCrl,\n\t\t\t\t});\n\t\t\t}\n\t\t} else {\n\t\t\treturn checkCertificateRevocationAgainstCrlSuccess({\n\t\t\t\tstatus: 'revoked',\n\t\t\t\tcrl: completeCrl,\n\t\t\t\trevocationDate: deltaEntry.revocationDate,\n\t\t\t\t...(deltaEntry.reasonCode === undefined ? {} : { reasonCode: deltaEntry.reasonCode }),\n\t\t\t});\n\t\t}\n\t}\n\tif (completeEntry === undefined) {\n\t\treturn checkCertificateRevocationAgainstCrlSuccess({ status: 'good', crl: completeCrl });\n\t}\n\treturn checkCertificateRevocationAgainstCrlSuccess({\n\t\tstatus: 'revoked',\n\t\tcrl: completeCrl,\n\t\trevocationDate: completeEntry.revocationDate,\n\t\t...(completeEntry.reasonCode === undefined ? {} : { reasonCode: completeEntry.reasonCode }),\n\t});\n}\n\n/**\n * Returns `true` if the certificate's distribution-point name matches the CRL's IDP name.\n *\n * Per RFC 5280 §4.2.1.13, when the certificate uses `nameRelativeToCRLIssuer` (relativeName),\n * the full distribution point name is formed by appending that RDN to the CRL issuer's DN.\n * This resolved name is then compared against the CRL's IDP fullName directoryName entries.\n */\nfunction matchesDistributionPointName(\n\tcertificatePoint: ParsedDistributionPointName | undefined,\n\tcrlPoint: ParsedDistributionPointName | undefined,\n\tcrlIssuer: ParsedName,\n): boolean {\n\tif (crlPoint === undefined) {\n\t\treturn true;\n\t}\n\tif (certificatePoint === undefined) {\n\t\treturn false;\n\t}\n\t// Both have fullName — direct comparison\n\tif (certificatePoint.fullName !== undefined && crlPoint.fullName !== undefined) {\n\t\treturn certificatePoint.fullName.some(\n\t\t\t(leftName) =>\n\t\t\t\tcrlPoint.fullName?.some((rightName) => compareGeneralNames(leftName, rightName)) === true,\n\t\t);\n\t}\n\t// Cert has relativeName, CRL has fullName — resolve relativeName to full DN\n\tif (certificatePoint.relativeName !== undefined && crlPoint.fullName !== undefined) {\n\t\tconst resolvedDnHex = resolveRelativeNameToDnHex(crlIssuer, certificatePoint.relativeName);\n\t\treturn crlPoint.fullName.some(\n\t\t\t(name) => name.type === 'directoryName' && name.derHex === resolvedDnHex,\n\t\t);\n\t}\n\t// Both have relativeName — direct RDN comparison\n\tif (certificatePoint.relativeName !== undefined && crlPoint.relativeName !== undefined) {\n\t\treturn compareRelativeDistinguishedNames(certificatePoint.relativeName, crlPoint.relativeName);\n\t}\n\t// Cert has fullName, CRL has relativeName — resolve CRL relativeName to full DN\n\tif (certificatePoint.fullName !== undefined && crlPoint.relativeName !== undefined) {\n\t\tconst resolvedDnHex = resolveRelativeNameToDnHex(crlIssuer, crlPoint.relativeName);\n\t\treturn certificatePoint.fullName.some(\n\t\t\t(name) => name.type === 'directoryName' && name.derHex === resolvedDnHex,\n\t\t);\n\t}\n\treturn false;\n}\n\n/** Constructs a full DN by appending an RDN to an existing Name, returning hex-encoded DER. */\nfunction resolveRelativeNameToDnHex(\n\tissuer: ParsedName,\n\trelativeName: ParsedRelativeDistinguishedName,\n): string {\n\t// Collect existing RDN DER bytes from issuer\n\tconst rdnDerParts = issuer.rdns.map((rdn) => hexToBytes(rdn.derHex));\n\t// The relativeName.derHex has implicit tag [1] (0xa1) from the CHOICE encoding.\n\t// Extract the content and re-wrap as a proper RDN SET (tag 0x31).\n\tconst relativeNameDer = hexToBytes(relativeName.derHex);\n\tconst relativeNameRdnDer = rebuildRelativeNameAsSet(relativeNameDer);\n\trdnDerParts.push(relativeNameRdnDer);\n\t// Wrap in a Name SEQUENCE\n\treturn toHex(sequence(rdnDerParts));\n}\n\n/** Converts an implicit-tagged [1] relativeName to a proper SET-tagged RDN. */\nfunction rebuildRelativeNameAsSet(implicitTaggedDer: Uint8Array): Uint8Array {\n\t// The implicit tag [1] is 0xa1 (context-specific constructed).\n\t// Parse the element to get content, then re-wrap with SET tag 0x31.\n\tconst element = readRootElement(implicitTaggedDer, { maxDepth: DEFAULT_MAX_DER_DEPTH });\n\treturn tlv(0x31, element.value);\n}\n\n/** Deep-compares two issuing distribution point extensions for delta-CRL compatibility. */\nfunction sameIssuingDistributionPoint(\n\tleft: ParsedIssuingDistributionPoint | undefined,\n\tright: ParsedIssuingDistributionPoint | undefined,\n): boolean {\n\tif (left === undefined || right === undefined) {\n\t\treturn left === right;\n\t}\n\treturn (\n\t\tsameDistributionPointName(left.distributionPoint, right.distributionPoint) &&\n\t\t(left.onlyContainsUserCerts === true) === (right.onlyContainsUserCerts === true) &&\n\t\t(left.onlyContainsCACerts === true) === (right.onlyContainsCACerts === true) &&\n\t\t(left.indirectCrl === true) === (right.indirectCrl === true) &&\n\t\t(left.onlyContainsAttributeCerts === true) === (right.onlyContainsAttributeCerts === true) &&\n\t\tsameReasonSet(left.onlySomeReasons, right.onlySomeReasons)\n\t);\n}\n\n/** Compares two DistributionPointName values (fullName set or relativeName). */\nfunction sameDistributionPointName(\n\tleft: ParsedDistributionPointName | undefined,\n\tright: ParsedDistributionPointName | undefined,\n): boolean {\n\tif (left === undefined || right === undefined) {\n\t\treturn left === right;\n\t}\n\tif (left.fullName !== undefined || right.fullName !== undefined) {\n\t\tif (left.fullName === undefined || right.fullName === undefined) {\n\t\t\treturn false;\n\t\t}\n\t\treturn sameGeneralNameSet(left.fullName, right.fullName);\n\t}\n\tif (left.relativeName === undefined || right.relativeName === undefined) {\n\t\treturn false;\n\t}\n\treturn compareRelativeDistinguishedNames(left.relativeName, right.relativeName);\n}\n\n/** Set-equality comparison for GeneralName arrays (order-independent). */\nfunction sameGeneralNameSet(left: readonly GeneralName[], right: readonly GeneralName[]): boolean {\n\tif (left.length !== right.length) {\n\t\treturn false;\n\t}\n\tconst matched = new Array(right.length).fill(false);\n\tfor (const leftName of left) {\n\t\tlet found = false;\n\t\tfor (let index = 0; index < right.length; index += 1) {\n\t\t\tconst rightName = right[index];\n\t\t\tif (rightName === undefined || matched[index]) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!compareGeneralNames(leftName, rightName)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tmatched[index] = true;\n\t\t\tfound = true;\n\t\t\tbreak;\n\t\t}\n\t\tif (!found) {\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\n/** Set-equality comparison for DistributionPointReason arrays. */\nfunction sameReasonSet(\n\tleft: ParsedBitFlags<DistributionPointReason> | undefined,\n\tright: ParsedBitFlags<DistributionPointReason> | undefined,\n): boolean {\n\tif (left === undefined || right === undefined) {\n\t\treturn left === right;\n\t}\n\tif (left.flags.length !== right.flags.length) {\n\t\treturn false;\n\t}\n\treturn left.flags.every((reason) => right.flags.includes(reason));\n}\n\n/** Returns `true` if the certificate's DP reasons overlap the CRL's `onlySomeReasons`, or if either is absent (= all reasons). */\nfunction hasOverlappingReasons(\n\tcertificateReasons: ParsedBitFlags<DistributionPointReason> | undefined,\n\tcrlReasons: ParsedBitFlags<DistributionPointReason> | undefined,\n): boolean {\n\tif (certificateReasons === undefined || crlReasons === undefined) {\n\t\treturn true;\n\t}\n\treturn certificateReasons.flags.some((reason) => crlReasons.flags.includes(reason));\n}\n\n/** Value-equality for two GeneralName entries, using DER comparison for directoryName. */\nfunction compareGeneralNames(left: GeneralName, right: GeneralName): boolean {\n\tif (left.type === 'dns' && right.type === 'dns') {\n\t\treturn left.value === right.value;\n\t}\n\tif (left.type === 'email' && right.type === 'email') {\n\t\treturn left.value === right.value;\n\t}\n\tif (left.type === 'ip' && right.type === 'ip') {\n\t\treturn left.value === right.value;\n\t}\n\tif (left.type === 'uri' && right.type === 'uri') {\n\t\treturn left.value === right.value;\n\t}\n\tif (left.type === 'directoryName' && right.type === 'directoryName') {\n\t\tconst leftName = parseDerHexName(left.derHex);\n\t\tconst rightName = parseDerHexName(right.derHex);\n\t\tif (leftName === undefined || rightName === undefined) {\n\t\t\treturn false;\n\t\t}\n\t\treturn compareDistinguishedNames(leftName, rightName);\n\t}\n\tif (left.type === 'unknown' && right.type === 'unknown') {\n\t\treturn left.tag === right.tag && bytesEqual(left.value, right.value);\n\t}\n\treturn false;\n}\n\n/** Constant-length byte-array equality check. */\nfunction bytesEqual(left: Uint8Array, right: Uint8Array): boolean {\n\tif (left.length !== right.length) {\n\t\treturn false;\n\t}\n\tfor (let index = 0; index < left.length; index += 1) {\n\t\tif (left[index] !== right[index]) {\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\n/** Set-equality comparison for RDN attribute sets (order-independent, RFC 4518 string prep). */\nfunction compareRelativeDistinguishedNames(\n\tleft: ParsedRelativeDistinguishedName,\n\tright: ParsedRelativeDistinguishedName,\n): boolean {\n\tif (left.attributes.length !== right.attributes.length) {\n\t\treturn false;\n\t}\n\tconst matched = new Array(right.attributes.length).fill(false);\n\tfor (const leftAttribute of left.attributes) {\n\t\tlet found = false;\n\t\tfor (let index = 0; index < right.attributes.length; index += 1) {\n\t\t\tconst rightAttribute = right.attributes[index];\n\t\t\tif (rightAttribute === undefined || matched[index]) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!compareNameAttributeValue(leftAttribute, rightAttribute)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tmatched[index] = true;\n\t\t\tfound = true;\n\t\t\tbreak;\n\t\t}\n\t\tif (!found) {\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\n/** Assembles the CRL-level v2 extensions (AKI, CRLNumber, deltaCRLIndicator, IDP, freshestCRL). */\nasync function buildCrlExtensions(\n\tissuerPublicKey: CryptoKey | undefined,\n\tcrlNumber: number | undefined,\n\tbaseCrlNumber?: number,\n\tissuingDistributionPoint?: IssuingDistributionPoint,\n\tfreshestCrlDistributionPoints?: readonly DistributionPoint[],\n): Promise<Uint8Array[]> {\n\tconst extensions: Uint8Array[] = [];\n\tif (issuerPublicKey !== undefined) {\n\t\tconst spki = await exportSpkiDer(issuerPublicKey);\n\t\textensions.push(\n\t\t\tencodeExtension(\n\t\t\t\tOIDS.authorityKeyIdentifier,\n\t\t\t\tsequence([implicitPrimitiveContext(0, buildSubjectKeyIdentifier(spki))]),\n\t\t\t),\n\t\t);\n\t}\n\tif (crlNumber !== undefined) {\n\t\textensions.push(encodeExtension(OIDS.cRLNumber, integerFromNumber(crlNumber)));\n\t}\n\tif (baseCrlNumber !== undefined) {\n\t\textensions.push(\n\t\t\tencodeExtension(OIDS.deltaCRLIndicator, integerFromNumber(baseCrlNumber), true),\n\t\t);\n\t}\n\tif (issuingDistributionPoint !== undefined) {\n\t\textensions.push(\n\t\t\tencodeExtension(\n\t\t\t\tOIDS.issuingDistributionPoint,\n\t\t\t\tencodeIssuingDistributionPoint(issuingDistributionPoint),\n\t\t\t\ttrue,\n\t\t\t),\n\t\t);\n\t}\n\tif (freshestCrlDistributionPoints !== undefined && freshestCrlDistributionPoints.length > 0) {\n\t\textensions.push(\n\t\t\tencodeExtension(OIDS.freshestCRL, encodeCrlDistributionPoints(freshestCrlDistributionPoints)),\n\t\t);\n\t}\n\treturn extensions;\n}\n\n/** DER-encodes a single revokedCertificate SEQUENCE (serial, date, optional extensions). */\nfunction createRevokedCertificate(entry: RevokedCertificateInput, thisUpdate: Date): Uint8Array {\n\tconst extensions = buildRevokedCertificateExtensions(entry);\n\treturn sequence([\n\t\tinteger(entry.serialNumber),\n\t\ttime(entry.revocationDate ?? thisUpdate),\n\t\t...(extensions.length === 0 ? [] : [sequence(extensions)]),\n\t]);\n}\n\n/** Encodes CRL entry extensions (reasonCode, invalidityDate). */\nfunction buildRevokedCertificateExtensions(entry: RevokedCertificateInput): Uint8Array[] {\n\tconst extensions: Uint8Array[] = [];\n\tif (entry.reasonCode !== undefined) {\n\t\textensions.push(\n\t\t\tencodeExtension(\n\t\t\t\tOIDS.cRLReason,\n\t\t\t\ttlv(0x0a, Uint8Array.of(REVOCATION_REASON_CODES[entry.reasonCode])),\n\t\t\t),\n\t\t);\n\t}\n\tif (entry.invalidityDate !== undefined) {\n\t\textensions.push(encodeExtension(OIDS.invalidityDate, generalizedTime(entry.invalidityDate)));\n\t}\n\treturn extensions;\n}\n\n/** Decodes per-entry CRL extensions (reasonCode, invalidityDate, certificateIssuer). */\nfunction parseRevokedCertificateExtensions(\n\tentryDer: Uint8Array | undefined,\n\telement: DerElement | undefined,\n): {\n\treadonly reasonCode?: RevocationReason;\n\treadonly invalidityDate?: Date;\n\treadonly certificateIssuer?: readonly GeneralName[];\n} {\n\tif (entryDer === undefined || element === undefined) {\n\t\treturn {};\n\t}\n\tlet reasonCode: RevocationReason | undefined;\n\tlet invalidityDate: Date | undefined;\n\tlet certificateIssuer: readonly GeneralName[] | undefined;\n\tconst seenOids = new Set<string>();\n\tfor (const extension of childrenOf(entryDer, element)) {\n\t\tconst parts = childrenOf(entryDer, extension);\n\t\tif (parts.length < 2 || parts.length > 3) {\n\t\t\tthrow new Error('Malformed revoked certificate extension');\n\t\t}\n\t\tif (parts.length === 3 && parts[1]?.tag !== 0x01) {\n\t\t\tthrow new Error('Malformed revoked certificate extension');\n\t\t}\n\t\tconst oid = decodeObjectIdentifier(\n\t\t\trequireElement(parts[0], 'revoked certificate extension OID').value,\n\t\t);\n\t\tif (seenOids.has(oid)) {\n\t\t\tthrow new Error(`Duplicate revoked certificate extension OID: ${oid}`);\n\t\t}\n\t\tseenOids.add(oid);\n\t\tconst valueElement = requireElement(\n\t\t\tparts[parts.length - 1],\n\t\t\t'revoked certificate extension value',\n\t\t);\n\t\tif (valueElement.tag !== 0x04) {\n\t\t\tthrow new Error('Revoked certificate extension value must use OCTET STRING');\n\t\t}\n\t\tif (oid === OIDS.cRLReason) {\n\t\t\treasonCode = revocationReasonFromCode(readElement(valueElement.value).value[0]);\n\t\t}\n\t\tif (oid === OIDS.invalidityDate) {\n\t\t\tinvalidityDate = parseTime(readElement(valueElement.value));\n\t\t}\n\t\tif (oid === OIDS.certificateIssuer) {\n\t\t\tconst generalNames = readRootElement(valueElement.value, { maxDepth: DEFAULT_MAX_DER_DEPTH });\n\t\t\tif (generalNames.tag !== 0x30) {\n\t\t\t\tthrow new Error('certificateIssuer must use SEQUENCE');\n\t\t\t}\n\t\t\tcertificateIssuer = parseGeneralNames(valueElement.value, generalNames);\n\t\t}\n\t}\n\treturn {\n\t\t...(reasonCode === undefined ? {} : { reasonCode }),\n\t\t...(invalidityDate === undefined ? {} : { invalidityDate }),\n\t\t...(certificateIssuer === undefined ? {} : { certificateIssuer }),\n\t};\n}\n\n/** Decodes the IssuingDistributionPoint extension from its OCTET STRING content. */\nfunction parseIssuingDistributionPoint(valueDer: Uint8Array): ParsedIssuingDistributionPoint {\n\tconst sequenceElement = readRootElement(valueDer, { maxDepth: DEFAULT_MAX_DER_DEPTH });\n\tlet distributionPoint: ParsedDistributionPointName | undefined;\n\tlet onlyContainsUserCerts: boolean | undefined;\n\tlet onlyContainsCACerts: boolean | undefined;\n\tlet onlySomeReasons: ParsedBitFlags<DistributionPointReason> | undefined;\n\tlet indirectCrl: boolean | undefined;\n\tlet onlyContainsAttributeCerts: boolean | undefined;\n\tfor (const child of childrenOf(valueDer, sequenceElement)) {\n\t\tif (child.tag === 0xa0) {\n\t\t\tif (distributionPoint !== undefined) {\n\t\t\t\tthrow new Error('IssuingDistributionPoint distributionPoint must not repeat');\n\t\t\t}\n\t\t\tconst parsedDistributionPoint = parseDistributionPointName(valueDer, child);\n\t\t\tif (parsedDistributionPoint !== undefined) {\n\t\t\t\tdistributionPoint = parsedDistributionPoint;\n\t\t\t}\n\t\t} else if (child.tag === 0x81) {\n\t\t\tif (onlyContainsUserCerts !== undefined) {\n\t\t\t\tthrow new Error('IssuingDistributionPoint onlyContainsUserCerts must not repeat');\n\t\t\t}\n\t\t\tonlyContainsUserCerts = parseImplicitBoolean(child);\n\t\t} else if (child.tag === 0x82) {\n\t\t\tif (onlyContainsCACerts !== undefined) {\n\t\t\t\tthrow new Error('IssuingDistributionPoint onlyContainsCACerts must not repeat');\n\t\t\t}\n\t\t\tonlyContainsCACerts = parseImplicitBoolean(child);\n\t\t} else if (child.tag === 0x83) {\n\t\t\tif (onlySomeReasons !== undefined) {\n\t\t\t\tthrow new Error('IssuingDistributionPoint onlySomeReasons must not repeat');\n\t\t\t}\n\t\t\tonlySomeReasons = parseDistributionPointReasonFlagsContent(child.value);\n\t\t} else if (child.tag === 0x84) {\n\t\t\tif (indirectCrl !== undefined) {\n\t\t\t\tthrow new Error('IssuingDistributionPoint indirectCrl must not repeat');\n\t\t\t}\n\t\t\tindirectCrl = parseImplicitBoolean(child);\n\t\t} else if (child.tag === 0x85) {\n\t\t\tif (onlyContainsAttributeCerts !== undefined) {\n\t\t\t\tthrow new Error('IssuingDistributionPoint onlyContainsAttributeCerts must not repeat');\n\t\t\t}\n\t\t\tonlyContainsAttributeCerts = parseImplicitBoolean(child);\n\t\t} else {\n\t\t\tthrow new Error(`Unsupported IssuingDistributionPoint field tag: ${String(child.tag)}`);\n\t\t}\n\t}\n\tconst scopeFlags = [\n\t\tonlyContainsUserCerts,\n\t\tonlyContainsCACerts,\n\t\tonlyContainsAttributeCerts,\n\t].filter((value) => value === true).length;\n\tif (scopeFlags > 1) {\n\t\tthrow new Error('IssuingDistributionPoint scope booleans are mutually exclusive');\n\t}\n\treturn {\n\t\t...(distributionPoint === undefined ? {} : { distributionPoint }),\n\t\t...(onlyContainsUserCerts === undefined ? {} : { onlyContainsUserCerts }),\n\t\t...(onlyContainsCACerts === undefined ? {} : { onlyContainsCACerts }),\n\t\t...(onlySomeReasons === undefined ? {} : { onlySomeReasons }),\n\t\t...(indirectCrl === undefined ? {} : { indirectCrl }),\n\t\t...(onlyContainsAttributeCerts === undefined ? {} : { onlyContainsAttributeCerts }),\n\t};\n}\n\n/** Decodes a SEQUENCE OF DistributionPoint from DER. */\nfunction parseDistributionPoints(valueDer: Uint8Array): readonly ParsedDistributionPoint[] {\n\tconst sequenceElement = readRootElement(valueDer, { maxDepth: DEFAULT_MAX_DER_DEPTH });\n\tif (sequenceElement.tag !== 0x30) {\n\t\tthrow new Error('DistributionPoints must use SEQUENCE');\n\t}\n\tconst elements = childrenOf(valueDer, sequenceElement);\n\tif (elements.length === 0) {\n\t\tthrow new Error('DistributionPoints must not be empty');\n\t}\n\treturn elements.map((distributionPoint) => parseDistributionPoint(valueDer, distributionPoint));\n}\n\n/** Decodes a DistributionPointName (fullName or relativeName). */\nfunction parseDistributionPointName(\n\tvalueDer: Uint8Array,\n\telement: DerElement,\n): ParsedDistributionPointName | undefined {\n\tconst children = childrenOf(valueDer, element);\n\tif (children.length !== 1) {\n\t\tthrow new Error('distributionPointName must contain exactly one choice');\n\t}\n\tconst distributionPointName = requireElement(children[0], 'distributionPointName');\n\tif (distributionPointName.tag === 0xa0) {\n\t\tconst fullName = childrenOf(valueDer, distributionPointName);\n\t\tif (fullName.length === 0) {\n\t\t\tthrow new Error('distributionPointName fullName must not be empty');\n\t\t}\n\t\tfor (const name of fullName) {\n\t\t\tif ((name.tag & 0xc0) !== 0x80) {\n\t\t\t\tthrow new Error('distributionPointName fullName must contain GeneralName entries');\n\t\t\t}\n\t\t}\n\t\treturn {\n\t\t\tfullName: fullName.map((name) => parseGeneralName(name)),\n\t\t};\n\t}\n\tif (distributionPointName.tag === 0xa1) {\n\t\tconst relativeName = parseRelativeName(valueDer, distributionPointName);\n\t\treturn { relativeName };\n\t}\n\tthrow new Error(`Unsupported distributionPointName tag: ${String(distributionPointName.tag)}`);\n}\n\n/** Decodes a single DistributionPoint SEQUENCE. */\nfunction parseDistributionPoint(\n\tvalueDer: Uint8Array,\n\telement: DerElement,\n): ParsedDistributionPoint {\n\tif (element.tag !== 0x30) {\n\t\tthrow new Error('DistributionPoint must use SEQUENCE');\n\t}\n\tlet distributionPoint: ParsedDistributionPointName | undefined;\n\tlet reasons: ParsedBitFlags<DistributionPointReason> | undefined;\n\tlet crlIssuer: readonly GeneralName[] | undefined;\n\tfor (const child of childrenOf(valueDer, element)) {\n\t\tif (child.tag === 0xa0) {\n\t\t\tif (distributionPoint !== undefined) {\n\t\t\t\tthrow new Error('DistributionPoint distributionPoint must not repeat');\n\t\t\t}\n\t\t\tconst parsedDistributionPoint = parseDistributionPointName(valueDer, child);\n\t\t\tif (parsedDistributionPoint !== undefined) {\n\t\t\t\tdistributionPoint = parsedDistributionPoint;\n\t\t\t}\n\t\t} else if (child.tag === 0x81) {\n\t\t\tif (reasons !== undefined) {\n\t\t\t\tthrow new Error('DistributionPoint reasons must not repeat');\n\t\t\t}\n\t\t\treasons = parseDistributionPointReasonFlagsContent(child.value);\n\t\t} else if (child.tag === 0xa2) {\n\t\t\tif (crlIssuer !== undefined) {\n\t\t\t\tthrow new Error('DistributionPoint crlIssuer must not repeat');\n\t\t\t}\n\t\t\tcrlIssuer = parseGeneralNames(valueDer, child);\n\t\t} else {\n\t\t\tthrow new Error(`Unsupported DistributionPoint field tag: ${String(child.tag)}`);\n\t\t}\n\t}\n\tif (distributionPoint === undefined && crlIssuer === undefined) {\n\t\tthrow new Error('DistributionPoint must include distributionPoint or crlIssuer');\n\t}\n\treturn {\n\t\t...(distributionPoint === undefined ? {} : { distributionPoint }),\n\t\t...(reasons === undefined ? {} : { reasons }),\n\t\t...(crlIssuer === undefined ? {} : { crlIssuer }),\n\t};\n}\n\n/** Decodes a GeneralName from its context-tagged ASN.1 element. */\nfunction parseGeneralName(element: DerElement): GeneralName {\n\tswitch (element.tag) {\n\t\tcase 0x81:\n\t\t\treturn { type: 'email' as const, value: textDecoder.decode(element.value) };\n\t\tcase 0x82:\n\t\t\treturn { type: 'dns' as const, value: textDecoder.decode(element.value) };\n\t\tcase 0x86:\n\t\t\treturn { type: 'uri' as const, value: textDecoder.decode(element.value) };\n\t\tcase 0x87:\n\t\t\treturn { type: 'ip' as const, value: decodeIpAddress(element.value) };\n\t\tcase 0xa4:\n\t\t\treturn {\n\t\t\t\ttype: 'directoryName' as const,\n\t\t\t\tderHex: toHex(rebuildDirectoryNameFromImplicit(element)),\n\t\t\t};\n\t\tdefault:\n\t\t\treturn { type: 'unknown' as const, tag: element.tag, value: new Uint8Array(element.value) };\n\t}\n}\n\nfunction parseGeneralNames(valueDer: Uint8Array, element: DerElement): readonly GeneralName[] {\n\tconst names = childrenOf(valueDer, element);\n\tif (names.length === 0) {\n\t\tthrow new Error('GeneralNames must not be empty');\n\t}\n\tfor (const name of names) {\n\t\tif ((name.tag & 0xc0) !== 0x80) {\n\t\t\tthrow new Error('GeneralNames must contain GeneralName entries');\n\t\t}\n\t}\n\treturn names.map((name) => parseGeneralName(name));\n}\n\n/** Decodes a RelativeDistinguishedName SET from an implicitly-tagged context element. */\nfunction parseRelativeName(\n\tvalueDer: Uint8Array,\n\telement: DerElement,\n): ParsedRelativeDistinguishedName {\n\tconst attributes: ParsedNameAttribute[] = [];\n\tconst values: Partial<Record<NameFieldKey, string>> = {};\n\tfor (const attributeSequence of childrenOf(valueDer, element)) {\n\t\tconst parts = childrenOf(valueDer, attributeSequence);\n\t\tconst oid = decodeObjectIdentifier(requireElement(parts[0], 'name OID').value);\n\t\tconst valueElement = requireElement(parts[1], 'name value');\n\t\tconst fieldKey = nameFieldKeyFromOid(oid);\n\t\tconst fieldValue = decodeNameValue(valueElement);\n\t\tconst attribute: ParsedNameAttribute =\n\t\t\tfieldKey === undefined\n\t\t\t\t? { oid, valueTag: valueElement.tag, value: fieldValue }\n\t\t\t\t: { oid, key: fieldKey, valueTag: valueElement.tag, value: fieldValue };\n\t\tattributes.push(attribute);\n\t\tif (fieldKey !== undefined && values[fieldKey] === undefined) {\n\t\t\tvalues[fieldKey] = fieldValue;\n\t\t}\n\t}\n\treturn {\n\t\tderHex: toHex(valueDer.slice(element.start - element.headerLength, element.end)),\n\t\tattributes,\n\t\tvalues,\n\t};\n}\n\n/** Decodes an ASN.1 string element (UTF8String, PrintableString, etc.) to a JS string. */\nfunction decodeNameValue(element: DerElement): string {\n\treturn decodeString(element.tag, element.value);\n}\n\n/** Reads an implicitly-tagged BOOLEAN from a context element. Absent content → `false`. */\nfunction parseImplicitBoolean(element: DerElement): boolean {\n\treturn (element.value[0] ?? 0) !== 0;\n}\n\n/** DER-encodes an IssuingDistributionPoint extension value. Throws on mutually-exclusive scope flags. */\nfunction encodeIssuingDistributionPoint(value: IssuingDistributionPoint): Uint8Array {\n\tconst certificateScopeFlags = [\n\t\tvalue.onlyContainsUserCerts === true,\n\t\tvalue.onlyContainsCACerts === true,\n\t\tvalue.onlyContainsAttributeCerts === true,\n\t].filter(Boolean).length;\n\tif (certificateScopeFlags > 1) {\n\t\tthrow new Error(\n\t\t\t'IssuingDistributionPoint can assert at most one of user, CA, or attribute cert scope',\n\t\t);\n\t}\n\tconst fields: Uint8Array[] = [];\n\tif (value.distributionPoint !== undefined) {\n\t\tfields.push(\n\t\t\timplicitConstructedContext(0, encodeDistributionPointName(value.distributionPoint)),\n\t\t);\n\t}\n\tif (value.onlyContainsUserCerts) {\n\t\tfields.push(implicitPrimitiveContext(1, Uint8Array.of(0xff)));\n\t}\n\tif (value.onlyContainsCACerts) {\n\t\tfields.push(implicitPrimitiveContext(2, Uint8Array.of(0xff)));\n\t}\n\tif (value.onlySomeReasons !== undefined && value.onlySomeReasons.length > 0) {\n\t\tfields.push(\n\t\t\timplicitPrimitiveContext(3, encodeDistributionPointReasonFlagsContent(value.onlySomeReasons)),\n\t\t);\n\t}\n\tif (value.indirectCrl) {\n\t\tfields.push(implicitPrimitiveContext(4, Uint8Array.of(0xff)));\n\t}\n\tif (value.onlyContainsAttributeCerts) {\n\t\tfields.push(implicitPrimitiveContext(5, Uint8Array.of(0xff)));\n\t}\n\treturn sequence(fields);\n}\n\n/** DER-encodes a DistributionPointName (fullName or relativeName). */\nfunction encodeDistributionPointName(\n\tvalue: IssuingDistributionPoint['distributionPoint'],\n): Uint8Array {\n\tif (value === undefined) {\n\t\tthrow new Error('IssuingDistributionPoint distributionPoint is required');\n\t}\n\tif (value.fullName !== undefined && value.relativeName !== undefined) {\n\t\tthrow new Error('DistributionPointName cannot contain both fullName and relativeName');\n\t}\n\tif (value.fullName !== undefined) {\n\t\tif (value.fullName.length === 0) {\n\t\t\tthrow new Error('DistributionPointName fullName must not be empty');\n\t\t}\n\t\treturn implicitConstructedContext(0, concatGeneralNames(value.fullName));\n\t}\n\tif (value.relativeName !== undefined) {\n\t\tconst relativeName = encodeRelativeDistinguishedName(value.relativeName);\n\t\tconst relativeNameElement = readElement(relativeName);\n\t\treturn implicitConstructedContext(\n\t\t\t1,\n\t\t\trelativeName.slice(relativeNameElement.start, relativeNameElement.end),\n\t\t);\n\t}\n\tthrow new Error('DistributionPointName must contain fullName or relativeName');\n}\n\n/** DER-encodes and concatenates a list of GeneralName values. */\nfunction concatGeneralNames(names: readonly GeneralName[]): Uint8Array {\n\treturn concatBytes(names.map((name) => encodeSubjectAltName(name)));\n}\n\n/** Re-wraps an implicitly-tagged directoryName as an explicit SEQUENCE (tag 0x30). */\n/**\n * Extracts the Name SEQUENCE from an implicitly-tagged directoryName [4].\n *\n * Handles two encoding styles found in the wild:\n * - Proper implicit: [4] replaces SEQUENCE tag, content is RDN SETs directly → wrap with 0x30\n * - Explicit-like: [4] wraps entire SEQUENCE, content starts with 0x30 → return content as-is\n */\nfunction rebuildDirectoryNameFromImplicit(element: DerElement): Uint8Array {\n\t// If content already starts with SEQUENCE tag, it's explicit-style encoding\n\tif (element.value.length > 0 && element.value[0] === 0x30) {\n\t\treturn new Uint8Array(element.value);\n\t}\n\t// Otherwise, wrap content with SEQUENCE tag (true implicit encoding)\n\treturn tlv(0x30, element.value);\n}\n\n/** Parses a Name SEQUENCE element into a full {@linkcode ParsedName}. */\nfunction parseIssuer(source: Uint8Array, element: DerElement): ParsedName {\n\tconst derHex = toHex(source.slice(element.start - element.headerLength, element.end));\n\tconst rdns: ParsedRelativeDistinguishedName[] = [];\n\tconst allAttributes: ParsedNameAttribute[] = [];\n\tconst values: Partial<Record<NameFieldKey, string>> = {};\n\tfor (const setElement of childrenOf(source, element)) {\n\t\tconst rdnAttributes: ParsedNameAttribute[] = [];\n\t\tconst rdnValues: Partial<Record<NameFieldKey, string>> = {};\n\t\tfor (const attrSequence of childrenOf(source, setElement)) {\n\t\t\tconst parts = childrenOf(source, attrSequence);\n\t\t\tconst oidElement = requireElement(parts[0], 'issuer attribute OID');\n\t\t\tconst valueElement = requireElement(parts[1], 'issuer attribute value');\n\t\t\tconst oid = decodeObjectIdentifier(oidElement.value);\n\t\t\tlet fieldValue: string;\n\t\t\ttry {\n\t\t\t\tfieldValue = decodeString(valueElement.tag, valueElement.value);\n\t\t\t} catch {\n\t\t\t\tfieldValue = textDecoder.decode(valueElement.value);\n\t\t\t}\n\t\t\tconst fieldKey = nameFieldKeyFromOid(oid);\n\t\t\tconst attribute: ParsedNameAttribute =\n\t\t\t\tfieldKey !== undefined\n\t\t\t\t\t? { oid, key: fieldKey, valueTag: valueElement.tag, value: fieldValue }\n\t\t\t\t\t: { oid, valueTag: valueElement.tag, value: fieldValue };\n\t\t\trdnAttributes.push(attribute);\n\t\t\tallAttributes.push(attribute);\n\t\t\tif (fieldKey !== undefined) {\n\t\t\t\tif (rdnValues[fieldKey] === undefined) {\n\t\t\t\t\trdnValues[fieldKey] = fieldValue;\n\t\t\t\t}\n\t\t\t\tif (values[fieldKey] === undefined) {\n\t\t\t\t\tvalues[fieldKey] = fieldValue;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\trdns.push({\n\t\t\tderHex: toHex(source.slice(setElement.start - setElement.headerLength, setElement.end)),\n\t\t\tattributes: rdnAttributes,\n\t\t\tvalues: rdnValues,\n\t\t});\n\t}\n\treturn { derHex, rdns, attributes: allAttributes, values };\n}\n\n/** Re-parses a hex-encoded DER Name into a {@linkcode ParsedName}. Returns `undefined` on malformed input. */\nfunction parseDerHexName(hex: string): ParsedName | undefined {\n\ttry {\n\t\tconst bytes = hexToBytes(hex);\n\t\tconst root = readRootElement(bytes, { maxDepth: DEFAULT_MAX_DER_DEPTH });\n\t\tconst element = unwrapNameElement(bytes, root);\n\t\tif (element.tag !== 0x30) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn parseIssuer(bytes, element);\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction unwrapNameElement(source: Uint8Array, element: DerElement): DerElement {\n\tif (element.tag !== 0x30) {\n\t\treturn element;\n\t}\n\tconst children = childrenOf(source, element);\n\tconst child = children[0];\n\tif (children.length === 1 && child?.tag === 0x30) {\n\t\treturn child;\n\t}\n\treturn element;\n}\n\n/** Semantic comparison of a GeneralName directoryName `derHex` against a {@linkcode ParsedName}. */\nfunction directoryNameDerHexMatchesParsedName(derHex: string, name: ParsedName): boolean {\n\tconst parsed = parseDerHexName(derHex);\n\tif (parsed === undefined) {\n\t\treturn false;\n\t}\n\treturn compareDistinguishedNames(parsed, name);\n}\n\n/** Extracts the keyIdentifier field from an AuthorityKeyIdentifier extension value. */\nfunction parseAuthorityKeyIdentifier(bytes: Uint8Array): string | undefined {\n\tconst sequenceElement = readRootElement(bytes, {\n\t\tmaxDepth: DEFAULT_MAX_DER_DEPTH,\n\t\tallowOpaqueConstructedTags: [0xa1, 0xa2],\n\t});\n\tif (sequenceElement.tag !== 0x30) {\n\t\tthrow new Error('authorityKeyIdentifier must use SEQUENCE');\n\t}\n\tlet keyIdentifier: string | undefined;\n\tlet sawAuthorityCertIssuer = false;\n\tlet sawAuthorityCertSerialNumber = false;\n\tlet lastFieldOrder = -1;\n\tfor (const child of childrenOf(bytes, sequenceElement)) {\n\t\tif (child.tag === 0x80) {\n\t\t\tif (keyIdentifier !== undefined) {\n\t\t\t\tthrow new Error('authorityKeyIdentifier keyIdentifier must not repeat');\n\t\t\t}\n\t\t\tif (lastFieldOrder >= 0) {\n\t\t\t\tthrow new Error('authorityKeyIdentifier fields must preserve DER order');\n\t\t\t}\n\t\t\tkeyIdentifier = toHex(child.value);\n\t\t\tlastFieldOrder = 0;\n\t\t\tcontinue;\n\t\t}\n\t\tif (child.tag === 0xa1) {\n\t\t\tif (sawAuthorityCertIssuer) {\n\t\t\t\tthrow new Error('authorityKeyIdentifier authorityCertIssuer must not repeat');\n\t\t\t}\n\t\t\tif (lastFieldOrder >= 1) {\n\t\t\t\tthrow new Error('authorityKeyIdentifier fields must preserve DER order');\n\t\t\t}\n\t\t\tparseGeneralNames(bytes, child);\n\t\t\tsawAuthorityCertIssuer = true;\n\t\t\tlastFieldOrder = 1;\n\t\t\tcontinue;\n\t\t}\n\t\tif (child.tag === 0x82) {\n\t\t\tif (sawAuthorityCertSerialNumber) {\n\t\t\t\tthrow new Error('authorityKeyIdentifier authorityCertSerialNumber must not repeat');\n\t\t\t}\n\t\t\tif (lastFieldOrder >= 2 || !sawAuthorityCertIssuer) {\n\t\t\t\tthrow new Error('authorityKeyIdentifier fields must preserve DER order');\n\t\t\t}\n\t\t\tvalidateImplicitSerialNumberEncoding(\n\t\t\t\tchild.value,\n\t\t\t\t'authorityKeyIdentifier authorityCertSerialNumber',\n\t\t\t);\n\t\t\tsawAuthorityCertSerialNumber = true;\n\t\t\tlastFieldOrder = 2;\n\t\t\tcontinue;\n\t\t}\n\t\tthrow new Error(`Unsupported authorityKeyIdentifier field tag: ${String(child.tag)}`);\n\t}\n\treturn keyIdentifier;\n}\n\nfunction validateImplicitSerialNumberEncoding(bytes: Uint8Array, label: string): void {\n\tconst first = bytes[0];\n\tif (first === undefined) {\n\t\tthrow new Error(`${label} must not be empty`);\n\t}\n\tif ((first & 0x80) !== 0) {\n\t\tthrow new Error(`${label} must be non-negative`);\n\t}\n\tif (bytes.length > 1 && first === 0 && ((bytes[1] ?? 0) & 0x80) === 0) {\n\t\tthrow new Error(`${label} must use minimal encoding`);\n\t}\n}\n\n/** Extracts the algorithm OID from an AlgorithmIdentifier SEQUENCE. */\nfunction parseAlgorithmIdentifier(\n\tsource: Uint8Array,\n\telement: DerElement,\n): {\n\treadonly oid: string;\n\treadonly parametersDer?: Uint8Array;\n} {\n\tconst children = childrenOf(source, element);\n\tconst oid = requireElement(children[0], 'algorithm OID');\n\tconst parameters = children[1];\n\treturn parameters === undefined\n\t\t? { oid: decodeObjectIdentifier(oid.value) }\n\t\t: {\n\t\t\t\toid: decodeObjectIdentifier(oid.value),\n\t\t\t\tparametersDer: source.slice(parameters.start - parameters.headerLength, parameters.end),\n\t\t\t};\n}\n\n/** Lowercases a hex string for bytewise serial-number comparison. */\nfunction normalizeHex(value: string): string {\n\treturn value.toLowerCase();\n}\n\n/** Maps an integer CRLReason code back to its {@linkcode RevocationReason} string, or `undefined` for unknown codes. */\nfunction revocationReasonFromCode(code: number | undefined): RevocationReason | undefined {\n\tswitch (code) {\n\t\tcase 0:\n\t\t\treturn 'unspecified';\n\t\tcase 1:\n\t\t\treturn 'keyCompromise';\n\t\tcase 2:\n\t\t\treturn 'cACompromise';\n\t\tcase 3:\n\t\t\treturn 'affiliationChanged';\n\t\tcase 4:\n\t\t\treturn 'superseded';\n\t\tcase 5:\n\t\t\treturn 'cessationOfOperation';\n\t\tcase 6:\n\t\t\treturn 'certificateHold';\n\t\tcase 8:\n\t\t\treturn 'removeFromCRL';\n\t\tcase 9:\n\t\t\treturn 'privilegeWithdrawn';\n\t\tcase 10:\n\t\t\treturn 'aACompromise';\n\t}\n\treturn undefined;\n}\n\n/** Thin wrapper — parses a DER-encoded certificate for use as a CRL issuer. */\nfunction parseIssuerCertificateDer(der: Uint8Array): ParsedCertificate {\n\treturn parseCertificateFromSource(der);\n}\n\n/** Thin wrapper — parses a PEM-encoded certificate for use as a CRL issuer. */\nfunction parseIssuerCertificatePem(pem: string): ParsedCertificate {\n\treturn parseCertificateFromSource(pem);\n}\n\n/** Accepts PEM, DER, or already-parsed CRL and returns a parsed CRL. */\nfunction normalizeCrl(source: CrlSource): ParsedCertificateRevocationList {\n\tif (typeof source === 'string') {\n\t\treturn parseCertificateRevocationListPem(source);\n\t}\n\tif (source instanceof Uint8Array) {\n\t\treturn parseCertificateRevocationListDer(new Uint8Array(source));\n\t}\n\tif (hasReparseableCrlShape(source)) {\n\t\treturn parseCertificateRevocationListDer(new Uint8Array(source.der));\n\t}\n\tthrow new Error('certificate revocation list input is malformed');\n}\n\nfunction parseSignedCrlFields(tbsCertListDer: Uint8Array): {\n\treadonly version: number;\n\treadonly issuer: ParsedName;\n\treadonly thisUpdate: Date;\n\treadonly nextUpdate?: Date;\n\treadonly authorityKeyIdentifier?: string;\n\treadonly crlNumber?: number;\n\treadonly baseCrlNumber?: number;\n\treadonly issuingDistributionPoint?: ParsedIssuingDistributionPoint;\n\treadonly freshestCrlDistributionPoints?: readonly ParsedDistributionPoint[];\n\treadonly revokedCertificates: readonly ParsedRevokedCertificate[];\n} {\n\tconst tbsCertList = readRootElement(tbsCertListDer, { maxDepth: DEFAULT_MAX_DER_DEPTH });\n\tconst tbsChildren = childrenOf(tbsCertListDer, tbsCertList);\n\tlet index = 0;\n\tlet version = 1;\n\tconst firstChild = tbsChildren[index];\n\tif (firstChild !== undefined && firstChild.tag !== 0x02 && firstChild.tag !== 0x30) {\n\t\tthrow new Error('version must use INTEGER');\n\t}\n\tif (firstChild?.tag === 0x02) {\n\t\tconst versionElement = requireElement(tbsChildren[index], 'version');\n\t\tif (versionElement.tag !== 0x02) {\n\t\t\tthrow new Error('version must use INTEGER');\n\t\t}\n\t\tversion = decodeIntegerNumber(versionElement.value) + 1;\n\t\tif (version !== 2) {\n\t\t\tthrow new Error(`Unsupported CRL version: ${String(version)}`);\n\t\t}\n\t\tindex += 1;\n\t}\n\tindex += 1;\n\tconst issuerElement = requireElement(tbsChildren[index], 'issuer');\n\tconst thisUpdateElement = requireElement(tbsChildren[index + 1], 'thisUpdate');\n\tlet cursor = index + 2;\n\tconst maybeNextUpdate = tbsChildren[cursor];\n\tconst nextUpdate =\n\t\tmaybeNextUpdate !== undefined && (maybeNextUpdate.tag === 0x17 || maybeNextUpdate.tag === 0x18)\n\t\t\t? parseTime(maybeNextUpdate)\n\t\t\t: undefined;\n\tif (nextUpdate !== undefined) {\n\t\tcursor += 1;\n\t}\n\tlet revokedCertificates: readonly ParsedRevokedCertificate[] = [];\n\tconst maybeRevoked = tbsChildren[cursor];\n\tif (maybeRevoked?.tag === 0x30) {\n\t\trevokedCertificates = childrenOf(tbsCertListDer, maybeRevoked).map((entry) => {\n\t\t\tconst entryDer = tbsCertListDer.slice(entry.start - entry.headerLength, entry.end);\n\t\t\tconst parts = readSequenceChildren(entryDer);\n\t\t\tconst serialNumber = requireElement(parts[0], 'revoked serialNumber');\n\t\t\tif (serialNumber.tag !== 0x02) {\n\t\t\t\tthrow new Error('revoked serialNumber must use INTEGER');\n\t\t\t}\n\t\t\tconst entryExtensions = parts[2];\n\t\t\tif (entryExtensions !== undefined && version !== 2) {\n\t\t\t\tthrow new Error('revoked certificate extensions require CRL version 2');\n\t\t\t}\n\t\t\tconst parsedEntryExtensions = parseRevokedCertificateExtensions(entryDer, entryExtensions);\n\t\t\treturn {\n\t\t\t\tserialNumberHex: toHex(serialNumber.value),\n\t\t\t\trevocationDate: parseTime(requireElement(parts[1], 'revocationDate')),\n\t\t\t\t...(parsedEntryExtensions.reasonCode === undefined\n\t\t\t\t\t? {}\n\t\t\t\t\t: { reasonCode: parsedEntryExtensions.reasonCode }),\n\t\t\t\t...(parsedEntryExtensions.invalidityDate === undefined\n\t\t\t\t\t? {}\n\t\t\t\t\t: { invalidityDate: parsedEntryExtensions.invalidityDate }),\n\t\t\t\t...(parsedEntryExtensions.certificateIssuer === undefined\n\t\t\t\t\t? {}\n\t\t\t\t\t: { certificateIssuer: parsedEntryExtensions.certificateIssuer }),\n\t\t\t};\n\t\t});\n\t\tcursor += 1;\n\t}\n\tlet authorityKeyIdentifier: string | undefined;\n\tlet crlNumber: number | undefined;\n\tlet baseCrlNumber: number | undefined;\n\tlet issuingDistributionPoint: ParsedIssuingDistributionPoint | undefined;\n\tlet freshestCrlDistributionPoints: readonly ParsedDistributionPoint[] | undefined;\n\tconst maybeExtensions = tbsChildren[cursor];\n\tif (maybeExtensions?.tag === 0xa0) {\n\t\tif (version !== 2) {\n\t\t\tthrow new Error('CRL extensions require version 2');\n\t\t}\n\t\tconst seenOids = new Set<string>();\n\t\tconst extensionSequence = requireElement(\n\t\t\tchildrenOf(tbsCertListDer, maybeExtensions)[0],\n\t\t\t'crl extensions',\n\t\t);\n\t\tfor (const extension of childrenOf(tbsCertListDer, extensionSequence)) {\n\t\t\tconst parts = childrenOf(tbsCertListDer, extension);\n\t\t\tif (parts.length < 2 || parts.length > 3) {\n\t\t\t\tthrow new Error('Malformed CRL extension');\n\t\t\t}\n\t\t\tif (parts.length === 3 && parts[1]?.tag !== 0x01) {\n\t\t\t\tthrow new Error('Malformed CRL extension');\n\t\t\t}\n\t\t\tconst oid = decodeObjectIdentifier(requireElement(parts[0], 'extension OID').value);\n\t\t\tif (seenOids.has(oid)) {\n\t\t\t\tthrow new Error(`Duplicate CRL extension OID: ${oid}`);\n\t\t\t}\n\t\t\tseenOids.add(oid);\n\t\t\tconst critical =\n\t\t\t\tparts.length === 3\n\t\t\t\t\t? decodeBoolean(requireElement(parts[1], 'extension critical').value)\n\t\t\t\t\t: false;\n\t\t\tconst valueElement = requireElement(parts[parts.length - 1], 'extension value');\n\t\t\tif (valueElement.tag !== 0x04) {\n\t\t\t\tthrow new Error('CRL extension value must use OCTET STRING');\n\t\t\t}\n\t\t\tif (\n\t\t\t\toid !== OIDS.authorityKeyIdentifier &&\n\t\t\t\toid !== OIDS.cRLNumber &&\n\t\t\t\toid !== OIDS.deltaCRLIndicator &&\n\t\t\t\toid !== OIDS.issuingDistributionPoint &&\n\t\t\t\toid !== OIDS.freshestCRL &&\n\t\t\t\tcritical\n\t\t\t) {\n\t\t\t\tthrow new Error(`Unsupported critical CRL extension OID: ${oid}`);\n\t\t\t}\n\t\t\tif (oid === OIDS.authorityKeyIdentifier) {\n\t\t\t\tauthorityKeyIdentifier = parseAuthorityKeyIdentifier(valueElement.value);\n\t\t\t}\n\t\t\tif (oid === OIDS.cRLNumber) {\n\t\t\t\tcrlNumber = decodeIntegerNumber(readElement(valueElement.value).value);\n\t\t\t}\n\t\t\tif (oid === OIDS.deltaCRLIndicator) {\n\t\t\t\tbaseCrlNumber = decodeIntegerNumber(readElement(valueElement.value).value);\n\t\t\t}\n\t\t\tif (oid === OIDS.issuingDistributionPoint) {\n\t\t\t\tissuingDistributionPoint = parseIssuingDistributionPoint(valueElement.value);\n\t\t\t}\n\t\t\tif (oid === OIDS.freshestCRL) {\n\t\t\t\tfreshestCrlDistributionPoints = parseDistributionPoints(valueElement.value);\n\t\t\t}\n\t\t}\n\t}\n\treturn {\n\t\tversion,\n\t\tissuer: parseIssuer(tbsCertListDer, issuerElement),\n\t\tthisUpdate: parseTime(thisUpdateElement),\n\t\t...(nextUpdate === undefined ? {} : { nextUpdate }),\n\t\t...(authorityKeyIdentifier === undefined ? {} : { authorityKeyIdentifier }),\n\t\t...(crlNumber === undefined ? {} : { crlNumber }),\n\t\t...(baseCrlNumber === undefined ? {} : { baseCrlNumber }),\n\t\t...(issuingDistributionPoint === undefined ? {} : { issuingDistributionPoint }),\n\t\t...(freshestCrlDistributionPoints === undefined ? {} : { freshestCrlDistributionPoints }),\n\t\trevokedCertificates,\n\t};\n}\n\n/** Accepts PEM, DER, or already-parsed certificate and returns a parsed certificate. */\nfunction normalizeCrlCertificate(source: CrlCertificateSource): ParsedCertificate {\n\treturn hasParsedCertificateShape(source)\n\t\t? parseCertificateDer(new Uint8Array(source.der))\n\t\t: parseCertificateFromSource(source);\n}\n\nfunction hasParsedCertificateShape(value: CrlCertificateSource): value is ParsedCertificate {\n\treturn typeof value !== 'string' && 'subjectPublicKeyInfoDer' in value;\n}\n\nfunction hasReparseableCrlShape(\n\tcrl: ParsedCertificateRevocationList,\n): crl is ParsedCertificateRevocationList & { readonly der: Uint8Array } {\n\treturn 'der' in crl && crl.der instanceof Uint8Array;\n}\n\n/** Shared UTF-8 decoder instance. */\nconst textDecoder = new TextDecoder();\n"],"mappings":"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"}
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import { ErrorResult, Micro509Error } from "../result/result.js";
|
|
2
|
+
import { ParsedCertificate, ParsedName } from "../x509/parse.js";
|
|
3
|
+
|
|
4
|
+
//#region src/revocation/ocsp.d.ts
|
|
5
|
+
/** Hash algorithm used to compute OCSP CertID fields. SHA-1 is the RFC 6960 default. */
|
|
6
|
+
type OcspHashAlgorithm = "SHA-1" | "SHA-256";
|
|
7
|
+
/** PEM string, DER bytes, or already-parsed certificate. */
|
|
8
|
+
type OcspCertificateSource = string | Uint8Array | ParsedCertificate;
|
|
9
|
+
/** PEM string, DER bytes, or already-parsed OCSP request. */
|
|
10
|
+
type OcspRequestSource = string | Uint8Array | ParsedOcspRequest;
|
|
11
|
+
/**
|
|
12
|
+
* One certificate whose status to query in an OCSP request.
|
|
13
|
+
* Used as an element of {@linkcode CreateOcspRequestInput.requests}.
|
|
14
|
+
*/
|
|
15
|
+
interface CreateOcspRequestItemInput {
|
|
16
|
+
/** Certificate whose revocation status is being queried. */
|
|
17
|
+
readonly certificate: OcspCertificateSource;
|
|
18
|
+
/** Issuer of `certificate` — needed to compute the CertID hash. */
|
|
19
|
+
readonly issuerCertificate: OcspCertificateSource;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Input for {@linkcode createOcspRequest}.
|
|
23
|
+
*/
|
|
24
|
+
interface CreateOcspRequestInput {
|
|
25
|
+
/** One or more certificates to query (batched into a single OCSP request). */
|
|
26
|
+
readonly requests: readonly CreateOcspRequestItemInput[];
|
|
27
|
+
/** Hash algorithm for CertID computation. Defaults to `'SHA-1'`. */
|
|
28
|
+
readonly hashAlgorithm?: OcspHashAlgorithm;
|
|
29
|
+
/** Random nonce for replay protection. Omit to skip the nonce extension. */
|
|
30
|
+
readonly nonce?: Uint8Array;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Encoded OCSP request in multiple serialisation formats, returned by {@linkcode createOcspRequest}.
|
|
34
|
+
*/
|
|
35
|
+
interface OcspRequestMaterial {
|
|
36
|
+
/** Raw DER bytes. */
|
|
37
|
+
readonly der: Uint8Array;
|
|
38
|
+
/** PEM-encoded request (`-----BEGIN OCSP REQUEST-----`). */
|
|
39
|
+
readonly pem: string;
|
|
40
|
+
/** Base64-encoded DER (no PEM armour). */
|
|
41
|
+
readonly base64: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Decoded OCSP CertID — identifies a certificate by hashed issuer name,
|
|
45
|
+
* hashed issuer key, and serial number.
|
|
46
|
+
*/
|
|
47
|
+
interface ParsedOcspCertId {
|
|
48
|
+
/** OID of the hash algorithm used for the name and key hashes. */
|
|
49
|
+
readonly hashAlgorithmOid: string;
|
|
50
|
+
/** Human-readable hash algorithm name (e.g. `"SHA-256"`). */
|
|
51
|
+
readonly hashAlgorithmName: string;
|
|
52
|
+
/** Hex-encoded hash of the issuer's distinguished name DER. */
|
|
53
|
+
readonly issuerNameHashHex: string;
|
|
54
|
+
/** Hex-encoded hash of the issuer's SubjectPublicKey BIT STRING content. */
|
|
55
|
+
readonly issuerKeyHashHex: string;
|
|
56
|
+
/** Hex-encoded serial number of the certificate. */
|
|
57
|
+
readonly serialNumberHex: string;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Decoded OCSP request, returned by {@linkcode parseOcspRequestDer} / {@linkcode parseOcspRequestPem}.
|
|
61
|
+
*/
|
|
62
|
+
interface ParsedOcspRequest {
|
|
63
|
+
/** Original DER bytes when this object came from {@linkcode parseOcspRequestDer} or PEM parsing. */
|
|
64
|
+
readonly der?: Uint8Array;
|
|
65
|
+
/** CertIDs of the certificates being queried. */
|
|
66
|
+
readonly requests: readonly ParsedOcspCertId[];
|
|
67
|
+
/** Hex-encoded nonce extension value, if present. */
|
|
68
|
+
readonly nonce?: string;
|
|
69
|
+
}
|
|
70
|
+
/** RFC 6960 certificate status reported by the responder for a single CertID. */
|
|
71
|
+
type OcspCertStatus = "good" | "revoked" | "unknown";
|
|
72
|
+
/** RFC 6960 overall response status — anything other than `'successful'` means the response body is absent or unusable. */
|
|
73
|
+
type OcspResponseStatus = "successful" | "malformedRequest" | "internalError" | "tryLater" | "sigRequired" | "unauthorized";
|
|
74
|
+
/**
|
|
75
|
+
* Status of one certificate inside an OCSP BasicResponse.
|
|
76
|
+
*/
|
|
77
|
+
interface ParsedOcspSingleResponse {
|
|
78
|
+
/** Which certificate this status applies to. */
|
|
79
|
+
readonly certId: ParsedOcspCertId;
|
|
80
|
+
/** Responder's verdict: `good`, `revoked`, or `unknown`. */
|
|
81
|
+
readonly certStatus: OcspCertStatus;
|
|
82
|
+
/** Start of the validity window for this status assertion. */
|
|
83
|
+
readonly thisUpdate: Date;
|
|
84
|
+
/** End of the validity window. Absent if the responder does not commit to a schedule. */
|
|
85
|
+
readonly nextUpdate?: Date;
|
|
86
|
+
/** When the certificate was revoked (only for `certStatus === 'revoked'`). */
|
|
87
|
+
readonly revokedAt?: Date;
|
|
88
|
+
/** CRLReason integer (only for `certStatus === 'revoked'`). */
|
|
89
|
+
readonly revocationReasonCode?: number;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* How the OCSP responder identifies itself — either by distinguished name or
|
|
93
|
+
* by SHA-1 hash of its public key.
|
|
94
|
+
*/
|
|
95
|
+
type ParsedOcspResponderId = {
|
|
96
|
+
/** Responder identified by its certificate subject name. */readonly type: "byName"; /** Parsed distinguished name of the responder. */
|
|
97
|
+
readonly name: ParsedName;
|
|
98
|
+
} | {
|
|
99
|
+
/** Responder identified by public-key hash. */readonly type: "byKeyHash"; /** Hex-encoded SHA-1 hash of the responder's SubjectPublicKey content. */
|
|
100
|
+
readonly keyHashHex: string;
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* Decoded OCSP response, returned by {@linkcode parseOcspResponseDer} / {@linkcode parseOcspResponsePem}.
|
|
104
|
+
*
|
|
105
|
+
* When `responseStatus` is not `'successful'`, most fields are absent.
|
|
106
|
+
*/
|
|
107
|
+
interface ParsedOcspResponse {
|
|
108
|
+
/** Original DER bytes when this object came from {@linkcode parseOcspResponseDer} or PEM parsing. */
|
|
109
|
+
readonly der?: Uint8Array;
|
|
110
|
+
/** Overall response status. Only `'successful'` carries a BasicOCSPResponse body. */
|
|
111
|
+
readonly responseStatus: OcspResponseStatus;
|
|
112
|
+
/** OID of the response type (normally `id-pkix-ocsp-basic`). */
|
|
113
|
+
readonly responseTypeOid?: string;
|
|
114
|
+
/** DER-encoded ResponseData — the signed payload for signature verification. */
|
|
115
|
+
readonly responseDataDer?: Uint8Array;
|
|
116
|
+
/** How the responder identifies itself. */
|
|
117
|
+
readonly responderId?: ParsedOcspResponderId;
|
|
118
|
+
/** OID of the algorithm used to sign this response. */
|
|
119
|
+
readonly signatureAlgorithmOid?: string;
|
|
120
|
+
/** Human-readable signature algorithm name. */
|
|
121
|
+
readonly signatureAlgorithmName?: string;
|
|
122
|
+
/** Raw signature bytes. */
|
|
123
|
+
readonly signatureValue?: Uint8Array;
|
|
124
|
+
/** Timestamp when the responder produced this response. */
|
|
125
|
+
readonly producedAt?: Date;
|
|
126
|
+
/** Per-certificate status entries. */
|
|
127
|
+
readonly responses?: readonly ParsedOcspSingleResponse[];
|
|
128
|
+
/** Hex-encoded nonce, if the response echoed one. */
|
|
129
|
+
readonly nonce?: string;
|
|
130
|
+
/** Certificates embedded in the response (typically the responder's chain). */
|
|
131
|
+
readonly certificates?: readonly ParsedCertificate[];
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* One certificate's status entry for {@linkcode CreateOcspResponseInput.responses}.
|
|
135
|
+
* Extends {@linkcode CreateOcspRequestItemInput} with status and timing fields.
|
|
136
|
+
*/
|
|
137
|
+
interface CreateOcspSingleResponseInput extends CreateOcspRequestItemInput {
|
|
138
|
+
/** Status to assert for this certificate. */
|
|
139
|
+
readonly certStatus: OcspCertStatus;
|
|
140
|
+
/** Start of the validity window for this status assertion. Defaults to `new Date()`. */
|
|
141
|
+
readonly thisUpdate?: Date;
|
|
142
|
+
/** End of the validity window. Omit for open-ended assertions. */
|
|
143
|
+
readonly nextUpdate?: Date;
|
|
144
|
+
/** Revocation time (required when `certStatus` is `'revoked'`). Defaults to `thisUpdate`. */
|
|
145
|
+
readonly revokedAt?: Date;
|
|
146
|
+
/** CRLReason integer code (only meaningful when `certStatus` is `'revoked'`). */
|
|
147
|
+
readonly revocationReasonCode?: number;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Input for {@linkcode createOcspResponse}.
|
|
151
|
+
*/
|
|
152
|
+
interface CreateOcspResponseInput {
|
|
153
|
+
/** Private key used to sign the response. Algorithm is inferred from the key. */
|
|
154
|
+
readonly signerPrivateKey: CryptoKey;
|
|
155
|
+
/** Certificate of the OCSP responder — used to build the responder ID (by key hash). */
|
|
156
|
+
readonly signerCertificate: OcspCertificateSource;
|
|
157
|
+
/** Per-certificate status entries to include in the BasicOCSPResponse. */
|
|
158
|
+
readonly responses: readonly CreateOcspSingleResponseInput[];
|
|
159
|
+
/** Timestamp for the `producedAt` field. Defaults to `new Date()`. */
|
|
160
|
+
readonly producedAt?: Date;
|
|
161
|
+
/** Nonce to echo back for replay protection. */
|
|
162
|
+
readonly nonce?: Uint8Array;
|
|
163
|
+
/** Hash algorithm for CertID computation. Defaults to `'SHA-1'`. */
|
|
164
|
+
readonly hashAlgorithm?: OcspHashAlgorithm;
|
|
165
|
+
/** Extra certificates to embed in the response (e.g. the responder's issuer chain). */
|
|
166
|
+
readonly includedCertificates?: readonly OcspCertificateSource[];
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Encoded OCSP response in multiple serialisation formats, returned by {@linkcode createOcspResponse}.
|
|
170
|
+
*/
|
|
171
|
+
interface OcspResponseMaterial {
|
|
172
|
+
/** Raw DER bytes. */
|
|
173
|
+
readonly der: Uint8Array;
|
|
174
|
+
/** PEM-encoded response (`-----BEGIN OCSP RESPONSE-----`). */
|
|
175
|
+
readonly pem: string;
|
|
176
|
+
/** Base64-encoded DER (no PEM armour). */
|
|
177
|
+
readonly base64: string;
|
|
178
|
+
}
|
|
179
|
+
/** Failure detail when OCSP response signature verification fails. */
|
|
180
|
+
interface VerifyOcspResponseFailure extends Micro509Error<"signature_invalid"> {
|
|
181
|
+
/** Always `false` for failures. */
|
|
182
|
+
readonly ok: false;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Result of {@linkcode verifyOcspResponse}.
|
|
186
|
+
*
|
|
187
|
+
* On success, `value` is the parsed response whose signature has been verified.
|
|
188
|
+
*/
|
|
189
|
+
type VerifyOcspResponseResult = {
|
|
190
|
+
readonly ok: true; /** Parsed response with a verified signature. */
|
|
191
|
+
readonly value: ParsedOcspResponse;
|
|
192
|
+
} | ErrorResult<"signature_invalid", Record<never, never>, VerifyOcspResponseFailure>;
|
|
193
|
+
/**
|
|
194
|
+
* Input for {@linkcode validateOcspResponse}.
|
|
195
|
+
*/
|
|
196
|
+
interface ValidateOcspResponseInput {
|
|
197
|
+
/** The OCSP response to validate. */
|
|
198
|
+
readonly response: string | Uint8Array | ParsedOcspResponse;
|
|
199
|
+
/** Certificate of the CA that issued the target certificate. */
|
|
200
|
+
readonly issuerCertificate: OcspCertificateSource;
|
|
201
|
+
/** Original request — enables nonce and request-coverage checks. */
|
|
202
|
+
readonly request?: OcspRequestSource;
|
|
203
|
+
/** Explicit responder certificate — overrides embedded certificate discovery. */
|
|
204
|
+
readonly responderCertificate?: OcspCertificateSource;
|
|
205
|
+
/** When `true`, allows delegated responder chain validation beyond direct issuance. */
|
|
206
|
+
readonly allowChainedResponderCertificate?: boolean;
|
|
207
|
+
/** Evaluation time for freshness checks. Defaults to `new Date()`. */
|
|
208
|
+
readonly at?: Date;
|
|
209
|
+
/** Clock-skew tolerance in milliseconds for `thisUpdate`/`nextUpdate`/`producedAt`. */
|
|
210
|
+
readonly clockSkewMs?: number;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Failure detail for {@linkcode validateOcspResponse}.
|
|
214
|
+
*
|
|
215
|
+
* Possible codes: `response_status_invalid`, `signature_invalid`,
|
|
216
|
+
* `responder_id_mismatch`, `nonce_mismatch`, `request_mismatch`,
|
|
217
|
+
* `issuer_mismatch`, `responder_chain_invalid`, `ocsp_signing_missing`,
|
|
218
|
+
* `stale_response`.
|
|
219
|
+
*/
|
|
220
|
+
interface ValidateOcspResponseFailure extends Micro509Error<"response_status_invalid" | "signature_invalid" | "responder_id_mismatch" | "nonce_mismatch" | "request_mismatch" | "issuer_mismatch" | "responder_chain_invalid" | "ocsp_signing_missing" | "stale_response"> {
|
|
221
|
+
/** Always `false` for failures. */
|
|
222
|
+
readonly ok: false;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Result of {@linkcode validateOcspResponse}.
|
|
226
|
+
*
|
|
227
|
+
* On success, the response has passed status, signature, responder binding,
|
|
228
|
+
* freshness, nonce, and request-coverage checks.
|
|
229
|
+
*/
|
|
230
|
+
type ValidateOcspResponseResult = {
|
|
231
|
+
readonly ok: true; /** Fully validated OCSP response. */
|
|
232
|
+
readonly value: ParsedOcspResponse;
|
|
233
|
+
} | ErrorResult<"response_status_invalid" | "signature_invalid" | "responder_id_mismatch" | "nonce_mismatch" | "request_mismatch" | "issuer_mismatch" | "responder_chain_invalid" | "ocsp_signing_missing" | "stale_response", Record<never, never>, ValidateOcspResponseFailure>;
|
|
234
|
+
/**
|
|
235
|
+
* Builds a DER-encoded OCSP request containing one or more CertID entries
|
|
236
|
+
* and an optional nonce extension.
|
|
237
|
+
*
|
|
238
|
+
* @example
|
|
239
|
+
* ```ts
|
|
240
|
+
* import { createOcspRequest } from 'micro509';
|
|
241
|
+
*
|
|
242
|
+
* const req = await createOcspRequest({
|
|
243
|
+
* requests: [{ certificate: leafPem, issuerCertificate: caPem }],
|
|
244
|
+
* hashAlgorithm: 'SHA-256',
|
|
245
|
+
* nonce: crypto.getRandomValues(new Uint8Array(16)),
|
|
246
|
+
* });
|
|
247
|
+
* // POST req.der to the OCSP responder URI
|
|
248
|
+
* ```
|
|
249
|
+
*/
|
|
250
|
+
declare function createOcspRequest(input: CreateOcspRequestInput): Promise<OcspRequestMaterial>;
|
|
251
|
+
/** Decodes a DER-encoded OCSP request into a structured {@linkcode ParsedOcspRequest}. */
|
|
252
|
+
declare function parseOcspRequestDer(der: Uint8Array): ParsedOcspRequest;
|
|
253
|
+
/** Decodes a PEM-encoded OCSP request (`-----BEGIN OCSP REQUEST-----`). */
|
|
254
|
+
declare function parseOcspRequestPem(pem: string): ParsedOcspRequest;
|
|
255
|
+
/** Decodes a DER-encoded OCSP response into a structured {@linkcode ParsedOcspResponse}. Does not verify the signature. */
|
|
256
|
+
declare function parseOcspResponseDer(der: Uint8Array): ParsedOcspResponse;
|
|
257
|
+
/**
|
|
258
|
+
* Decodes a PEM-encoded OCSP response (`-----BEGIN OCSP RESPONSE-----`).
|
|
259
|
+
*
|
|
260
|
+
* @example
|
|
261
|
+
* ```ts
|
|
262
|
+
* import { parseOcspResponsePem } from 'micro509';
|
|
263
|
+
*
|
|
264
|
+
* const resp = parseOcspResponsePem(pemString);
|
|
265
|
+
* if (resp.responseStatus === 'successful') {
|
|
266
|
+
* for (const entry of resp.responses ?? []) {
|
|
267
|
+
* console.log(entry.certId.serialNumberHex, entry.certStatus);
|
|
268
|
+
* }
|
|
269
|
+
* }
|
|
270
|
+
* ```
|
|
271
|
+
*/
|
|
272
|
+
declare function parseOcspResponsePem(pem: string): ParsedOcspResponse;
|
|
273
|
+
/**
|
|
274
|
+
* Signs and encodes an OCSP BasicResponse with a `successful` status.
|
|
275
|
+
*
|
|
276
|
+
* The responder is identified by key hash (SHA-1 of the signer's SubjectPublicKey).
|
|
277
|
+
* Use `includedCertificates` to embed the responder's chain for relying parties.
|
|
278
|
+
*
|
|
279
|
+
* @example
|
|
280
|
+
* ```ts
|
|
281
|
+
* import { createOcspResponse } from 'micro509';
|
|
282
|
+
*
|
|
283
|
+
* const resp = await createOcspResponse({
|
|
284
|
+
* signerPrivateKey: responderPrivateKey,
|
|
285
|
+
* signerCertificate: responderCertPem,
|
|
286
|
+
* responses: [
|
|
287
|
+
* {
|
|
288
|
+
* certificate: leafPem,
|
|
289
|
+
* issuerCertificate: caPem,
|
|
290
|
+
* certStatus: 'good',
|
|
291
|
+
* thisUpdate: new Date('2025-01-01'),
|
|
292
|
+
* nextUpdate: new Date('2025-01-08'),
|
|
293
|
+
* },
|
|
294
|
+
* ],
|
|
295
|
+
* nonce: requestNonce,
|
|
296
|
+
* });
|
|
297
|
+
* // resp.der, resp.pem, resp.base64
|
|
298
|
+
* ```
|
|
299
|
+
*/
|
|
300
|
+
declare function createOcspResponse(input: CreateOcspResponseInput): Promise<OcspResponseMaterial>;
|
|
301
|
+
/**
|
|
302
|
+
* Verifies the OCSP response signature against the given signer certificate.
|
|
303
|
+
*
|
|
304
|
+
* Does **not** check responder binding, freshness, or nonce — use
|
|
305
|
+
* {@linkcode validateOcspResponse} for full validation.
|
|
306
|
+
*/
|
|
307
|
+
declare function verifyOcspResponse(response: string | Uint8Array | ParsedOcspResponse, signerCertificate: OcspCertificateSource): Promise<VerifyOcspResponseResult>;
|
|
308
|
+
/**
|
|
309
|
+
* Full OCSP response validation: response status check, signature verification,
|
|
310
|
+
* responder ID binding (byName or byKeyHash), delegated-responder chain and
|
|
311
|
+
* ocspSigning EKU checks, `producedAt`/`thisUpdate`/`nextUpdate` freshness,
|
|
312
|
+
* nonce match, and request-coverage completeness.
|
|
313
|
+
*
|
|
314
|
+
* @example
|
|
315
|
+
* ```ts
|
|
316
|
+
* import { validateOcspResponse } from 'micro509';
|
|
317
|
+
*
|
|
318
|
+
* const result = await validateOcspResponse({
|
|
319
|
+
* response: ocspResponseDer,
|
|
320
|
+
* issuerCertificate: caPem,
|
|
321
|
+
* request: ocspRequestDer,
|
|
322
|
+
* });
|
|
323
|
+
* if (result.ok) {
|
|
324
|
+
* const entry = result.value.responses?.[0];
|
|
325
|
+
* console.log(entry?.certStatus); // 'good' | 'revoked' | 'unknown'
|
|
326
|
+
* }
|
|
327
|
+
* ```
|
|
328
|
+
*/
|
|
329
|
+
declare function validateOcspResponse(input: ValidateOcspResponseInput): Promise<ValidateOcspResponseResult>;
|
|
330
|
+
//#endregion
|
|
331
|
+
export { CreateOcspRequestInput, CreateOcspRequestItemInput, CreateOcspResponseInput, CreateOcspSingleResponseInput, OcspCertStatus, OcspCertificateSource, OcspHashAlgorithm, OcspRequestMaterial, OcspRequestSource, OcspResponseMaterial, OcspResponseStatus, ParsedOcspCertId, ParsedOcspRequest, ParsedOcspResponderId, ParsedOcspResponse, ParsedOcspSingleResponse, ValidateOcspResponseFailure, ValidateOcspResponseInput, ValidateOcspResponseResult, VerifyOcspResponseFailure, VerifyOcspResponseResult, createOcspRequest, createOcspResponse, parseOcspRequestDer, parseOcspRequestPem, parseOcspResponseDer, parseOcspResponsePem, validateOcspResponse, verifyOcspResponse };
|
|
332
|
+
//# sourceMappingURL=ocsp.d.ts.map
|