@blamejs/pki 0.8.22 → 0.8.23
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/CHANGELOG.md +19 -0
- package/README.md +1 -1
- package/lib/lint.js +249 -3
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,25 @@ All notable changes to `@blamejs/pki` are documented here. The format
|
|
|
4
4
|
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this
|
|
5
5
|
project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## v0.8.23 — 2026-09-24
|
|
8
|
+
|
|
9
|
+
A root program's requirement is graded by the clause that states it, so the same key purpose is a warning in one place and an error in another.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- `pki.lint.certificate(der, { profile: "chrome-root" })` runs the Chrome Root Program Policy v1.8 profile. Naming it is the only way to reach these rows, and passing no profile leaves every one of them inert.
|
|
14
|
+
- The section 1.3.2 rules for a subordinate CA certificate, which the policy states as two admitted forms, `id-kp-serverAuth` alone or `id-kp-serverAuth` with `id-kp-clientAuth`. Both forms require the extKeyUsage extension and require `id-kp-serverAuth` in it, and neither admits any other purpose, so those three are errors and `anyExtendedKeyUsage` is reported among the others. Each offending purpose draws its own finding naming that purpose. The second form is open only where the certificate was disclosed to the CCADB before 15 June 2026, which a certificate does not carry, so `id-kp-clientAuth` is reported as a warning whose message names the date that settles it.
|
|
15
|
+
- Those four rows reach the unexpired subordinate CA certificates the clause names, so a certificate whose notAfter precedes 15 June 2026 is passed over: the phase-out begins that day and the certificate was not among the unexpired ones then. They do reach a branch serving another purpose, which is the point of the section rather than an overreach, since it governs every subordinate CA certificate beneath a root in the store and exists to phase out the multi-purpose hierarchies. Whether a certificate is revoked is not in the bytes and stays outside the reading.
|
|
16
|
+
- The same section's rule for a subscriber certificate, which admits `id-kp-serverAuth` alone and is gated on issuance rather than disclosure. The three rows are silent on a certificate whose notBefore precedes 15 March 2027 and report on the same bytes after it, and `id-kp-clientAuth` is an error there rather than the warning it draws on a subordinate CA. A subscriber certificate is not every end entity: a delegated OCSP responder has its own profile in the Baseline Requirements that section 1.1.1 binds, asking for `id-kp-OCSPSigning` and no other purpose, so a certificate whose extKeyUsage names that purpose and nothing else is passed over. One naming it beside others is not a responder under that profile and is read here as the subscriber certificate it is.
|
|
17
|
+
- The section 1.3.1.3 ceiling of three years on a subordinate CA certificate's validity period, reported as a warning at the strength the clause states it. A ceiling given in years is measured in months, so a 29 February notBefore clamps to 28 February rather than moving into March.
|
|
18
|
+
- The section 1.3.1.2 root CA term limit. The clause determines the age of key material from the earliest certificate that carries the key, and a self-signed root's own notBefore is at or after that date. The schedule is monotone, so the removal date computed from notBefore is the latest one the schedule can give, and a notAfter past it runs beyond the term whatever earlier certificate exists. The finding names a self-signed CA certificate rather than a root, which is as far as the bytes reach: a CA outside any store issues itself one of these during its own key rollover and it matches a root in every field. What the finding says is that if this certificate is a root in the store the caller named, its term ends before its validity does, and the notice grade is that conditional. It carries the removal date.
|
|
19
|
+
- Matching issuer and subject names make a certificate self-issued, which is not the same as self-signed: a CA reissued in its own name under a new key carries the same two names and was signed by its predecessor. The term limit governs a root, so it reads the key identifiers as well, and passes over a certificate whose authorityKeyIdentifier names a key other than its own subjectKeyIdentifier. A certificate that omits the extension, or names its own key in it, is held to the clause. A keyIdentifier with no readable subjectKeyIdentifier beside it settles nothing, and the row rests on settling it, so that reads as not a root too. What remains outside that reading is a reissued CA carrying no authorityKeyIdentifier keyIdentifier at all, which RFC 5280 section 4.2.1.1 permits only in a self-signed certificate and which the `rfc5280` profile reports on its own row.
|
|
20
|
+
- A subordinate CA is a role in a hierarchy rather than something a certificate states, so the rows that govern one read the certificate's own issuer and subject names and pass over a certificate issued in its own name. Section 1.3.1.1 requires a cross-certificate from a root to its replacement, which carries one name in both fields, so those rows under-report rather than reporting a succession certificate for the extended key usage a root does not need.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- `pki.lint.rules()` enumerates the new rows, which stay inert under the default rule set because each one asks whether the caller named the profile.
|
|
25
|
+
|
|
7
26
|
## v0.8.22 — 2026-09-24
|
|
8
27
|
|
|
9
28
|
An algorithm identifier is compared as the bytes a root program fixed, not as the algorithm it names.
|
package/README.md
CHANGED
|
@@ -290,7 +290,7 @@ comment blocks, is at [pkijs.com](https://pkijs.com).
|
|
|
290
290
|
| `pki.sigstore` | Offline verifier for a Sigstore bundle, the artifact `npm publish --provenance` produces and the registry serves. `verifyBundle` composes five fail-closed legs against caller-pinned trust (Fulcio CA roots and Rekor log keys, never trusted from the bundle): the DSSE signature over its PAE preimage under the Fulcio leaf key, the ephemeral Fulcio certificate chain validated as of the Rekor log time, the RFC 9162 inclusion proof folded to a Rekor-signed tree root, the log entry bound to this exact signature, and the in-toto SLSA subject digest the caller confirms against the published artifact. It reuses the X.509 parser, the RFC 5280 path validator, and the Merkle verifier; the net-new codecs are the DSSE PAE byte-builder and a fail-closed JSON reader. `verified: true` says the artifact was signed and logged, not that a party you trust signed it — Fulcio issues to anyone who completes an OIDC flow, so who signed is decided only by `opts.identity`, and `identityChecked` reports which of its fields were compared. An identity policy naming no field, or a field name that is not one of the three, is refused rather than satisfied — either would accept every signer while reading as a policy in force. A sixth leg is opt-in: pinning the certificate-transparency logs in `opts.ctLogs` checks the receipt Fulcio embeds in the certificate it issues (RFC 6962 §3.2), verified over the certificate as it stood before the receipt was added and under the key of the log that issued it, so the verdict says the signing certificate was public when it was issued rather than handed out quietly. At least one receipt must verify against a pinned log, the verdict reports `sctChecked` and `validScts`, and an empty log set is refused rather than read as a policy that checks nothing. Both content arms are read: a DSSE-wrapped in-toto attestation, and a message signature over an artifact's own bytes, which `cosign sign-blob` produces. For that arm `opts.artifact` supplies the bytes, and they are held to the hash the Rekor `hashedrekord` entry records before the signature is checked over them; the `messageDigest` the bundle carries is covered by no signature, so it is compared against the computed digest and never used in its place. `contentType` names the arm and the verdict has one shape for both — `pae`, `parseBundle`, `verifyBundle` |
|
|
291
291
|
| `pki.inspect` | Human-readable inspection, the pure-JS equivalent of `openssl x509/crl/req/cms -text`. `certificate(pem \| der \| parsed)` renders an OpenSSL-style report: version, serial, signature algorithm, issuer and subject distinguished names, validity, public-key details (curve or modulus size plus the raw point or modulus), every decoded extension with its critical flag, and the signature. `crl`, `csr`, and `cms` render the other formats the same way — a CRL like `openssl crl -text`, a CSR like `openssl req -text`, and a CMS message like `openssl cms -cmsout -print`, with a stable summary for a non-SignedData ContentInfo — and `any(input)` detects the format and routes to the right report. Built over the strict parsers and the two-way OID registry with one set of field renderers, it names extension and algorithm OIDs an OpenSSL build shows only as raw bytes. No OpenSSL dependency, and the format is stable and OpenSSL-familiar rather than pinned to one OpenSSL version. A certificate policy's user notice renders as text, both its explicit text and a notice reference with the notice numbers that identify it, rather than hex, and a malformed part falls back to a hex dump rather than throwing — `certificate`, `crl`, `csr`, `cms`, `any` |
|
|
292
292
|
| `pki.webauthn` | WebAuthn and passkey verification, both halves: offline trust evaluation of a W3C WebAuthn (Level 3) registration, and signature verification of the assertion every login returns. `parseAttestationObject(bytes)` decodes the CBOR attestation object, authenticatorData, and COSE credential key over the strict `pki.cbor` codec; `parseAuthenticatorData(bytes)` reads the bare form an assertion carries through the same parser; `parseClientData(bytes, opts)` decodes the `clientDataJSON` no signature check looks inside, through the shared JSON guard since these are attacker-chosen bytes, returning the challenge decoded so a caller compares bytes rather than spellings, and checking the ceremony type, challenge, and origin when the relying party supplies what it issued. `verifyAssertion(input)` verifies an assertion signature over `authenticatorData \|\| SHA-256(clientDataJSON)` — raw bytes, no COSE_Sign1, an ES256 signature in ASN.1 DER — and applies the §7.2 step 21 counter rule when a stored `previousSignCount` is given, so a counter that fails to advance is refused as a cloned authenticator. `verify(attestationObject, clientDataHash, opts)` checks the attestation-statement signature and each format's structural bindings for packed, tpm, android-key, apple, fido-u2f, and none: the x5c leaf key, the apple nonce, the tpm `certInfo` Name and `extraData` over the `pubArea`, the android `KeyDescription`, and the fido-u2f `verificationData`. It binds the credential public key to each attestation, through the signed authenticatorData for packed and fido-u2f or a cert or `pubArea`-key equality check for android-key, apple, and tpm, and enforces each leaf's certificate requirements. The credential-key check covers the full WebAuthn COSE algorithm set — ES256/384/512, RS256/384/512, PS256/384/512, EdDSA (Ed25519), and the RFC 9864 fully-specified identifiers ESP256/384/512, Ed25519, and Ed448 — validating the public-key point on its curve, rejecting the compressed EC point form, and enforcing a minimally encoded DER ECDSA signature. The verdict field is `attestationVerified`, and `signatureVerified` for an assertion, rather than a bare `verified`, because a sound statement is a different claim from an acceptable ceremony: an attestation naming another origin's RP ID with user presence clear is perfectly sound and must not be registered. Pass `expectedRpId`, `requireUserPresence`, `requireUserVerification`, or `allowedAlgorithms` and those are checked, with `bindingChecked` reporting which ran, so a check that passed can be told from one that never happened. Pass `opts.clientDataJSON` instead of the digest and the client data is read here too: the ceremony type is checked unconditionally, since a login response replayed into a registration is what that rule stops, and the challenge, origin, and top-level origin are checked against what the relying party issued, with `clientData.checked` reporting which comparisons ran. A registration verdict also carries the `credentialId`, `credentialPublicKey`, and initial `signCount` a later login needs. A credential key declaring COSE algorithm `-65535` (RSASSA-PKCS1-v1_5 with SHA-1) is refused unless `allowedAlgorithms` names it, since every signature that credential ever makes would use SHA-1. Anchoring the trust path has two routes: `opts.metadata` resolves the roots the authenticator's own model registered, and `opts.rootCertificates` pins roots directly, which is what anchors the formats FIDO MDS does not cover, Apple's authenticators and the Google hardware-attestation roots among them. `metadata` governs when both are given, and `anchoredTo` names every route that anchored the path, joined with `+` when more than one did: `"metadata"`, `"rootCertificates"`, and `"safetyNetRoots"` for the android-safetynet chain, which anchors through the roots that format requires whether or not either other route was asked for. It is `null` only when nothing anchored the path. `verifyMetadataBlob(blob, opts)` reads a FIDO Metadata Service (MDS v3) BLOB, the signed catalogue of registered authenticator models, verifying its JWS and chaining its signer to an operator-supplied FIDO root before the payload is parsed, with sequence-number rollback and `nextUpdate` freshness checks. Passing the result as `opts.metadata` to `verify` resolves the authenticator's registered attestation roots from its identifier and requires the trust path to fully validate to one of them, refusing an unlisted or revoked model. `metadataAnchors(entry, opts)` applies the same status gate for a caller anchoring the path themselves: a model the catalogue has disqualified registers no anchors. Both of the catalogue's key spaces are covered: an aaguid, and the attestation-certificate key identifiers a U2F authenticator is listed under instead. No FIDO root is bundled, there is no trust-on-first-use, and retrieving the BLOB is out of scope. Fail-closed with typed `webauthn/*` errors — `parseAttestationObject`, `verify`, `verifyMetadataBlob`, `metadataFor`, `metadataAnchors` |
|
|
293
|
-
| `pki.lint` | Certificate linting, the zlint or pkilint of JavaScript. `certificate(pem \| der \| parsed, opts)` walks a parsed certificate and emits graded advisory findings, each with a stable id, a severity (`fatal`, `error`, `warn`, `notice`), a source, a spec-clause citation, and a message, against the RFC 5280 profile, the post-quantum certificate profiles, and a representative CA/Browser Forum TLS BR subset: serial sign and size, validity ordering and the SC081v3 reducing validity schedule, keyCertSign coherence, extension criticality (basicConstraints, nameConstraints, policyConstraints and inhibitAnyPolicy must be critical, and keyUsage should be), nameConstraints CA-scope, unknown critical extensions, empty-subject SAN, SKI and AKI presence including the end-entity subjectKeyIdentifier, the six extensions a conforming CA must mark non-critical (reported by clause rather than as unrecognized) and the two it should (issuerAltName, cRLDistributionPoints), a policy mapping to or from anyPolicy, a unique identifier a conforming CA must not generate, a critical subjectAltName beside a non-empty subject, a critical extKeyUsage naming anyExtendedKeyUsage, a userNotice noticeRef, SAN required and CN-in-SAN, dNSName syntax, serverAuth EKU, weak keys, the CA/Browser Forum subscriber certificate profile of §7.1.2.7 (the extension presence and criticality table, the authorityInformationAccess accessMethod and accessLocation tables, basicConstraints cA and pathLenConstraint, certificatePolicies against anyPolicy and the exactly-one reserved identifier rule and the permitted-qualifier table, the forbidden extended key purposes, the two Key Usage tables that differ by key type, and the subjectAltName GeneralName table with its criticality rule in both directions), the CA/Browser Forum S/MIME BR subscriber profile of §7.1.2.3 (a) through (n) as `cabf-smime`, whose requirements follow the generation a certificate declares through its reserved policy identifier (the distribution-point and accessMethod URI scheme rules, the policy qualifier rules for an id-qt-cps URL and an id-qt-unotice explicitText and noticeRef, the key-usage table for the certificate's own key algorithm including a bit set past the nine named positions, the extended key purposes forbidden at every generation and the ones Strict alone forbids, the authorityKeyIdentifier field rules, a required mailbox in the subjectAltName as an rfc822Name or an id-on-SmtpUTF8Mailbox otherName, the criticality each extension must carry, the subjectDirectoryAttributes and Adobe extensions the Strict generation prohibits, the Legal Entity Identifier extensions keyed on the certificate type rather than the generation, and the Legacy generation retired on 15 July 2025), the CA/Browser Forum Code Signing BR subscriber profile of §7.1.2.3 as `cabf-cs`, whose rows follow the kind a certificate declares through one of the three reserved identifiers of §7.1.6.1 (the extension presence and criticality rules at the strengths this document states them, a HTTP URL required in the distribution points and in each accessMethod location, a critical keyUsage asserting digitalSignature with `keyCertSign` and `cRLSign` forbidden and every other bit discouraged, the extended key purpose each kind must carry with the critical extKeyUsage a timestamp certificate needs, the §7.1.6.4 exactly-one-identifier rule gated to 15 September 2026, the §6.3.2 ceilings of 39 months, 460 days and 135 months, and the §6.1.5.2 floors of a 3072-bit RSA modulus, NIST P-256, P-384 or P-521, or DSA parameters of 2048 bits with a 224- or 256-bit modulus length, and the §7.1.4.2 subject distinguished name rows: a required commonName, a prohibited domainComponent, a subject attribute made only of the metadata characters the clause names, a required organizationName and two-letter countryName, one of localityName and stateOrProvinceName, and on an EV certificate a businessCategory from the four strings the clause names, a jurisdictionCountryName and a serialNumber), the ETSI qualified-certificate profile of EN 319 412-5 as `etsi-qc` (a non-critical qcStatements extension, a QcType naming one and only one of the three purposes the clause defines, a QcPDS providing an https URL and, on an EU qualified certificate, a document in English and no more than one per language, plus the EN 319 412-1 semantics identifiers holding a subject serialNumber or organizationIdentifier to a three-character identity type reference, a two-character country code, a hyphen-minus and an identifier, with XG required of an LEI value and a name registration authority URI required of a locally defined reference), the Mozilla Root Store Policy as `mozilla-root`, which is named rather than detected because a root program's requirements apply by virtue of the store a certificate chains to (the §5.1 AlgorithmIdentifier encodings compared as DER, an RSA modulus of at least 2048 bits and a whole number of octets, a public exponent above 1, an EdDSA key only beside id-kp-emailProtection, the §5.1.3 SHA-1 prohibitions on their own dates, a serial greater than zero with a short magnitude reported as a warning, an extKeyUsage on every end entity without anyExtendedKeyUsage, and the §5.3 intermediate rules from 2019, which pass over a certificate issued in its own name), the post-quantum key-usage rules binding a certificate's keyUsage bits to its subject key type (ML-KEM under RFC 9935 §5, ML-DSA under RFC 9881 §5, and SLH-DSA under RFC 9909 §6 across both its pure and prehash identifiers) together with the ML-KEM encapsulation-key encoding and size, and the §4.2.1.4 certificate-policy user-notice rules (a VisibleString or BMPString explicitText, a notice past 200 characters, an empty notice, control characters, and a non-NFC UTF8String notice, each at the strength the clause states). Alone among these entries the data path never throws: hostile bytes return a `fatal` `lint/unparseable` finding carrying the strict parser's code, so a whole directory lints without a try/catch, and only config-time misuse throws a typed `LintError` — `certificate`, `crl` (the RFC 5280 §5 revocation-list profile: nextUpdate presence, update ordering, cRLNumber presence and its 20-octet ceiling, authorityKeyIdentifier presence, the criticality each profiled CRL and entry extension must carry, freshestCRL in a delta CRL, and unrecognized critical extensions), `ocsp` (the RFC 6960 response profile: an empty signature, each SingleResponse's update ordering and its relation to producedAt, an empty `certs` field, the extended revoked definition and the fixed shape of a revoked answer for a non-issued certificate, the list each extension belongs in, the criticality and value syntax of the CRL entry extensions a SingleResponse may carry, unrecognized critical extensions, the reasons unspecified and removeFromCRL, and two answers for one CertID; the RFC 5019 lightweight rows run on request), `csr` (the artifact a CA reads before it issues: an empty subject naming no identity, an empty subject whose subjectAltName is not critical, a request for an extension the issuer determines, a request asking for a CA certificate, a broken signing digest, and a challengePassword traveling with the request; `opts.profile: "cabf-tls"` adds the certificate profile's own commonName-in-SAN, dNSName syntax, SAN-required and key-strength rows, applied to what the request asks for, and they run only when named because a request carries no extKeyUsage. It does not check the request's signature, which is a cryptographic operation `pki.csr.verify` owns), `rules`, `profiles` |
|
|
293
|
+
| `pki.lint` | Certificate linting, the zlint or pkilint of JavaScript. `certificate(pem \| der \| parsed, opts)` walks a parsed certificate and emits graded advisory findings, each with a stable id, a severity (`fatal`, `error`, `warn`, `notice`), a source, a spec-clause citation, and a message, against the RFC 5280 profile, the post-quantum certificate profiles, and a representative CA/Browser Forum TLS BR subset: serial sign and size, validity ordering and the SC081v3 reducing validity schedule, keyCertSign coherence, extension criticality (basicConstraints, nameConstraints, policyConstraints and inhibitAnyPolicy must be critical, and keyUsage should be), nameConstraints CA-scope, unknown critical extensions, empty-subject SAN, SKI and AKI presence including the end-entity subjectKeyIdentifier, the six extensions a conforming CA must mark non-critical (reported by clause rather than as unrecognized) and the two it should (issuerAltName, cRLDistributionPoints), a policy mapping to or from anyPolicy, a unique identifier a conforming CA must not generate, a critical subjectAltName beside a non-empty subject, a critical extKeyUsage naming anyExtendedKeyUsage, a userNotice noticeRef, SAN required and CN-in-SAN, dNSName syntax, serverAuth EKU, weak keys, the CA/Browser Forum subscriber certificate profile of §7.1.2.7 (the extension presence and criticality table, the authorityInformationAccess accessMethod and accessLocation tables, basicConstraints cA and pathLenConstraint, certificatePolicies against anyPolicy and the exactly-one reserved identifier rule and the permitted-qualifier table, the forbidden extended key purposes, the two Key Usage tables that differ by key type, and the subjectAltName GeneralName table with its criticality rule in both directions), the CA/Browser Forum S/MIME BR subscriber profile of §7.1.2.3 (a) through (n) as `cabf-smime`, whose requirements follow the generation a certificate declares through its reserved policy identifier (the distribution-point and accessMethod URI scheme rules, the policy qualifier rules for an id-qt-cps URL and an id-qt-unotice explicitText and noticeRef, the key-usage table for the certificate's own key algorithm including a bit set past the nine named positions, the extended key purposes forbidden at every generation and the ones Strict alone forbids, the authorityKeyIdentifier field rules, a required mailbox in the subjectAltName as an rfc822Name or an id-on-SmtpUTF8Mailbox otherName, the criticality each extension must carry, the subjectDirectoryAttributes and Adobe extensions the Strict generation prohibits, the Legal Entity Identifier extensions keyed on the certificate type rather than the generation, and the Legacy generation retired on 15 July 2025), the CA/Browser Forum Code Signing BR subscriber profile of §7.1.2.3 as `cabf-cs`, whose rows follow the kind a certificate declares through one of the three reserved identifiers of §7.1.6.1 (the extension presence and criticality rules at the strengths this document states them, a HTTP URL required in the distribution points and in each accessMethod location, a critical keyUsage asserting digitalSignature with `keyCertSign` and `cRLSign` forbidden and every other bit discouraged, the extended key purpose each kind must carry with the critical extKeyUsage a timestamp certificate needs, the §7.1.6.4 exactly-one-identifier rule gated to 15 September 2026, the §6.3.2 ceilings of 39 months, 460 days and 135 months, and the §6.1.5.2 floors of a 3072-bit RSA modulus, NIST P-256, P-384 or P-521, or DSA parameters of 2048 bits with a 224- or 256-bit modulus length, and the §7.1.4.2 subject distinguished name rows: a required commonName, a prohibited domainComponent, a subject attribute made only of the metadata characters the clause names, a required organizationName and two-letter countryName, one of localityName and stateOrProvinceName, and on an EV certificate a businessCategory from the four strings the clause names, a jurisdictionCountryName and a serialNumber), the ETSI qualified-certificate profile of EN 319 412-5 as `etsi-qc` (a non-critical qcStatements extension, a QcType naming one and only one of the three purposes the clause defines, a QcPDS providing an https URL and, on an EU qualified certificate, a document in English and no more than one per language, plus the EN 319 412-1 semantics identifiers holding a subject serialNumber or organizationIdentifier to a three-character identity type reference, a two-character country code, a hyphen-minus and an identifier, with XG required of an LEI value and a name registration authority URI required of a locally defined reference), the Mozilla Root Store Policy as `mozilla-root`, which is named rather than detected because a root program's requirements apply by virtue of the store a certificate chains to (the §5.1 AlgorithmIdentifier encodings compared as DER, an RSA modulus of at least 2048 bits and a whole number of octets, a public exponent above 1, an EdDSA key only beside id-kp-emailProtection, the §5.1.3 SHA-1 prohibitions on their own dates, a serial greater than zero with a short magnitude reported as a warning, an extKeyUsage on every end entity without anyExtendedKeyUsage, and the §5.3 intermediate rules from 2019, which pass over a certificate issued in its own name), the Chrome Root Program Policy v1.8 as `chrome-root`, named on the same grounds (the §1.3.2 dedicated-hierarchy rules holding a subordinate CA certificate's extKeyUsage to id-kp-serverAuth with id-kp-clientAuth reported as a warning, scoped to the unexpired certificates the clause names by passing over a notAfter before 15 June 2026, the same rules over a subscriber certificate issued on or after 15 March 2027 where id-kp-serverAuth stands alone and an extKeyUsage naming id-kp-OCSPSigning and nothing else is a delegated OCSP responder rather than a subscriber, the §1.3.1.3 three-year subordinate CA validity ceiling, and the §1.3.1.2 root term limit, which reports a self-signed CA certificate whose validity runs past the removal date its own notBefore fixes and passes over a CA reissued in its own name whose authorityKeyIdentifier names a key other than its own subjectKeyIdentifier), the post-quantum key-usage rules binding a certificate's keyUsage bits to its subject key type (ML-KEM under RFC 9935 §5, ML-DSA under RFC 9881 §5, and SLH-DSA under RFC 9909 §6 across both its pure and prehash identifiers) together with the ML-KEM encapsulation-key encoding and size, and the §4.2.1.4 certificate-policy user-notice rules (a VisibleString or BMPString explicitText, a notice past 200 characters, an empty notice, control characters, and a non-NFC UTF8String notice, each at the strength the clause states). Alone among these entries the data path never throws: hostile bytes return a `fatal` `lint/unparseable` finding carrying the strict parser's code, so a whole directory lints without a try/catch, and only config-time misuse throws a typed `LintError` — `certificate`, `crl` (the RFC 5280 §5 revocation-list profile: nextUpdate presence, update ordering, cRLNumber presence and its 20-octet ceiling, authorityKeyIdentifier presence, the criticality each profiled CRL and entry extension must carry, freshestCRL in a delta CRL, and unrecognized critical extensions), `ocsp` (the RFC 6960 response profile: an empty signature, each SingleResponse's update ordering and its relation to producedAt, an empty `certs` field, the extended revoked definition and the fixed shape of a revoked answer for a non-issued certificate, the list each extension belongs in, the criticality and value syntax of the CRL entry extensions a SingleResponse may carry, unrecognized critical extensions, the reasons unspecified and removeFromCRL, and two answers for one CertID; the RFC 5019 lightweight rows run on request), `csr` (the artifact a CA reads before it issues: an empty subject naming no identity, an empty subject whose subjectAltName is not critical, a request for an extension the issuer determines, a request asking for a CA certificate, a broken signing digest, and a challengePassword traveling with the request; `opts.profile: "cabf-tls"` adds the certificate profile's own commonName-in-SAN, dNSName syntax, SAN-required and key-strength rows, applied to what the request asks for, and they run only when named because a request carries no extKeyUsage. It does not check the request's signature, which is a cryptographic operation `pki.csr.verify` owns), `rules`, `profiles` |
|
|
294
294
|
| `pki.C` / `pki.constants` | Version-stable constants: the functional scale helpers `C.TIME.*` and `C.BYTES.*`, the codec `LIMITS`, and `version` |
|
|
295
295
|
| `pki.errors` | The `PkiError` taxonomy — `defineClass` plus `ConstantsError`, `Asn1Error`, `OidError`, `PemError`, `CertificateError`, `CrlError`, `CsrError`, `Pkcs8Error`, `CmsError`, `OcspError`, `TspError`, `AttrCertError`, `CrmfError`, `Pkcs12Error`, `CmpError`, `PathError`, `CtError`, `JoseError`, `AcmeError`, `WebauthnError`, and `LintError`, each carrying a stable `code` in `domain/reason` form |
|
|
296
296
|
| `pki` CLI | `pki version`, `pki oid <dotted\|name>`, `pki parse <cert>`, `pki inspect <cert>`, `pki lint <cert\|crl\|ocsp-response>`, `pki convert <file> --to der\|pem`, `pki verify <cert>... --anchor <cert>`, `pki sign <file> --cert <c> --key <k>` |
|
package/lib/lint.js
CHANGED
|
@@ -1912,6 +1912,251 @@ var MOZILLA_RULES = [
|
|
|
1912
1912
|
},
|
|
1913
1913
|
];
|
|
1914
1914
|
|
|
1915
|
+
/** @internal The two purposes sec. 1.3.2 admits in a subordinate CA certificate, as (a)
|
|
1916
|
+
* id-kp-serverAuth alone or (b) id-kp-serverAuth with id-kp-clientAuth. */
|
|
1917
|
+
var CHROME_TLS_PURPOSES = ["serverAuth", "clientAuth"].map(function (n) { return oid.byName(n); });
|
|
1918
|
+
/** @internal "All corresponding subscriber certificates issued on or after March 15, 2027"
|
|
1919
|
+
* (sec. 1.3.2). Issuance is a date the certificate carries, unlike the CCADB disclosure date the
|
|
1920
|
+
* subordinate CA half of the same clause turns on. */
|
|
1921
|
+
var CHROME_SUBSCRIBER_EKU_FROM = new Date("2027-03-15T00:00:00Z");
|
|
1922
|
+
/** @internal "Beginning June 15, 2026, the Chrome Root Program will phase-out PKI hierarchies found
|
|
1923
|
+
* in violation of the below requirements" (sec. 1.3.2). */
|
|
1924
|
+
var CHROME_DEDICATED_FROM = new Date("2026-06-15T00:00:00Z");
|
|
1925
|
+
var CHROME_SUBORDINATE_MAX_MONTHS = 36;
|
|
1926
|
+
/** @internal The sec. 1.3.1.2 removal schedule, keyed on the date the key material was generated.
|
|
1927
|
+
* Each row holds the exclusive upper bound of its bracket; a date at or after the last bound takes
|
|
1928
|
+
* the clause's tail, fifteen years from generation. */
|
|
1929
|
+
var CHROME_TERM_SCHEDULE = [
|
|
1930
|
+
{ until: new Date("2008-01-01T00:00:00Z"), removal: new Date("2026-04-15T00:00:00Z") },
|
|
1931
|
+
{ until: new Date("2010-01-01T00:00:00Z"), removal: new Date("2027-04-15T00:00:00Z") },
|
|
1932
|
+
{ until: new Date("2012-01-01T00:00:00Z"), removal: new Date("2028-04-15T00:00:00Z") },
|
|
1933
|
+
{ until: new Date("2014-04-15T00:00:00Z"), removal: new Date("2029-04-15T00:00:00Z") },
|
|
1934
|
+
];
|
|
1935
|
+
var CHROME_TERM_MONTHS = 180;
|
|
1936
|
+
|
|
1937
|
+
function _chromeRemoval(notBefore) {
|
|
1938
|
+
var at = guard.time.instantOf(notBefore);
|
|
1939
|
+
for (var i = 0; i < CHROME_TERM_SCHEDULE.length; i++) {
|
|
1940
|
+
if (at < guard.time.instantOf(CHROME_TERM_SCHEDULE[i].until)) return CHROME_TERM_SCHEDULE[i].removal;
|
|
1941
|
+
}
|
|
1942
|
+
return guard.time.addMonths(notBefore, CHROME_TERM_MONTHS, _cfg, "lint/bad-input", "notBefore");
|
|
1943
|
+
}
|
|
1944
|
+
/** @internal The profile is NAMED, never detected, for the reason mozilla-root is. */
|
|
1945
|
+
function _isChrome(cert, ctx) { return ctx.profile === "chrome-root"; }
|
|
1946
|
+
/** @internal A SUBORDINATE CA, which is what sec. 1.3.2 and sec. 1.3.1.3 govern. Being one is a
|
|
1947
|
+
* role in a hierarchy that a certificate does not state, so this reads the one thing the bytes
|
|
1948
|
+
* give and passes over a certificate issued in its own name. That is every root, which those
|
|
1949
|
+
* clauses do not govern, and it is also a rollover CA reissued in its own name, which they do, so
|
|
1950
|
+
* the rows under-report rather than reporting a root for carrying no extended key usage. */
|
|
1951
|
+
function _isChromeSubordinate(cert, ctx) {
|
|
1952
|
+
return _isChrome(cert, ctx) && ctx.isCaCert && !_sameName(cert.issuer, cert.subject);
|
|
1953
|
+
}
|
|
1954
|
+
/** @internal Sec. 1.3.2 reaches "all corresponding UNEXPIRED and unrevoked subordinate CA
|
|
1955
|
+
* certificates", and its phase-out begins on 15 June 2026. A certificate whose notAfter precedes
|
|
1956
|
+
* that day was not among the unexpired ones the clause names when it began to bite, so the four
|
|
1957
|
+
* extended key usage rows pass over it. Whether a certificate is revoked is not in these bytes and
|
|
1958
|
+
* stays outside the reading. Sec. 2.3 states the same requirement for an Applicant hierarchy with
|
|
1959
|
+
* no date on it, and a certificate does not say whether its hierarchy is an Applicant's, so the
|
|
1960
|
+
* date sec. 1.3.2 carries is the one used.
|
|
1961
|
+
* Expiry is an EXEMPTION from a clause the names have already brought the certificate under, so a
|
|
1962
|
+
* notAfter that cannot be read leaves the rows applying: an exemption nothing establishes is not
|
|
1963
|
+
* granted. That is the opposite default from `_selfSignedIdentifiers`, where the unreadable field
|
|
1964
|
+
* is what the row's own premise rests on. */
|
|
1965
|
+
function _isChromeTlsSubordinate(cert, ctx) {
|
|
1966
|
+
if (!_isChromeSubordinate(cert, ctx)) return false;
|
|
1967
|
+
var notAfter = cert.validity && cert.validity.notAfter;
|
|
1968
|
+
if (!guard.time.isDate(notAfter)) return true;
|
|
1969
|
+
return guard.time.instantOf(notAfter) >= guard.time.instantOf(CHROME_DEDICATED_FROM);
|
|
1970
|
+
}
|
|
1971
|
+
/** @internal A SUBSCRIBER certificate, which is not every end entity beneath a root. A delegated
|
|
1972
|
+
* OCSP responder has its own profile in the Baseline Requirements that sec. 1.1.1 binds, and that
|
|
1973
|
+
* profile asks for id-kp-OCSPSigning and NO other purpose, so reading one as a subscriber would
|
|
1974
|
+
* report a conforming responder for the id-kp-serverAuth it must not carry. The exemption is the
|
|
1975
|
+
* purpose standing ALONE rather than merely present: a certificate naming it beside others is not
|
|
1976
|
+
* a responder under that profile, and exempting on presence would let any certificate leave every
|
|
1977
|
+
* row here by adding the purpose. Naming this profile does not run the cabf-tls set, so no other
|
|
1978
|
+
* row would catch it. */
|
|
1979
|
+
function _isChromeSubscriber(cert, ctx) {
|
|
1980
|
+
if (!_isChrome(cert, ctx) || ctx.isCaCert) return false;
|
|
1981
|
+
var purposes = _chromePurposes(ctx);
|
|
1982
|
+
if (!purposes || purposes.length !== 1) return true;
|
|
1983
|
+
return purposes[0] !== oid.byName("ocspSigning");
|
|
1984
|
+
}
|
|
1985
|
+
/** @internal Matching issuer and subject names make a certificate self-ISSUED, which is not the
|
|
1986
|
+
* same as self-signed: a CA reissued in its own name under a new key carries the same two names
|
|
1987
|
+
* and was signed by its predecessor. The key identifiers separate the two without a signature
|
|
1988
|
+
* check, which this synchronous surface cannot make. A certificate whose authorityKeyIdentifier
|
|
1989
|
+
* names a key other than its own subjectKeyIdentifier was signed by a different key and is not a
|
|
1990
|
+
* root; one that omits the extension, or names its own, is consistent with a self-signature. A
|
|
1991
|
+
* keyIdentifier present with no readable subjectKeyIdentifier beside it settles nothing, and the
|
|
1992
|
+
* row rests on settling it, so that reads as not a root too. The residual is a rollover CA that
|
|
1993
|
+
* carries no authorityKeyIdentifier keyIdentifier at all, which RFC 5280 sec. 4.2.1.1 permits
|
|
1994
|
+
* only in a self-signed certificate and which the rfc5280 profile reports on its own row. */
|
|
1995
|
+
function _selfSignedIdentifiers(cert, ctx) {
|
|
1996
|
+
var aki = ctx.decode("authorityKeyIdentifier");
|
|
1997
|
+
var kid = aki && aki.value ? aki.value.keyIdentifier : null;
|
|
1998
|
+
if (!Buffer.isBuffer(kid)) return true;
|
|
1999
|
+
var ski = ctx.decode("subjectKeyIdentifier");
|
|
2000
|
+
return Buffer.isBuffer(ski && ski.value) && kid.equals(ski.value);
|
|
2001
|
+
}
|
|
2002
|
+
function _isChromeRoot(cert, ctx) {
|
|
2003
|
+
return _isChrome(cert, ctx) && ctx.isCaCert && _sameName(cert.issuer, cert.subject) &&
|
|
2004
|
+
_selfSignedIdentifiers(cert, ctx);
|
|
2005
|
+
}
|
|
2006
|
+
function _chromePurposes(ctx) {
|
|
2007
|
+
var d = ctx.decode("extKeyUsage");
|
|
2008
|
+
return (d && Array.isArray(d.value)) ? d.value : null;
|
|
2009
|
+
}
|
|
2010
|
+
function _chromePurposeLabel(dotted) { return oid.name(dotted) || dotted; }
|
|
2011
|
+
|
|
2012
|
+
/** @internal The Chrome Root Program Policy v1.8 profile. */
|
|
2013
|
+
var CHROME_RULES = [
|
|
2014
|
+
{
|
|
2015
|
+
id: "lint/chrome-root/subordinate-ca-eku-missing", severity: "error", source: "chrome-root", citation: "Chrome Root Program Policy 1.3.2",
|
|
2016
|
+
/** @internal Both admitted forms include the extension, so its absence breaks the clause on
|
|
2017
|
+
* either side of the CCADB disclosure date that decides between them. */
|
|
2018
|
+
message: "a subordinate CA certificate must carry an extKeyUsage extension",
|
|
2019
|
+
appliesTo: _isChromeTlsSubordinate,
|
|
2020
|
+
check: function (cert, ctx) { return ctx.raw("extKeyUsage") ? null : true; },
|
|
2021
|
+
},
|
|
2022
|
+
{
|
|
2023
|
+
id: "lint/chrome-root/subordinate-ca-eku-server-auth-missing", severity: "error", source: "chrome-root", citation: "Chrome Root Program Policy 1.3.2",
|
|
2024
|
+
/** @internal Both forms require the purpose, so its absence needs no date either. A missing
|
|
2025
|
+
* extension is the row above, which is why this one reads a present extension only. */
|
|
2026
|
+
message: "a subordinate CA certificate's extKeyUsage must name id-kp-serverAuth",
|
|
2027
|
+
appliesTo: _isChromeTlsSubordinate,
|
|
2028
|
+
check: function (cert, ctx) {
|
|
2029
|
+
var purposes = _chromePurposes(ctx);
|
|
2030
|
+
if (purposes === null) return null;
|
|
2031
|
+
return purposes.indexOf(oid.byName("serverAuth")) === -1 ? true : null;
|
|
2032
|
+
},
|
|
2033
|
+
},
|
|
2034
|
+
{
|
|
2035
|
+
id: "lint/chrome-root/subordinate-ca-eku-not-tls", severity: "error", source: "chrome-root", citation: "Chrome Root Program Policy 1.3.2",
|
|
2036
|
+
/** @internal Neither form admits a purpose outside the two, and anyExtendedKeyUsage is such a
|
|
2037
|
+
* purpose. Each offending purpose draws its own finding, since a certificate naming two is
|
|
2038
|
+
* two breaches and an operator fixes them one at a time.
|
|
2039
|
+
* A non-TLS branch is inside this row rather than outside it. The clause governs "all
|
|
2040
|
+
* corresponding unexpired and unrevoked subordinate CA certificates operated beneath an
|
|
2041
|
+
* existing root included in the Chrome Root Store", and it exists "to align all PKI
|
|
2042
|
+
* hierarchies included in the Chrome Root Store on the principle of serving only TLS server
|
|
2043
|
+
* authentication use cases", so a code signing intermediate beneath such a root is the
|
|
2044
|
+
* multi-purpose hierarchy the section phases out. */
|
|
2045
|
+
message: "a subordinate CA certificate's extKeyUsage must name no purpose other than id-kp-serverAuth and id-kp-clientAuth",
|
|
2046
|
+
appliesTo: _isChromeTlsSubordinate,
|
|
2047
|
+
check: function (cert, ctx) {
|
|
2048
|
+
var purposes = _chromePurposes(ctx);
|
|
2049
|
+
if (purposes === null) return null;
|
|
2050
|
+
var out = [];
|
|
2051
|
+
purposes.forEach(function (dotted) {
|
|
2052
|
+
if (CHROME_TLS_PURPOSES.indexOf(dotted) === -1) {
|
|
2053
|
+
out.push({ context: { purpose: _chromePurposeLabel(dotted) } });
|
|
2054
|
+
}
|
|
2055
|
+
});
|
|
2056
|
+
return out.length ? out : null;
|
|
2057
|
+
},
|
|
2058
|
+
},
|
|
2059
|
+
{
|
|
2060
|
+
id: "lint/chrome-root/subordinate-ca-eku-client-auth", severity: "warn", source: "chrome-root", citation: "Chrome Root Program Policy 1.3.2",
|
|
2061
|
+
/** @internal Form (b) is open only to a subordinate CA disclosed to the CCADB before 15 June
|
|
2062
|
+
* 2026, and sec. 2.3 gives an Applicant hierarchy the same form with 15 June 2025. Neither
|
|
2063
|
+
* date is in these bytes, so the row reports below error and its message names what decides
|
|
2064
|
+
* it. */
|
|
2065
|
+
message: "a subordinate CA certificate naming id-kp-clientAuth is admitted only where it was disclosed to the CCADB before 15 June 2026, which a certificate does not carry",
|
|
2066
|
+
appliesTo: _isChromeTlsSubordinate,
|
|
2067
|
+
check: function (cert, ctx) {
|
|
2068
|
+
var purposes = _chromePurposes(ctx);
|
|
2069
|
+
if (purposes === null) return null;
|
|
2070
|
+
return purposes.indexOf(oid.byName("clientAuth")) !== -1 ? true : null;
|
|
2071
|
+
},
|
|
2072
|
+
},
|
|
2073
|
+
{
|
|
2074
|
+
id: "lint/chrome-root/subscriber-eku-missing", severity: "error", source: "chrome-root", citation: "Chrome Root Program Policy 1.3.2",
|
|
2075
|
+
message: "a subscriber certificate must carry an extKeyUsage extension",
|
|
2076
|
+
appliesTo: _isChromeSubscriber,
|
|
2077
|
+
effectiveDate: CHROME_SUBSCRIBER_EKU_FROM,
|
|
2078
|
+
check: function (cert, ctx) { return ctx.raw("extKeyUsage") ? null : true; },
|
|
2079
|
+
},
|
|
2080
|
+
{
|
|
2081
|
+
id: "lint/chrome-root/subscriber-eku-server-auth-missing", severity: "error", source: "chrome-root", citation: "Chrome Root Program Policy 1.3.2",
|
|
2082
|
+
message: "a subscriber certificate's extKeyUsage must name id-kp-serverAuth",
|
|
2083
|
+
appliesTo: _isChromeSubscriber,
|
|
2084
|
+
effectiveDate: CHROME_SUBSCRIBER_EKU_FROM,
|
|
2085
|
+
check: function (cert, ctx) {
|
|
2086
|
+
var purposes = _chromePurposes(ctx);
|
|
2087
|
+
if (purposes === null) return null;
|
|
2088
|
+
return purposes.indexOf(oid.byName("serverAuth")) === -1 ? true : null;
|
|
2089
|
+
},
|
|
2090
|
+
},
|
|
2091
|
+
{
|
|
2092
|
+
id: "lint/chrome-root/subscriber-eku-not-server-auth", severity: "error", source: "chrome-root", citation: "Chrome Root Program Policy 1.3.2",
|
|
2093
|
+
/** @internal The subscriber half admits id-kp-serverAuth alone, so id-kp-clientAuth is
|
|
2094
|
+
* reported here rather than warned as it is on a subordinate CA: the same purpose, graded by
|
|
2095
|
+
* which sentence of the clause reads it. */
|
|
2096
|
+
message: "a subscriber certificate's extKeyUsage must name no purpose other than id-kp-serverAuth",
|
|
2097
|
+
appliesTo: _isChromeSubscriber,
|
|
2098
|
+
effectiveDate: CHROME_SUBSCRIBER_EKU_FROM,
|
|
2099
|
+
check: function (cert, ctx) {
|
|
2100
|
+
var purposes = _chromePurposes(ctx);
|
|
2101
|
+
if (purposes === null) return null;
|
|
2102
|
+
var out = [];
|
|
2103
|
+
purposes.forEach(function (dotted) {
|
|
2104
|
+
if (dotted !== oid.byName("serverAuth")) {
|
|
2105
|
+
out.push({ context: { purpose: _chromePurposeLabel(dotted) } });
|
|
2106
|
+
}
|
|
2107
|
+
});
|
|
2108
|
+
return out.length ? out : null;
|
|
2109
|
+
},
|
|
2110
|
+
},
|
|
2111
|
+
{
|
|
2112
|
+
id: "lint/chrome-root/subordinate-ca-validity-over-three-years", severity: "warn", source: "chrome-root", citation: "Chrome Root Program Policy 1.3.1.3",
|
|
2113
|
+
/** @internal "Subordinate CA certificates SHOULD have a maximum validity period of three (3)
|
|
2114
|
+
* years." A ceiling stated in years is measured in months, through guard.time.addMonths, since
|
|
2115
|
+
* how many days three years hold depends on which three. The clause's other sentence asks that
|
|
2116
|
+
* CA owners deploy a new subordinate CA each year, which is a schedule rather than anything
|
|
2117
|
+
* one certificate answers for. */
|
|
2118
|
+
message: "a subordinate CA certificate's validity period should not exceed three years",
|
|
2119
|
+
appliesTo: _isChromeSubordinate,
|
|
2120
|
+
check: function (cert) {
|
|
2121
|
+
var v = cert.validity;
|
|
2122
|
+
if (!guard.time.isDate(v.notBefore) || !guard.time.isDate(v.notAfter)) return null;
|
|
2123
|
+
var ceiling = guard.time.addMonths(v.notBefore, CHROME_SUBORDINATE_MAX_MONTHS, _cfg,
|
|
2124
|
+
"lint/bad-input", "notBefore");
|
|
2125
|
+
return guard.time.instantOf(v.notAfter) > guard.time.instantOf(ceiling)
|
|
2126
|
+
? { context: { maxMonths: CHROME_SUBORDINATE_MAX_MONTHS } } : null;
|
|
2127
|
+
},
|
|
2128
|
+
},
|
|
2129
|
+
{
|
|
2130
|
+
id: "lint/chrome-root/self-signed-ca-term-limit-outlived", severity: "notice", source: "chrome-root", citation: "Chrome Root Program Policy 1.3.1.2",
|
|
2131
|
+
/** @internal The clause determines the age of key material by the earlier of a key generation
|
|
2132
|
+
* report, which is not in these bytes, or "the validity date of the earliest appearing
|
|
2133
|
+
* certificate that contains the corresponding public key". A self-signed root's own notBefore
|
|
2134
|
+
* is the validity date of one such certificate, so the determined date is at or before it. The
|
|
2135
|
+
* schedule is monotone, so the removal date computed from notBefore is the LATEST the removal
|
|
2136
|
+
* can fall, and reporting only a notAfter past that date holds whatever earlier certificate
|
|
2137
|
+
* carries the key.
|
|
2138
|
+
* The table opens on 1 January 2006 and gives no row for an older key; an older key is removed
|
|
2139
|
+
* no later than a 2006 one, so the first bracket takes it. Its fourth bracket ends on 14 April
|
|
2140
|
+
* 2014 and its tail begins after 15 April 2014, and the two agree on the day between them,
|
|
2141
|
+
* since 15 April 2014 plus fifteen years is 15 April 2029.
|
|
2142
|
+
* The row names a SELF-SIGNED CA certificate rather than a root, which is as far as the bytes
|
|
2143
|
+
* reach. A CA outside the store issues itself one of these during its own key rollover, and it
|
|
2144
|
+
* is identical in every field to a root's. What the finding says is that IF this certificate
|
|
2145
|
+
* is a root in the store the caller named, its term ends before its validity does, and the
|
|
2146
|
+
* notice grade is that conditional. The CA's other certificate, the one its parent issued,
|
|
2147
|
+
* carries different names and is read by the sec. 1.3.2 rows. */
|
|
2148
|
+
message: "a self-signed CA certificate whose validity runs past the removal date this clause fixes for key material of its age",
|
|
2149
|
+
appliesTo: _isChromeRoot,
|
|
2150
|
+
check: function (cert) {
|
|
2151
|
+
var v = cert.validity;
|
|
2152
|
+
if (!guard.time.isDate(v.notBefore) || !guard.time.isDate(v.notAfter)) return null;
|
|
2153
|
+
var removal = _chromeRemoval(v.notBefore);
|
|
2154
|
+
return guard.time.instantOf(v.notAfter) > guard.time.instantOf(removal)
|
|
2155
|
+
? { context: { removal: removal.toISOString().slice(0, 10) } } : null;
|
|
2156
|
+
},
|
|
2157
|
+
},
|
|
2158
|
+
];
|
|
2159
|
+
|
|
1915
2160
|
/** @internal The three purposes ETSI EN 319 412-5 clause 4.2.3 names, of which a QcType declares
|
|
1916
2161
|
* "one and only one". The ASN.1 carries an extension marker, so an identifier outside the three
|
|
1917
2162
|
* is admitted and is not one of the purposes being counted. */
|
|
@@ -4353,12 +4598,13 @@ var PROFILES = Object.assign(Object.create(null), {
|
|
|
4353
4598
|
"cabf-cs": CABF_CS_RULES.concat(RFC5280_EXTENSION_SYNTAX_RULES),
|
|
4354
4599
|
"etsi-qc": ETSI_QC_RULES.concat(RFC5280_EXTENSION_SYNTAX_RULES),
|
|
4355
4600
|
"mozilla-root": MOZILLA_RULES.concat(RFC5280_EXTENSION_SYNTAX_RULES),
|
|
4601
|
+
"chrome-root": CHROME_RULES.concat(RFC5280_EXTENSION_SYNTAX_RULES),
|
|
4356
4602
|
});
|
|
4357
4603
|
var ALL_RULES = RFC5280_RULES.concat(RFC9881_RULES).concat(RFC9909_RULES).concat(RFC9935_RULES)
|
|
4358
4604
|
.concat(CABF_TLS_RULES).concat(CABF_SMIME_RULES).concat(CABF_CS_RULES).concat(ETSI_QC_RULES)
|
|
4359
|
-
/** @internal The
|
|
4360
|
-
* under the default set because each one asks whether the caller named the profile. */
|
|
4361
|
-
.concat(MOZILLA_RULES);
|
|
4605
|
+
/** @internal The root-program rows are in the registry so `rules()` enumerates them, and stay
|
|
4606
|
+
* inert under the default set because each one asks whether the caller named the profile. */
|
|
4607
|
+
.concat(MOZILLA_RULES).concat(CHROME_RULES);
|
|
4362
4608
|
|
|
4363
4609
|
var CABF_TLS_CSR_RULES = CABF_TLS_RULES.filter(function (r) {
|
|
4364
4610
|
return CABF_TLS_CSR_RULE_IDS.indexOf(r.id) !== -1;
|
package/package.json
CHANGED
package/sbom.cdx.json
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
|
|
3
3
|
"bomFormat": "CycloneDX",
|
|
4
4
|
"specVersion": "1.5",
|
|
5
|
-
"serialNumber": "urn:uuid:
|
|
5
|
+
"serialNumber": "urn:uuid:3b5edc83-2243-4232-883c-f9c904da9bbb",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-09-
|
|
8
|
+
"timestamp": "2026-09-24T15:20:10.358Z",
|
|
9
9
|
"lifecycles": [
|
|
10
10
|
{
|
|
11
11
|
"phase": "build"
|
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
}
|
|
20
20
|
],
|
|
21
21
|
"component": {
|
|
22
|
-
"bom-ref": "@blamejs/pki@0.8.
|
|
22
|
+
"bom-ref": "@blamejs/pki@0.8.23",
|
|
23
23
|
"type": "application",
|
|
24
24
|
"name": "pki",
|
|
25
|
-
"version": "0.8.
|
|
25
|
+
"version": "0.8.23",
|
|
26
26
|
"scope": "required",
|
|
27
27
|
"author": "blamejs contributors",
|
|
28
28
|
"description": "Pure-JavaScript PKI toolkit that owns its stack — X.509, ASN.1/DER, CMS, PQC-first.",
|
|
29
|
-
"purl": "pkg:npm/%40blamejs/pki@0.8.
|
|
29
|
+
"purl": "pkg:npm/%40blamejs/pki@0.8.23",
|
|
30
30
|
"properties": [],
|
|
31
31
|
"externalReferences": [
|
|
32
32
|
{
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"components": [],
|
|
55
55
|
"dependencies": [
|
|
56
56
|
{
|
|
57
|
-
"ref": "@blamejs/pki@0.8.
|
|
57
|
+
"ref": "@blamejs/pki@0.8.23",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|