@blamejs/pki 0.8.31 → 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 +393 -0
  2. package/README.md +66 -15
  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 +26 -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 +33 -6
  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 +228 -2
  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
@@ -4,12 +4,210 @@
4
4
  // @internal
5
5
 
6
6
  var nodeCrypto = require("node:crypto");
7
- var _freeze = require("./guard-intrinsic").freeze;
7
+ var intrinsic = require("./guard-intrinsic");
8
+ var _freeze = intrinsic.freeze;
9
+
10
+ /** @internal The hash operations, captured at load. The prototype is read from an instance rather than
11
+ * named, `node:crypto` exporting no Hash constructor. */
12
+ var _createHash = nodeCrypto.createHash;
13
+ var _hashProto = intrinsic.getPrototypeOf(_createHash("sha256"));
14
+ var _hashUpdate = intrinsic.uncurry(_hashProto.update);
15
+ var _hashDigest = intrinsic.uncurry(_hashProto.digest);
16
+
17
+ /** @internal The MAC operations, captured at load. `node:crypto` exports no Hmac constructor either,
18
+ * and an Hmac does not share the Hash prototype, so it gets its own pair. */
19
+ var _createHmac = nodeCrypto.createHmac;
20
+ var _hmacProto = intrinsic.getPrototypeOf(_createHmac("sha256", "x"));
21
+ var _hmacUpdate = intrinsic.uncurry(_hmacProto.update);
22
+ var _hmacDigest = intrinsic.uncurry(_hmacProto.digest);
23
+
24
+ /** @internal The native signature operations, captured at load. */
25
+ var _nodeVerify = nodeCrypto.verify;
26
+ var _nodeSign = nodeCrypto.sign;
27
+
28
+ /** @internal The constant-time comparison, captured at load. It is the operation a MAC verdict rests
29
+ * on, so a replacement answering `true` accepts any equal-length wrong MAC. */
30
+ var _timingSafeEqual = nodeCrypto.timingSafeEqual;
31
+
32
+ /** @internal The RSA padding and salt-length values, in one home rather than read from the module at
33
+ * each call site. MEASURED: this is NOT a capture against replacement, because there is nothing to
34
+ * capture against. `crypto.constants` is a non-writable, non-configurable property of the module and
35
+ * each value on it is non-writable and non-configurable too, so assignment silently fails,
36
+ * `defineProperty` throws, and replacing the whole object silently fails. What this gives is one place
37
+ * that answers which padding scheme a verb runs under, so a reader does not re-derive the question per
38
+ * module and a new module does not pick a value by copying a neighbor. */
39
+ var _nodeConstants = nodeCrypto.constants;
40
+ var padding = _freeze({
41
+ RSA_PKCS1: _nodeConstants.RSA_PKCS1_PADDING,
42
+ RSA_PKCS1_OAEP: _nodeConstants.RSA_PKCS1_OAEP_PADDING,
43
+ RSA_PKCS1_PSS: _nodeConstants.RSA_PKCS1_PSS_PADDING,
44
+ });
45
+ var saltLength = _freeze({
46
+ AUTO: _nodeConstants.RSA_PSS_SALTLEN_AUTO,
47
+ DIGEST: _nodeConstants.RSA_PSS_SALTLEN_DIGEST,
48
+ });
49
+
50
+ /** @internal The key imports, captured at load. A verifier checks a signature against whatever key
51
+ * object it is handed, so the import decides the verdict as surely as the verifier does: one
52
+ * returning another genuine key of the same family makes a signature made by that key verify against
53
+ * the material a caller pinned. */
54
+ var _createPublicKey = nodeCrypto.createPublicKey;
55
+ var _createPrivateKey = nodeCrypto.createPrivateKey;
56
+ var _createSecretKey = nodeCrypto.createSecretKey;
57
+
58
+ /** @internal The three KeyObject `export` methods, captured at load. `node:crypto` exports no
59
+ * constructor carrying them: `KeyObject.prototype` has no `export`, and `PublicKeyObject`,
60
+ * `PrivateKeyObject` and `SecretKeyObject` each own one. So one sample pair and one secret key are
61
+ * made here to read the three prototypes from, measured at 0.02 ms, and the keys are discarded.
62
+ *
63
+ * The sample is a P-256 pair, and the generation is part of `require`. An algorithm outside the
64
+ * FIPS 140-3 boundary would therefore make the whole package unloadable under `crypto.setFips(1)`:
65
+ * OpenSSL's FIPS provider carries no Ed25519, so the generation raises ERR_OSSL_EVP_UNSUPPORTED
66
+ * before any export is reached, and a caller needing only approved RSA or EC operations could not
67
+ * require the package at all. The prototypes do not depend on the algorithm, measured across the
68
+ * Ed25519, P-256 and RSA pairs, so one approved pair yields both. */
69
+ var _sampleKeyPair = nodeCrypto.generateKeyPairSync("ec", { namedCurve: "prime256v1" });
70
+ var _publicProto = intrinsic.getPrototypeOf(_sampleKeyPair.publicKey);
71
+ var _privateProto = intrinsic.getPrototypeOf(_sampleKeyPair.privateKey);
72
+ var _secretProto = intrinsic.getPrototypeOf(_createSecretKey(intrinsic.bufferAlloc(32)));
73
+ var _publicExport = intrinsic.uncurry(_publicProto.export);
74
+ var _privateExport = intrinsic.uncurry(_privateProto.export);
75
+ var _secretExport = intrinsic.uncurry(_secretProto.export);
76
+
77
+ /** A signature verified through the operation captured at load. Same arguments as `crypto.verify`.
78
+ *
79
+ * `crypto.verify` is an ordinary writable property of the module object, and it was read once per
80
+ * signature at every call site, so a replacement installed after the package loaded decided the
81
+ * verdict: one returning `true` made TUF metadata carrying a 64-byte all-zero signature meet its
82
+ * threshold against a genuine pinned key. That is the whole answer, not a detail of it.
83
+ *
84
+ * The shape excludes its own routing call: `guard.crypto.verify(` contains `crypto.verify(`, so
85
+ * without the lookbehind the detector fires on every consumer it is meant to bless.
86
+ *
87
+ * @enforced-by guard-shape-reinlined
88
+ * @guard-shape (?<!guard\.)(?:nodeCrypto|crypto)\.verify\s*\(
89
+ */
90
+ function verify(algorithm, data, key, signature) {
91
+ return _nodeVerify(algorithm, data, key, signature);
92
+ }
93
+
94
+ /** A signature produced through the operation captured at load. Same arguments as `crypto.sign`.
95
+ *
96
+ * Captured for the reason `verify` above gives: a replaced signer produces a signature over bytes
97
+ * the caller did not ask about, and the key-pair probe in `pki.key` checks a signature it made
98
+ * itself, so a replacement that agreed with its own forgery would pass the probe.
99
+ *
100
+ * @enforced-by guard-shape-reinlined
101
+ * @guard-shape (?<!guard\.)(?:nodeCrypto|crypto)\.sign\s*\(
102
+ */
103
+ function sign(algorithm, data, key) {
104
+ return _nodeSign(algorithm, data, key);
105
+ }
106
+
107
+ /** A digest taken through operations captured at load. `createHash(name).update(b).digest()` reaches
108
+ * `update` and `digest` off the live hash prototype, both ordinary writable properties, so a
109
+ * replacement decides what the digest is OVER. Measured: a replaced `update` that filled the content
110
+ * buffer before forwarding put the digest of other bytes into the attribute set
111
+ * `pki.cms.authenticate` computes its MAC over, and the message then failed to verify against the
112
+ * content it carries. A digest in this toolkit names a key, binds a certificate, or is the value a
113
+ * signature covers, so the operation that computes it is not the caller's to choose.
114
+ *
115
+ * @enforced-by behavioral -- the shape detector for this one is written and NOT armed. Its rule is
116
+ * `createHash\s*\([^)]*\)\s*\.(?:update|digest)\s*\(`, which is rename-proof and would catch the class
117
+ * anywhere, and 19 sites across 11 lib modules still carry it, so arming it now would fail the gate on
118
+ * a clean tree. It arms with that migration. A module that captures `createHash` into a local and then
119
+ * chains off it is the worst of the three states, since it reads as converted and is not: those are
120
+ * gone. Until then the guard is the RED conformance vector: a
121
+ * replaced hash `update` cannot choose the content a message digest covers, driven through
122
+ * `pki.cms.authenticate` and read back with `pki.cms.decrypt`.
123
+ */
124
+ function digest(name, bytes) {
125
+ return _hashDigest(_hashUpdate(_createHash(name), bytes));
126
+ }
127
+
128
+ /** A digest over SEVERAL parts, fed in order through the operations captured at load. It is `digest`
129
+ * for a preimage the caller assembles as a list rather than as one buffer, which keeps a multi-part
130
+ * hash from having to concatenate first: a transparency-log key identifier is a digest over a name, a
131
+ * separator, an algorithm byte and the key, and the identifier is what matches a signature line to a
132
+ * caller's key.
133
+ *
134
+ * A part is passed to the captured `update` as it is, so a string part is read as UTF-8 exactly as
135
+ * `hash.update(string)` would read it.
136
+ *
137
+ * @enforced-by behavioral -- shares the unarmed shape `digest` below describes.
138
+ */
139
+ function digestParts(name, parts) {
140
+ var h = _createHash(name);
141
+ for (var i = 0; i < parts.length; i++) _hashUpdate(h, parts[i]);
142
+ return _hashDigest(h);
143
+ }
144
+
145
+ /** An extendable-output function squeezed to `outLen` bytes, through the operations captured at load.
146
+ * A single-stage KDF derives a key pair and the HPKE key schedule's secrets this way, so a replaced
147
+ * `update` chooses what the derivation covers and a replaced `digest` chooses the key material itself.
148
+ * Measured: an `update` returning its receiver without forwarding made two X25519/SHAKE128 setups with
149
+ * different encapsulations derive identical exports, so a session key no longer depended on the
150
+ * encapsulation it was negotiated under.
151
+ *
152
+ * @enforced-by behavioral -- shares the unarmed shape `digest` above describes. The guard is the RED
153
+ * conformance vector: two HPKE setups whose encapsulations differ must not export the same secret
154
+ * while the hash prototype is replaced.
155
+ */
156
+ function xof(name, bytes, outLen) {
157
+ return _hashDigest(_hashUpdate(_createHash(name, { outputLength: outLen }), bytes));
158
+ }
159
+
160
+ /** A MAC over `bytes` under `key`, through the operations captured at load. HKDF-Extract and each
161
+ * HKDF-Expand round are MACs, so a replacement here decides the pseudo-random key an entire key
162
+ * schedule hangs off, and the HPKE and CMC paths both derive their traffic keys through it.
163
+ *
164
+ * @enforced-by behavioral -- the Hmac prototype's `update` and `digest` are ordinary writable
165
+ * properties, with the same absence of a rename-proof shape as `digest` above. The guard is the RED
166
+ * conformance vector over the key schedule.
167
+ */
168
+ function hmac(name, key, bytes) {
169
+ return _hmacDigest(_hmacUpdate(_createHmac(name, key), bytes));
170
+ }
171
+
172
+ /** A key imported through the operation captured at load. Same arguments as `crypto.createPublicKey`,
173
+ * `crypto.createPrivateKey` and `crypto.createSecretKey`.
174
+ *
175
+ * @enforced-by behavioral -- the shape `nodeCrypto\.create(?:Public|Private|Secret)Key\s*\(` is
176
+ * rename-proof and would catch the class anywhere, and 42 sites across 9 lib modules still reach the
177
+ * module property directly on the key-agreement, KEM and key-format paths, so arming it now would fail
178
+ * the gate on a clean tree. It arms with that migration. Until then the guard is the RED conformance
179
+ * vector: a replaced `crypto.createPublicKey` returning another genuine key cannot substitute the key a
180
+ * signature is verified against, driven through `pki.tlog.verifyNote` and `pki.key.publicFromPrivate`.
181
+ */
182
+ function publicKey(spec) { return _createPublicKey(spec); }
183
+
184
+ // @enforced-by behavioral -- shares the migration note on `publicKey` above; the signer's own key is
185
+ function privateKey(spec) { return _createPrivateKey(spec); }
186
+
187
+ // @enforced-by behavioral -- shares the migration note on `publicKey` above; a MAC key substituted at
188
+ function secretKey(material) { return _createSecretKey(material); }
189
+
190
+ /** A KeyObject exported through the operation captured at load. `keyObject.export(spec)` reaches the
191
+ * method off the live prototype, so a replacement decides the bytes a caller is handed: one exporting
192
+ * a fixed value made two distinct authorized TUF keys carry the same material identifier, which took a
193
+ * two-key threshold down to one signature. The method is chosen by the key's PROTOTYPE rather than by
194
+ * its `type`, which is itself a replaceable accessor, and an object whose prototype is none of the
195
+ * three is refused rather than guessed at.
196
+ *
197
+ * @enforced-by behavioral -- shares the migration note on the imports above.
198
+ */
199
+ function exportKey(keyObject, spec) {
200
+ var proto = keyObject === null || keyObject === undefined ? null : intrinsic.getPrototypeOf(keyObject);
201
+ if (proto === _publicProto) return _publicExport(keyObject, spec);
202
+ if (proto === _privateProto) return _privateExport(keyObject, spec);
203
+ if (proto === _secretProto) return _secretExport(keyObject, spec);
204
+ throw new TypeError("guard.crypto.exportKey: expects a node:crypto KeyObject");
205
+ }
8
206
 
9
207
  // @enforced-by guard-shape-reinlined
10
208
  // @guard-shape \.timingSafeEqual\s*\(
11
209
  function constantTimeEqual(a, b) {
12
- return a.length === b.length && nodeCrypto.timingSafeEqual(a, b);
210
+ return a.length === b.length && _timingSafeEqual(a, b);
13
211
  }
14
212
 
15
213
  // @enforced-by behavioral -- shares the octet-alignment rule below; a bare
@@ -25,4 +223,8 @@ function assertOctetAligned(bitString, E, code, label) {
25
223
  return bitString;
26
224
  }
27
225
 
28
- module.exports = _freeze({ constantTimeEqual: constantTimeEqual, isOctetAligned: isOctetAligned, assertOctetAligned: assertOctetAligned });
226
+ module.exports = _freeze({ constantTimeEqual: constantTimeEqual, digest: digest,
227
+ digestParts: digestParts, xof: xof, hmac: hmac, verify: verify, sign: sign,
228
+ publicKey: publicKey, privateKey: privateKey, secretKey: secretKey, exportKey: exportKey,
229
+ padding: padding, saltLength: saltLength,
230
+ isOctetAligned: isOctetAligned, assertOctetAligned: assertOctetAligned });
@@ -142,7 +142,12 @@ function percentEncode(bytes, E, code, label) {
142
142
 
143
143
  /** @internal One path segment of a URI, built from `text` read as its latin1 bytes. Every byte
144
144
  * outside the unreserved set becomes a triplet, so a value carrying `/`, `?`, `#` or a percent of
145
- * its own cannot reach past the segment it was placed in. */
145
+ * its own cannot reach past the segment it was placed in.
146
+ *
147
+ * @enforced-by behavioral -- the encoding itself is `percentEncode`, whose shape detector catches a
148
+ * re-inlined triplet anywhere in lib/; what this adds is reading the text as its latin1 bytes, which is a
149
+ * conversion with no shape of its own, pinned by the vector that a character above 0x7f encodes as the
150
+ * bytes latin1 gives it rather than as its UTF-8 pair */
146
151
  function uriPathSegment(text, E, code, label) {
147
152
  if (typeof text !== "string") {
148
153
  throw _rawError(E)(code, (label || "URI path segment") + " must be a string");
@@ -40,7 +40,8 @@ var _Number = intrinsic.Number;
40
40
  var _BigInt = intrinsic.BigInt;
41
41
  var _Object = intrinsic.Object;
42
42
  var _BufferProto = intrinsic.BufferProto;
43
- var _append = require("./guard-list").append;
43
+ var guardList = require("./guard-list");
44
+ var _append = guardList.append;
44
45
 
45
46
  function _isDottedDecimal(str) {
46
47
  if (str.length === 0) return false;
@@ -108,9 +109,16 @@ function snapshotOptions(obj, known) {
108
109
  function optionNames(obj, known) {
109
110
  if (obj === null || obj === undefined) return [];
110
111
  var out = [];
111
- _forEach(_ownKeys(_Object(obj)), function (k) {
112
+ var o = _Object(obj);
113
+ _forEach(_ownKeys(o), function (k) {
112
114
  if (typeof k !== "string") return;
113
- if (known !== null && known !== undefined && !_hasOwn(known, k)) return;
115
+ var recognized = known !== null && known !== undefined && _hasOwn(known, k);
116
+ if (known !== null && known !== undefined && !recognized) return;
117
+ /** @internal The non-thenable mask is the object's machinery rather than one of its options, and a
118
+ * caller building its list of named forms from this found one more form than the object carries.
119
+ * A caller that RECOGNIZES the name means it as content, so the exemption stands aside: the whole
120
+ * mechanism is about a name nothing asked for. */
121
+ if (!recognized && _isOwnStructuralName(o, k, _getOwnPropertyDescriptor(o, k), false)) return;
114
122
  _append(out, k);
115
123
  });
116
124
  return out;
@@ -127,6 +135,10 @@ function ownOptions(obj) {
127
135
  var o = _Object(obj);
128
136
  _forEach(_ownKeys(o), function (k) {
129
137
  if (typeof k !== "string") return;
138
+ /** @internal The copy has a null prototype, so there is no inherited `then` for a non-thenable
139
+ * mask to stand in front of, and copying one in would turn a hidden name holding `undefined` into
140
+ * a visible option the next door reports as unknown. */
141
+ if (_isOwnStructuralName(o, k, _getOwnPropertyDescriptor(o, k), false)) return;
130
142
  out[k] = o[k];
131
143
  });
132
144
  return out;
@@ -153,7 +165,11 @@ function _requireFactory(E) {
153
165
 
154
166
  function _refuseUnenumerable(v, E, code, label) {
155
167
  _requireFactory(E);
156
- if (v === null || typeof v !== "object") return;
168
+ /** @internal A Proxy whose TARGET is a function reports `typeof "function"`, so a receiver tested for
169
+ * `typeof !== "object"` was returned on unexamined: one hid an own `then` through its `ownKeys` trap
170
+ * and the name walk above never saw it. A callable receiver is examined the same way; a plain
171
+ * function is not a Proxy and still passes through. */
172
+ if (v === null || (typeof v !== "object" && typeof v !== "function")) return;
157
173
  var visited = new _Set();
158
174
  for (var o = v; o && !_setHas(visited, o); o = _getPrototypeOf(o)) {
159
175
  _setAdd(visited, o);
@@ -209,6 +225,10 @@ var _TYPED_ARRAY_PROTO = _getPrototypeOf(Uint8Array.prototype);
209
225
  var _INTRINSIC_HOLDERS = (function () {
210
226
  var m = new _Map([[_TYPED_ARRAY_PROTO, _TYPED_PROTO_MEMBERS]]);
211
227
  function pair(ctor, members) { if (ctor && ctor.prototype) _mapSet(m, ctor.prototype, members); }
228
+ /** @internal This map IS the record of what the real intrinsic prototypes were when the package
229
+ * loaded, so reading a global here is the capture rather than a call-time read of one: a
230
+ * replacement installed afterwards cannot change a map already built. */
231
+ // allow:guard-reads-runtime-live -- module-init, see above
212
232
  pair(Array, _ARRAY_PROTO_MEMBERS);
213
233
  pair(RegExp, _REGEXP_PROTO_MEMBERS);
214
234
  _forEach([Map, Set], function (c) { pair(c, _SIZED_PROTO_MEMBERS); });
@@ -216,6 +236,7 @@ var _INTRINSIC_HOLDERS = (function () {
216
236
  pair(ArrayBuffer, _ARRAYBUFFER_PROTO_MEMBERS);
217
237
  pair(typeof SharedArrayBuffer === "function" ? SharedArrayBuffer : null, _SHARED_PROTO_MEMBERS);
218
238
  pair(DataView, _DATAVIEW_PROTO_MEMBERS);
239
+ // allow:guard-reads-runtime-live -- module-init, see above
219
240
  pair(Buffer, _BUFFER_PROTO_MEMBERS);
220
241
  _forEach(_getOwnPropertyNames(globalThis), function (n) {
221
242
  var d = _getOwnPropertyDescriptor(globalThis, n);
@@ -256,8 +277,24 @@ function _kindMembers(v) {
256
277
  return [];
257
278
  }
258
279
 
259
- function _isOwnStructuralName(v, k) {
260
- return util.types.isRegExp(v) && k === "lastIndex";
280
+ function _isOwnStructuralName(v, k, d, inherited) {
281
+ if (util.types.isRegExp(v) && k === "lastIndex") return true;
282
+ /** @internal The non-thenable mask `guard.verdict.shield` leaves on a value the toolkit hands back:
283
+ * a non-enumerable own `then` holding `undefined`. It is part of the object's machinery rather than
284
+ * its content, and a name holding `undefined` answers nothing to a reader either way, so counting it
285
+ * had the toolkit refuse its OWN output: `pki.jose.parseJson` returns a shielded object, and a
286
+ * caller passing one as an options bag was told `then` was an unknown option while the same document
287
+ * through `JSON.parse` was accepted. An own `then` holding a VALUE is content and is still counted,
288
+ * so a document that really carries one is still reported. */
289
+ /** @internal A DATA descriptor, asked for the way `shield` asks: a descriptor carrying no `value`
290
+ * key is an accessor, and `{get: undefined, set: undefined}` is an accessor whose reads answer
291
+ * `undefined`, which `!d.get && !d.set` alone mistook for data.
292
+ * And a descriptor only means what it says when the object answering for it is a real one. A Proxy
293
+ * reports whatever its trap returns, so one reporting the mask's shape for a `then` that reads 42
294
+ * would have the name hidden and the value live. A Proxy is refused at every door that validates;
295
+ * here it simply does not qualify. */
296
+ return !inherited && k === "then" && !!d && _hasOwn(d, "value") &&
297
+ d.enumerable === false && d.value === undefined && !util.types.isProxy(v);
261
298
  }
262
299
 
263
300
  function _looksIntrinsic(d) {
@@ -292,8 +329,8 @@ function _readableNames(obj) {
292
329
  if (seen[k]) return;
293
330
  if (indexedSelf && typeof k === "string" && _isArrayIndex(k)) return;
294
331
  if (indexedSelf && k === "length" && !inherited) return;
295
- if (_isOwnStructuralName(obj, k)) return;
296
332
  var d = _getOwnPropertyDescriptor(o, k);
333
+ if (_isOwnStructuralName(obj, k, d, inherited)) return;
297
334
  var here = _holderMembers(o);
298
335
  if (inherited && _looksIntrinsic(d) && !_shadowsAnother(obj, k, o) &&
299
336
  (here === null ? _indexOf(members, k) !== -1 : _indexOf(here, k) !== -1)) return;
@@ -364,6 +401,11 @@ function _proxyInChain(v) {
364
401
  }
365
402
  return false;
366
403
  }
404
+ /** @internal The predicate form of `assertPlainRecord`, for a caller deciding rather than refusing.
405
+ *
406
+ * @enforced-by behavioral -- a plain-record test has no single rename-proof shape that a legitimate
407
+ * `typeof v === "object"` does not also match; the vectors driving a Proxy, an array, a Buffer and a
408
+ * built-in exotic through it are the guard */
367
409
  function isPlainRecord(v) {
368
410
  if (v === null || typeof v !== "object") return false;
369
411
  if (_proxyInChain(v)) return false;
@@ -402,7 +444,12 @@ function _descriptorHolder(v, k) {
402
444
 
403
445
  // @enforced-by behavioral -- reading an accessor before it is refused has no rename-proof code
404
446
  function refuseAccessorFields(obj, names, E, code, label) {
405
- for (var i = 0; i < names.length; i++) {
447
+ /** @internal The count is narrowed where it is taken, as every walk in `guard.list` is. This is a
448
+ * DOOR, and `i < names.length` asked a caller's list for its length on every comparison: a Proxy over
449
+ * `["a", "secret"]` whose length coerced to 2 and then to 0 had this walk pass the accessor named
450
+ * `secret` and return as though it had checked it. */
451
+ var n = guardList.count(names, "refuseAccessorFields");
452
+ for (var i = 0; i < n; i++) {
406
453
  var holder = _descriptorHolder(obj, names[i]);
407
454
  if (holder && (holder.get || holder.set)) {
408
455
  throw E(code, label + " supplies " +
@@ -47,8 +47,27 @@ var _mathCeil = Math.ceil;
47
47
  var _mathMin = Math.min;
48
48
  var _mathMax = Math.max;
49
49
  var _parseInt = parseInt;
50
+ /** @internal The promise constructor itself, captured at load. `Promise.resolve` and `Promise.reject`
51
+ * are captured below, but they build their promise from the RECEIVER they are called on, so reading
52
+ * the global binding at the call hands the construction to whatever that binding holds. A verdict
53
+ * returned as `Promise.resolve(false)` from a hostile constructor is not a refusal. */
54
+ var _Promise = Promise;
50
55
  var _promiseResolve = Promise.resolve;
51
56
  var _promiseReject = Promise.reject;
57
+ /** @internal The aggregators, captured alongside them. `all` is what assembles a verdict out of
58
+ * component results, so a replacement that resolves a fabricated array never awaits the components
59
+ * and hands its own values to the caller as the verdict. All four are captured together: a member
60
+ * with no captured form is a member that stays live. */
61
+ var _promiseAll = Promise.all;
62
+ var _promiseAllSettled = Promise.allSettled;
63
+ var _promiseRace = Promise.race;
64
+ var _promiseAny = Promise.any;
65
+ /** @internal The factory methods, captured for the same reason. `withResolvers` hands back the resolve
66
+ * and reject capability for a promise it built, and `try` decides whether the callback runs at all, so
67
+ * a replacement of either chooses what the caller goes on to settle or never settles. Both are
68
+ * present on the supported runtime floor. */
69
+ var _promiseTry = Promise["try"];
70
+ var _promiseWithResolvers = Promise.withResolvers;
52
71
  var _asyncIterator = Symbol.asyncIterator;
53
72
 
54
73
  var _arrForEach = uncurry(Array.prototype.forEach);
@@ -57,11 +76,23 @@ var _arrFilter = uncurry(Array.prototype.filter);
57
76
  var _arrEvery = uncurry(Array.prototype.every);
58
77
  var _arrSome = uncurry(Array.prototype.some);
59
78
  var _arrIndexOf = uncurry(Array.prototype.indexOf);
79
+ /** @internal The in-place sort, captured at load. A DER SET OF is ordered by its encoded bytes, so the
80
+ * sort decides the bytes emitted: MEASURED, a replaced `slice` or `sort` made an attribute set encode a
81
+ * member other than the one that was validated, on a path whose output is then signed. */
60
82
  var _arrSort = uncurry(Array.prototype.sort);
61
83
  var _arrConcat = uncurry(Array.prototype.concat);
62
84
  var _arrSlice = uncurry(Array.prototype.slice);
63
85
  var _arrJoin = uncurry(Array.prototype.join);
64
86
  var _arrPush = uncurry(Array.prototype.push);
87
+ /** @internal The mutators. A list is what a rule is enforced over, so an operation that drops or moves
88
+ * an element decides whether the rule reaches it: a replaced `pop` that popped twice took a signed
89
+ * note's last signature line away with the empty tail element, and the forged signature on that line
90
+ * was never checked. */
91
+ var _arrPop = uncurry(Array.prototype.pop);
92
+ var _arrShift = uncurry(Array.prototype.shift);
93
+ var _arrUnshift = uncurry(Array.prototype.unshift);
94
+ var _arrSplice = uncurry(Array.prototype.splice);
95
+ var _arrReverse = uncurry(Array.prototype.reverse);
65
96
 
66
97
  var _strToUpperCase = uncurry(String.prototype.toUpperCase);
67
98
  var _strToLowerCase = uncurry(String.prototype.toLowerCase);
@@ -179,6 +210,11 @@ module.exports = Object.freeze({
179
210
  max: _mathMax,
180
211
  parseInt: _parseInt,
181
212
  push: _arrPush,
213
+ pop: _arrPop,
214
+ shift: _arrShift,
215
+ unshift: _arrUnshift,
216
+ splice: _arrSplice,
217
+ reverse: _arrReverse,
182
218
  toUpperCase: _strToUpperCase,
183
219
  toLowerCase: _strToLowerCase,
184
220
  stringIndexOf: _strIndexOf,
@@ -186,8 +222,15 @@ module.exports = Object.freeze({
186
222
  arrayBufferSlice: _abSlice,
187
223
  numberToString: _numToString,
188
224
  bigIntToString: _bigIntToString,
225
+ Promise: _Promise,
189
226
  promiseResolve: _promiseResolve,
190
227
  promiseReject: _promiseReject,
228
+ promiseAll: _promiseAll,
229
+ promiseAllSettled: _promiseAllSettled,
230
+ promiseRace: _promiseRace,
231
+ promiseAny: _promiseAny,
232
+ promiseTry: _promiseTry,
233
+ promiseWithResolvers: _promiseWithResolvers,
191
234
  asyncIterator: _asyncIterator,
192
235
  forEach: _arrForEach,
193
236
  map: _arrMap,
package/lib/guard-json.js CHANGED
@@ -11,6 +11,10 @@ var _strSlice = require("./guard-intrinsic").uncurry(String.prototype.slice);
11
11
  var _hasOwn = require("./guard-intrinsic").hasOwn;
12
12
  var _Number = require("./guard-intrinsic").Number;
13
13
  var _isFinite = require("./guard-intrinsic").isFinite;
14
+ var _BigInt = require("./guard-intrinsic").BigInt;
15
+ /** @internal The largest integer a double holds exactly, as a BigInt, for `spec.exactIntegers`. Past it a
16
+ * value reads back as a different one: 2^53 + 1 converts to 2^53. */
17
+ var _MAX_EXACT = _BigInt(Number.MAX_SAFE_INTEGER);
14
18
  var _stringify = require("./guard-intrinsic").stringify;
15
19
  var _append = require("./guard-list").append;
16
20
  var limits = require("./guard-limits");
@@ -46,6 +50,9 @@ function parse(input, ErrorClass, spec) {
46
50
  fail("unexpected token");
47
51
  return undefined;
48
52
  }
53
+ /** @internal The member name whose value is being parsed, or null at the top level and inside an array.
54
+ * Read only by the number policy below. */
55
+ var currentMember = null;
49
56
  function object(depth) {
50
57
  i++;
51
58
  var out = {};
@@ -59,7 +66,17 @@ function parse(input, ErrorClass, spec) {
59
66
  ws();
60
67
  if (str[i] !== ":") fail("expected ':'");
61
68
  i++;
62
- _defineProperty(out, key, { value: value(depth + 1), writable: true, enumerable: true, configurable: true });
69
+ /** @internal The member name is carried into the value so a per-member number policy can apply to
70
+ * the members that need it. A document can be ordinary JSON, where a fractional number is legal
71
+ * somewhere in it, and still read ONE member as an integer: the FIDO metadata BLOB carries
72
+ * biometric false-accept rates as genuine doubles beside a rollback counter that must be an exact
73
+ * integer, so a document-wide rule refuses the real catalogue and no rule at all leaves the
74
+ * counter readable as a rounded spelling. */
75
+ var outerMember = currentMember;
76
+ currentMember = key;
77
+ var memberValue;
78
+ try { memberValue = value(depth + 1); } finally { currentMember = outerMember; }
79
+ _defineProperty(out, key, { value: memberValue, writable: true, enumerable: true, configurable: true });
63
80
  ws();
64
81
  if (str[i] === ",") { i++; continue; }
65
82
  if (str[i] === "}") { i++; return out; }
@@ -71,13 +88,21 @@ function parse(input, ErrorClass, spec) {
71
88
  var out = [];
72
89
  ws();
73
90
  if (str[i] === "]") { i++; return out; }
74
- for (;;) {
75
- _append(out, value(depth + 1));
76
- ws();
77
- if (str[i] === ",") { i++; continue; }
78
- if (str[i] === "]") { i++; return out; }
79
- fail("expected ',' or ']'");
80
- }
91
+ /** @internal The enclosing member's name does not reach the elements. A per-member number policy names
92
+ * a member whose VALUE is an integer, so a member holding an array holds no number for the rule to
93
+ * apply to, and letting the name reach inside would constrain a whole subtree from one name: the
94
+ * document-wide rule in miniature, and the shape that refuses a conforming document. */
95
+ var outerMember = currentMember;
96
+ currentMember = null;
97
+ try {
98
+ for (;;) {
99
+ _append(out, value(depth + 1));
100
+ ws();
101
+ if (str[i] === ",") { i++; continue; }
102
+ if (str[i] === "]") { i++; return out; }
103
+ fail("expected ',' or ']'");
104
+ }
105
+ } finally { currentMember = outerMember; }
81
106
  }
82
107
  function string() {
83
108
  i++;
@@ -121,18 +146,106 @@ function parse(input, ErrorClass, spec) {
121
146
  var intLen = i - intStart;
122
147
  if (intLen === 0) fail("malformed number");
123
148
  if (intLen > 1 && str[intStart] === "0") fail("malformed number");
149
+ var intDigits = _strSlice(str, intStart, i);
150
+ var fracDigits = "";
151
+ var expValue = 0;
152
+ var notAnInteger = false;
124
153
  if (str[i] === ".") {
125
154
  i++;
126
155
  var fracStart = i;
127
156
  while (i < n && str[i] >= "0" && str[i] <= "9") i++;
128
157
  if (i === fracStart) fail("malformed number");
158
+ fracDigits = _strSlice(str, fracStart, i);
159
+ notAnInteger = true;
129
160
  }
130
161
  if (str[i] === "e" || str[i] === "E") {
131
162
  i++;
132
- if (str[i] === "+" || str[i] === "-") i++;
163
+ var expNeg = false;
164
+ if (str[i] === "+") i++;
165
+ else if (str[i] === "-") { expNeg = true; i++; }
133
166
  var expStart = i;
134
167
  while (i < n && str[i] >= "0" && str[i] <= "9") i++;
135
168
  if (i === expStart) fail("malformed number");
169
+ /** @internal The exponent is read as written, with no bound on its spelling. Bounding the character
170
+ * COUNT refuses valid numbers: `1e0000000` is 1 and `0e1000000` is 0, both exactly representable,
171
+ * and the bound applied to every caller whether or not one asked for an integer policy. Nothing
172
+ * below needs the bound, because the exact-integer decision is taken from digit COUNTS and never
173
+ * expands the value, and a magnitude that overflows a double is caught by the `_isFinite` check
174
+ * that every number already passes through. */
175
+ expValue = _Number(_strSlice(str, expStart, i));
176
+ if (expNeg) expValue = -expValue;
177
+ notAnInteger = true;
178
+ }
179
+ /** @internal `spec.integersOnly` refuses a fractional or exponent-form token HERE, while the token is
180
+ * still text. Conversion to a Number loses the distinction: 1.0000000000000001 becomes 1, so a check
181
+ * for an integer afterwards sees one and a format that permits only integers has been read as
182
+ * conforming. A caller asks for this when its own format forbids floating point. */
183
+ if (spec.integersOnly && notAnInteger) {
184
+ fail("a fractional or exponent-form number, where this format admits only integers");
185
+ }
186
+ /** @internal `spec.exactIntegers` is the laxer rule, for a format that is ordinary JSON but will treat
187
+ * this value as an integer. It refuses nothing a producer may legally write: `1e3`, `1.0` and
188
+ * `1500e-2` all denote integers and are accepted. What it refuses is a spelling whose VALUE is not
189
+ * exactly its own integer, which conversion to a Number cannot be asked about afterwards:
190
+ * `1.0000000000000001` becomes 1, so an `isInteger` check made after conversion sees a conforming
191
+ * document and the value the caller reports is not the spelling the producer sent. Where that value
192
+ * is folded back into a binary preimage, as a CT tree size is, the rounded spelling verifies.
193
+ *
194
+ * Decided on the DIGITS, with no float arithmetic: the token is `digits x 10^scale`, so it denotes an
195
+ * integer exactly when the scale is non-negative or when every digit the scale would drop is a zero.
196
+ * The resulting integer must also be one a double holds exactly, since 2^53 + 1 reads back as 2^53
197
+ * and that is the same loss by another route. */
198
+ /** @internal The rule applies to every number when `spec.exactIntegers` is set, and to the named
199
+ * members alone when `spec.exactIntegerMembers` is. The second form is for a document that is
200
+ * ordinary JSON, where a fractional number is legal somewhere in it, but reads one member as an
201
+ * integer: the FIDO metadata BLOB is that, and the live catalogue carries ten genuinely non-integral
202
+ * biometric rates, so the document-wide rule refuses it outright. Verified against the published
203
+ * BLOB rather than against the specification text. */
204
+ var exactWanted = spec.exactIntegers ||
205
+ (spec.exactIntegerMembers && currentMember !== null && _hasOwn(spec.exactIntegerMembers, currentMember));
206
+ if (exactWanted) {
207
+ var digits = intDigits + fracDigits;
208
+ var scale = expValue - fracDigits.length;
209
+ /** @internal Decided from the DIGIT COUNT and the scale, never by expanding the value. Computing
210
+ * `digits x 10^scale` to find out whether it is an integer means building the power first, and an
211
+ * exponent is six digits wide: `0e999999` expands to a million digits and then multiplies by a
212
+ * zero mantissa, so it passed, and a document can carry one such token per few bytes of its size
213
+ * cap. Measured at 1566 ms for a 907-byte CT response before this. Nothing below constructs a
214
+ * BigInt until the value is known to be small. */
215
+ var lead = 0;
216
+ while (lead < digits.length && digits[lead] === "0") lead++;
217
+ if (lead === digits.length) {
218
+ /** @internal Every digit is a zero, so the value is zero whatever the scale, and zero is an
219
+ * exact integer. Taken here because it is also the shape that made the expansion free to
220
+ * request: `0e999999` is this case. */
221
+ var zeroValue = _Number(_strSlice(str, start, i));
222
+ if (!_isFinite(zeroValue)) fail("bad number");
223
+ return zeroValue;
224
+ }
225
+ /** @internal The significant digits, and the trailing zeros among them that the scale may cancel.
226
+ * A negative scale drops that many digits off the end, and the value is an integer only when every
227
+ * digit it drops is a zero. Counted rather than sliced, so a long mantissa costs a scan. */
228
+ var lastSig = digits.length - 1;
229
+ while (lastSig > lead && digits[lastSig] === "0") lastSig--;
230
+ var trailingZeros = digits.length - 1 - lastSig;
231
+ if (scale < 0 && -scale > trailingZeros) {
232
+ fail("a number whose value is not exactly an integer, where this format reads it as one");
233
+ }
234
+ /** @internal The integer's decimal width, from the counts alone. MAX_SAFE_INTEGER is 16 digits, so
235
+ * anything wider than that is outside the exactly-representable range and is refused before a
236
+ * BigInt exists. Only a value already known to be at most 17 digits reaches the arithmetic. */
237
+ var sigCount = lastSig - lead + 1;
238
+ var intWidth = sigCount + scale + trailingZeros;
239
+ if (intWidth > 17) {
240
+ fail("an integer beyond the range a double represents exactly, so the value read back would not be the value written");
241
+ }
242
+ var exactDigits = _strSlice(digits, lead, lastSig + 1);
243
+ var zerosAfter = scale + trailingZeros;
244
+ for (var pad = 0; pad < zerosAfter; pad++) exactDigits += "0";
245
+ var exact = _BigInt(exactDigits);
246
+ if (exact > _MAX_EXACT) {
247
+ fail("an integer beyond the range a double represents exactly, so the value read back would not be the value written");
248
+ }
136
249
  }
137
250
  var v = _Number(_strSlice(str, start, i));
138
251
  if (!_isFinite(v)) fail("bad number");
@@ -79,6 +79,12 @@ function BudgetExceeded(message) {
79
79
  return e;
80
80
  }
81
81
 
82
+ /** @internal Whether a caught error is the budget throw above, which a door converts to its own typed
83
+ * refusal.
84
+ *
85
+ * @enforced-by behavioral -- reading one marker property off a caught error is the shape of any error
86
+ * test; the vector that a door converts a budget throw to its own typed refusal, and rethrows anything
87
+ * else unchanged, is the guard */
82
88
  function isBudgetExceeded(e) {
83
89
  return !!(e && e.budgetExceeded === true);
84
90
  }
@@ -86,7 +92,12 @@ function isBudgetExceeded(e) {
86
92
  /** @internal One budget per top-level parse, carried into every nested decode. `exhausted`
87
93
  * latches, and the object is frozen, so a caller that catches the throw cannot clear it. A
88
94
  * door that re-reads the latch before returning a result therefore still refuses, whatever
89
- * code between the two caught the exception. */
95
+ * code between the two caught the exception.
96
+ *
97
+ * @enforced-by behavioral -- an accumulator that throws past a maximum is the shape of every bounded
98
+ * loop in the toolkit, so no pattern separates this from one; the guard is the vector that the latch
99
+ * stays set after a caller catches the throw, which a re-implementation that tracked the total without
100
+ * latching would fail */
90
101
  function budget(limits) {
91
102
  var spent = 0;
92
103
  var exhausted = false;