@blamejs/pki 0.8.30 → 0.8.48

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 (87) hide show
  1. package/CHANGELOG.md +411 -0
  2. package/README.md +67 -16
  3. package/bin/pki.js +716 -30
  4. package/examples/issue-a-certificate.js +90 -0
  5. package/examples/post-quantum.js +66 -0
  6. package/examples/read-a-certificate.js +56 -0
  7. package/examples/revoke-and-check.js +86 -0
  8. package/examples/sign-and-verify-cms.js +63 -0
  9. package/index.js +18 -5
  10. package/lib/acme.js +223 -74
  11. package/lib/alt-sig.js +441 -0
  12. package/lib/asn1-der.js +48 -25
  13. package/lib/attrcert-sign.js +27 -19
  14. package/lib/cbor-det.js +24 -14
  15. package/lib/cmc-build.js +9 -2
  16. package/lib/cmc-verify.js +14 -3
  17. package/lib/cmp-build.js +17 -5
  18. package/lib/cmp-session.js +24 -11
  19. package/lib/cmp-verify.js +28 -19
  20. package/lib/cms-compress.js +3 -1
  21. package/lib/cms-decrypt.js +102 -7
  22. package/lib/cms-encrypt.js +59 -7
  23. package/lib/cms-sign.js +31 -9
  24. package/lib/cms-verify.js +22 -18
  25. package/lib/composite-kem.js +30 -18
  26. package/lib/composite-sig.js +29 -14
  27. package/lib/constants.js +28 -0
  28. package/lib/crl-sign.js +111 -19
  29. package/lib/crmf-sign.js +167 -15
  30. package/lib/csr-sign.js +117 -6
  31. package/lib/ct.js +939 -48
  32. package/lib/est.js +29 -16
  33. package/lib/framework-error.js +12 -0
  34. package/lib/guard-async.js +1 -1
  35. package/lib/guard-bytes.js +103 -20
  36. package/lib/guard-crypto.js +205 -3
  37. package/lib/guard-encoding.js +6 -1
  38. package/lib/guard-identifier.js +55 -8
  39. package/lib/guard-intrinsic.js +43 -0
  40. package/lib/guard-json.js +122 -9
  41. package/lib/guard-limits.js +12 -1
  42. package/lib/guard-list.js +63 -8
  43. package/lib/guard-name.js +167 -20
  44. package/lib/guard-range.js +43 -5
  45. package/lib/guard-text.js +41 -0
  46. package/lib/guard-verdict.js +39 -8
  47. package/lib/hpke.js +688 -42
  48. package/lib/http-transport.js +156 -4
  49. package/lib/identity-match.js +4 -4
  50. package/lib/inspect.js +448 -2
  51. package/lib/jose.js +93 -25
  52. package/lib/key.js +18 -13
  53. package/lib/lint.js +165 -1
  54. package/lib/merkle.js +233 -27
  55. package/lib/ocsp.js +27 -14
  56. package/lib/oid.js +14 -4
  57. package/lib/path-validate.js +143 -61
  58. package/lib/pkcs12-build.js +7 -2
  59. package/lib/pki-build.js +31 -7
  60. package/lib/possession.js +575 -0
  61. package/lib/related-cert.js +708 -0
  62. package/lib/revocation-fetch.js +6 -6
  63. package/lib/scep.js +15 -5
  64. package/lib/schema-c509.js +5 -2
  65. package/lib/schema-cmc.js +1 -1
  66. package/lib/schema-cmp.js +8 -8
  67. package/lib/schema-cms.js +2 -15
  68. package/lib/schema-csr.js +69 -0
  69. package/lib/schema-engine.js +10 -3
  70. package/lib/schema-pkix.js +267 -4
  71. package/lib/schema-tsp.js +7 -6
  72. package/lib/sign-scheme.js +22 -9
  73. package/lib/sigstore.js +684 -168
  74. package/lib/sleep.js +8 -1
  75. package/lib/smime.js +22 -1
  76. package/lib/tlog.js +1235 -0
  77. package/lib/tls-cert-compress.js +25 -8
  78. package/lib/tsp-sign.js +120 -35
  79. package/lib/tuf.js +1395 -0
  80. package/lib/validator-tpm.js +4 -1
  81. package/lib/verify-seam.js +7 -1
  82. package/lib/webauthn-mds.js +41 -4
  83. package/lib/webauthn.js +43 -25
  84. package/lib/webcrypto.js +48 -46
  85. package/lib/x509-sign.js +225 -19
  86. package/package.json +2 -1
  87. package/sbom.cdx.json +6 -6
package/lib/merkle.js CHANGED
@@ -42,34 +42,59 @@
42
42
  * `verifyConsistency` over sync SHA-256, fail-closed, transport-free.
43
43
  */
44
44
 
45
- var nodeCrypto = require("node:crypto");
46
45
  var constants = require("./constants");
47
46
  var frameworkError = require("./framework-error");
48
47
  var guard = require("./guard-all");
48
+ var intrinsic = require("./guard-intrinsic");
49
+
50
+ /** @internal The operations this module decides with, bound at load. A proof is assembled by
51
+ * concatenation, so a replaced `Array.prototype.concat` chooses which nodes the proof carries and in
52
+ * what order, and a replaced `Buffer.concat` chooses the bytes a node hash covers. Taking the captures
53
+ * also puts this file in the live-read gate's scope, which is how it stays converted. */
54
+ var _concat = intrinsic.concat;
55
+ var _bufferConcat = intrinsic.bufferConcat;
56
+ var _bufferFrom = intrinsic.bufferFrom;
57
+ var _bufferAlloc = intrinsic.bufferAlloc;
58
+ var _isArray = intrinsic.isArray;
59
+ var _assign = intrinsic.assign;
60
+ var _create = intrinsic.create;
61
+ var _BigInt = intrinsic.BigInt;
49
62
 
50
63
  var MerkleError = frameworkError.MerkleError;
51
64
 
52
65
  function _merkleErr(c, m) { return new MerkleError(c, m); }
53
66
 
54
67
  var SHA256_BYTES = 32;
55
- var LEAF_PREFIX = Buffer.from([0x00]);
56
- var NODE_PREFIX = Buffer.from([0x01]);
57
- var EMPTY = Buffer.alloc(0);
68
+ var LEAF_PREFIX = _bufferFrom([0x00]);
69
+ var NODE_PREFIX = _bufferFrom([0x01]);
70
+ var EMPTY = _bufferAlloc(0);
58
71
 
59
72
  function _sha256(buf) {
60
- return nodeCrypto.createHash("sha256").update(buf).digest();
73
+ return guard.crypto.digest("sha256", buf);
61
74
  }
62
75
 
76
+ /** @internal A COPY, not a view. Reading each slot once is not enough when what is read shares the
77
+ * caller's store: a getter handing out one scratch buffer per leaf satisfies the read-once rule and still
78
+ * leaves every collected leaf pointing at the same bytes, so collecting the second overwrote the first
79
+ * and the root of [A, B] came out as the root of [B, B] with nothing reporting it. Each value is 32 bytes
80
+ * by the check above, so copying costs nothing worth measuring, and every byte this module folds, hashes
81
+ * or compares is then its own. */
63
82
  function _toBuffer(v, field) {
64
- return guard.bytes.view(v, MerkleError, "merkle/bad-input", field);
83
+ return guard.bytes.snapshot(v, MerkleError, "merkle/bad-input", field);
65
84
  }
66
85
 
86
+ /** @internal The width is read on a VIEW, before the copy. A node is exactly 32 bytes, and the check
87
+ * enforcing that ran on the copy, so a value handed where a hash belongs was duplicated in full and
88
+ * only then refused: measured, `root([Buffer.alloc(64 * 1024 * 1024)])` allocated another 64 MiB to
89
+ * reject one leaf. Viewing allocates nothing, and the size comes off the view rather than off the
90
+ * caller's own `length`, which can be shadowed independently of the bytes. */
67
91
  function _node32(v, field) {
68
- var buf = _toBuffer(v, field);
69
- if (buf.length !== SHA256_BYTES) {
70
- throw new MerkleError("merkle/bad-hash-length", field + " must be exactly 32 bytes, got " + buf.length);
92
+ var viewed = guard.bytes.source(v, MerkleError, "merkle/bad-input", field);
93
+ var size = guard.bytes.lengthOf(viewed);
94
+ if (size !== SHA256_BYTES) {
95
+ throw new MerkleError("merkle/bad-hash-length", field + " must be exactly 32 bytes, got " + size);
71
96
  }
72
- return buf;
97
+ return guard.bytes.snapshot(viewed, MerkleError, "merkle/bad-input", field);
73
98
  }
74
99
 
75
100
  function _coerceCoord(v, field) {
@@ -77,19 +102,86 @@ function _coerceCoord(v, field) {
77
102
  }
78
103
 
79
104
  function _proofNodes(proof) {
80
- if (!Array.isArray(proof)) throw new MerkleError("merkle/bad-proof", "proof must be an array");
81
- if (proof.length > constants.LIMITS.MERKLE_MAX_PROOF_NODES) {
82
- throw new MerkleError("merkle/proof-too-large", "proof has " + proof.length + " nodes, exceeds " + constants.LIMITS.MERKLE_MAX_PROOF_NODES);
83
- }
84
- var out = [];
85
- for (var i = 0; i < proof.length; i++) out.push(_node32(proof[i], "proof[" + i + "]"));
86
- return out;
105
+ if (!_isArray(proof)) throw new MerkleError("merkle/bad-proof", "proof must be an array");
106
+ /** @internal Through the same guard as the leaf array, for the same reason: the node cap reads the
107
+ * length and a hand-rolled walk reads it again. The verifiers were not exposed the way the producers
108
+ * were, because a proof the walk collected short folds to a root that does not match the one supplied,
109
+ * so the comparison refuses it. That is fail-closed by consequence rather than by rule, and the rule is
110
+ * cheaper to keep here than to re-derive at each verifier. */
111
+ return guard.list.copyMap(proof, function (slot, i) { return _node32(slot, "proof[" + i + "]"); },
112
+ function (n) {
113
+ if (n > constants.LIMITS.MERKLE_MAX_PROOF_NODES) {
114
+ throw new MerkleError("merkle/proof-too-large",
115
+ "proof has " + n + " nodes, exceeds " + constants.LIMITS.MERKLE_MAX_PROOF_NODES);
116
+ }
117
+ });
87
118
  }
88
119
 
89
120
  function _ctEq(a, b) {
90
121
  return guard.crypto.constantTimeEqual(a, b);
91
122
  }
92
123
 
124
+ var NO_CONSISTENCY_CLAIM =
125
+ "an empty older tree (oldSize 0) makes no consistency claim about a non-empty newer tree: " +
126
+ "RFC 6962 sec. 2.1.2 defines the proof for 0 < oldSize < newSize, and nothing here binds " +
127
+ "newRoot. Verify an inclusion proof against the new tree, or start from a signed tree head " +
128
+ "you already trust";
129
+
130
+ /** @internal Each slot is read once, into the buffer the length check and every later fold both
131
+ * use, so an array whose accessor answers differently on a second read cannot put one value past
132
+ * the check and a different one into the tree. The COUNT is read once as well, by
133
+ * `guard.list.copyMap`, which is the point of going through it: a hand-rolled
134
+ * `for (i = 0; i < v.length; i++)` re-reads the length after every slot access, so a getter on one slot
135
+ * that shortened the array left the cap approving four leaves and the walk collecting one, and the root
136
+ * committed to a tree the caller never supplied. A slot past a length that shrank reads as undefined and
137
+ * `_node32` refuses it, so the array is refused rather than folded short. */
138
+ function _leafArray(v, field) {
139
+ if (!_isArray(v)) {
140
+ throw new MerkleError("merkle/bad-input", field + " must be an array of 32-byte leaf hashes");
141
+ }
142
+ return guard.list.copyMap(v, function (slot, i) { return _node32(slot, field + "[" + i + "]"); },
143
+ function (n) {
144
+ if (n > constants.LIMITS.MERKLE_MAX_LEAVES) {
145
+ throw new MerkleError("merkle/too-many-leaves",
146
+ field + " holds " + n + " leaves, exceeds " + constants.LIMITS.MERKLE_MAX_LEAVES);
147
+ }
148
+ });
149
+ }
150
+
151
+ /** @internal RFC 6962 sec. 2.1's "largest power of two smaller than n", for n >= 2. Doubled from
152
+ * 1 rather than derived through a logarithm: the leaf count is bounded well inside the exact
153
+ * integer range, and a float log would round at the boundary sizes this splits on. */
154
+ function _splitPoint(n) {
155
+ var k = 1;
156
+ while (k * 2 < n) k *= 2;
157
+ return k;
158
+ }
159
+
160
+ /** @internal MTH(D[lo:hi]) for hi > lo. The empty tree is answered by the caller. */
161
+ function _mth(leaves, lo, hi) {
162
+ if (hi - lo === 1) return leaves[lo];
163
+ var k = _splitPoint(hi - lo);
164
+ return nodeHash(_mth(leaves, lo, lo + k), _mth(leaves, lo + k, hi));
165
+ }
166
+
167
+ /** @internal PATH(m, D[lo:hi]) of RFC 6962 sec. 2.1.1, with m relative to lo. */
168
+ function _path(leaves, lo, hi, m) {
169
+ if (hi - lo === 1) return [];
170
+ var k = _splitPoint(hi - lo);
171
+ if (m < k) return _concat(_path(leaves, lo, lo + k, m), [_mth(leaves, lo + k, hi)]);
172
+ return _concat(_path(leaves, lo + k, hi, m - k), [_mth(leaves, lo, lo + k)]);
173
+ }
174
+
175
+ /** @internal SUBPROOF(m, D[lo:hi], b) of RFC 6962 sec. 2.1.2, with m relative to lo. The split
176
+ * compares m <= k here where PATH compares m < k, which is what puts the old tree's own root in
177
+ * the proof exactly when the caller did not already hold it. */
178
+ function _subproof(leaves, lo, hi, m, b) {
179
+ if (m === hi - lo) return b ? [] : [_mth(leaves, lo, hi)];
180
+ var k = _splitPoint(hi - lo);
181
+ if (m <= k) return _concat(_subproof(leaves, lo, lo + k, m, b), [_mth(leaves, lo + k, hi)]);
182
+ return _concat(_subproof(leaves, lo + k, hi, m - k, false), [_mth(leaves, lo, lo + k)]);
183
+ }
184
+
93
185
  /**
94
186
  * @primitive pki.merkle.leafHash
95
187
  * @signature pki.merkle.leafHash(entry) -> Buffer
@@ -107,7 +199,7 @@ function _ctEq(a, b) {
107
199
  */
108
200
  function leafHash(entry) {
109
201
  var e = _toBuffer(entry, "entry");
110
- return _sha256(Buffer.concat([LEAF_PREFIX, e]));
202
+ return _sha256(_bufferConcat([LEAF_PREFIX, e]));
111
203
  }
112
204
 
113
205
  /**
@@ -131,7 +223,7 @@ function leafHash(entry) {
131
223
  function nodeHash(left, right) {
132
224
  var l = _node32(left, "left");
133
225
  var r = _node32(right, "right");
134
- return _sha256(Buffer.concat([NODE_PREFIX, l, r]));
226
+ return _sha256(_bufferConcat([NODE_PREFIX, l, r]));
135
227
  }
136
228
 
137
229
  /**
@@ -180,7 +272,7 @@ function emptyRootHash() {
180
272
  * var lh = pki.merkle.leafHash(Buffer.from([0]));
181
273
  * pki.merkle.verifyInclusion({ leafIndex: 0, treeSize: 1, leafHash: lh, proof: [], rootHash: lh }); // -> true
182
274
  */
183
- var _INCLUSION_KEYS = Object.assign(Object.create(null), { leafIndex: 1, treeSize: 1, leafHash: 1, rootHash: 1, proof: 1 });
275
+ var _INCLUSION_KEYS = _assign(_create(null), { leafIndex: 1, treeSize: 1, leafHash: 1, rootHash: 1, proof: 1 });
184
276
  function verifyInclusion(opts) {
185
277
  opts = opts || {};
186
278
  guard.identifier.assertKnownKeys(opts, _INCLUSION_KEYS, function (c, m) { return new MerkleError(c, m); },
@@ -252,7 +344,7 @@ function verifyInclusion(opts) {
252
344
  * var r = pki.merkle.leafHash(Buffer.from([0]));
253
345
  * pki.merkle.verifyConsistency({ oldSize: 1, newSize: 1, oldRoot: r, newRoot: r, proof: [] }); // -> true
254
346
  */
255
- var _CONSISTENCY_KEYS = Object.assign(Object.create(null), { oldSize: 1, newSize: 1, oldRoot: 1, newRoot: 1, proof: 1 });
347
+ var _CONSISTENCY_KEYS = _assign(_create(null), { oldSize: 1, newSize: 1, oldRoot: 1, newRoot: 1, proof: 1 });
256
348
  function verifyConsistency(opts) {
257
349
  opts = opts || {};
258
350
  guard.identifier.assertKnownKeys(opts, _CONSISTENCY_KEYS, function (c, m) { return new MerkleError(c, m); },
@@ -271,11 +363,7 @@ function verifyConsistency(opts) {
271
363
  // allow:constant-time-compare-short-circuited -- oldRoot, newRoot, and er are public Merkle tree roots, not secrets, so short-circuiting the second compare leaks nothing confidential
272
364
  return _ctEq(oldRoot, er) && _ctEq(newRoot, er);
273
365
  }
274
- throw new MerkleError("merkle/no-consistency-claim",
275
- "an empty older tree (oldSize 0) makes no consistency claim about a non-empty newer tree: " +
276
- "RFC 6962 sec. 2.1.2 defines the proof for 0 < oldSize < newSize, and nothing here binds " +
277
- "newRoot. Verify an inclusion proof against the new tree, or start from a signed tree head " +
278
- "you already trust");
366
+ throw new MerkleError("merkle/no-consistency-claim", NO_CONSISTENCY_CLAIM);
279
367
  }
280
368
  if (oldSize === newSize) {
281
369
  if (proof.length !== 0) throw new MerkleError("merkle/sizes-equal-nonempty-proof", "equal tree sizes require an empty consistency proof");
@@ -285,7 +373,7 @@ function verifyConsistency(opts) {
285
373
 
286
374
  var path = proof;
287
375
  if ((oldSize & (oldSize - 1n)) === 0n) {
288
- path = [oldRoot].concat(proof);
376
+ path = _concat([oldRoot], proof);
289
377
  }
290
378
  var fn = oldSize - 1n;
291
379
  var sn = newSize - 1n;
@@ -313,10 +401,128 @@ function verifyConsistency(opts) {
313
401
  return okOld && okNew;
314
402
  }
315
403
 
404
+ /**
405
+ * @primitive pki.merkle.root
406
+ * @signature pki.merkle.root(leafHashes) -> Buffer
407
+ * @since 0.8.34
408
+ * @status stable
409
+ * @spec RFC 6962, RFC 9162
410
+ * @related pki.merkle.leafHash, pki.merkle.inclusionProof, pki.merkle.verifyInclusion
411
+ *
412
+ * The RFC 6962 Merkle Tree Hash `MTH(D[n])` over a whole tree, returned as a
413
+ * 32-byte root ready to pass to `verifyInclusion` as `rootHash`. Takes leaf
414
+ * HASHES, the same form `verifyInclusion` takes as `leafHash`; map entries
415
+ * through `pki.merkle.leafHash` first. An empty array is the empty tree and
416
+ * returns `emptyRootHash()`.
417
+ *
418
+ * Throws `merkle/bad-input` if `leafHashes` is not an array or a leaf is not a
419
+ * Buffer / Uint8Array, `merkle/bad-hash-length` if a leaf is not exactly 32
420
+ * bytes, and `merkle/too-many-leaves` above `C.LIMITS.MERKLE_MAX_LEAVES`.
421
+ *
422
+ * @example
423
+ * var leaves = [Buffer.from([0]), Buffer.from([1])].map(pki.merkle.leafHash);
424
+ * pki.merkle.root(leaves); // -> <Buffer 32-byte tree head>
425
+ */
426
+ function root(leafHashes) {
427
+ var leaves = _leafArray(leafHashes, "leafHashes");
428
+ if (leaves.length === 0) return emptyRootHash();
429
+ return _mth(leaves, 0, leaves.length);
430
+ }
431
+
432
+ /**
433
+ * @primitive pki.merkle.inclusionProof
434
+ * @signature pki.merkle.inclusionProof(opts) -> Buffer[]
435
+ * @since 0.8.34
436
+ * @status stable
437
+ * @spec RFC 6962, RFC 9162
438
+ * @related pki.merkle.verifyInclusion, pki.merkle.root
439
+ *
440
+ * Produce the RFC 6962 sec. 2.1.1 audit path `PATH(leafIndex, D[n])` for one
441
+ * leaf of a tree held in memory. The returned array is the `proof` that
442
+ * `verifyInclusion` folds against `root(leafHashes)`.
443
+ *
444
+ * The tree size is the length of `leafHashes` and is not a separate option, so
445
+ * a size and a leaf array cannot disagree. Throws `merkle/empty-tree` on an
446
+ * empty tree, `merkle/index-out-of-range` for a `leafIndex` at or above the leaf
447
+ * count, and the `leafHashes` errors `root` throws.
448
+ *
449
+ * @opts
450
+ * leafHashes: Buffer[], // the whole tree, each leaf a 32-byte hash
451
+ * leafIndex: number | bigint, // 0-based position of the leaf to prove
452
+ *
453
+ * @example
454
+ * var leaves = [Buffer.from([0]), Buffer.from([1])].map(pki.merkle.leafHash);
455
+ * pki.merkle.inclusionProof({ leafHashes: leaves, leafIndex: 0 }); // -> [<Buffer ...>]
456
+ */
457
+ var _INCLUSION_PROOF_KEYS = _assign(_create(null), { leafHashes: 1, leafIndex: 1 });
458
+ function inclusionProof(opts) {
459
+ opts = opts || {};
460
+ guard.identifier.assertKnownKeys(opts, _INCLUSION_PROOF_KEYS, _merkleErr,
461
+ "merkle/bad-input", "unknown inclusionProof option ");
462
+ var leaves = _leafArray(opts.leafHashes, "leafHashes");
463
+ var n = leaves.length;
464
+ if (n === 0) throw new MerkleError("merkle/empty-tree", "an empty tree has no leaves to include");
465
+ var leafIndex = _coerceCoord(opts.leafIndex, "leafIndex");
466
+ var m = guard.range.int(leafIndex, 0n, _BigInt(n - 1), _merkleErr,
467
+ "merkle/index-out-of-range", "leafIndex");
468
+ return _path(leaves, 0, n, m);
469
+ }
470
+
471
+ /**
472
+ * @primitive pki.merkle.consistencyProof
473
+ * @signature pki.merkle.consistencyProof(opts) -> Buffer[]
474
+ * @since 0.8.34
475
+ * @status stable
476
+ * @spec RFC 6962, RFC 9162
477
+ * @related pki.merkle.verifyConsistency, pki.merkle.root
478
+ *
479
+ * Produce the RFC 6962 sec. 2.1.2 consistency proof `PROOF(oldSize, D[n])`
480
+ * between an older tree of `oldSize` leaves and the tree held in `leafHashes`.
481
+ * The returned array is the `proof` that `verifyConsistency` checks against
482
+ * `root(leafHashes.slice(0, oldSize))` and `root(leafHashes)`.
483
+ *
484
+ * The newer size is the length of `leafHashes` and is not a separate option.
485
+ * Equal sizes produce the empty proof, which is what the verifier requires
486
+ * there. Throws `merkle/old-size-exceeds-new` when `oldSize` is past the leaf
487
+ * count and `merkle/no-consistency-claim` for an `oldSize` of 0 against a
488
+ * non-empty tree, matching `verifyConsistency`: RFC 6962 sec. 2.1.2 defines the
489
+ * proof for `0 < oldSize < newSize`, so there is no proof to produce.
490
+ *
491
+ * @opts
492
+ * leafHashes: Buffer[], // the newer tree, each leaf a 32-byte hash
493
+ * oldSize: number | bigint, // leaf count of the older tree
494
+ *
495
+ * @example
496
+ * var leaves = [Buffer.from([0]), Buffer.from([1])].map(pki.merkle.leafHash);
497
+ * pki.merkle.consistencyProof({ leafHashes: leaves, oldSize: 1 }); // -> [<Buffer ...>]
498
+ */
499
+ var _CONSISTENCY_PROOF_KEYS = _assign(_create(null), { leafHashes: 1, oldSize: 1 });
500
+ function consistencyProof(opts) {
501
+ opts = opts || {};
502
+ guard.identifier.assertKnownKeys(opts, _CONSISTENCY_PROOF_KEYS, _merkleErr,
503
+ "merkle/bad-input", "unknown consistencyProof option ");
504
+ var leaves = _leafArray(opts.leafHashes, "leafHashes");
505
+ var n = leaves.length;
506
+ var oldSize = _coerceCoord(opts.oldSize, "oldSize");
507
+ if (oldSize > _BigInt(n)) {
508
+ throw new MerkleError("merkle/old-size-exceeds-new", "oldSize " + oldSize + " exceeds newSize " + n);
509
+ }
510
+ if (oldSize === 0n) {
511
+ if (n === 0) return [];
512
+ throw new MerkleError("merkle/no-consistency-claim", NO_CONSISTENCY_CLAIM);
513
+ }
514
+ var m = guard.range.int(oldSize, 1n, _BigInt(n), _merkleErr, "merkle/bad-input", "oldSize");
515
+ if (m === n) return [];
516
+ return _subproof(leaves, 0, n, m, true);
517
+ }
518
+
316
519
  module.exports = {
317
520
  leafHash: leafHash,
318
521
  nodeHash: nodeHash,
319
522
  emptyRootHash: emptyRootHash,
523
+ root: root,
320
524
  verifyInclusion: verifyInclusion,
321
525
  verifyConsistency: verifyConsistency,
526
+ inclusionProof: inclusionProof,
527
+ consistencyProof: consistencyProof,
322
528
  };
package/lib/ocsp.js CHANGED
@@ -26,6 +26,19 @@
26
26
  var nodeCrypto = require("crypto");
27
27
  var asn1 = require("./asn1-der");
28
28
  var pkiBuild = require("./pki-build");
29
+ var _ocspIntrinsic = require("./guard-intrinsic");
30
+ /** @internal The promise a signature self-check is awaited through, built from the constructor captured
31
+ * at load. `Promise.resolve` builds from its RECEIVER, so reading the global binding at the call hands
32
+ * the construction to whatever that binding holds, and one settling without awaiting its argument runs
33
+ * the continuation before the check has finished. */
34
+ var _Promise = _ocspIntrinsic.Promise;
35
+ var _promiseResolve = _ocspIntrinsic.uncurry(_ocspIntrinsic.promiseResolve);
36
+ /** @internal A refusal returned as a REJECTED promise is a verdict too: read from the global binding, a
37
+ * replacement whose `reject` resolves instead turns each `ocsp/bad-input` below into a pass. */
38
+ var _promiseReject = _ocspIntrinsic.uncurry(_ocspIntrinsic.promiseReject);
39
+ /** @internal The CertID hashes bind a response to the certificate it answers for, and they are awaited
40
+ * as a pair, so a replaced `all` decides what that pair contains. */
41
+ var _promiseAll = _ocspIntrinsic.uncurry(_ocspIntrinsic.promiseAll);
29
42
  var oid = require("./oid");
30
43
  var x509 = require("./schema-x509");
31
44
  var ocspSchema = require("./schema-ocsp");
@@ -100,7 +113,7 @@ var REQUESTER_NONCE_FLOOR = 32, RESPONDER_NONCE_FLOOR = 1;
100
113
  function _buildCertID(cert, issuer, hashName) {
101
114
  var wc = HASH_WC[guard.text.keyOf(hashName)];
102
115
  if (!wc) throw _err("ocsp/bad-input", "unsupported CertID hashAlgorithm " + guard.text.showValue(hashName) + " (sha1 / sha256 / sha384 / sha512)");
103
- return Promise.all([
116
+ return _promiseAll(_Promise, [
104
117
  _digest(wc, issuer.subject.bytes),
105
118
  _digest(wc, _keyValue(issuer.subjectPublicKeyInfo.bytes)),
106
119
  ]).then(function (hashes) {
@@ -189,7 +202,7 @@ function _buildRequest(query, opts) {
189
202
  requestorNameDer = signerCert.subject.bytes;
190
203
  }
191
204
 
192
- return Promise.all(queries.map(function (q) {
205
+ return _promiseAll(_Promise, queries.map(function (q) {
193
206
  if (!q || typeof q !== "object") throw _err("ocsp/bad-input", "each query must be { cert, issuer }");
194
207
  guard.identifier.assertKnownKeys(q, _QUERY_KEYS, _err, "ocsp/bad-input", "unknown query field (a query is { cert, issuer, singleRequestExtensions? }): ");
195
208
  if (q.cert == null || q.issuer == null) throw _err("ocsp/bad-input", "each query must be { cert, issuer }");
@@ -227,7 +240,7 @@ function _buildRequest(query, opts) {
227
240
  return signScheme.signOverTbs(scheme, opts.signer.key, tbsRequest, _signE).then(function (sig) {
228
241
  /** @internal The request is held to the same proof a certificate is: the signature verifies
229
242
  * against the public key the certificate it carries declares. */
230
- return Promise.resolve(_b.assertSignatureVerifies(tbsRequest, sig, signerCert.subjectPublicKeyInfo.bytes, scheme)).then(function () {
243
+ return _promiseResolve(_Promise, _b.assertSignatureVerifies(tbsRequest, sig, signerCert.subjectPublicKeyInfo.bytes, scheme)).then(function () {
231
244
  var optionalSignature = b.sequence([scheme.sigAlgId, b.bitString(sig, 0), b.explicit(0, b.sequence([b.raw(signerCertDer)]))]);
232
245
  return _emitReq(b.sequence([tbsRequest, b.explicit(0, optionalSignature)]), opts);
233
246
  });
@@ -347,7 +360,7 @@ function _signResponse(responseData, responder, respCert, respCertDer, responder
347
360
  if (!responses.length) throw _err("ocsp/bad-input", "a response MUST include at least one SingleResponse (RFC 6960 sec. 4.2.1)");
348
361
 
349
362
  return _responderID(responseData.responderID, respCert).then(function (ridNode) {
350
- return Promise.all(responses.map(function (r) { return _buildSingleResponse(r, opts); })).then(function (built) {
363
+ return _promiseAll(_Promise, responses.map(function (r) { return _buildSingleResponse(r, opts); })).then(function (built) {
351
364
  var srNodes = built.map(function (x) { return x.node; });
352
365
  var anyNonIssued = built.some(function (x) { return x.nonIssued; });
353
366
  /** @internal Sec. 2.2 and 4.4.8: a "revoked" answer for a non-issued certificate MUST carry the
@@ -366,7 +379,7 @@ function _signResponse(responseData, responder, respCert, respCertDer, responder
366
379
  return signScheme.signOverTbs(scheme, responderKey, responseDataDer, _signE).then(function (sig) {
367
380
  /** @internal The response is held to the same proof a certificate is: the signature verifies
368
381
  * against the public key the responder certificate declares. */
369
- return Promise.resolve(_b.assertSignatureVerifies(responseDataDer, sig, respCert.subjectPublicKeyInfo.bytes, scheme)).then(function () {
382
+ return _promiseResolve(_Promise, _b.assertSignatureVerifies(responseDataDer, sig, respCert.subjectPublicKeyInfo.bytes, scheme)).then(function () {
370
383
  var basicChildren = [responseDataDer, scheme.sigAlgId, b.bitString(sig, 0)];
371
384
  if (opts.embedCert !== false) basicChildren.push(b.explicit(0, b.sequence([b.raw(respCertDer)])));
372
385
  var responseBytes = b.sequence([b.oid(OID_OCSP_BASIC), b.octetString(b.sequence(basicChildren))]);
@@ -406,19 +419,19 @@ function _snapshotSignerKey(key, owned) {
406
419
  }
407
420
 
408
421
  function _responderID(rid, respCert) {
409
- if (rid == null || rid === "byName") return Promise.resolve(b.explicit(1, b.raw(respCert.subject.bytes)));
422
+ if (rid == null || rid === "byName") return _promiseResolve(_Promise, b.explicit(1, b.raw(respCert.subject.bytes)));
410
423
  if (rid === "byKey") return _digest("SHA-1", _keyValue(respCert.subjectPublicKeyInfo.bytes)).then(function (kh) { return b.explicit(2, b.octetString(kh)); });
411
424
  throw _err("ocsp/bad-input", "responderID must be \"byName\" or \"byKey\"");
412
425
  }
413
426
  function _buildSingleResponse(r, opts) {
414
427
  r = r || {};
415
428
  var certIdP;
416
- if (typeof r !== "object") return Promise.reject(_err("ocsp/bad-input", "each response entry must be an object"));
429
+ if (typeof r !== "object") return _promiseReject(_Promise, _err("ocsp/bad-input", "each response entry must be an object"));
417
430
  try { guard.identifier.assertKnownKeys(r, _SINGLE_RESPONSE_KEYS, _err, "ocsp/bad-input", "unknown response entry field (accepted: cert, issuer, certID, hashAlgorithm, status, thisUpdate, nextUpdate, singleExtensions): "); }
418
- catch (e) { return Promise.reject(e); }
419
- if (r.certID != null) certIdP = Promise.resolve(b.raw(guard.bytes.view(r.certID, OcspError, "ocsp/bad-input", "a response entry's certID")));
431
+ catch (e) { return _promiseReject(_Promise, e); }
432
+ if (r.certID != null) certIdP = _promiseResolve(_Promise, b.raw(guard.bytes.view(r.certID, OcspError, "ocsp/bad-input", "a response entry's certID")));
420
433
  else if (r.cert != null && r.issuer != null) certIdP = _buildCertID(_certOf(r.cert, "a response certificate"), _certOf(r.issuer, "a response issuer"), r.hashAlgorithm || "sha1");
421
- else return Promise.reject(_err("ocsp/bad-input", "each response entry needs { certID } or { cert, issuer }"));
434
+ else return _promiseReject(_Promise, _err("ocsp/bad-input", "each response entry needs { certID } or { cert, issuer }"));
422
435
  /** @internal Every per-entry refusal stays INSIDE this chain. These entries are built through a
423
436
  * map into Promise.all, so a synchronous throw here would abort the map before Promise.all
424
437
  * attached handlers, and an earlier entry's rejection would be orphaned: the caller catches
@@ -653,15 +666,15 @@ function verify(response, opts) {
653
666
  opts = guard.identifier.optionsObject(opts, _err, "ocsp/bad-input", "pki.ocsp.verify options");
654
667
  guard.identifier.assertKnownKeys(opts, _VERIFY_OPTS, _err, "ocsp/bad-input",
655
668
  "pki.ocsp.verify has an unknown option (the request nonce to bind against is `requestNonce`): ");
656
- } catch (e) { return Promise.reject(e); }
657
- if (opts.cert == null || opts.issuer == null) return Promise.reject(_err("ocsp/bad-input", "verify requires opts.cert and opts.issuer"));
669
+ } catch (e) { return _promiseReject(_Promise, e); }
670
+ if (opts.cert == null || opts.issuer == null) return _promiseReject(_Promise, _err("ocsp/bad-input", "verify requires opts.cert and opts.issuer"));
658
671
  var parsed, cert, issuerCert, time;
659
672
  try {
660
673
  parsed = _responseFromBytes(response);
661
674
  cert = _certOf(opts.cert, "the target certificate");
662
675
  issuerCert = _certOf(opts.issuer, "the issuer certificate");
663
676
  time = opts.time == null ? new Date() : _asDate(opts.time);
664
- } catch (e) { return Promise.reject(e); }
677
+ } catch (e) { return _promiseReject(_Promise, e); }
665
678
  /** @internal The nonce rule is applied by the acceptance gate rather than restated here, so this
666
679
  * door and every other consumer of that gate answer alike. The bytes are snapshotted here so a
667
680
  * fault in them carries this door's own code; a value that is not bytes is passed through for the
@@ -670,7 +683,7 @@ function verify(response, opts) {
670
683
  var reqNonce = opts.requestNonce;
671
684
  if (guard.bytes.isByteSource(reqNonce)) {
672
685
  try { reqNonce = guard.bytes.snapshotSource(reqNonce, OcspError, "ocsp/bad-input", "opts.requestNonce"); }
673
- catch (e) { return Promise.reject(e); }
686
+ catch (e) { return _promiseReject(_Promise, e); }
674
687
  }
675
688
  return pathValidate.verifyOcspResponse(parsed, cert, issuerCert, time,
676
689
  { historicalMode: opts.historicalMode === true, requestNonce: reqNonce }).then(function (verdict) {
package/lib/oid.js CHANGED
@@ -65,11 +65,13 @@ var FAMILIES = Object.assign(Object.create(null), {
65
65
  policyConstraints: 36, extKeyUsage: 37, freshestCRL: 46,
66
66
  inhibitAnyPolicy: 54,
67
67
  anyPolicy: [32, 0], anyExtendedKeyUsage: [37, 0],
68
- targetInformation: 55, noRevAvail: 56 } },
68
+ targetInformation: 55, noRevAvail: 56,
69
+ subjectAltPublicKeyInfo: 72, altSignatureAlgorithm: 73, altSignatureValue: 74 } },
69
70
 
70
71
  pkixAccess: { base: [1, 3, 6, 1, 5, 5, 7, 1], of: {
71
72
  authorityInfoAccess: 1, acAuditIdentity: 4, aaControls: 6, acProxying: 10,
72
73
  acmeIdentifier: 31,
74
+ relatedCertificate: 36,
73
75
  subjectInfoAccess: 11,
74
76
  tlsFeature: 24,
75
77
  qcStatements: 3,
@@ -187,6 +189,12 @@ var FAMILIES = Object.assign(Object.create(null), {
187
189
 
188
190
  wisun: { base: [1, 3, 6, 1, 4, 1, 45605], of: { fanDevice: 1 } },
189
191
 
192
+ /** @internal RFC 9883 assigns its attribute on a private-enterprise arc rather than a PKIX one, and
193
+ * uses the SAME value as a PKCS#10 attribute and as a CRMF registration control, so the two names
194
+ * resolve to one dotted OID. `byName` is one-to-one in the other direction, so the attribute name is
195
+ * the one the registry returns for the dotted form. */
196
+ rfc9883: { base: [1, 3, 6, 1, 4, 1, 22112, 2], of: { statementOfPossession: 1 } },
197
+
190
198
  ct: { base: [1, 3, 6, 1, 4, 1, 11129, 2, 4], of: {
191
199
  signedCertificateTimestampList: 2, precertificatePoison: 3,
192
200
  precertificateSigningCert: 4, ocspSignedCertificateTimestampList: 5 } },
@@ -314,7 +322,9 @@ var FAMILIES = Object.assign(Object.create(null), {
314
322
  msgSigDigest: 5, contentIdentifier: 7, equivalentLabels: 9, contentReference: 10,
315
323
  signingCertificate: 12, timeStampToken: 14, decryptKeyID: 37, signingCertificateV2: 47,
316
324
  cmcUnsignedData: 34,
317
- asymmDecryptKeyID: 54, certificationRequestInfoTemplate: 61, extensionReqTemplate: 62 } },
325
+ binarySigningTime: 46,
326
+ asymmDecryptKeyID: 54, relatedCertRequest: 60,
327
+ certificationRequestInfoTemplate: 61, extensionReqTemplate: 62 } },
318
328
 
319
329
  ansiX962: { base: [1, 2, 840, 10045], of: {
320
330
  ecPublicKey: [2, 1], prime192v1: [3, 1, 1], prime256v1: [3, 1, 7],
@@ -443,8 +453,8 @@ function _assertEncodableArcs(arcs, who) {
443
453
  if (arcs.length < 2) {
444
454
  throw new OidError("oid/bad-input", who + ": an OID must have at least 2 arcs");
445
455
  }
446
- var root = typeof arcs[0] === "bigint" ? arcs[0] : BigInt(arcs[0]);
447
- var second = typeof arcs[1] === "bigint" ? arcs[1] : BigInt(arcs[1]);
456
+ var root = guard.range.authoredInteger(arcs[0], _oidError, "oid/bad-arc", who + ": the root arc");
457
+ var second = guard.range.authoredInteger(arcs[1], _oidError, "oid/bad-arc", who + ": the second arc");
448
458
  if (root > 2n) throw new OidError("oid/bad-arc", who + ": the root arc must be 0, 1, or 2 (X.660)");
449
459
  if (root < 2n && second > 39n) throw new OidError("oid/bad-arc", who + ": the second arc must be 0..39 under roots 0 and 1 (X.660)");
450
460
  }