@blamejs/pki 0.6.13 → 0.6.15

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.
Files changed (114) hide show
  1. package/CHANGELOG.md +22 -1
  2. package/README.md +1 -1
  3. package/index.js +1 -1
  4. package/lib/acme.js +59 -815
  5. package/lib/asn1-der.js +7 -245
  6. package/lib/attrcert-sign.js +12 -260
  7. package/lib/attrcert-verify.js +1 -7
  8. package/lib/byte-reader.js +0 -8
  9. package/lib/byte-writer.js +0 -11
  10. package/lib/cbor-det.js +0 -54
  11. package/lib/cmc-build.js +15 -357
  12. package/lib/cmc-verify.js +6 -239
  13. package/lib/cmp-build.js +41 -317
  14. package/lib/cmp-session.js +73 -760
  15. package/lib/cmp-verify.js +32 -488
  16. package/lib/cms-compress.js +3 -32
  17. package/lib/cms-decrypt.js +29 -294
  18. package/lib/cms-encrypt.js +14 -234
  19. package/lib/cms-sign.js +29 -324
  20. package/lib/cms-verify.js +9 -500
  21. package/lib/composite-kem.js +2 -102
  22. package/lib/composite-sig.js +1 -77
  23. package/lib/constants.js +0 -249
  24. package/lib/crl-sign.js +9 -237
  25. package/lib/crl-verify.js +1 -10
  26. package/lib/crmf-sign.js +19 -198
  27. package/lib/crmf-verify.js +1 -12
  28. package/lib/csr-sign.js +3 -34
  29. package/lib/csr-verify.js +1 -12
  30. package/lib/ct.js +28 -253
  31. package/lib/edwards-point.js +10 -44
  32. package/lib/est.js +28 -764
  33. package/lib/framework-error.js +4 -182
  34. package/lib/guard-all.js +1 -58
  35. package/lib/guard-async.js +1 -36
  36. package/lib/guard-bytes.js +6 -650
  37. package/lib/guard-compress.js +5 -102
  38. package/lib/guard-crypto.js +1 -40
  39. package/lib/guard-der.js +2 -93
  40. package/lib/guard-encoding.js +2 -60
  41. package/lib/guard-header.js +1 -36
  42. package/lib/guard-identifier.js +7 -531
  43. package/lib/guard-intrinsic.js +1 -150
  44. package/lib/guard-json.js +10 -73
  45. package/lib/guard-limits.js +1 -77
  46. package/lib/guard-list.js +1 -49
  47. package/lib/guard-name.js +3 -209
  48. package/lib/guard-parsed.js +7 -335
  49. package/lib/guard-range.js +1 -71
  50. package/lib/guard-secret.js +1 -102
  51. package/lib/guard-text.js +1 -64
  52. package/lib/guard-time.js +1 -97
  53. package/lib/hpke.js +3 -105
  54. package/lib/http-digest.js +14 -142
  55. package/lib/http-retry-after.js +1 -47
  56. package/lib/http-transport.js +21 -131
  57. package/lib/inspect.js +30 -284
  58. package/lib/ip-utils.js +6 -47
  59. package/lib/jose.js +7 -142
  60. package/lib/key.js +2 -67
  61. package/lib/lint.js +9 -194
  62. package/lib/merkle.js +1 -27
  63. package/lib/mime.js +9 -82
  64. package/lib/ocsp-request-verify.js +1 -14
  65. package/lib/ocsp-verify.js +1 -82
  66. package/lib/ocsp.js +11 -169
  67. package/lib/oid.js +5 -337
  68. package/lib/path-validate.js +103 -1492
  69. package/lib/pbes2.js +2 -89
  70. package/lib/pkcs12-build.js +31 -379
  71. package/lib/pki-build.js +64 -290
  72. package/lib/rc2.js +2 -18
  73. package/lib/rfc3339.js +4 -27
  74. package/lib/scep.js +59 -233
  75. package/lib/schema-all.js +0 -122
  76. package/lib/schema-attrcert.js +4 -146
  77. package/lib/schema-c509.js +153 -802
  78. package/lib/schema-cmc.js +5 -220
  79. package/lib/schema-cmp.js +2 -223
  80. package/lib/schema-cms.js +13 -427
  81. package/lib/schema-crl.js +5 -68
  82. package/lib/schema-crmf.js +6 -167
  83. package/lib/schema-csr.js +1 -54
  84. package/lib/schema-csrattrs.js +2 -87
  85. package/lib/schema-engine.js +0 -152
  86. package/lib/schema-ocsp.js +0 -117
  87. package/lib/schema-pkcs12.js +2 -93
  88. package/lib/schema-pkcs8.js +3 -35
  89. package/lib/schema-pkix.js +24 -655
  90. package/lib/schema-smime.js +0 -54
  91. package/lib/schema-tsp.js +3 -99
  92. package/lib/schema-x509.js +4 -63
  93. package/lib/shbs.js +11 -95
  94. package/lib/sign-scheme.js +2 -97
  95. package/lib/sigstore.js +9 -169
  96. package/lib/sleep.js +2 -8
  97. package/lib/smime.js +15 -408
  98. package/lib/tls-cert-compress.js +0 -62
  99. package/lib/trust.js +19 -185
  100. package/lib/tsp-sign.js +16 -270
  101. package/lib/validator-all.js +1 -30
  102. package/lib/validator-attcert.js +2 -46
  103. package/lib/validator-cose.js +2 -153
  104. package/lib/validator-keydesc.js +1 -37
  105. package/lib/validator-sig.js +1 -46
  106. package/lib/validator-tls.js +1 -88
  107. package/lib/validator-tpm.js +19 -122
  108. package/lib/verify-seam.js +1 -24
  109. package/lib/webauthn-mds.js +3 -400
  110. package/lib/webauthn.js +12 -833
  111. package/lib/webcrypto.js +5 -485
  112. package/lib/x509-sign.js +4 -108
  113. package/package.json +1 -1
  114. package/sbom.cdx.json +6 -6
package/lib/crl-sign.js CHANGED
@@ -16,12 +16,6 @@
16
16
  * @spec RFC 5280
17
17
  * @card Build, sign, and verify an X.509 CRL (RFC 5280 sec. 5) over any registry algorithm.
18
18
  */
19
- //
20
- // The signature matrix comes from the shared sign-scheme resolver (the same registry pki.x509.sign /
21
- // pki.cms.sign drive), so a new algorithm is a registry row, never a branch here. The TBSCertList DER is
22
- // assembled through the canonical asn1.build.* layer + the shared pki-build producing primitives; the
23
- // strict schema-crl decoder round-trips it and OpenSSL cross-checks it. Verify composes the single
24
- // path-validate.verifyCrlSignature engine pki.path.crlChecker uses; there is no second, weaker verifier.
25
19
 
26
20
  var asn1 = require("./asn1-der");
27
21
  var oid = require("./oid");
@@ -30,16 +24,13 @@ var x509Schema = require("./schema-x509");
30
24
  var signScheme = require("./sign-scheme");
31
25
  var guard = require("./guard-all");
32
26
  var intrinsic = require("./guard-intrinsic");
33
- // Membership is asked through an operation taken at load. Written out, the question reads two
34
- // replaceable properties to answer one thing, and either of them answering true admits a name the
35
- // table never carried -- here, a reason code the CRL profile does not define.
36
27
  var _hasOwn = intrinsic.hasOwn;
37
28
  var frameworkError = require("./framework-error");
38
29
  var pkix = require("./schema-pkix");
39
30
  var schema = require("./schema-engine");
40
31
  var pkiBuild = require("./pki-build");
41
32
  var constants = require("./constants");
42
- require("./path-validate"); // side-effect: path-validate injects its signature engine into crl-verify at load
33
+ require("./path-validate");
43
34
  var crlVerify = require("./crl-verify");
44
35
 
45
36
  var CrlError = frameworkError.CrlError;
@@ -49,92 +40,52 @@ var SPKI_SCHEMA = pkix.spki(NS);
49
40
  var b = asn1.build;
50
41
  var TAGS = asn1.TAGS;
51
42
 
52
- // Two error factories (x509-sign pattern): `_err` takes a full crl/* code; `_signE` prepends the domain so
53
- // the shared sign-scheme resolver/signer faults keep crl/* codes. Both are FACTORIES (guard.* and
54
- // resolveSignScheme invoke them as E(code, msg) with no `new`).
55
43
  function _err(code, message, cause) { return new CrlError(code, message, cause); }
56
44
  function _signE(kind, message, cause) { return new CrlError("crl/" + kind, message, cause); }
57
45
  function O(n) { return oid.byName(n); }
58
46
 
59
- // The shared PKIX producing primitives (lib/pki-build.js), bound to the crl namespace so they keep the
60
- // frozen crl/* codes (Hard rule #5: one encode definition, no per-format hand-roll).
61
47
  var _b = pkiBuild.makeBuilder({ ErrorClass: CrlError, prefix: "crl", O: O, NS: NS, NAME_SCHEMA: NAME_SCHEMA, SPKI_SCHEMA: SPKI_SCHEMA, EXT_DECODERS: {} });
62
48
  var _encodeName = _b.encodeName, _isEmptyName = _b.isEmptyName, _reqDer = _b.reqDer,
63
49
  _assertValidSpki = _b.assertValidSpki, _assertValidExtension = _b.assertValidExtension,
64
50
  _timeDer = _b.timeDer, _ext = _b.ext, _extAki = _b.extAki, _spkiKeyId = _b.spkiKeyId,
65
51
  _serialInteger = _b.serialInteger, _encodeGeneralName = _b.encodeGeneralName,
66
52
  _certLikeFromSpki = _b.certLikeFromSpki, _assertSignatureVerifies = _b.assertSignatureVerifies;
67
- // The RFC 5280 sec. 4.2.1 extension value decoders (shared with pki.schema.x509.parse), used to confirm an
68
- // issuer certificate's keyUsage asserts cRLSign before a CRL is signed under it.
69
53
  var EXT_DECODERS = pkix.certExtensionDecoders(NS).byOid;
70
54
 
71
55
  var OID_SKI = O("subjectKeyIdentifier");
72
56
 
73
- // CRLReason value<->name (RFC 5280 sec. 5.3.1). The value->name table is the frozen constants source;
74
- // build the name->value reverse once at load. Value 7 is unused/reserved and absent from the table.
75
57
  var CRL_REASON = constants.NAMES.CRL_REASON;
76
58
  var REASON_BY_NAME = {};
77
59
  Object.keys(CRL_REASON).forEach(function (v) { REASON_BY_NAME[CRL_REASON[v]] = Number(v); });
78
60
 
79
61
  var KNOWN_CRL_EXT_KEYS = { authorityKeyIdentifier: 1, issuingDistributionPoint: 1, deltaCRLIndicator: 1, freshestCRL: 1, authorityInfoAccess: 1 };
80
- // The three caller-owned objects pki.crl.sign reads. `crlNumber` and `extensions` are read by
81
- // _buildCrlExtensions, which _sign hands the whole spec to, so they belong here even though _sign
82
- // never names them -- the table is derived from what the call ACTUALLY reads, including through
83
- // its delegations, not from the doc block.
84
62
  var KNOWN_SPEC_KEYS = {
85
63
  issuer: 1, thisUpdate: 1, nextUpdate: 1, revoked: 1, crlNumber: 1, extensions: 1,
86
64
  };
87
- // spec.issuer is the third place an issuer distinguished name can come from, and it is read only
88
- // when neither issuer.cert nor issuer.name supplies one: both of those win outright. A spec naming
89
- // one issuer beside a certificate for another was signed under the certificate's.
90
65
  var KNOWN_SPEC_KEYS_ISSUER_NAMED = {
91
66
  thisUpdate: 1, nextUpdate: 1, revoked: 1, crlNumber: 1, extensions: 1,
92
67
  };
93
- // Two FORMS, exactly as x509-sign has: when `issuer.cert` is present the builder takes the DN and
94
- // the public key from it and never reads `name` or `publicKey`, so a union signed under the
95
- // certificate's identity while discarding the one the caller wrote out.
96
68
  var KNOWN_ISSUER_CERT_KEYS = { key: 1, cert: 1 };
97
69
  var KNOWN_ISSUER_EXPLICIT_KEYS = { key: 1, name: 1, publicKey: 1 };
98
70
  var KNOWN_SIGN_OPTS = { digestAlgorithm: 1, pem: 1, pss: 1 };
99
71
 
100
72
  var KNOWN_IDP_KEYS = { fullName: 1, onlyContainsUserCerts: 1, onlyContainsCACerts: 1, onlyContainsAttributeCerts: 1, indirectCRL: 1 };
101
73
 
102
- // The fields ONE revoked-certificate entry carries. Every optional one is encoded only when
103
- // present, so a misspelling is missed by nothing and the entry is signed without it: `reson` for
104
- // `reason` lists the certificate as revoked with no reasonCode at all, and a relying party reading
105
- // the CRL cannot tell a caller who meant keyCompromise from one who gave no reason. The entry is
106
- // where this bites hardest, because the CRL as a whole still verifies. `certificateIssuer` is
107
- // listed because the builder DOES read it -- to refuse it with its own message.
108
74
  var KNOWN_REVOKED_KEYS = {
109
75
  serialNumber: 1, revocationDate: 1, reason: 1, invalidityDate: 1, certificateIssuer: 1, extensions: 1,
110
76
  };
111
77
 
112
- // RFC 5280 sec. 5.2 -- the profile-fixed criticality of each recognized CRL extension. cRLNumber (5.2.3),
113
- // authorityKeyIdentifier (5.2.1), freshestCRL (5.2.6), and authorityInfoAccess (5.2.7) MUST be non-critical;
114
- // issuingDistributionPoint (5.2.5) and deltaCRLIndicator (5.2.4) MUST be critical. Enforced on the object
115
- // form by construction AND on the pre-encoded escape hatch, so the hatch cannot bypass the profile.
116
78
  var REQUIRED_CRITICALITY = {};
117
79
  [["cRLNumber", false], ["authorityKeyIdentifier", false], ["freshestCRL", false], ["authorityInfoAccess", false],
118
80
  ["issuingDistributionPoint", true], ["deltaCRLIndicator", true]].forEach(function (r) { REQUIRED_CRITICALITY[O(r[0])] = r[1]; });
119
81
 
120
- // RFC 5280 sec. 5.3 -- the profile-fixed criticality of each recognized crlEntryExtension: reasonCode (5.3.1)
121
- // and invalidityDate (5.3.2) MUST be non-critical; certificateIssuer (5.3.3) MUST be critical.
122
82
  var REQUIRED_ENTRY_CRITICALITY = {};
123
83
  [["reasonCode", false], ["invalidityDate", false]].forEach(function (r) { REQUIRED_ENTRY_CRITICALITY[O(r[0])] = r[1]; });
124
84
 
125
- // certificateIssuer (indirect CRLs) is deferred: pki.path.crlChecker skips a CRL carrying any critical entry
126
- // extension other than reasonCode (it does not track the per-entry issuer of an indirect CRL), so a CRL
127
- // signed with certificateIssuer would verify but never be authoritative for revocation. Reject it, on both
128
- // the object form and the pre-encoded hatch, until the checker processes indirect CRLs.
129
85
  var _CERT_ISSUER_DEFERRED = "certificateIssuer (indirect CRLs) is not yet supported -- pki.path.crlChecker skips a CRL carrying any critical entry extension other than reasonCode, so an indirect CRL would never be authoritative for revocation. Re-enabled once the checker processes certificateIssuer / indirect CRLs.";
130
- // The IDP indirectCRL flag marks the whole CRL as indirect, which crlChecker skips outright. Deferred for
131
- // the same reason as certificateIssuer, on both the object form and the pre-encoded hatch.
132
86
  var _INDIRECT_IDP_DEFERRED = "issuingDistributionPoint indirectCRL (indirect CRLs) is not yet supported -- pki.path.crlChecker skips a CRL whose IDP marks it indirect, so it would never be authoritative for revocation. Re-enabled once the checker processes indirect CRLs.";
133
87
 
134
- // ---- shared numeric / reason / key-id helpers ------------------------------
135
88
 
136
- // A non-negative INTEGER (0..MAX) of at most 20 content octets: cRLNumber (sec. 5.2.3) and
137
- // deltaCRLIndicator baseCRLNumber (sec. 5.2.4). b.integer has no size cap of its own (unlike serialInteger).
138
89
  function _boundedInteger(v, label) {
139
90
  var n;
140
91
  if (typeof v === "bigint") n = v;
@@ -148,13 +99,10 @@ function _boundedInteger(v, label) {
148
99
  return { tlv: tlv, value: n };
149
100
  }
150
101
 
151
- // Decode a pre-encoded Extension's extnValue (the trailing OCTET STRING's content) to one well-formed DER
152
- // node, or reject, so a recognized extension's value is validated on the escape hatch, not emitted opaque.
153
102
  function _extInner(node, label) {
154
103
  try { return asn1.decode(asn1.read.octetString(node.children[node.children.length - 1])); }
155
104
  catch (e) { throw _err("crl/bad-input", "pre-encoded " + label + " extension value is not valid DER", e); }
156
105
  }
157
- // A pre-encoded cRLNumber / baseCRLNumber value: a non-negative INTEGER of at most 20 octets, or reject.
158
106
  function _requireBoundedInt(inner, label) {
159
107
  if (inner.tagClass !== "universal" || inner.tagNumber !== TAGS.INTEGER) throw _err("crl/bad-crl-number", "pre-encoded " + label + " value must be an INTEGER (RFC 5280 sec. 5.2.3 / 5.2.4)");
160
108
  var v = asn1.read.integer(inner);
@@ -163,8 +111,6 @@ function _requireBoundedInt(inner, label) {
163
111
  return v;
164
112
  }
165
113
 
166
- // Resolve a CRLReason to its numeric code: validate membership, reject 7/undefined, gate removeFromCRL(8)
167
- // to a delta CRL. unspecified(0) is returned so the caller OMITS it (SHOULD be absent, sec. 5.3.1).
168
114
  function _resolveReason(reason, isDelta) {
169
115
  var codeNum;
170
116
  if (typeof reason === "number") codeNum = reason;
@@ -177,24 +123,19 @@ function _resolveReason(reason, isDelta) {
177
123
  return codeNum;
178
124
  }
179
125
 
180
- // The AKI keyIdentifier from the issuer: an explicit Buffer, or true -> the issuer cert's subjectKeyIdentifier,
181
- // else the SHA-1 of the issuer SPKI (RFC 5280 sec. 5.2.1 key-identifier method).
182
126
  function _akiKeyId(val, ctx) {
183
127
  if (guard.bytes.isByteSource(val)) return guard.bytes.snapshotSource(val, CrlError, "crl/bad-input", "the authorityKeyIdentifier keyIdentifier");
184
128
  if (val === true) {
185
129
  if (ctx.issuerCert) {
186
130
  var ski = (ctx.issuerCert.extensions || []).filter(function (e) { return e.oid === OID_SKI; })[0];
187
- if (ski) { try { return asn1.read.octetString(asn1.decode(ski.value)); } catch (_e) { /* fall through to re-derive from the issuer SPKI */ } }
131
+ if (ski) { try { return asn1.read.octetString(asn1.decode(ski.value)); } catch (_e) { } } // allow:swallow-unverified -- a malformed issuer SKI is read as absent; the authorityKeyIdentifier keyIdentifier is then derived from the issuer public key
188
132
  }
189
133
  return _spkiKeyId(ctx.issuerSpki);
190
134
  }
191
135
  throw _err("crl/bad-input", "authorityKeyIdentifier must be true (auto-derive from the issuer) or a Buffer key id");
192
136
  }
193
137
 
194
- // ---- CRL extension value encoders (sec. 5.2) -------------------------------
195
138
 
196
- // IssuingDistributionPoint (sec. 5.2.5): critical; MUST NOT DER-encode to an empty SEQUENCE; at most one
197
- // scope boolean TRUE; onlyContainsAttributeCerts MUST be FALSE; DEFAULT-FALSE booleans omitted.
198
139
  function _idpValue(idp) {
199
140
  if (!idp || typeof idp !== "object" || Array.isArray(idp) || Buffer.isBuffer(idp)) throw _err("crl/bad-idp", "issuingDistributionPoint must be an object");
200
141
  guard.identifier.assertKnownKeys(idp, KNOWN_IDP_KEYS, _err, "crl/bad-idp", function (k) {
@@ -205,7 +146,6 @@ function _idpValue(idp) {
205
146
  if (idp.fullName != null) {
206
147
  var entries = Array.isArray(idp.fullName) ? pkiBuild.reqDenseArray(idp.fullName, "issuingDistributionPoint.fullName", _err, "crl/bad-idp") : [idp.fullName];
207
148
  if (!entries.length) throw _err("crl/bad-idp", "issuingDistributionPoint fullName must carry at least one GeneralName");
208
- // distributionPoint [0] { fullName [0] IMPLICIT GeneralNames }
209
149
  children.push(b.contextConstructed(0, b.contextConstructed(0, Buffer.concat(entries.map(_encodeGeneralName)))));
210
150
  }
211
151
  var scopeTrue = 0;
@@ -217,19 +157,10 @@ function _idpValue(idp) {
217
157
  return b.sequence(children);
218
158
  }
219
159
 
220
- // Validate a pre-encoded IssuingDistributionPoint value against the same RFC 5280 sec. 5.2.5 profile the
221
- // object form enforces: MUST NOT be empty; at most one of onlyContainsUserCerts [1] / onlyContainsCACerts [2]
222
- // TRUE; onlyContainsAttributeCerts [5] MUST be FALSE; indirectCRL [4] deferred (crlChecker skips indirect CRLs).
223
- // Read through the shared sec. 5.2.5 grammar, not a walk over the raw children: each scope flag is
224
- // an IMPLICIT BOOLEAN whose meaning is fixed by the encoding rules, and a caller-supplied encoding
225
- // is exactly where a flag that is not one content octet of 0x00 or 0xFF arrives. Deciding TRUE from
226
- // a content byte would emit a CRL whose scope this toolkit and the relying party read differently.
227
160
  var _PRE_ENCODED_IDP_SCHEMA = pkix.issuingDistributionPoint("crl/bad-idp");
228
161
  function _validatePreEncodedIdp(inner) {
229
162
  if (!inner.children || !inner.children.length) throw _err("crl/bad-idp", "pre-encoded issuingDistributionPoint MUST NOT be empty (RFC 5280 sec. 5.2.5)");
230
163
  var f = schema.walk(_PRE_ENCODED_IDP_SCHEMA, inner, NS).fields;
231
- // A present DEFAULT-FALSE flag encodes a default DER omits (X.690 sec. 11.5), so it is rejected
232
- // whichever value it carries, never counted as the FALSE it spells.
233
164
  ["onlyContainsUserCerts", "onlyContainsCACerts", "onlyContainsAttributeCerts"].forEach(function (name) {
234
165
  if (f[name].present && f[name].value !== true) throw _err("crl/bad-idp", "pre-encoded issuingDistributionPoint " + name + " encodes its DEFAULT FALSE, which DER omits (X.690 sec. 11.5)");
235
166
  });
@@ -238,12 +169,9 @@ function _validatePreEncodedIdp(inner) {
238
169
  if (f.onlyContainsUserCerts.present && f.onlyContainsCACerts.present) throw _err("crl/bad-idp", "at most one of onlyContainsUserCerts / onlyContainsCACerts may be TRUE (RFC 5280 sec. 5.2.5)");
239
170
  }
240
171
 
241
- // freshestCRL / CRLDistributionPoints (sec. 5.2.6): SEQUENCE OF DistributionPoint carrying only
242
- // distributionPoint (reasons + cRLIssuer omitted). Accepts an array of GeneralName entries (one DP's
243
- // fullName) or an array of { fullName } distribution points.
244
172
  function _freshestValue(spec) {
245
173
  if (!Array.isArray(spec) || !spec.length) throw _err("crl/bad-input", "freshestCRL must be a non-empty array of GeneralNames (or { fullName } distribution points)");
246
- var dense = _b.reqDenseArray(spec, "freshestCRL"); // density before every()/map() skip a hole into b.sequence
174
+ var dense = _b.reqDenseArray(spec, "freshestCRL");
247
175
  var dps = dense.every(function (e) { return e && typeof e === "object" && !Buffer.isBuffer(e) && Object.keys(e).length === 1 && e.fullName != null; })
248
176
  ? dense.map(function (dp) { return dp.fullName; })
249
177
  : [dense];
@@ -254,17 +182,12 @@ function _freshestValue(spec) {
254
182
  }));
255
183
  }
256
184
 
257
- // authorityInfoAccess (sec. 5.2.7): SEQUENCE OF AccessDescription, caIssuers-only. Accepts an array of
258
- // GeneralName entries, each a caIssuers accessLocation.
259
185
  function _aiaValue(spec) {
260
186
  if (!Array.isArray(spec) || !spec.length) throw _err("crl/bad-input", "authorityInfoAccess must be a non-empty array of caIssuers GeneralNames");
261
187
  var caIssuers = O("caIssuers");
262
188
  return b.sequence(spec.map(function (gn) { return b.sequence([b.oid(caIssuers), _encodeGeneralName(gn)]); }));
263
189
  }
264
190
 
265
- // The crlExtensions [0] set. Fixed per-extension criticality (sec. 5.2); at most one instance of each
266
- // (sec. 5.2). `spec.extensions` is an object of the recognized keys OR an array of pre-encoded Extension
267
- // DER (escape hatch for an extension the object form does not model). Returns { exts, isDelta }.
268
191
  function _buildCrlExtensions(spec, ctx) {
269
192
  var out = [], seen = {}, isDelta = false;
270
193
  function push(oidName, critical, valueDer) {
@@ -286,10 +209,6 @@ function _buildCrlExtensions(spec, ctx) {
286
209
  var extnId = asn1.read.oid(node.children[0]);
287
210
  if (seen[extnId]) throw _err("crl/bad-input", "duplicate extension " + extnId + " (RFC 5280 sec. 5.2)");
288
211
  seen[extnId] = true;
289
- // RFC 5280 sec. 5.2 -- a recognized CRL extension is held to the profile even via the escape hatch: its
290
- // fixed criticality (assertValidExtension rejects an explicit critical=FALSE, so 3 children == critical,
291
- // 2 == non-critical) AND a well-formed value of the right type (INTEGER for cRLNumber/deltaCRLIndicator,
292
- // else a SEQUENCE), never emitted opaque.
293
212
  if (_hasOwn(REQUIRED_CRITICALITY, extnId)) {
294
213
  if ((node.children.length === 3) !== REQUIRED_CRITICALITY[extnId]) {
295
214
  throw _err("crl/bad-input", "pre-encoded " + (oid.name(extnId) || extnId) + " extension has the wrong criticality (RFC 5280 sec. 5.2 requires it " + (REQUIRED_CRITICALITY[extnId] ? "critical" : "non-critical") + ")");
@@ -299,17 +218,13 @@ function _buildCrlExtensions(spec, ctx) {
299
218
  else if (extnId === O("deltaCRLIndicator")) arrBase = _requireBoundedInt(inner, "deltaCRLIndicator baseCRLNumber");
300
219
  else {
301
220
  if (inner.tagClass !== "universal" || inner.tagNumber !== TAGS.SEQUENCE) throw _err("crl/bad-input", "pre-encoded " + (oid.name(extnId) || extnId) + " extension value must be a SEQUENCE (RFC 5280 sec. 5.2)");
302
- // A pre-encoded IssuingDistributionPoint is held to the same sec. 5.2.5 profile as the object form.
303
221
  if (extnId === O("issuingDistributionPoint")) _validatePreEncodedIdp(inner);
304
222
  }
305
223
  }
306
224
  if (extnId === O("deltaCRLIndicator")) isDelta = true;
307
225
  out.push(b.raw(der));
308
226
  });
309
- // RFC 5280 sec. 5.2.3 / 5.2.4 -- a delta CRL MUST carry a cRLNumber greater than its baseCRLNumber, whether
310
- // that cRLNumber is pre-encoded here or supplied via spec.crlNumber; the escape hatch is held to the same rule.
311
227
  if (isDelta) {
312
- // RFC 5280 sec. 5.2.6 -- freshestCRL MUST NOT appear in a delta CRL, including via the pre-encoded hatch.
313
228
  if (seen[O("freshestCRL")]) throw _err("crl/bad-input", "freshestCRL MUST NOT appear in a delta CRL (RFC 5280 sec. 5.2.6)");
314
229
  var eff = crlNumberVal != null ? crlNumberVal : arrCrlNum;
315
230
  if (eff == null) throw _err("crl/bad-input", "a delta CRL MUST include a cRLNumber (RFC 5280 sec. 5.2.3 / 5.2.4)");
@@ -326,8 +241,6 @@ function _buildCrlExtensions(spec, ctx) {
326
241
  if (ext.issuingDistributionPoint != null) push("issuingDistributionPoint", true, _idpValue(ext.issuingDistributionPoint));
327
242
  if (ext.deltaCRLIndicator != null) {
328
243
  var base = _boundedInteger(ext.deltaCRLIndicator, "deltaCRLIndicator baseCRLNumber");
329
- // RFC 5280 sec. 5.2.3 / 5.2.4 -- a conforming delta CRL MUST carry a cRLNumber (sec. 5.2.3 requires it in
330
- // every CRL), and that cRLNumber MUST be greater than the BaseCRLNumber it is built against (sec. 5.2.4).
331
244
  if (crlNumberVal == null) throw _err("crl/bad-input", "a delta CRL MUST include a cRLNumber (set spec.crlNumber) (RFC 5280 sec. 5.2.3 / 5.2.4)");
332
245
  if (crlNumberVal <= base.value) throw _err("crl/bad-crl-number", "a delta CRL's cRLNumber (" + crlNumberVal + ") MUST be greater than its baseCRLNumber (" + base.value + ") (RFC 5280 sec. 5.2.4)");
333
246
  push("deltaCRLIndicator", true, base.tlv);
@@ -340,25 +253,17 @@ function _buildCrlExtensions(spec, ctx) {
340
253
  return { exts: out, isDelta: isDelta };
341
254
  }
342
255
 
343
- // The revokedCertificates entries (sec. 5.1.2.6 / 5.3). Each entry: serial + revocationDate + optional
344
- // crlEntryExtensions (reasonCode ENUMERATED, invalidityDate GeneralizedTime-only, certificateIssuer, or a
345
- // pre-encoded escape hatch). Returns { entries, anyExt }, where anyExt forces v2 (sec. 5.1.2.1).
346
256
  function _buildRevoked(entryList, isDelta) {
347
257
  if (!Array.isArray(entryList)) throw _err("crl/bad-input", "revoked must be an array of revoked-certificate entries");
348
258
  var anyExt = false, seenSerials = {};
349
- // Density before the map, which would otherwise skip a hole into b.sequence as a native error.
350
259
  var entries = _b.reqDenseArray(entryList, "revoked").map(function (e, idx) {
351
260
  if (!e || typeof e !== "object" || Buffer.isBuffer(e)) throw _err("crl/bad-input", "each revoked entry must be an object");
352
- // Before any field of the entry is encoded, so an unrecognized name is a refusal rather than an
353
- // entry signed without what it asked for.
354
261
  guard.identifier.assertKnownKeys(e, KNOWN_REVOKED_KEYS, _err, "crl/bad-input", function (k) {
355
262
  return "unknown field " + JSON.stringify(k) + " in revoked entry [" + idx +
356
263
  "]; an entry is { serialNumber, revocationDate, reason?, invalidityDate?, extensions? }";
357
264
  });
358
265
  if (e.serialNumber == null) throw _err("crl/bad-input", "revoked entry [" + idx + "] requires a serialNumber");
359
266
  var serialTlv = _serialInteger(e.serialNumber);
360
- // RFC 5280 sec. 5.1.2.6 -- a serial number identifies a certificate uniquely, so a CRL MUST NOT list the
361
- // same serial twice; dedup on the DER content (the same value the parser surfaces as serialNumberHex).
362
267
  var serialKey = asn1.decode(serialTlv).content.toString("hex");
363
268
  if (seenSerials[serialKey]) throw _err("crl/bad-input", "revoked entry [" + idx + "] duplicates a serial number already listed in this CRL (RFC 5280 sec. 5.1.2.6)");
364
269
  seenSerials[serialKey] = true;
@@ -372,11 +277,10 @@ function _buildRevoked(entryList, isDelta) {
372
277
  }
373
278
  if (e.reason != null) {
374
279
  var codeNum = _resolveReason(e.reason, isDelta);
375
- if (codeNum !== 0) pushE("reasonCode", false, b.enumerated(BigInt(codeNum))); // unspecified(0) omitted (sec. 5.3.1)
280
+ if (codeNum !== 0) pushE("reasonCode", false, b.enumerated(BigInt(codeNum)));
376
281
  }
377
282
  if (e.invalidityDate != null) {
378
283
  guard.time.assertValid(e.invalidityDate, _err, "crl/bad-input", "invalidityDate");
379
- // sec. 5.3.2: always GeneralizedTime (never the UTCTime cutover), no fractional seconds.
380
284
  pushE("invalidityDate", false, b.generalizedTime(e.invalidityDate));
381
285
  }
382
286
  if (e.certificateIssuer != null) throw _err("crl/bad-input", _CERT_ISSUER_DEFERRED);
@@ -389,11 +293,7 @@ function _buildRevoked(entryList, isDelta) {
389
293
  var extnId = asn1.read.oid(xnode.children[0]);
390
294
  if (seen[extnId]) throw _err("crl/bad-input", "duplicate entry extension " + extnId + " in revoked entry [" + idx + "]");
391
295
  seen[extnId] = true;
392
- // certificateIssuer (indirect CRLs) is not usable end-to-end yet (crlChecker skips such a CRL); reject it.
393
296
  if (extnId === O("certificateIssuer")) throw _err("crl/bad-input", _CERT_ISSUER_DEFERRED);
394
- // RFC 5280 sec. 5.3 -- a recognized entry extension is held to the profile even via the escape hatch:
395
- // its fixed criticality AND a well-formed value of the right type (reasonCode an ENUMERATED in the
396
- // legal set, invalidityDate a GeneralizedTime).
397
297
  if (_hasOwn(REQUIRED_ENTRY_CRITICALITY, extnId)) {
398
298
  if ((xnode.children.length === 3) !== REQUIRED_ENTRY_CRITICALITY[extnId]) throw _err("crl/bad-input", "pre-encoded entry extension " + (oid.name(extnId) || extnId) + " has the wrong criticality (RFC 5280 sec. 5.3 requires it " + (REQUIRED_ENTRY_CRITICALITY[extnId] ? "critical" : "non-critical") + ")");
399
299
  var einner = _extInner(xnode, oid.name(extnId) || extnId);
@@ -416,19 +316,11 @@ function _buildRevoked(entryList, isDelta) {
416
316
  return { entries: entries, anyExt: anyExt };
417
317
  }
418
318
 
419
- // ---- the primitives --------------------------------------------------------
420
319
 
421
320
  function _parseIssuerCert(cert) {
422
- // The issuer certificate decides who may have signed this CRL: its key verifies the signature and
423
- // its keyUsage says whether it may sign CRLs at all. A caller-assembled one could carry a real
424
- // CA's name and cRLSign bit beside a substituted key, so it is re-derived like every other.
425
321
  return guard.parsed.acceptDerived(cert, "certificate", x509Schema.parse, _err, "crl/bad-input", "issuer.cert");
426
322
  }
427
323
 
428
- // RFC 5280 sec. 4.2.1.3 -- a certificate whose key signs CRLs asserts the cRLSign keyUsage bit. When the
429
- // issuer certificate carries a keyUsage extension it MUST include cRLSign, or the CRL it signs is rejected
430
- // by a conforming relying party (pki.path.crlChecker included). Mirrors x509-sign's keyCertSign gate; a
431
- // certificate with no keyUsage extension is unrestricted.
432
324
  function _assertIssuerCanSignCrl(issuerCert) {
433
325
  var kuExt = (issuerCert.extensions || []).filter(function (e) { return e.oid === O("keyUsage"); })[0];
434
326
  if (!kuExt) return;
@@ -442,13 +334,6 @@ function _sign(spec, issuer, opts) {
442
334
  opts = opts || {};
443
335
  if (!spec || typeof spec !== "object" || Buffer.isBuffer(spec)) throw _err("crl/bad-input", "the CRL spec must be an object");
444
336
  issuer = issuer || {};
445
- // Before any field is read. A dropped key here is worse than elsewhere in the toolkit: a CRL
446
- // asserts what is revoked, so the failure is a correctly signed, structurally valid CRL saying
447
- // NOTHING is revoked. `revokedCertificates` earns its own hint because it is the name the parser
448
- // uses for the same list -- a caller round-tripping a parsed CRL back into sign reaches for it
449
- // first, and got a signed empty list.
450
- // The issuer form is settled once, before either door, so the spec table matches the source the
451
- // distinguished name will actually come from.
452
337
  var byCert = issuer.cert != null, byName = issuer.name != null;
453
338
  guard.identifier.assertKnownKeys(spec, byCert || byName ? KNOWN_SPEC_KEYS_ISSUER_NAMED : KNOWN_SPEC_KEYS,
454
339
  _err, "crl/bad-input", function (k) {
@@ -467,7 +352,6 @@ function _sign(spec, issuer, opts) {
467
352
  guard.identifier.assertKnownKeys(opts, KNOWN_SIGN_OPTS, _err, "crl/bad-input", "pki.crl.sign has an unknown option ");
468
353
  if (issuer.key == null) throw _err("crl/bad-input", "a signing key (issuer.key, a WebCrypto CryptoKey or a PKCS#8 private key DER/PEM) is required");
469
354
 
470
- // Resolve the issuer name + signing-key SPKI. `{ cert }` supplies both; else `{ name|spec.issuer, publicKey }`.
471
355
  var issuerDer, issuerSpki, issuerCert = null;
472
356
  if (byCert) {
473
357
  issuerCert = _parseIssuerCert(issuer.cert);
@@ -481,43 +365,35 @@ function _sign(spec, issuer, opts) {
481
365
  if (dnSource == null) throw _err("crl/bad-issuer", "an issuer distinguished name is required (issuer.name or spec.issuer) when no issuer.cert is given");
482
366
  issuerDer = _encodeName(dnSource);
483
367
  }
484
- // RFC 5280 sec. 5.1.2.3 -- the issuer MUST be a non-empty distinguished name.
485
368
  if (_isEmptyName(issuerDer)) throw _err("crl/bad-issuer", "issuer must be a non-empty distinguished name (RFC 5280 sec. 5.1.2.3)");
486
369
 
487
- // thisUpdate (required) + nextUpdate (optional; MUST NOT precede thisUpdate). _timeDer validates each date
488
- // and applies the RFC 5280 sec. 5.1.2.4/.5 UTCTime<=2049-else-GeneralizedTime cutover.
489
370
  if (spec.thisUpdate == null) throw _err("crl/bad-input", "thisUpdate is required (RFC 5280 sec. 5.1.2.4)");
490
371
  var thisU = _timeDer(spec.thisUpdate, "thisUpdate");
491
372
  var nextU = null;
492
373
  if (spec.nextUpdate != null) {
493
374
  nextU = _timeDer(spec.nextUpdate, "nextUpdate");
494
375
  // allow:nan-date-comparison-unguarded -- both operands are guard.time.assertValid'd via _timeDer on the
495
- // two lines above (thisUpdate + nextUpdate), so an Invalid Date throws before this comparison.
496
376
  if (guard.time.instantOf(spec.nextUpdate) < guard.time.instantOf(spec.thisUpdate)) throw _err("crl/bad-input", "nextUpdate must not be before thisUpdate (RFC 5280 sec. 5.1.2.5)");
497
377
  }
498
378
 
499
379
  var extResult = _buildCrlExtensions(spec, { issuerCert: issuerCert, issuerSpki: issuerSpki });
500
380
  var crlExts = extResult.exts;
501
381
  var revoked = spec.revoked != null ? _buildRevoked(spec.revoked, extResult.isDelta) : { entries: [], anyExt: false };
502
- // RFC 5280 sec. 5.1.2.1 -- v2 iff any CRL or entry extension is present, else v1 (version omitted).
503
382
  var version = (crlExts.length || revoked.anyExt) ? 2 : 1;
504
383
 
505
- // The signature scheme resolves from the SIGNING key's SPKI algorithm (the whole registry, for free).
506
384
  var scheme = signScheme.resolveSignScheme(_certLikeFromSpki(issuerSpki), { combinedRsaSig: true, pss: opts.pss, digestAlgorithm: opts.digestAlgorithm }, true, _signE);
507
385
 
508
386
  var tbsChildren = [];
509
- if (version === 2) tbsChildren.push(b.integer(1n)); // bare INTEGER(1), NOT a [0] EXPLICIT tag like a certificate
510
- tbsChildren.push(scheme.sigAlgId); // signature == signatureAlgorithm (sec. 5.1.1.2), single source
387
+ if (version === 2) tbsChildren.push(b.integer(1n));
388
+ tbsChildren.push(scheme.sigAlgId);
511
389
  tbsChildren.push(issuerDer);
512
390
  tbsChildren.push(thisU);
513
391
  if (nextU) tbsChildren.push(nextU);
514
- if (revoked.entries.length) tbsChildren.push(b.sequence(revoked.entries)); // omit the field when empty (sec. 5.1.2.6)
515
- if (crlExts.length) tbsChildren.push(b.explicit(0, b.sequence(crlExts))); // crlExtensions [0] EXPLICIT
392
+ if (revoked.entries.length) tbsChildren.push(b.sequence(revoked.entries));
393
+ if (crlExts.length) tbsChildren.push(b.explicit(0, b.sequence(crlExts)));
516
394
  var tbsDer = b.sequence(tbsChildren);
517
395
 
518
396
  return signScheme.signOverTbs(scheme, issuer.key, tbsDer, _signE).then(function (sig) {
519
- // The signature MUST verify under the issuer public key before returning, or the CRL would be rejected
520
- // downstream (composite returns a promise; the classical/PQC path throws synchronously on a mismatch).
521
397
  return Promise.resolve(_assertSignatureVerifies(tbsDer, sig, issuerSpki, scheme)).then(function () {
522
398
  var crlDer = b.sequence([tbsDer, scheme.sigAlgId, b.bitString(sig, 0)]);
523
399
  return opts.pem ? crlSchema.pemEncode(crlDer, "X509 CRL") : crlDer;
@@ -575,36 +451,20 @@ function _sign(spec, issuer, opts) {
575
451
  * pki.schema.crl.parse(der).revokedCertificates[0].serialNumberHex; // "1234"
576
452
  */
577
453
  function sign(spec, issuer, opts) {
578
- // Every caller-owned argument copied at entry and released when the call settles -- see the note
579
- // on the same call in x509-sign.
580
454
  return guard.bytes.fixedCall(CrlError, "crl/bad-input", [
581
455
  [spec, "the CRL spec"], [issuer, "the issuer"], [opts, "pki.crl.sign options"],
582
456
  ], _sign);
583
457
  }
584
458
 
585
- // A CRL these verbs answer from is re-derived from the bytes its parser read. Completeness -- every
586
- // field present with the right type -- is not enough for a verdict: the signature covers a byte
587
- // range, while the revocation list and the scope extensions are separate properties of the parsed
588
- // object. Keep a correctly signed CRL's `tbsBytes` and signature and empty `revokedCertificates`,
589
- // and the signature still verifies while `isRevoked` answers from the edited list. An emptied
590
- // `crlExtensions` does the same to scope: a shard that may only speak for some reasons, or a delta
591
- // that may not answer alone, becomes one that answers for everything.
592
459
  function _coerceCrl(crl) {
593
460
  return guard.parsed.acceptDerived(crl, "crl", crlSchema.parse, _err, "crl/bad-input", "the CRL");
594
461
  }
595
462
 
596
- // The issuer's key AND, when the caller supplied a certificate rather than a bare key, the
597
- // certificate itself -- because a CRL signature that verifies says only that SOME key signed these
598
- // bytes. Whether that key was allowed to sign a CRL, and whether it belongs to the issuer this CRL
599
- // names, are separate questions that only a certificate can answer.
600
463
  function _resolveIssuer(issuer) {
601
464
  if (issuer == null) throw _err("crl/bad-input", "an issuer is required to verify a CRL");
602
465
  if (guard.bytes.isByteSource(issuer)) { var _isp = guard.bytes.source(issuer, CrlError, "crl/bad-input", "issuer SPKI"); _assertValidSpki(_isp, "issuer SPKI"); return { spki: _isp, cert: null }; }
603
466
  if (issuer.cert != null) { var ic = _parseIssuerCert(issuer.cert); return { spki: ic.subjectPublicKeyInfo.bytes, cert: ic }; }
604
467
  if (issuer.publicKey != null) { var spki = _reqDer(issuer.publicKey, "issuer.publicKey"); _assertValidSpki(spki, "issuer.publicKey"); return { spki: spki, cert: null }; }
605
- // A parsed certificate passed directly. It reaches the same identity and cRLSign checks the
606
- // { cert } form does, so it goes through the same door: an object carrying an SPKI but missing
607
- // the subject those checks compare would decide them on undefined.
608
468
  if (issuer.subjectPublicKeyInfo && issuer.subjectPublicKeyInfo.bytes) {
609
469
  var pc = _parseIssuerCert(issuer);
610
470
  return { spki: pc.subjectPublicKeyInfo.bytes, cert: pc };
@@ -612,26 +472,10 @@ function _resolveIssuer(issuer) {
612
472
  throw _err("crl/bad-input", "issuer must be { cert }, { publicKey } (SPKI DER), or a raw SPKI Buffer");
613
473
  }
614
474
 
615
- // RFC 5280 sec. 5.1.1.2 / 6.3.3: the certificate that signed a CRL must BE the CRL's issuer, and if
616
- // it carries a keyUsage extension that extension must assert cRLSign. Neither is implied by the
617
- // signature verifying: any key can sign any bytes, and a certificate restricted to digitalSignature
618
- // chains to its root perfectly well while having no authority to revoke anything. The producing
619
- // side of this same file already refuses to SIGN a CRL with a certificate lacking cRLSign; the
620
- // verifying side asked neither question, so a CRL minted under an end-entity certificate of the
621
- // same CA verified as that CA's CRL.
622
- // Returns false rather than throwing, because both answers are statements about the CRL, not about
623
- // the caller's input: "this CRL was not validly issued by the certificate you named". That keeps
624
- // the verb's boolean contract, and keeps working the ordinary pattern of trying each candidate
625
- // issuer to find which one issued a CRL. A MALFORMED keyUsage is different -- it is a defect in the
626
- // caller's own certificate rather than a verdict about the CRL -- and throws.
627
475
  function _issuerMaySign(parsed, cert) {
628
476
  if (!guard.name.dnEqual(parsed.issuer.rdns, cert.subject.rdns, _err, "crl/bad-issuer", "the CRL issuer")) return false;
629
- // Through the shared reader, not a local BIT STRING read: keyUsage is a NamedBitList, so DER drops
630
- // its trailing zero bits and sec. 4.2.1.3 requires at least one bit set, and a boundary reading
631
- // the bits itself applies neither -- authorizing here a certificate the signing side and the path
632
- // validator both refuse as malformed.
633
477
  var ku = pkix.keyUsageOf(NS, cert, _err, "crl/bad-issuer", "issuer certificate");
634
- if (!ku) return true; // absent keyUsage places no restriction (sec. 4.2.1.3)
478
+ if (!ku) return true;
635
479
  return ku.cRLSign === true;
636
480
  }
637
481
 
@@ -677,10 +521,6 @@ function verify(crl, issuer) { return guard.async.deferred(function () { return
677
521
  function _verify(crl, issuer) {
678
522
  var parsed = _coerceCrl(crl);
679
523
  var resolved = _resolveIssuer(issuer);
680
- // A bare SPKI carries no certificate, so the two cert-only questions cannot be asked and only the
681
- // signature is checked (issuerMaySign is then reported true). Both checks are surfaced on the verdict:
682
- // a bare `true` used to hide whether the signer was ALLOWED to sign this CRL, so `if (verify(...))`
683
- // accepted a CRL minted under an end-entity certificate of the same CA as that CA's own.
684
524
  var issuerMaySign = resolved.cert ? _issuerMaySign(parsed, resolved.cert) : true;
685
525
  return crlVerify.verifyCrlSignature(parsed, resolved.spki).then(function (sigOk) {
686
526
  var signatureValid = sigOk === true;
@@ -704,7 +544,6 @@ function _serialHexOf(serial) {
704
544
  else if (guard.bytes.isByteSource(serial)) { var _sb = guard.bytes.source(serial, CrlError, "crl/bad-input", "serialNumber"); v = _sb.length ? BigInt("0x" + _sb.toString("hex")) : 0n; }
705
545
  else throw _err("crl/bad-input", "serialNumber must be a BigInt, integer, hex string, or BufferSource");
706
546
  if (v <= 0n) throw _err("crl/bad-input", "serialNumber must be a positive integer");
707
- // Match schema-crl's serialNumberHex: the DER INTEGER content octets (preserving sign padding).
708
547
  return asn1.decode(b.integer(v)).content.toString("hex");
709
548
  }
710
549
 
@@ -770,8 +609,6 @@ function isRevoked(crl, serialNumber, opts) {
770
609
  return "pki.crl.isRevoked has an unknown option " + JSON.stringify(k) +
771
610
  "; it takes `time` and `historicalMode`";
772
611
  });
773
- // Both name the same instant, so one without the other asks nothing: historicalMode says how to
774
- // read an entry against the time, and there is no time to read it against.
775
612
  if (o.historicalMode !== undefined && o.time === undefined) {
776
613
  throw _err("crl/bad-input", "pki.crl.isRevoked opts.historicalMode says how to read an entry " +
777
614
  "against the instant asked about, so it needs opts.time as well");
@@ -781,16 +618,6 @@ function isRevoked(crl, serialNumber, opts) {
781
618
  }
782
619
  var parsed = _coerceCrl(crl);
783
620
  var hex = _serialHexOf(serialNumber);
784
- // CURRENCY, asked on the same footing as scope below and for the same reason: a CRL states the
785
- // window it speaks for, and outside that window a serial being absent says nothing about the
786
- // certificate -- the issuer promised a further list by `nextUpdate` and this one predates
787
- // whatever it says. Read alone, `null` from an expired CRL reads as "not revoked", which is the
788
- // composition this refusal exists to stop.
789
- //
790
- // Only when a time is given. This verb is handed a CRL and a serial, and the instant a question
791
- // is asked at is the caller's to state throughout this toolkit rather than something read off
792
- // the system clock here. Without one the verb stays the structural lookup it has always been,
793
- // and its docstring says which question then went unasked.
794
621
  if (o.time !== undefined) {
795
622
  guard.time.assertValid(o.time, _err, "crl/bad-input", "pki.crl.isRevoked opts.time");
796
623
  var at = guard.time.instantOf(o.time);
@@ -804,39 +631,17 @@ function isRevoked(crl, serialNumber, opts) {
804
631
  "requires one of a conforming CA); ask pki.path.crlChecker, which decides currency against " +
805
632
  "the material it fetched");
806
633
  }
807
- // Strictly past, so a time equal to nextUpdate is still covered -- the same boundary
808
- // pki.path.crlChecker and the OCSP currency check use, and RFC 5280 sec. 5.1.2.5 makes
809
- // nextUpdate the date BY WHICH the next list arrives rather than the moment this one lapses.
810
634
  if (at > guard.time.instantOf(parsed.nextUpdate)) {
811
635
  throw _err("crl/not-current", "this CRL was superseded after " +
812
636
  parsed.nextUpdate.toISOString() + "; a serial missing from it says nothing about the " +
813
637
  "certificate at the time asked about, since the issuer promised a further list by then");
814
638
  }
815
639
  }
816
- // SCOPE, before the serial is looked for at all. A serial number is only meaningful within the
817
- // set of certificates a CRL actually speaks for, and two shapes of CRL speak for a different set
818
- // than a caller matching on serial alone assumes. pki.path.crlChecker already refuses both; this
819
- // verb answered from any CRL it was handed, so the toolkit knew the rule and applied it in one
820
- // consumer -- which is how the standalone verb came to give the opposite answer.
821
- //
822
- // A DELTA CRL lists CHANGES since its base, so a serial present in it may be there to say the
823
- // certificate was RELEASED (removeFromCRL); read alone, the extension that means "no longer
824
- // revoked" reads as "revoked". The answer is only derivable from the delta MERGED with its base,
825
- // which pki.path.crlChecker does.
826
640
  if (_findExtOid(parsed.crlExtensions, "deltaCRLIndicator")) {
827
641
  throw _err("crl/delta-not-authoritative", "this is a delta CRL: it lists changes since a base CRL, so a serial appearing in it may be RELEASED rather than revoked. Merge it with its base -- pki.path.crlChecker does -- rather than reading a revocation status out of the delta alone");
828
642
  }
829
- // An INDIRECT CRL carries entries for OTHER issuers' certificates, identified by a per-entry
830
- // certificateIssuer attribute. Matching on serial alone attributes another issuer's revocation to
831
- // this one's certificate of the same serial number -- serials are unique per issuer, not globally.
832
643
  var idp = _findExtOid(parsed.crlExtensions, "issuingDistributionPoint");
833
644
  if (idp) _assertAuthoritativeScope(idp);
834
- // certificateIssuer (sec. 5.3.3) names the issuer an entry belongs to, and is meaningful ONLY on
835
- // an indirect CRL -- which was refused above. One here means the CRL declares itself direct while
836
- // carrying an entry that says otherwise, and the two readings disagree about whose certificates
837
- // this list covers. That is a property of the CRL, so it is settled BEFORE the lookup and over
838
- // EVERY entry: checking only the entry that matches would leave the not-listed answer -- the one
839
- // that says the certificate is fine -- coming from a list whose own entries dispute what it lists.
840
645
  for (var i = 0; i < parsed.revokedCertificates.length; i++) {
841
646
  if (_findExtOid(parsed.revokedCertificates[i].crlEntryExtensions, "certificateIssuer")) {
842
647
  throw _err("crl/indirect-not-supported", "a revoked-certificate entry carries certificateIssuer, which names another issuer, on a CRL that does not declare itself indirect -- the entry and the CRL disagree about whose certificates this list covers, so no revocation status follows from it");
@@ -844,12 +649,6 @@ function isRevoked(crl, serialNumber, opts) {
844
649
  }
845
650
  for (var j = 0; j < parsed.revokedCertificates.length; j++) {
846
651
  if (parsed.revokedCertificates[j].serialNumberHex !== hex) continue;
847
- // A revocation takes effect on its revocationDate (RFC 5280 sec. 5.3), and how that reads
848
- // against the instant asked about is the same question pki.path.crlChecker answers -- so the
849
- // same answer is given here. Asked at the present moment a listed serial is revoked whatever
850
- // that date says, because a future one is post-dating or clock skew and must not read good.
851
- // Only an explicit historical reading -- validating as of a past instant, a timestamped
852
- // signature say -- has an entry dated after that instant not yet applying.
853
652
  if (o.historicalMode === true &&
854
653
  guard.time.instantOf(parsed.revokedCertificates[j].revocationDate) >
855
654
  guard.time.instantOf(o.time)) continue;
@@ -858,36 +657,11 @@ function isRevoked(crl, serialNumber, opts) {
858
657
  return null;
859
658
  }
860
659
 
861
- // One extension out of a parsed list, by registered OID name. Null-safe: an absent list is no
862
- // extension rather than a fault, which is what every caller here means by it.
863
660
  function _findExtOid(list, name) {
864
661
  var want = O(name);
865
662
  return (list || []).filter(function (e) { return e.oid === want; })[0] || null;
866
663
  }
867
664
 
868
- // The issuingDistributionPoint states which certificates a CRL speaks for (sec. 5.2.5), and every
869
- // form of it narrows that set below what a bare serial lookup assumes. `isRevoked` is handed a
870
- // serial and nothing else, so it cannot tell whether the certificate behind that serial falls in
871
- // the scope -- that decision needs the certificate, which is what pki.path.crlChecker has and this
872
- // verb does not. So the presence of the extension is the refusal, whichever field carries it:
873
- //
874
- // indirectCRL entries name their own issuers, and serials are unique per issuer: a match
875
- // would attribute another issuer's revocation to your certificate.
876
- // distributionPoint the CRL covers the certificates whose own distribution point corresponds to
877
- // this one (sec. 6.3.3(b)(2)(i)) -- a byte-exact comparison against a field of
878
- // the certificate.
879
- // onlyContainsUserCerts / onlyContainsCACerts / onlyContainsAttributeCerts
880
- // the CRL covers one KIND of certificate; a serial does not say which kind it
881
- // names, so an absent entry says nothing about a certificate of another kind.
882
- // onlySomeReasons the CRL covers some revocation REASONS; a certificate revoked for a reason
883
- // outside the set is absent from it while being revoked.
884
- //
885
- // Written as a throwing assertion rather than a predicate so the malformed case cannot be mistaken
886
- // for a verdict about the certificate. The extension is read through the shared sec. 5.2.5 grammar
887
- // rather than by walking its children, because the flags are IMPLICIT BOOLEANs and only the encoding
888
- // rules say what their bytes mean: a walk that tests a content byte reads an EMPTY [4] as an absent
889
- // flag and a multi-octet one by whichever byte it indexes, and "absent, so unscoped" is precisely
890
- // the reading that lets an unreadable scope license an answer.
891
665
  var _IDP_SCHEMA = pkix.issuingDistributionPoint("crl/scope-not-authoritative");
892
666
  var _INDIRECT_REFUSAL = "this CRL is marked indirect (issuingDistributionPoint indirectCRL): its entries name their own issuers, so a serial alone does not identify a certificate. Reading a revocation status from it by serial would attribute another issuer's revocation to yours";
893
667
  var _SCOPED_REFUSAL = "this CRL carries an issuingDistributionPoint, so it speaks for part of its issuer's certificates rather than all of them, and which part is decided against fields of the certificate -- which this verb is not given. A serial absent from it is not a certificate that is unrevoked. Use pki.path.crlChecker, which is handed the certificate and performs the RFC 5280 sec. 6.3.3 scope correspondence";
@@ -896,8 +670,6 @@ function _assertAuthoritativeScope(ext) {
896
670
  try { m = schema.walk(_IDP_SCHEMA, asn1.decode(ext.value), NS); }
897
671
  catch (e) { throw _err("crl/scope-not-authoritative", "the issuingDistributionPoint cannot be read, so this CRL's scope cannot be established", e); }
898
672
  var f = m.fields;
899
- // Named first so the sharper reason wins: indirect is not merely a narrower scope, it is a list
900
- // whose serials belong to other issuers.
901
673
  if (f.indirectCRL.present && f.indirectCRL.value === true) throw _err("crl/indirect-not-supported", _INDIRECT_REFUSAL);
902
674
  throw _err("crl/scope-not-authoritative", _SCOPED_REFUSAL);
903
675
  }