@blamejs/pki 0.6.14 → 0.6.16

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 (112) hide show
  1. package/CHANGELOG.md +27 -1
  2. package/lib/acme.js +59 -815
  3. package/lib/asn1-der.js +7 -245
  4. package/lib/attrcert-sign.js +12 -260
  5. package/lib/attrcert-verify.js +1 -7
  6. package/lib/byte-reader.js +0 -8
  7. package/lib/byte-writer.js +0 -11
  8. package/lib/cbor-det.js +0 -54
  9. package/lib/cmc-build.js +15 -357
  10. package/lib/cmc-verify.js +6 -239
  11. package/lib/cmp-build.js +41 -317
  12. package/lib/cmp-session.js +73 -760
  13. package/lib/cmp-verify.js +32 -488
  14. package/lib/cms-compress.js +3 -32
  15. package/lib/cms-decrypt.js +29 -294
  16. package/lib/cms-encrypt.js +14 -234
  17. package/lib/cms-sign.js +29 -324
  18. package/lib/cms-verify.js +9 -500
  19. package/lib/composite-kem.js +2 -102
  20. package/lib/composite-sig.js +1 -77
  21. package/lib/constants.js +0 -249
  22. package/lib/crl-sign.js +9 -237
  23. package/lib/crl-verify.js +1 -10
  24. package/lib/crmf-sign.js +19 -198
  25. package/lib/crmf-verify.js +1 -12
  26. package/lib/csr-sign.js +3 -34
  27. package/lib/csr-verify.js +1 -12
  28. package/lib/ct.js +28 -253
  29. package/lib/edwards-point.js +10 -44
  30. package/lib/est.js +28 -764
  31. package/lib/framework-error.js +4 -182
  32. package/lib/guard-all.js +1 -58
  33. package/lib/guard-async.js +1 -36
  34. package/lib/guard-bytes.js +6 -650
  35. package/lib/guard-compress.js +5 -102
  36. package/lib/guard-crypto.js +1 -40
  37. package/lib/guard-der.js +2 -93
  38. package/lib/guard-encoding.js +2 -60
  39. package/lib/guard-header.js +1 -36
  40. package/lib/guard-identifier.js +7 -531
  41. package/lib/guard-intrinsic.js +1 -150
  42. package/lib/guard-json.js +10 -73
  43. package/lib/guard-limits.js +1 -77
  44. package/lib/guard-list.js +1 -49
  45. package/lib/guard-name.js +3 -209
  46. package/lib/guard-parsed.js +7 -335
  47. package/lib/guard-range.js +1 -71
  48. package/lib/guard-secret.js +1 -102
  49. package/lib/guard-text.js +1 -64
  50. package/lib/guard-time.js +1 -97
  51. package/lib/hpke.js +3 -105
  52. package/lib/http-digest.js +14 -142
  53. package/lib/http-retry-after.js +1 -47
  54. package/lib/http-transport.js +21 -131
  55. package/lib/inspect.js +30 -284
  56. package/lib/ip-utils.js +6 -47
  57. package/lib/jose.js +7 -142
  58. package/lib/key.js +2 -67
  59. package/lib/lint.js +9 -194
  60. package/lib/merkle.js +1 -27
  61. package/lib/mime.js +9 -82
  62. package/lib/ocsp-request-verify.js +1 -14
  63. package/lib/ocsp-verify.js +1 -82
  64. package/lib/ocsp.js +11 -169
  65. package/lib/oid.js +5 -337
  66. package/lib/path-validate.js +103 -1492
  67. package/lib/pbes2.js +2 -89
  68. package/lib/pkcs12-build.js +31 -379
  69. package/lib/pki-build.js +64 -290
  70. package/lib/rc2.js +2 -18
  71. package/lib/rfc3339.js +4 -27
  72. package/lib/scep.js +140 -286
  73. package/lib/schema-all.js +0 -122
  74. package/lib/schema-attrcert.js +4 -146
  75. package/lib/schema-c509.js +153 -802
  76. package/lib/schema-cmc.js +5 -220
  77. package/lib/schema-cmp.js +2 -223
  78. package/lib/schema-cms.js +13 -427
  79. package/lib/schema-crl.js +5 -68
  80. package/lib/schema-crmf.js +6 -167
  81. package/lib/schema-csr.js +1 -54
  82. package/lib/schema-csrattrs.js +2 -87
  83. package/lib/schema-engine.js +0 -152
  84. package/lib/schema-ocsp.js +0 -117
  85. package/lib/schema-pkcs12.js +2 -93
  86. package/lib/schema-pkcs8.js +3 -35
  87. package/lib/schema-pkix.js +24 -655
  88. package/lib/schema-smime.js +0 -54
  89. package/lib/schema-tsp.js +3 -99
  90. package/lib/schema-x509.js +4 -63
  91. package/lib/shbs.js +11 -95
  92. package/lib/sign-scheme.js +2 -97
  93. package/lib/sigstore.js +9 -169
  94. package/lib/sleep.js +2 -8
  95. package/lib/smime.js +15 -408
  96. package/lib/tls-cert-compress.js +0 -62
  97. package/lib/trust.js +19 -185
  98. package/lib/tsp-sign.js +16 -270
  99. package/lib/validator-all.js +1 -30
  100. package/lib/validator-attcert.js +2 -46
  101. package/lib/validator-cose.js +2 -153
  102. package/lib/validator-keydesc.js +1 -37
  103. package/lib/validator-sig.js +1 -46
  104. package/lib/validator-tls.js +1 -88
  105. package/lib/validator-tpm.js +19 -122
  106. package/lib/verify-seam.js +1 -24
  107. package/lib/webauthn-mds.js +3 -400
  108. package/lib/webauthn.js +12 -833
  109. package/lib/webcrypto.js +5 -485
  110. package/lib/x509-sign.js +4 -108
  111. package/package.json +1 -1
  112. package/sbom.cdx.json +6 -6
package/lib/trust.js CHANGED
@@ -41,8 +41,6 @@ var errors = require("./framework-error");
41
41
  var asn1 = require("./asn1-der");
42
42
  var guard = require("./guard-all");
43
43
  var intrinsic = require("./guard-intrinsic");
44
- // Own-key membership through an operation taken at load. Written out, the question reads two
45
- // replaceable properties to answer one thing, and here it decides which trust bits an anchor carries.
46
44
  var _hasOwn = intrinsic.hasOwn;
47
45
  var _charCodeAt = intrinsic.uncurry(String.prototype.charCodeAt);
48
46
  var _strSlice = intrinsic.uncurry(String.prototype.slice);
@@ -54,40 +52,27 @@ var LIMITS = constants.LIMITS;
54
52
 
55
53
  function E(code, message, cause) { return new TrustError(code, message, cause); }
56
54
 
57
- // The three NSS purposes an anchor carries; the keys double as the
58
- // `opts.checkPurpose` vocabulary `pki.path.validate` consumes.
59
55
  var PURPOSES = ["serverAuth", "emailProtection", "codeSigning"];
60
56
 
61
- // CKA_TRUST_* attribute -> purpose key.
62
57
  var PURPOSE_ATTRS = [
63
58
  ["CKA_TRUST_SERVER_AUTH", "serverAuth"],
64
59
  ["CKA_TRUST_EMAIL_PROTECTION", "emailProtection"],
65
60
  ["CKA_TRUST_CODE_SIGNING", "codeSigning"],
66
61
  ];
67
62
 
68
- // The recognized CK_TRUST vocabulary. Only CKT_NSS_TRUSTED_DELEGATOR grants a
69
- // purpose; the rest are recognized-but-untrusted (fail-closed: an absent or
70
- // non-delegator bit never defaults to trusted). A token outside this set is a
71
- // typed trust/bad-trust-value, never a silent false.
72
63
  var CKT_RECOGNIZED = Object.create(null);
73
64
  ["CKT_NSS_TRUSTED_DELEGATOR", "CKT_NSS_TRUSTED", "CKT_NSS_MUST_VERIFY_TRUST",
74
65
  "CKT_NSS_TRUST_UNKNOWN", "CKT_NSS_NOT_TRUSTED", "CKT_NSS_VALID_DELEGATOR"]
75
66
  .forEach(function (t) { CKT_RECOGNIZED[t] = true; });
76
67
  var CKT_DELEGATOR = "CKT_NSS_TRUSTED_DELEGATOR";
77
68
 
78
- // An attribute line: `CKA_<NAME> <type> [<value>]`. The value grammar is
79
- // per-type (a quoted UTF8 string, a single token, or nothing for
80
- // MULTILINE_OCTAL whose payload follows on its own lines).
81
- // ---- certdata scanners by character (no regex) ----------------------------------------------------
82
- function _isNameChar(c) { return (c >= 65 && c <= 90) || (c >= 48 && c <= 57) || c === 95; } // A-Z 0-9 _
69
+ function _isNameChar(c) { return (c >= 65 && c <= 90) || (c >= 48 && c <= 57) || c === 95; }
83
70
  function _isDigitCode(c) { return c >= 48 && c <= 57; }
84
- function _isDateSepCode(c) { return c === 46 || c === 47 || c === 45; } // . / -
85
- function _isOctalDigit(c) { return c >= 48 && c <= 55; } // 0-7
71
+ function _isDateSepCode(c) { return c === 46 || c === 47 || c === 45; }
72
+ function _isOctalDigit(c) { return c >= 48 && c <= 55; }
86
73
  function _isWsCode(c) { return pkix.isJsWhitespace(c); }
87
- function _containsWs(s) { for (var i = 0; i < s.length; i++) { if (_isWsCode(_charCodeAt(s, i))) return true; } return false; } // /\s/.test(s)
74
+ function _containsWs(s) { for (var i = 0; i < s.length; i++) { if (_isWsCode(_charCodeAt(s, i))) return true; } return false; }
88
75
 
89
- // An attribute line `CKA_<NAME> [<type> [<value>]]`, matching
90
- // /^(CKA_[A-Z0-9_]+)(?:\s+(\S+)(?:\s+(.*))?)?$/. Returns [name, type|undefined, value|undefined] or null.
91
76
  function _parseAttrLine(s) {
92
77
  var n = s.length;
93
78
  if (_strSlice(s, 0, 4) !== "CKA_") return null;
@@ -108,17 +93,13 @@ function _parseAttrLine(s) {
108
93
  return [name, type, _strSlice(s, p)];
109
94
  }
110
95
 
111
- // A single double-quoted value with no interior quote (matching /^"([^"]*)"$/): the inner content, or
112
- // null. Empty content ("\"\"") returns "" (a valid value), distinct from the null no-match.
113
96
  function _unquote(s) {
114
97
  var n = s.length;
115
- if (n < 2 || _charCodeAt(s, 0) !== 34 || _charCodeAt(s, n - 1) !== 34) return null; // surrounding "
116
- for (var i = 1; i < n - 1; i++) if (_charCodeAt(s, i) === 34) return null; // no interior "
98
+ if (n < 2 || _charCodeAt(s, 0) !== 34 || _charCodeAt(s, n - 1) !== 34) return null;
99
+ for (var i = 1; i < n - 1; i++) if (_charCodeAt(s, i) === 34) return null;
117
100
  return _strSlice(s, 1, n - 1);
118
101
  }
119
102
 
120
- // String.prototype.split(/\s+/): the pieces between whitespace runs (a leading/trailing run yields a
121
- // "" element; "" -> [""]). Index assignment (not .push) so the scanner adds no live intrinsic read.
122
103
  function _splitWhitespace(s) {
123
104
  var out = [], i = 0, n = s.length, pieceStart = 0;
124
105
  while (i < n) {
@@ -132,23 +113,21 @@ function _splitWhitespace(s) {
132
113
  return out;
133
114
  }
134
115
 
135
- // Exactly three octal digits (matching /^[0-7]{3}$/).
136
116
  function _isOctal3(d) {
137
117
  if (d.length !== 3) return false;
138
118
  for (var i = 0; i < 3; i++) { if (!_isOctalDigit(_charCodeAt(d, i))) return false; }
139
119
  return true;
140
120
  }
141
121
 
142
- // String.prototype.split(/\r\n|\n|\r/): split on CRLF, LF, or CR line terminators.
143
122
  function _splitLines(text) {
144
123
  var out = [], start = 0, i = 0, n = text.length;
145
124
  while (i < n) {
146
125
  var c = _charCodeAt(text, i);
147
- if (c === 13) { // CR (consume a following LF as one)
126
+ if (c === 13) {
148
127
  out[out.length] = _strSlice(text, start, i);
149
128
  if (i + 1 < n && _charCodeAt(text, i + 1) === 10) i += 1;
150
129
  i += 1; start = i;
151
- } else if (c === 10) { // LF
130
+ } else if (c === 10) {
152
131
  out[out.length] = _strSlice(text, start, i);
153
132
  i += 1; start = i;
154
133
  } else i += 1;
@@ -157,8 +136,6 @@ function _splitLines(text) {
157
136
  return out;
158
137
  }
159
138
 
160
- // A Y-M-D date with dotted/dashed/slashed separators, matching /^(\d{4})[./-](\d{1,2})[./-](\d{1,2})$/.
161
- // Returns [year, month, day] (raw digit strings) or null.
162
139
  function _parseYmd(s) {
163
140
  var n = s.length;
164
141
  for (var a = 0; a < 4; a++) if (!_isDigitCode(_charCodeAt(s, a))) return null;
@@ -177,15 +154,7 @@ function _parseYmd(s) {
177
154
  return [year, month, day];
178
155
  }
179
156
 
180
- // ---------------------------------------------------------------------------
181
- // certdata.txt lexer -- line-oriented, C.LIMITS-bounded, fail-closed
182
- // ---------------------------------------------------------------------------
183
157
 
184
- // Decode a MULTILINE_OCTAL body: whitespace-separated runs of `\ooo` escapes
185
- // (exactly a backslash + three octal digits, \000..\377), terminated by a line
186
- // that is exactly `END`. Every fault is refused before the byte lands: an
187
- // escape past \377, a short or non-octal escape, a stray token, a blank line
188
- // or EOF before END, or a blob past TRUST_MAX_OCTAL_BYTES.
189
158
  function _readOctalBody(lines, idx) {
190
159
  var out = [];
191
160
  var cap = LIMITS.TRUST_MAX_OCTAL_BYTES;
@@ -197,14 +166,14 @@ function _readOctalBody(lines, idx) {
197
166
  for (var c = 0; c < chunks.length; c++) {
198
167
  var chunk = chunks[c];
199
168
  for (var k = 0; k < chunk.length; k += 4) {
200
- if (chunk.charCodeAt(k) !== 92 /* backslash */) {
169
+ if (chunk.charCodeAt(k) !== 92 ) {
201
170
  throw E("trust/bad-octal", "MULTILINE_OCTAL expects \\ooo escapes, got " + JSON.stringify(chunk.slice(k, k + 4)));
202
171
  }
203
172
  var d = chunk.slice(k + 1, k + 4);
204
173
  if (!_isOctal3(d)) {
205
174
  throw E("trust/bad-octal", "a MULTILINE_OCTAL escape is a backslash + exactly three octal digits, got " + JSON.stringify("\\" + d));
206
175
  }
207
- if (d.charCodeAt(0) > 51 /* '3' */) {
176
+ if (d.charCodeAt(0) > 51 ) {
208
177
  throw E("trust/bad-octal", "octal escape \\" + d + " exceeds \\377 (one byte)");
209
178
  }
210
179
  if (out.length >= cap) throw E("trust/bad-block", "MULTILINE_OCTAL blob exceeds LIMITS.TRUST_MAX_OCTAL_BYTES");
@@ -215,10 +184,6 @@ function _readOctalBody(lines, idx) {
215
184
  throw E("trust/bad-octal", "MULTILINE_OCTAL body reached end of input before END");
216
185
  }
217
186
 
218
- // Lex the object stream: a `#` line is a comment, the preamble ends at
219
- // BEGINDATA, blank lines delimit object blocks, each block is typed
220
- // `CKA_* <type> <value>` attribute lines. Returns [{ attrs }] where attrs maps
221
- // the attribute name to { type, value | bytes }.
222
187
  function _lexObjects(text) {
223
188
  var lines = _splitLines(text);
224
189
  var n = lines.length;
@@ -243,7 +208,6 @@ function _lexObjects(text) {
243
208
  var rawVal = m[2] !== undefined ? m[2].trim() : undefined;
244
209
  if (!type) throw E("trust/bad-block", "attribute line is missing its type token: " + name);
245
210
  if (!cur) {
246
- // Refuse the block bomb before lexing another block's payloads.
247
211
  if (objects.length >= LIMITS.TRUST_MAX_OBJECTS) throw E("trust/bad-block", "certdata object count exceeds LIMITS.TRUST_MAX_OBJECTS");
248
212
  cur = { attrs: Object.create(null) };
249
213
  }
@@ -272,8 +236,6 @@ function _lexObjects(text) {
272
236
  if (rawVal === undefined || _containsWs(rawVal)) throw E("trust/bad-block", name + " " + type + " must carry a single token value");
273
237
  entry = { type: type, value: rawVal };
274
238
  } else {
275
- // An unknown value type cannot be lexed safely (its value grammar is
276
- // unknown), so fail closed instead of guessing past it.
277
239
  throw E("trust/bad-block", "unrecognized attribute type " + JSON.stringify(type) + " on " + name);
278
240
  }
279
241
  cur.attrs[name] = entry;
@@ -283,9 +245,6 @@ function _lexObjects(text) {
283
245
  return objects;
284
246
  }
285
247
 
286
- // ---------------------------------------------------------------------------
287
- // certdata semantics -- pairing, distrust-after, purpose bits
288
- // ---------------------------------------------------------------------------
289
248
 
290
249
  function _requireOctal(obj, name, what) {
291
250
  var a = obj.attrs[name];
@@ -295,11 +254,6 @@ function _requireOctal(obj, name, what) {
295
254
  return a.bytes;
296
255
  }
297
256
 
298
- // The NSS distrust-after payload is the bare ASCII of a YYMMDDHHMMSSZ UTCTime
299
- // (13 bytes) or a YYYYMMDDHHMMSSZ GeneralizedTime (15 bytes), with no DER tag or
300
- // length. Synthesize the minimal TLV and route it through the strict asn1
301
- // time reader so the Z-terminator / mandatory-seconds / component-rollover /
302
- // RFC 5280 sec. 4.1.2.5.1 2050-pivot rules are enforced once, never re-derived.
303
257
  function _strictTime(payload, code, label) {
304
258
  var tlv;
305
259
  if (payload.length === 13) tlv = Buffer.concat([Buffer.from([0x17, 0x0d]), payload]);
@@ -309,8 +263,6 @@ function _strictTime(payload, code, label) {
309
263
  catch (e) { throw E(code, label + " does not decode as a strict time", e); }
310
264
  }
311
265
 
312
- // CKA_NSS_*_DISTRUST_AFTER: either `CK_BBOOL CK_FALSE` (no distrust-after in
313
- // force) or a MULTILINE_OCTAL bare-ASCII time. Anything else fails closed.
314
266
  function _distrustDate(obj, name) {
315
267
  var a = obj.attrs[name];
316
268
  if (!a) return null;
@@ -331,16 +283,13 @@ function _certEntry(obj) {
331
283
  var issuer = _requireOctal(obj, "CKA_ISSUER", "certificate");
332
284
  var serial = _requireOctal(obj, "CKA_SERIAL_NUMBER", "certificate");
333
285
 
334
- // Fail-closed cross-check (the pairing key vs the parsed DER): a block whose
335
- // own identity octets disagree with its certificate would let trust metadata
336
- // attach to the wrong root -- a trust-elevation bug, not a tolerable skew.
337
286
  if (!issuer.equals(cert.issuer.bytes)) {
338
287
  throw E("trust/pairing-mismatch", "CKA_ISSUER disagrees with the certificate's issuer DER");
339
288
  }
340
289
  var serialContentHex;
341
290
  try {
342
291
  var sn = asn1.decode(serial);
343
- asn1.read.integer(sn); // strict INTEGER (tag + minimal content)
292
+ asn1.read.integer(sn);
344
293
  serialContentHex = sn.content.toString("hex");
345
294
  } catch (e) {
346
295
  throw E("trust/pairing-mismatch", "CKA_SERIAL_NUMBER is not a DER INTEGER", e);
@@ -371,7 +320,7 @@ function _certEntry(obj) {
371
320
  label: label ? label.value : null,
372
321
  mozillaCaPolicy: !!(policy && policy.value === true),
373
322
  distrustAfter: distrustAfter,
374
- purposes: null, // attached by the paired CKO_NSS_TRUST object
323
+ purposes: null,
375
324
  };
376
325
  }
377
326
 
@@ -381,50 +330,28 @@ function _trustEntry(obj) {
381
330
  var purposes = { serverAuth: false, emailProtection: false, codeSigning: false };
382
331
  PURPOSE_ATTRS.forEach(function (pair) {
383
332
  var a = obj.attrs[pair[0]];
384
- if (!a) return; // an absent bit is untrusted (fail-closed)
333
+ if (!a) return;
385
334
  if (a.type !== "CK_TRUST") throw E("trust/bad-block", pair[0] + " must be CK_TRUST");
386
335
  purposes[pair[1]] = a.value === CKT_DELEGATOR;
387
336
  });
388
337
  return { key: issuer.toString("hex") + "/" + serial.toString("hex"), purposes: purposes };
389
338
  }
390
339
 
391
- // ---------------------------------------------------------------------------
392
- // Anchor assembly + dedup (shared by both sources)
393
- // ---------------------------------------------------------------------------
394
340
 
395
341
  function _mkAnchor(cert, meta) {
396
342
  var spki = cert.subjectPublicKeyInfo;
397
343
  var entry = {
398
- name: cert.subject, // the object with .rdns (name chaining)
399
- publicKey: spki.bytes, // the full SPKI SEQUENCE TLV
400
- algorithm: spki.algorithm.oid, // the SPKI public-key algorithm OID
401
- parameters: spki.algorithm.parameters, // e.g. an EC namedCurve (Buffer|null)
344
+ name: cert.subject,
345
+ publicKey: spki.bytes,
346
+ algorithm: spki.algorithm.oid,
347
+ parameters: spki.algorithm.parameters,
402
348
  subjectDer: cert.subject.bytes,
403
349
  distrustAfter: meta.distrustAfter,
404
350
  purposes: meta.purposes,
405
351
  label: meta.label,
406
352
  mozillaCaPolicy: meta.mozillaCaPolicy,
407
353
  };
408
- // A trust anchor is the pair (name, key) per RFC 5280 sec. 6.1.1, so those two fields are one
409
- // fact about one certificate, and they were derived here from one. `anchor()` re-derives them
410
- // instead of reading them back, so an entry rebuilt with a substituted key cannot carry the
411
- // store's NAME and its per-purpose trust metadata over to a key the store never vouched for.
412
- // Recorded off the object, so rebuilding the entry loses the record along with the binding.
413
- // Copied, not aliased. The record and the entry would otherwise hold the same Buffer and the same
414
- // name object, so overwriting the entry's publicKey in place (otherSpki.copy(entry.publicKey)
415
- // for an equal-length key) would overwrite the record with it, and re-deriving would hand back
416
- // exactly the substituted key. A record that changes with the thing it is meant to pin is not one.
417
- //
418
- // The store's METADATA is recorded with them, and for the stronger reason: `purposes` IS the
419
- // authorization -- it is what the purpose gate reads to decide whether this root may vouch for
420
- // TLS -- and `distrustAfter` is the date that authorization ends. Pinning the key while reading
421
- // the authorization off the mutable entry pins the less important half: `entry.purposes.serverAuth
422
- // = true` would then produce a server-auth anchor from a root the store marked for e-mail only,
423
- // and deleting a distrust date would outlast the store's own policy. Everything the anchor asserts
424
- // now comes from what the store read.
425
354
  _DERIVED_FROM.set(entry, {
426
- // The RDN entries are copied too, not just the arrays holding them: a shallow slice shares
427
- // every attribute object, so editing one in place would still reach the record.
428
355
  name: _copyName(cert.subject),
429
356
  publicKey: Buffer.from(spki.bytes),
430
357
  algorithm: spki.algorithm.oid,
@@ -436,10 +363,6 @@ function _mkAnchor(cert, meta) {
436
363
  }
437
364
  var _DERIVED_FROM = new WeakMap();
438
365
 
439
- // The per-purpose distrust dates, with fresh Date objects. A Date is mutable, so handing back the
440
- // entry's own would let a consumer move the date this anchor is judged against.
441
- // The three trust bits, normalized to booleans. Read by the purpose gate and handed back on every
442
- // anchor, so it is built fresh from whichever source is authoritative and never shared.
443
366
  function _copyPurposes(src) {
444
367
  return {
445
368
  serverAuth: !!src && src.serverAuth === true,
@@ -457,16 +380,6 @@ function _copyDistrustAfter(src) {
457
380
  return out;
458
381
  }
459
382
 
460
- // A parsed Name, copied faithfully: every field the parser assigns, at every level, with nothing
461
- // mutable shared. Used on both sides of the record, writing it in and handing it out, so
462
- // neither direction shares an object with the caller.
463
- //
464
- // Faithful, not "the fields the path validator reads", because this Name is handed back on
465
- // pki.trust.anchor and is the same structure pki.schema.x509.parse produces. A copy that keeps the
466
- // subset one consumer needs silently degrades it for every other reader: an attribute would lose
467
- // its registry `name`, so a caller walking anchor.name.rdns would see a different shape depending
468
- // on whether the anchor came from a store entry or straight from the parser. Each attribute value
469
- // is a string and so needs no copy of its own; the DER does.
470
383
  function _copyName(name) {
471
384
  if (!name || !Array.isArray(name.rdns)) return name;
472
385
  var out = Object.assign({}, name);
@@ -501,11 +414,6 @@ function _anchorsAgree(x, y) {
501
414
  _datesEqual(x.distrustAfter, y.distrustAfter);
502
415
  }
503
416
 
504
- // Dedup by (subjectDer, publicKey): one root appearing twice (a duplicated
505
- // block, or certdata + CCADB merged by the operator) collapses to one anchor,
506
- // while a distinct root that merely shares a subject DN has a different key and
507
- // survives. Two entries for one root that disagree on trust metadata are
508
- // ambiguous, so they fail closed instead of silently picking one.
509
417
  function _dedupAnchors(anchors) {
510
418
  var seen = Object.create(null);
511
419
  var out = [];
@@ -520,9 +428,6 @@ function _dedupAnchors(anchors) {
520
428
  return out;
521
429
  }
522
430
 
523
- // ---------------------------------------------------------------------------
524
- // Public surface
525
- // ---------------------------------------------------------------------------
526
431
 
527
432
  /**
528
433
  * @primitive pki.trust.parseCertdata
@@ -586,9 +491,6 @@ function parseCertdata(text) {
586
491
  var e = _certEntry(obj);
587
492
  var prev = byKey[e.key];
588
493
  if (prev) {
589
- // A byte-identical duplicated block collapses; two certificate
590
- // objects claiming one (issuer, serial) with different content are a
591
- // forged identity -- fail closed.
592
494
  if (!prev.der.equals(e.der) || prev.label !== e.label ||
593
495
  prev.mozillaCaPolicy !== e.mozillaCaPolicy ||
594
496
  !_datesEqual(prev.distrustAfter, e.distrustAfter)) {
@@ -601,12 +503,11 @@ function parseCertdata(text) {
601
503
  } else if (cls.value === "CKO_NSS_TRUST") {
602
504
  trusts.push(_trustEntry(obj));
603
505
  }
604
- // Any other class (CKO_NSS_BUILTIN_ROOT_LIST, ...) is not a root: skipped.
605
506
  });
606
507
 
607
508
  trusts.forEach(function (t) {
608
509
  var c = byKey[t.key];
609
- if (!c) return; // a trust object with no certificate object grants nothing
510
+ if (!c) return;
610
511
  if (c.purposes) {
611
512
  if (!_purposesEqual(c.purposes, t.purposes)) {
612
513
  throw E("trust/pairing-mismatch", "two trust objects for one certificate disagree on purpose bits");
@@ -628,9 +529,6 @@ function parseCertdata(text) {
628
529
  };
629
530
  }
630
531
 
631
- // ---------------------------------------------------------------------------
632
- // CCADB CSV -- a small RFC 4180 sub-lexer + header-keyed column mapping
633
- // ---------------------------------------------------------------------------
634
532
 
635
533
  var CSV_REQUIRED = [
636
534
  "Common Name or Certificate Name",
@@ -640,10 +538,6 @@ var CSV_REQUIRED = [
640
538
  "PEM Info",
641
539
  ];
642
540
 
643
- // RFC 4180 field lexer: quoted fields, embedded commas / newlines, `""`
644
- // escapes. Strict: a quote may only open at the start of a field, nothing may
645
- // follow a closing quote but a separator, and an unterminated quote at EOF is
646
- // a framing violation. Row and field ceilings are refused as they are hit.
647
541
  function _csvRows(text) {
648
542
  var rows = [], row = [], field = "";
649
543
  var inQuotes = false, quoted = false, closed = false;
@@ -685,16 +579,6 @@ function _csvRows(text) {
685
579
  return rows;
686
580
  }
687
581
 
688
- // `Trust Bits` is set-valued (`Websites; Email`) and CCADB spells it in two
689
- // vocabularies: the Mozilla-report labels (Websites / Email / Code) and the
690
- // EKU-derived trust-bit names current exports carry (Server Authentication /
691
- // Secure Email / Code Signing). Both map to the same purposes. The KNOWN CCADB
692
- // purposes the anchor model does not track (Client Authentication, Document
693
- // Signing, Time Stamping, OCSP Signing) are tolerated and grant NOTHING -- a
694
- // purpose is only ever granted by a recognized delegator token, so ignoring an
695
- // unmapped one fails closed. An empty cell grants nothing; a token outside the
696
- // known universe is still surfaced (a silently-skipped NEW bit would hide
697
- // vocabulary drift), never guessed.
698
582
  var _TRUST_BIT_TOKENS = {
699
583
  "websites": "serverAuth", "server authentication": "serverAuth",
700
584
  "email": "emailProtection", "secure email": "emailProtection",
@@ -717,11 +601,6 @@ function _trustBits(cell) {
717
601
  return purposes;
718
602
  }
719
603
 
720
- // The CCADB distrust columns are DATE-only. Expand Y-M-D to the end-of-day
721
- // ...T23:59:59Z instant -- the same instant NSS encodes as `...235959Z` -- by
722
- // synthesizing a GeneralizedTime and routing through the strict reader (which
723
- // also rejects a rolled-over calendar date). Dotted, dashed, and slashed
724
- // separators are accepted (the export format has drifted between them).
725
604
  function _csvDate(cell, label) {
726
605
  var s = String(cell).trim();
727
606
  if (s === "") return null;
@@ -729,9 +608,6 @@ function _csvDate(cell, label) {
729
608
  if (!m) throw E("trust/bad-csv", label + " must be a Y-M-D date, got " + JSON.stringify(s));
730
609
  var pad = function (x) { return x.length === 1 ? "0" + x : x; };
731
610
  var payload = Buffer.from(m[0] + pad(m[1]) + pad(m[2]) + "235959Z", "latin1");
732
- // _strictTime tags every rejection (a rolled-over month/day among them) with the code passed here,
733
- // so the re-throw carries it unchanged; the wrap normalizes any other failure to the same typed code
734
- // so a bad row is always trust/bad-csv, never an untyped exception escaping parseCcadbCsv.
735
611
  try { return _strictTime(payload, "trust/bad-csv", label); }
736
612
  catch (e) {
737
613
  if (e && e.code === "trust/bad-csv") throw e;
@@ -739,9 +615,6 @@ function _csvDate(cell, label) {
739
615
  }
740
616
  }
741
617
 
742
- // A CCADB `PEM Info` cell: the PEM certificate, commonly wrapped in one layer
743
- // of single quotes (a spreadsheet-import guard) -- strip that layer, then
744
- // parse strictly.
745
618
  function _pemCell(cell) {
746
619
  var s = String(cell).trim();
747
620
  if (s.charAt(0) === "'") s = s.slice(1);
@@ -796,14 +669,10 @@ function parseCcadbCsv(text) {
796
669
  var rows = _csvRows(text);
797
670
  if (rows.length === 0) throw E("trust/bad-csv", "CSV input carries no header row");
798
671
 
799
- // trim() also strips a leading U+FEFF, so a BOM-prefixed export resolves
800
- // its first header cell normally.
801
672
  var header = rows[0].map(function (h) { return h.trim(); });
802
673
  var col = Object.create(null);
803
674
  header.forEach(function (h, idx) {
804
675
  if (col[h] !== undefined) {
805
- // Two columns with one REQUIRED name make the mapping ambiguous (which
806
- // Trust Bits column binds?) -- fail closed rather than silently pick.
807
676
  if (CSV_REQUIRED.indexOf(h) !== -1) throw E("trust/bad-csv", "duplicate required column " + JSON.stringify(h));
808
677
  return;
809
678
  }
@@ -829,7 +698,7 @@ function parseCcadbCsv(text) {
829
698
  distrustAfter: distrustAfter,
830
699
  purposes: _trustBits(row[col["Trust Bits"]]),
831
700
  label: label === "" ? null : label,
832
- mozillaCaPolicy: false, // certdata-only metadata
701
+ mozillaCaPolicy: false,
833
702
  }));
834
703
  }
835
704
  return { anchors: _dedupAnchors(anchors) };
@@ -877,9 +746,6 @@ function parseCcadbCsv(text) {
877
746
  * await pki.path.validate([pki.schema.x509.parse(der)],
878
747
  * { time: new Date("2026-06-01T00:00:00Z"), trustAnchors: anchor, checkPurpose: "serverAuth" });
879
748
  */
880
- // `purpose` is the only option, and it gates the authorization itself, so a near-miss spelling must not
881
- // pass silently: a caller who wrote the plural `purposes` (or carried a sibling's word) would otherwise get
882
- // the anchor with NO purpose restriction rather than an error naming the option they meant.
883
749
  var _ANCHOR_OPTS = { purpose: 1 };
884
750
  function anchor(entry, opts) {
885
751
  if (!entry || typeof entry !== "object") {
@@ -887,18 +753,8 @@ function anchor(entry, opts) {
887
753
  }
888
754
  opts = guard.identifier.optionsObject(opts, E, "trust/bad-input", "pki.trust.anchor options");
889
755
  guard.identifier.assertKnownKeys(opts, _ANCHOR_OPTS, E, "trust/bad-input", "unknown pki.trust.anchor option ");
890
- // Where this entry came from a store, everything the anchor asserts is read from the record the
891
- // store minted, not from the entry -- the entry is a plain object the caller holds and can write
892
- // to. `purposes` is the authorization itself, so an entry whose serverAuth bit was flipped after
893
- // parsing must not open the gate, and a distrust date deleted from it must not outlive the
894
- // store's policy.
895
756
  var derived = _DERIVED_FROM.get(entry);
896
757
  var meta = derived || entry;
897
- // The tuple-shape check runs on the ENTRY only when there is no record, because only then are the
898
- // entry's own fields what the anchor is built from. With a record, an entry a caller has since
899
- // emptied still anchors to exactly what the store read: the answer does not depend on the object
900
- // the caller is holding, in either direction. Without one, the caller is asserting a bare
901
- // (name, key) anchor of their own and its shape is what there is to check.
902
758
  if (!derived && (!Buffer.isBuffer(entry.publicKey) || typeof entry.algorithm !== "string" ||
903
759
  !entry.name || !Array.isArray(entry.name.rdns))) {
904
760
  throw E("trust/bad-input", "anchor expects a trust-store entry ({ name, publicKey, algorithm, ... })");
@@ -911,37 +767,15 @@ function anchor(entry, opts) {
911
767
  throw E("trust/purpose-not-trusted", "this root is not a trusted delegator for " + opts.purpose);
912
768
  }
913
769
  }
914
- // Where this entry came from a store, the (name, key) pair is re-derived from the certificate it
915
- // was read out of rather than read back off the entry. Both halves are one fact about one
916
- // certificate, so an entry rebuilt with a substituted publicKey would otherwise carry the store's
917
- // name and its per-purpose trust metadata onto a key the store never vouched for. An entry a
918
- // caller built themselves has no such record and is their own assertion, which is what a bare
919
- // trust-anchor tuple is.
920
- //
921
- // An entry carrying the store's METADATA is claiming to be a store entry. The metadata is the
922
- // root program's statement -- these purposes, until this date -- and it is a statement about a
923
- // KEY. Without the record there is nothing binding it to the key the entry now names, so a copy
924
- // with a substituted publicKey would carry the program's word onto a key it never saw. A caller
925
- // asserting a bare (name, key) anchor of their own carries no metadata and is unaffected.
926
770
  if (!derived && (entry.purposes != null || (entry.distrustAfter && Object.keys(entry.distrustAfter).length))) {
927
771
  throw E("trust/bad-input", "this entry carries a trust store's per-purpose metadata but is not the entry the store produced -- it has been rebuilt, and the metadata is a statement about the key the store read, not about whichever key the copy now names. Pass pki.trust.parseCertdata / parseCcadbCsv output unmodified");
928
772
  }
929
- // Copies OUT as well as in. Handing back the record's own Buffer lets a consumer write through
930
- // the returned anchor into the record -- copy an equal-length SPKI over anchor.publicKey and the
931
- // next anchor() call re-derives the substituted key while still carrying the store's purposes and
932
- // distrust dates. Guarding only the write INTO the record leaves the same door open in the other
933
- // direction, so every call hands back fresh values.
934
773
  return {
935
774
  name: _copyName(meta.name),
936
775
  publicKey: Buffer.isBuffer(meta.publicKey) ? Buffer.from(meta.publicKey) : meta.publicKey,
937
776
  algorithm: meta.algorithm,
938
777
  parameters: Buffer.isBuffer(meta.parameters) ? Buffer.from(meta.parameters)
939
778
  : (meta.parameters !== undefined ? meta.parameters : null),
940
- // The METADATA comes from the same source as the key, and is copied for the same reason on the
941
- // way out: handing back the record's own object let a consumer write
942
- // `anchor(entry).purposes.serverAuth = true` and have the next call pass a gate the store never
943
- // opened. `distrustAfter` holds Dates, which are mutable in the same way. Everything on this
944
- // object is fresh, so two anchors from one entry share nothing.
945
779
  distrustAfter: _copyDistrustAfter(meta.distrustAfter),
946
780
  purposes: _copyPurposes(meta.purposes),
947
781
  };