@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.
- package/CHANGELOG.md +27 -1
- package/lib/acme.js +59 -815
- package/lib/asn1-der.js +7 -245
- package/lib/attrcert-sign.js +12 -260
- package/lib/attrcert-verify.js +1 -7
- package/lib/byte-reader.js +0 -8
- package/lib/byte-writer.js +0 -11
- package/lib/cbor-det.js +0 -54
- package/lib/cmc-build.js +15 -357
- package/lib/cmc-verify.js +6 -239
- package/lib/cmp-build.js +41 -317
- package/lib/cmp-session.js +73 -760
- package/lib/cmp-verify.js +32 -488
- package/lib/cms-compress.js +3 -32
- package/lib/cms-decrypt.js +29 -294
- package/lib/cms-encrypt.js +14 -234
- package/lib/cms-sign.js +29 -324
- package/lib/cms-verify.js +9 -500
- package/lib/composite-kem.js +2 -102
- package/lib/composite-sig.js +1 -77
- package/lib/constants.js +0 -249
- package/lib/crl-sign.js +9 -237
- package/lib/crl-verify.js +1 -10
- package/lib/crmf-sign.js +19 -198
- package/lib/crmf-verify.js +1 -12
- package/lib/csr-sign.js +3 -34
- package/lib/csr-verify.js +1 -12
- package/lib/ct.js +28 -253
- package/lib/edwards-point.js +10 -44
- package/lib/est.js +28 -764
- package/lib/framework-error.js +4 -182
- package/lib/guard-all.js +1 -58
- package/lib/guard-async.js +1 -36
- package/lib/guard-bytes.js +6 -650
- package/lib/guard-compress.js +5 -102
- package/lib/guard-crypto.js +1 -40
- package/lib/guard-der.js +2 -93
- package/lib/guard-encoding.js +2 -60
- package/lib/guard-header.js +1 -36
- package/lib/guard-identifier.js +7 -531
- package/lib/guard-intrinsic.js +1 -150
- package/lib/guard-json.js +10 -73
- package/lib/guard-limits.js +1 -77
- package/lib/guard-list.js +1 -49
- package/lib/guard-name.js +3 -209
- package/lib/guard-parsed.js +7 -335
- package/lib/guard-range.js +1 -71
- package/lib/guard-secret.js +1 -102
- package/lib/guard-text.js +1 -64
- package/lib/guard-time.js +1 -97
- package/lib/hpke.js +3 -105
- package/lib/http-digest.js +14 -142
- package/lib/http-retry-after.js +1 -47
- package/lib/http-transport.js +21 -131
- package/lib/inspect.js +30 -284
- package/lib/ip-utils.js +6 -47
- package/lib/jose.js +7 -142
- package/lib/key.js +2 -67
- package/lib/lint.js +9 -194
- package/lib/merkle.js +1 -27
- package/lib/mime.js +9 -82
- package/lib/ocsp-request-verify.js +1 -14
- package/lib/ocsp-verify.js +1 -82
- package/lib/ocsp.js +11 -169
- package/lib/oid.js +5 -337
- package/lib/path-validate.js +103 -1492
- package/lib/pbes2.js +2 -89
- package/lib/pkcs12-build.js +31 -379
- package/lib/pki-build.js +64 -290
- package/lib/rc2.js +2 -18
- package/lib/rfc3339.js +4 -27
- package/lib/scep.js +140 -286
- package/lib/schema-all.js +0 -122
- package/lib/schema-attrcert.js +4 -146
- package/lib/schema-c509.js +153 -802
- package/lib/schema-cmc.js +5 -220
- package/lib/schema-cmp.js +2 -223
- package/lib/schema-cms.js +13 -427
- package/lib/schema-crl.js +5 -68
- package/lib/schema-crmf.js +6 -167
- package/lib/schema-csr.js +1 -54
- package/lib/schema-csrattrs.js +2 -87
- package/lib/schema-engine.js +0 -152
- package/lib/schema-ocsp.js +0 -117
- package/lib/schema-pkcs12.js +2 -93
- package/lib/schema-pkcs8.js +3 -35
- package/lib/schema-pkix.js +24 -655
- package/lib/schema-smime.js +0 -54
- package/lib/schema-tsp.js +3 -99
- package/lib/schema-x509.js +4 -63
- package/lib/shbs.js +11 -95
- package/lib/sign-scheme.js +2 -97
- package/lib/sigstore.js +9 -169
- package/lib/sleep.js +2 -8
- package/lib/smime.js +15 -408
- package/lib/tls-cert-compress.js +0 -62
- package/lib/trust.js +19 -185
- package/lib/tsp-sign.js +16 -270
- package/lib/validator-all.js +1 -30
- package/lib/validator-attcert.js +2 -46
- package/lib/validator-cose.js +2 -153
- package/lib/validator-keydesc.js +1 -37
- package/lib/validator-sig.js +1 -46
- package/lib/validator-tls.js +1 -88
- package/lib/validator-tpm.js +19 -122
- package/lib/verify-seam.js +1 -24
- package/lib/webauthn-mds.js +3 -400
- package/lib/webauthn.js +12 -833
- package/lib/webcrypto.js +5 -485
- package/lib/x509-sign.js +4 -108
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/guard-parsed.js
CHANGED
|
@@ -1,126 +1,32 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
// Copyright (c) blamejs contributors
|
|
3
3
|
"use strict";
|
|
4
|
-
//
|
|
5
|
-
// @internal -- no operator-facing namespace. The documented surface is the verbs
|
|
6
|
-
// whose "a DER Buffer, a PEM string, or a parsed X" argument composes this guard
|
|
7
|
-
// (pki.path.validate / build, pki.crl.verify / isRevoked, pki.x509.sign,
|
|
8
|
-
// pki.attrcert.sign, pki.ocsp.*, pki.cmp.session).
|
|
9
|
-
//
|
|
10
|
-
// guard-parsed -- fail-closed acceptance of a CLAIMED-parsed structure.
|
|
11
|
-
//
|
|
12
|
-
// Every verb that takes bytes also takes the parser's output, so a caller holding
|
|
13
|
-
// a parsed certificate need not re-encode and re-parse it. The convenience carries
|
|
14
|
-
// a trust boundary: the object arrives from the caller, not from the parser, and
|
|
15
|
-
// the code downstream reads its fields as though the parser had produced them.
|
|
16
|
-
//
|
|
17
|
-
// Defends the type-confusion / unverified-provenance class (CWE-843, CWE-345).
|
|
18
|
-
// Two distinct failures follow from accepting a claimed-parsed object on a partial
|
|
19
|
-
// duck-type test, and the toolkit has had both:
|
|
20
|
-
//
|
|
21
|
-
// - A missing field reads as an absent feature, never as a malformed input.
|
|
22
|
-
// `crlExtensions` omitted altogether made a scope-restricted CRL answer as an
|
|
23
|
-
// unrestricted one, because every scope guard reads the list as `(list || [])`.
|
|
24
|
-
// The guard cannot fire on a list that is not there.
|
|
25
|
-
// - A partial object reaches code that dereferences a field it lacks, and the
|
|
26
|
-
// verb throws a raw TypeError instead of its own typed error: a fault an
|
|
27
|
-
// operator cannot catch by code and a fuzz harness reports as a finding.
|
|
28
|
-
//
|
|
29
|
-
// The rule is completeness, checked at the door: an object claiming to be parser
|
|
30
|
-
// output must carry every field the consuming code dereferences, each with the type
|
|
31
|
-
// that code assumes. It is not a signature check and does not pretend to be. An
|
|
32
|
-
// object can still describe a certificate that never existed, which is why the
|
|
33
|
-
// fields a decision depends on are re-derived from `tbsBytes` where that matters.
|
|
34
|
-
// What it buys is that "parsed" means one thing at every door, so a shape refused
|
|
35
|
-
// by `pki.path.build` is not accepted by `pki.path.validate`.
|
|
36
|
-
//
|
|
37
|
-
// Each entry point keeps its own typed error: `E` is the caller's (code, message)
|
|
38
|
-
// factory and `code` its own domain reason, so routing a door through this guard
|
|
39
|
-
// changes what is checked, never what the operator reads.
|
|
4
|
+
// @internal
|
|
40
5
|
|
|
41
6
|
var bytes = require("./guard-bytes");
|
|
42
7
|
var time = require("./guard-time");
|
|
43
|
-
// The array test, taken at load. Every shape check below asks it whether a field IS a list, and
|
|
44
|
-
// one answering `false` makes each of those checks reject a well-formed parsed structure -- the
|
|
45
|
-
// fail-closed direction, and still a caller deciding what this guard recognizes.
|
|
46
8
|
var _isArray = require("./guard-intrinsic").isArray;
|
|
47
9
|
var _freezeExports = require("./guard-intrinsic").freeze;
|
|
48
|
-
// The copy that becomes the recorded provenance bytes. A replacement returning a buffer over the
|
|
49
|
-
// caller's own memory would let those bytes change after they were recorded, which is the one
|
|
50
|
-
// thing provenance exists to prevent.
|
|
51
10
|
var _bufferFrom = require("./guard-intrinsic").bufferFrom;
|
|
52
|
-
// The sentinel the chain walk below stops at. Reached as a property of a replaceable global, a
|
|
53
|
-
// replacement makes the walk run past the top and report inherited names as the value's own.
|
|
54
11
|
var _ObjectProto = require("./guard-intrinsic").ObjectProto;
|
|
55
|
-
// The rest of what this module reads from the runtime, taken at load. The shape checks below ask
|
|
56
|
-
// these what a value IS and how it is put together, so a replacement decides what this guard
|
|
57
|
-
// recognizes as parser output. See guard-intrinsic for the whole captured set.
|
|
58
12
|
var _isBuffer = require("./guard-intrinsic").isBuffer;
|
|
59
13
|
var _create = require("./guard-intrinsic").create;
|
|
60
14
|
var _assign = require("./guard-intrinsic").assign;
|
|
61
15
|
var _getPrototypeOf = require("./guard-intrinsic").getPrototypeOf;
|
|
62
16
|
var _getOwnPropertyNames = require("./guard-intrinsic").getOwnPropertyNames;
|
|
63
|
-
// The array operations, uncurried. Each shape check below asks `every` whether a list's members
|
|
64
|
-
// are all well formed, so one replaced to return true makes every malformed list pass.
|
|
65
17
|
var _every = require("./guard-intrinsic").every;
|
|
66
|
-
// The provenance store's own operations. This WeakMap is what says a value came out of this
|
|
67
|
-
// toolkit's parser, so a `get` that answers for a key never stored hands the door a forged
|
|
68
|
-
// provenance record, and a `has` that answers true admits an object the parser never produced.
|
|
69
18
|
var _weakGet = require("./guard-intrinsic").weakGet;
|
|
70
19
|
var _weakSet = require("./guard-intrinsic").weakSet;
|
|
71
20
|
var _weakHas = require("./guard-intrinsic").weakHas;
|
|
72
21
|
var _WeakMap = require("./guard-intrinsic").WeakMap;
|
|
73
22
|
var _defineProperty = require("./guard-intrinsic").defineProperty;
|
|
74
|
-
// Appended by index rather than through `push`, so the growth reads no prototype method at all.
|
|
75
23
|
function _append(arr, v) { _defineProperty(arr, arr.length, { value: v, writable: true, enumerable: true, configurable: true }); }
|
|
76
24
|
|
|
77
|
-
// Every form the toolkit calls "bytes": a Buffer, any typed-array view, a DataView, or a bare
|
|
78
|
-
// ArrayBuffer. Testing `_isBuffer(x) || x instanceof Uint8Array` is narrower than what the
|
|
79
|
-
// verbs document, and the narrowness is invisible in the good case: an ArrayBuffer would fall
|
|
80
|
-
// through to the object branch and be refused as a rebuilt structure, which reads to the caller as
|
|
81
|
-
// "your certificate is malformed" when what happened is that their container was not recognized.
|
|
82
|
-
// guard.bytes is what finally reads them, and it accepts all four, so the door has to as well.
|
|
83
|
-
// Asked of guard.bytes rather than restated here, because a restatement is what drifts. An
|
|
84
|
-
// `x instanceof ArrayBuffer` here turns away a real buffer built in another realm that guard.bytes
|
|
85
|
-
// goes on to accept, so one container gets opposite answers from the two doors it passes through.
|
|
86
25
|
function _isBytes(x) {
|
|
87
26
|
return bytes.isByteSource(x);
|
|
88
27
|
}
|
|
89
28
|
|
|
90
|
-
// The shapes below assert the parser's complete output: every field
|
|
91
|
-
// pki.schema.x509.parse and pki.schema.crl.parse assign, with the type each is
|
|
92
|
-
// assigned, and not the subset some consumer happens to dereference today.
|
|
93
|
-
//
|
|
94
|
-
// The distinction is the whole point. A shape derived from one consumer's reads is
|
|
95
|
-
// correct for that consumer and silently incomplete at every other door it gets
|
|
96
|
-
// reused at, and the gap is invisible: the missing field is not named
|
|
97
|
-
// anywhere, so nothing fails until a caller supplies an object without it. This
|
|
98
|
-
// guard shipped derived from the path validator's reads, and three fields the other
|
|
99
|
-
// doors depend on were absent: `serialNumber`, which pki.ocsp.buildRequest encodes
|
|
100
|
-
// into the request; `issuer.bytes`, which the OCSP responder-identity comparison
|
|
101
|
-
// reads; and an extension entry's `critical`, whose ABSENCE reads as non-critical to
|
|
102
|
-
// `if (!ext.critical) continue`, so an unknown critical extension passes unhandled.
|
|
103
|
-
//
|
|
104
|
-
// Anchoring on the parser removes the enumeration step that produced those gaps: the
|
|
105
|
-
// only sanctioned way to obtain one of these objects is to run the parser, the parser
|
|
106
|
-
// assigns every field unconditionally, and so "complete" needs no consumer census and
|
|
107
|
-
// does not drift when a new consumer reads a new field. Adding a field to a parser's
|
|
108
|
-
// output belongs here in the same change; the api-snapshot pins that surface, so
|
|
109
|
-
// the two move together.
|
|
110
29
|
|
|
111
|
-
// A directory name. `dn` is the RFC 4514 string, `bytes` the raw encoded Name that a
|
|
112
|
-
// byte-level issuer/subject comparison hashes, and `rdns` the structure the RFC 5280
|
|
113
|
-
// sec. 7.1 comparison walks.
|
|
114
|
-
//
|
|
115
|
-
// The walk goes all the way to the attributes. `rdns` being an array is not the
|
|
116
|
-
// property anything downstream depends on: guard-name's dnEqual compares each
|
|
117
|
-
// attribute's `type` and canonicalizes its `value`, and the WebAuthn subject-CN
|
|
118
|
-
// lookup reads `name` and `value`, so an array of anything would pass a
|
|
119
|
-
// shallow test and reach code that dereferences three fields it does not have.
|
|
120
|
-
// Name ::= RDNSequence ::= SEQUENCE OF RelativeDistinguishedName, and an RDN is a
|
|
121
|
-
// SET SIZE (1..MAX), so the parser produces an array of non-empty arrays; the
|
|
122
|
-
// attribute value is always a string, either the decoded directory string or the
|
|
123
|
-
// RFC 4514 "#hex" form the parser falls back to for a type it does not decode.
|
|
124
30
|
function _isAttributeTypeAndValue(a) {
|
|
125
31
|
return !!a && typeof a.type === "string" && _isOptName(a.name) && typeof a.value === "string";
|
|
126
32
|
}
|
|
@@ -136,8 +42,6 @@ function _isName(n) {
|
|
|
136
42
|
return !!n && _isRdnSequence(n.rdns) && _isBuffer(n.bytes) && typeof n.dn === "string";
|
|
137
43
|
}
|
|
138
44
|
|
|
139
|
-
// `name` is the registry lookup of the OID and is null for an OID the registry does
|
|
140
|
-
// not carry, so the assertion is on the KEY's presence with the parser's own type.
|
|
141
45
|
function _isOptName(v) { return typeof v === "string" || v === null; }
|
|
142
46
|
|
|
143
47
|
function _isAlgorithmIdentifier(a) {
|
|
@@ -149,35 +53,17 @@ function _isBitString(b) {
|
|
|
149
53
|
return !!b && _isBuffer(b.bytes) && typeof b.unusedBits === "number";
|
|
150
54
|
}
|
|
151
55
|
|
|
152
|
-
// A parsed certificate extension entry: dispatched on `.oid`, gated on `.critical`,
|
|
153
|
-
// and its `.value` the raw extension bytes, which the certificate parser leaves
|
|
154
|
-
// undecoded. An entry carrying a `.name` but no `.oid` is the shape that slips past
|
|
155
|
-
// an OID-keyed lookup while reading as present to a human; one carrying no
|
|
156
|
-
// `.critical` is the shape that turns a fail-closed unknown-critical check into a
|
|
157
|
-
// skipped iteration.
|
|
158
56
|
function _isExtensionEntry(e) {
|
|
159
57
|
return !!e && typeof e.oid === "string" && _isOptName(e.name) &&
|
|
160
58
|
typeof e.critical === "boolean" && _isBuffer(e.value);
|
|
161
59
|
}
|
|
162
60
|
|
|
163
|
-
// A parsed CRL extension entry. The CRL parser decodes the values it knows (a
|
|
164
|
-
// cRLNumber surfaces as a BigInt, a reasonCode as a Number), so the value type
|
|
165
|
-
// varies by extension and only its presence can be asserted here. The dispatch key
|
|
166
|
-
// and the criticality flag are the same as a certificate's, and both decide.
|
|
167
61
|
function _isCrlExtensionEntry(e) {
|
|
168
62
|
return !!e && typeof e.oid === "string" && _isOptName(e.name) &&
|
|
169
63
|
typeof e.critical === "boolean" && e.value !== undefined;
|
|
170
64
|
}
|
|
171
65
|
|
|
172
|
-
// isCert(o) -- the complete parsed-certificate shape.
|
|
173
|
-
//
|
|
174
|
-
// Exported as a predicate beside the throwing door because a caller deriving a
|
|
175
|
-
// dedupe key (cmp's extraCerts pool) must answer "not usable" and never throw:
|
|
176
|
-
// a non-deduped entry is a redundant slot, while a key derived from a partial object
|
|
177
|
-
// could collapse two different certificates onto one.
|
|
178
|
-
//
|
|
179
66
|
// @enforced-by guard-shape-reinlined -- shares the `accept` shape below: any door
|
|
180
|
-
// testing tbsBytes and returning the object is the re-inline this replaces.
|
|
181
67
|
function isCert(o) {
|
|
182
68
|
return !!o && typeof o === "object" &&
|
|
183
69
|
_isBuffer(o.tbsBytes) &&
|
|
@@ -196,13 +82,6 @@ function isCert(o) {
|
|
|
196
82
|
_isArray(o.extensions) && _every(o.extensions, _isExtensionEntry);
|
|
197
83
|
}
|
|
198
84
|
|
|
199
|
-
// isCrl(o) -- the complete parsed-CertificateList shape. `crlExtensions` and each
|
|
200
|
-
// entry's `crlEntryExtensions` are required arrays, never optional ones: the
|
|
201
|
-
// scope guards read them as `(list || [])`, so an omitted list is indistinguishable
|
|
202
|
-
// from an empty one and the difference decides whether a serial may be answered.
|
|
203
|
-
// `nextUpdate` is the one genuinely optional field; the parser assigns null when
|
|
204
|
-
// the CRL omits it, and a validator distinguishes the two.
|
|
205
|
-
//
|
|
206
85
|
// @enforced-by guard-shape-reinlined -- shares the `accept` shape below.
|
|
207
86
|
function isCrl(o) {
|
|
208
87
|
return !!o && typeof o === "object" &&
|
|
@@ -222,51 +101,20 @@ function isCrl(o) {
|
|
|
222
101
|
});
|
|
223
102
|
}
|
|
224
103
|
|
|
225
|
-
// Neither predicate may throw. A caller-supplied object can define an accessor that
|
|
226
|
-
// throws on read, and a raw fault escaping a completeness check defeats the point of
|
|
227
|
-
// checking: the composing verb's contract is that every failure is its own typed
|
|
228
|
-
// error, and a dedupe caller using the predicate directly would get a raw fault where
|
|
229
|
-
// it expects a boolean. A property that cannot be read is not one the parser produced,
|
|
230
|
-
// so a throw is simply "not the shape". Wrapped here, and not at the door, because
|
|
231
|
-
// both are exported and the rule has to hold for both.
|
|
232
104
|
function _safe(shape) {
|
|
233
105
|
return function (o) {
|
|
234
106
|
try { return shape(o) === true; }
|
|
235
107
|
catch (_e) {
|
|
236
|
-
return false;
|
|
108
|
+
return false;
|
|
237
109
|
}
|
|
238
110
|
};
|
|
239
111
|
}
|
|
240
112
|
var certShape = _safe(isCert);
|
|
241
113
|
var crlShape = _safe(isCrl);
|
|
242
114
|
|
|
243
|
-
// Null-prototype: this table decides which kinds are recognized, and the lookup below is what the
|
|
244
|
-
// unknown-kind refusal rests on. Built on a `{}`, a planted `Object.prototype[kind]` supplies a
|
|
245
|
-
// predicate for a kind the table never carried, and an unrecognized structure is then authorized.
|
|
246
115
|
var _SHAPES = _assign(_create(null), { certificate: certShape, crl: crlShape });
|
|
247
116
|
|
|
248
|
-
// accept(input, kind, parse, E, code, label) -> the parsed structure | throws
|
|
249
|
-
//
|
|
250
|
-
// The door every "bytes or a parsed X" argument goes through. Bytes and PEM are
|
|
251
|
-
// handed to `parse`; an object claiming to be parser output (it carries the
|
|
252
|
-
// `tbsBytes` the parser always produces) must satisfy the complete shape for
|
|
253
|
-
// `kind`; anything else is a typed input fault naming what the argument accepts.
|
|
254
|
-
//
|
|
255
|
-
// The claim test is `tbsBytes !== undefined`, not a truthiness test: an object
|
|
256
|
-
// carrying `tbsBytes: null` is claiming to be parsed and failing, and must be told
|
|
257
|
-
// so, never sent to a byte parser that reports something unrelated.
|
|
258
|
-
//
|
|
259
|
-
// Reading the claim is itself a read of a caller-supplied object, so it goes through
|
|
260
|
-
// the same try/catch the shape walk does. A getter or Proxy trap that throws would
|
|
261
|
-
// otherwise escape from outside the guarded walk and reach the operator as a raw
|
|
262
|
-
// fault at every door composing this: the failure the walk is wrapped to prevent,
|
|
263
|
-
// one property earlier. An unreadable claim is treated as a claim made and failed,
|
|
264
|
-
// matching fromTrustedSource below: an object that cannot answer what it is has not
|
|
265
|
-
// come from the parser, and saying so is more useful than reporting its type.
|
|
266
|
-
//
|
|
267
117
|
// @enforced-by guard-shape-reinlined
|
|
268
|
-
// The shape is the partial duck-type acceptance this guard replaces: testing
|
|
269
|
-
// tbsBytes (with or without one companion field) and returning the object.
|
|
270
118
|
// @guard-shape (?:if\s*\(|return\s*\(?)[^;\n]*\.tbsBytes[^;\n]*\)\s*(?:\{\s*)?return\s+\w+\s*;
|
|
271
119
|
// @guard-via guard\.parsed\.(?:accept|isCert|isCrl)\(
|
|
272
120
|
function accept(input, kind, parse, E, code, label) {
|
|
@@ -278,7 +126,7 @@ function accept(input, kind, parse, E, code, label) {
|
|
|
278
126
|
if (input && typeof input === "object") {
|
|
279
127
|
try { claimsParsed = input.tbsBytes !== undefined; }
|
|
280
128
|
catch (_e) {
|
|
281
|
-
claimsParsed = true;
|
|
129
|
+
claimsParsed = true;
|
|
282
130
|
}
|
|
283
131
|
}
|
|
284
132
|
if (claimsParsed) {
|
|
@@ -290,91 +138,14 @@ function accept(input, kind, parse, E, code, label) {
|
|
|
290
138
|
throw E(code, who + " must be a " + kind + " DER Buffer, a PEM string, or a parsed " + kind);
|
|
291
139
|
}
|
|
292
140
|
|
|
293
|
-
// ---- provenance: the parser's own mark -------------------------------------
|
|
294
|
-
//
|
|
295
|
-
// Some verbs cannot be satisfied by completeness alone. A signature check has three
|
|
296
|
-
// parts -- the signature, the algorithm that verifies it, and the byte range it
|
|
297
|
-
// covers -- and on a parsed object all three are separate properties, each of them
|
|
298
|
-
// individually well-formed. Pair a real CA's signature over a certificate it issued
|
|
299
|
-
// with that certificate's own signed bytes and algorithm, relabel the three, and
|
|
300
|
-
// every part of the check passes for a structure that never existed. A MAC check has
|
|
301
|
-
// the same shape: the range MACed and the content returned as verified are two
|
|
302
|
-
// properties, so one object can say "verify this" and "return that".
|
|
303
|
-
//
|
|
304
|
-
// What those verbs need is not a shape but a PROVENANCE: these fields were derived
|
|
305
|
-
// together, from one byte string, by the parser. So the parser records the bytes it
|
|
306
|
-
// derived the object from, and the door derives the structure it verifies from those
|
|
307
|
-
// bytes again.
|
|
308
|
-
//
|
|
309
|
-
// Recording the bytes, and not a boolean, is what makes this hold. A flag saying
|
|
310
|
-
// "this came from the parser" is defeated by keeping a legitimately parsed object and
|
|
311
|
-
// then assigning over its fields: the flag is still there and now describes
|
|
312
|
-
// something else. Re-deriving from the recorded bytes discards every later edit,
|
|
313
|
-
// because the verdict is computed from the byte string and not from the object the
|
|
314
|
-
// caller is holding. The object a caller passes is therefore a way of naming bytes,
|
|
315
|
-
// which is all it was ever safe for it to be.
|
|
316
|
-
//
|
|
317
|
-
// The record is kept off the object, in a WeakMap the parser owns, keyed by the
|
|
318
|
-
// returned object itself. Nothing about the record is reachable through the object,
|
|
319
|
-
// which is what makes it unforgeable:
|
|
320
|
-
//
|
|
321
|
-
// - A property, however hidden, is read through the object, and every read of an
|
|
322
|
-
// object is interceptable. A Proxy's getOwnPropertyDescriptor and get traps are
|
|
323
|
-
// handed whatever key they are asked about (the symbol need not be known to be
|
|
324
|
-
// answered for), so a Proxy could claim any mark and name any bytes. A WeakMap
|
|
325
|
-
// lookup is on identity: a Proxy is a different object from the parser's result
|
|
326
|
-
// and simply is not a key.
|
|
327
|
-
// - `_create(parsed)` is likewise a different object, so it inherits no
|
|
328
|
-
// record, while `_assign({}, parsed)` and `{...parsed}` are different
|
|
329
|
-
// objects too. All three, the ways a mixed structure is actually assembled,
|
|
330
|
-
// fall out of the same rule, with no need for three checks.
|
|
331
|
-
//
|
|
332
|
-
// The recorded bytes are copied. Recording the caller's Buffer by reference would let
|
|
333
|
-
// it be overwritten between the parse and the verify, which is the same defeat by
|
|
334
|
-
// another route: the door would faithfully re-derive from bytes that had changed
|
|
335
|
-
// underneath it. A PEM string needs no copy, strings being immutable.
|
|
336
|
-
//
|
|
337
|
-
// This is not a signature check and does not pretend to be. It says only "verify the
|
|
338
|
-
// bytes this object was parsed from", which is precisely the claim the three-part
|
|
339
|
-
// check was resting on without ever asking for it.
|
|
340
141
|
var PROVENANCE = new _WeakMap();
|
|
341
142
|
|
|
342
|
-
// recordingParser(kind, parse) -> a parser that records what it parsed.
|
|
343
|
-
//
|
|
344
|
-
// Recording is not a separate step a caller could perform, and the record is not a
|
|
345
|
-
// value a caller can read back. Both follow from the same requirement: the record has
|
|
346
|
-
// to mean "the parser derived this object from these exact bytes", and every way of
|
|
347
|
-
// exposing it weakens that to something else.
|
|
348
|
-
//
|
|
349
|
-
// - An exported `mark(obj, kind, bytes)` would let any code assert provenance for an
|
|
350
|
-
// object it did not parse, or overwrite the provenance of one it did not produce.
|
|
351
|
-
// The only way to obtain a record is therefore to actually run the parse.
|
|
352
|
-
// - An exported `sourceOf(obj)` would hand back the recorded Buffer, which the caller
|
|
353
|
-
// could then overwrite, defeating the copy by writing through the value the copy
|
|
354
|
-
// was made to protect.
|
|
355
|
-
//
|
|
356
|
-
// The snapshot is taken before the parse and the parser is given the snapshot, so the
|
|
357
|
-
// bytes the parser reads and the bytes the record names are the same object by
|
|
358
|
-
// construction. Taking it afterwards from the caller's argument would leave a window:
|
|
359
|
-
// a typed-array subclass with stateful `byteOffset` / `byteLength` getters, or any
|
|
360
|
-
// SharedArrayBuffer, can present one set of bytes to the parser and another to a later
|
|
361
|
-
// copy. guard.bytes.snapshot reads through the BufferSource contract and copies.
|
|
362
|
-
//
|
|
363
143
|
// @enforced-by guard-shape-reinlined -- shares the fromTrustedSource shape below; a
|
|
364
|
-
// door re-testing the claim fields itself is the re-inline both replace.
|
|
365
144
|
function recordingParser(kind, parse, ErrorClass, code, label) {
|
|
366
145
|
return function (input) {
|
|
367
|
-
var isText = typeof input === "string";
|
|
146
|
+
var isText = typeof input === "string";
|
|
368
147
|
var snap = isText ? input : bytes.snapshotSource(input, ErrorClass, code, label);
|
|
369
148
|
var out = parse(snap);
|
|
370
|
-
// The record gets its own copy, not the buffer the parser just read.
|
|
371
|
-
//
|
|
372
|
-
// A parser surfaces raw byte ranges (tbsBytes, a Name's encoded form, an extension's value)
|
|
373
|
-
// as views onto the buffer it parsed, which is what makes them exact and cheap. So the object
|
|
374
|
-
// handed back reaches into that buffer, and a caller holding it can write through those views:
|
|
375
|
-
// `parsed.tbsBytes.fill(0)` would then overwrite the very bytes the record names, and the
|
|
376
|
-
// re-derivation would faithfully reproduce whatever was written. A record the object can reach
|
|
377
|
-
// is not a record of anything. This copy is never handed out and nothing aliases it.
|
|
378
149
|
if (out && typeof out === "object") {
|
|
379
150
|
_weakSet(PROVENANCE, out, { kind: kind, source: isText ? snap : _bufferFrom(snap),
|
|
380
151
|
shape: _shapeOf(out) });
|
|
@@ -383,25 +154,7 @@ function recordingParser(kind, parse, ErrorClass, code, label) {
|
|
|
383
154
|
};
|
|
384
155
|
}
|
|
385
156
|
|
|
386
|
-
// recordingWalker(kind, walkNode, decodeBytes) -> function (node) -> the walked structure.
|
|
387
|
-
//
|
|
388
|
-
// The recordingParser sibling for a producer handed an already-decoded node, not bytes.
|
|
389
|
-
// One structure needs it: the RFC 7292 authSafe's SignedData. A PFX is decoded BER-tolerantly
|
|
390
|
-
// because real stores carry the indefinite-length encoding, so its inner SignedData cannot be
|
|
391
|
-
// re-derived by the strict `parse` entry the byte doors use; that entry would refuse a store
|
|
392
|
-
// this toolkit accepts today. The record therefore names the bytes and how to walk them again.
|
|
393
|
-
//
|
|
394
|
-
// The same three properties hold as for recordingParser, and for the same reasons. The record can
|
|
395
|
-
// only be obtained by actually running the walk, so no code can assert provenance for a structure
|
|
396
|
-
// it did not produce. The recorded bytes are a private copy of the node's, because the walked
|
|
397
|
-
// result surfaces views onto the buffer the PFX was decoded from and a caller holding one could
|
|
398
|
-
// otherwise write through it into the very bytes the record names. And `derive` re-runs this same
|
|
399
|
-
// walker over that copy, so a re-derivation reproduces the structure exactly: the decode is the
|
|
400
|
-
// producer's own, never a stricter one substituted at the door.
|
|
401
|
-
//
|
|
402
157
|
// @enforced-by guard-shape-reinlined -- shares recordingParser's shape below: the PROVENANCE.set
|
|
403
|
-
// that binds a structure to the bytes it was derived from appears only in this module, so no
|
|
404
|
-
// producer elsewhere can mint a record for a structure it did not itself walk.
|
|
405
158
|
function recordingWalker(kind, walkNode, decodeBytes) {
|
|
406
159
|
return function (node) {
|
|
407
160
|
var out = walkNode(node);
|
|
@@ -417,46 +170,19 @@ function recordingWalker(kind, walkNode, decodeBytes) {
|
|
|
417
170
|
};
|
|
418
171
|
}
|
|
419
172
|
|
|
420
|
-
//
|
|
421
|
-
// property of `obj` at all -- the lookup is on the object's identity.
|
|
173
|
+
// @internal
|
|
422
174
|
function _recordOf(obj, kind) {
|
|
423
175
|
if (!obj || typeof obj !== "object") return undefined;
|
|
424
176
|
var rec = _weakGet(PROVENANCE, obj);
|
|
425
177
|
return (rec && rec.kind === kind) ? rec : undefined;
|
|
426
178
|
}
|
|
427
179
|
|
|
428
|
-
// isRecorded(obj) -> whether this exact object carries a provenance record, of any kind.
|
|
429
|
-
//
|
|
430
|
-
// The record is keyed on identity, so a copy of a parsed structure, however faithful, is
|
|
431
|
-
// not the parsed structure: it carries no record and every door that decides integrity refuses
|
|
432
|
-
// it. That makes a recorded object a handle, not data, and anything that walks a caller's
|
|
433
|
-
// spec taking copies has to leave it alone. guard-bytes.snapshotDeep asks this before descending.
|
|
434
180
|
// @enforced-by behavioral -- a predicate has no rename-proof shape to detect, and re-inlining it is
|
|
435
|
-
// not possible outside this module: the registry it reads is a module-private WeakMap. The guard
|
|
436
|
-
// is the RED vector that passes a parsed certificate through a producing verb's spec and asserts
|
|
437
|
-
// the integrity door still accepts it, which fails the moment a copy is taken instead.
|
|
438
181
|
function isRecorded(obj) {
|
|
439
182
|
return !!obj && typeof obj === "object" && _weakHas(PROVENANCE, obj);
|
|
440
183
|
}
|
|
441
184
|
|
|
442
|
-
// isRecordedAsProduced(obj) -> whether this is a recorded structure that still has the field set
|
|
443
|
-
// the parser gave it.
|
|
444
|
-
//
|
|
445
|
-
// A recorded structure is safe to hand on by identity, because the doors that decide integrity
|
|
446
|
-
// re-derive it from the bytes the record names and ignore whatever the object says. That reasoning
|
|
447
|
-
// covers it as a parsed value. It does not cover a caller who takes a parse result, adds an option
|
|
448
|
-
// to it, and passes it where a verb reads options by name: those fields no door re-derives, so
|
|
449
|
-
// they stay the caller's to change. The record therefore also names the shape the parser produced,
|
|
450
|
-
// and a structure that has grown fields since is no longer only a parse result.
|
|
451
|
-
// Only an added field matters. Deleting one changes nothing (the door re-derives the structure
|
|
452
|
-
// from the bytes the record names, so a missing property is not a missing value), and a caller
|
|
453
|
-
// pruning a parse result is a case the shipped vectors cover deliberately. What re-derivation does
|
|
454
|
-
// not reach is a name the parser never produced, which is exactly what an option added to a parse
|
|
455
|
-
// result is.
|
|
456
185
|
// @enforced-by behavioral -- a predicate has no rename-proof shape to detect, and the registry it
|
|
457
|
-
// reads is a module-private WeakMap so nothing outside can re-inline it. The guard is the RED
|
|
458
|
-
// vector that adds an option to a parse result, passes it where options are read by name, and
|
|
459
|
-
// asserts the value the verb used is the one that was there at entry.
|
|
460
186
|
function isRecordedAsProduced(obj) {
|
|
461
187
|
if (!isRecorded(obj)) return false;
|
|
462
188
|
var shape = _weakGet(PROVENANCE, obj).shape;
|
|
@@ -474,9 +200,6 @@ function _shapeOf(obj) {
|
|
|
474
200
|
return shape;
|
|
475
201
|
}
|
|
476
202
|
|
|
477
|
-
// Every name a `obj.field` lookup could resolve: own and inherited, enumerable or not. A verb
|
|
478
|
-
// reads an option by name and does not care how it was defined, so a comparison that only saw
|
|
479
|
-
// `Object.keys` would miss a field added with defineProperty and call the object unchanged.
|
|
480
203
|
function _allNames(obj) {
|
|
481
204
|
var names = [];
|
|
482
205
|
var seen = _create(null);
|
|
@@ -491,34 +214,18 @@ function _allNames(obj) {
|
|
|
491
214
|
return names;
|
|
492
215
|
}
|
|
493
216
|
|
|
494
|
-
// fromTrustedSource(input, kind, claimFields, parse, E, code, why) -> the parsed
|
|
495
|
-
// structure | throws.
|
|
496
|
-
//
|
|
497
|
-
// The door for a verb that decides integrity. Bytes are parsed. An object carrying
|
|
498
|
-
// the parser's record is re-parsed from the bytes that record names, so a caller
|
|
499
|
-
// may keep passing the parsed form, and anything done to that object since it was
|
|
500
|
-
// parsed is discarded, never trusted. An object that claims to be parser output
|
|
501
|
-
// (it carries a field only a parsed structure of this kind has) but carries no
|
|
502
|
-
// record was not produced by the parser, and is refused with the caller's own reason.
|
|
503
|
-
//
|
|
504
|
-
// `claimFields` exist so that refusal names what happened, instead of handing the
|
|
505
|
-
// object to a byte parser that would report something about its type.
|
|
506
|
-
//
|
|
507
217
|
// @enforced-by guard-shape-reinlined
|
|
508
218
|
// @guard-shape (?:responseStatus|integrityMode|macedBytes|tbsResponseDataBytes)\s*!==\s*undefined
|
|
509
219
|
// @guard-via guard\.parsed\.(?:fromTrustedSource|recordingParser)\(
|
|
510
220
|
function fromTrustedSource(input, kind, claimFields, parse, E, code, why) {
|
|
511
221
|
if (input && typeof input === "object" && !_isBytes(input)) {
|
|
512
|
-
// A structure the walker recorded carries its own way back; the byte doors re-derive with the
|
|
513
|
-
// parser they were given.
|
|
514
222
|
var rec = _recordOf(input, kind);
|
|
515
223
|
if (rec !== undefined) return (rec.derive || parse)(rec.source);
|
|
516
224
|
for (var i = 0; i < claimFields.length; i++) {
|
|
517
|
-
// A claim field can be an accessor that throws; a guard answers, it does not relay.
|
|
518
225
|
var claims;
|
|
519
226
|
try { claims = input[claimFields[i]] !== undefined; }
|
|
520
227
|
catch (_e) {
|
|
521
|
-
claims = true;
|
|
228
|
+
claims = true;
|
|
522
229
|
}
|
|
523
230
|
if (claims) throw E(code, why);
|
|
524
231
|
}
|
|
@@ -526,34 +233,6 @@ function fromTrustedSource(input, kind, claimFields, parse, E, code, why) {
|
|
|
526
233
|
return parse(input);
|
|
527
234
|
}
|
|
528
235
|
|
|
529
|
-
// ---- the certificate / CRL door --------------------------------------------
|
|
530
|
-
//
|
|
531
|
-
// acceptDerived(input, kind, parse, E, code, label) -> the parsed structure | throws.
|
|
532
|
-
//
|
|
533
|
-
// The single door for a certificate or a CRL, at every boundary that takes one. Bytes and PEM are
|
|
534
|
-
// parsed; the parser's own result is re-derived from the bytes it recorded; an object that claims to
|
|
535
|
-
// be one without that record is refused.
|
|
536
|
-
//
|
|
537
|
-
// It is one rule for every door, and never a rule for the doors that "reach a verdict", because
|
|
538
|
-
// that judgment is the thing that keeps being got wrong. A certificate is a trust decision wherever
|
|
539
|
-
// it appears: an issuer certificate handed to a signer names who the new certificate says issued it;
|
|
540
|
-
// a root handed to an attestation check is the anchor; a certificate handed to a request builder
|
|
541
|
-
// names what the request asks about. Sorting them into verdict and non-verdict doors invites exactly
|
|
542
|
-
// the substitution this prevents at whichever door was sorted wrong, so no door is sorted at all.
|
|
543
|
-
//
|
|
544
|
-
// The claim fields are the ones a parsed structure has and a byte input does not, so an object that
|
|
545
|
-
// was rebuilt is told what happened, never handed to a byte parser that would report something
|
|
546
|
-
// about its type.
|
|
547
|
-
//
|
|
548
|
-
// This list is a TYPE DETECTOR, not the integrity boundary, and reads as the latter if skimmed.
|
|
549
|
-
// Nothing is trusted because it appears here, and no field is unprotected because it does not:
|
|
550
|
-
// fromTrustedSource re-derives the WHOLE structure from the bytes the provenance record names, so a
|
|
551
|
-
// field edited on a parse result is discarded whether or not it is listed. Adding every
|
|
552
|
-
// security-relevant field would therefore change no verdict. What the list decides is narrower: it
|
|
553
|
-
// separates "an object claiming to be a parse result" from "bytes", so the first gets this domain's
|
|
554
|
-
// typed refusal instead of being handed to a byte parser.
|
|
555
|
-
// Null-prototype, for the same reason as _SHAPES: an inherited entry standing in for an absent one
|
|
556
|
-
// makes an unrecognized kind look recognized, and the refusal keyed on its absence never fires.
|
|
557
236
|
var _CLAIMS = _assign(_create(null), {
|
|
558
237
|
certificate: ["tbsBytes", "subjectPublicKeyInfo", "serialNumberHex"],
|
|
559
238
|
crl: ["tbsBytes", "revokedCertificates", "crlExtensions"],
|
|
@@ -571,21 +250,16 @@ var _WHY = _assign(_create(null), {
|
|
|
571
250
|
attributeCertificate: "the signed byte range, the holder the privileges attach to and the attributes granting them are separate properties of a parsed object, so a rebuilt attribute certificate could have them describe different grants: keep a genuine issuer's signed bytes and signature, replace only the holder or an attribute, and the signature still verifies while the privileges are read for someone the issuer never granted them to",
|
|
572
251
|
});
|
|
573
252
|
// @enforced-by guard-shape-reinlined -- shares the fromTrustedSource shape it composes: a door that
|
|
574
|
-
// tests the claim fields itself, instead of routing here, is the re-inline both replace.
|
|
575
253
|
function acceptDerived(input, kind, parse, E, code, label) {
|
|
576
254
|
var claims = _CLAIMS[kind];
|
|
577
255
|
if (!claims) throw new TypeError("guard.parsed.acceptDerived: unknown kind " + kind);
|
|
578
256
|
var who = label || "the argument";
|
|
579
|
-
// An object that is neither bytes nor a claim is named as the wrong type here, never handed
|
|
580
|
-
// to the byte parser. The parser would refuse it too, but with its own domain's code: a caller
|
|
581
|
-
// who passed the wrong thing to pki.path.validate should read a path/* fault, not an x509/* one
|
|
582
|
-
// from a layer they did not call.
|
|
583
257
|
if (input !== null && input !== undefined && typeof input === "object" && !_isBytes(input)) {
|
|
584
258
|
var claimsSomething = false;
|
|
585
259
|
for (var i = 0; i < claims.length && !claimsSomething; i++) {
|
|
586
260
|
try { claimsSomething = input[claims[i]] !== undefined; }
|
|
587
261
|
catch (_e) {
|
|
588
|
-
claimsSomething = true;
|
|
262
|
+
claimsSomething = true;
|
|
589
263
|
}
|
|
590
264
|
}
|
|
591
265
|
if (!claimsSomething) {
|
|
@@ -599,8 +273,6 @@ function acceptDerived(input, kind, parse, E, code, label) {
|
|
|
599
273
|
".parse result: " + _WHY[kind]);
|
|
600
274
|
}
|
|
601
275
|
|
|
602
|
-
// Frozen for the reason the family header gives: a boundary reads its guard off this object at the
|
|
603
|
-
// call, so a writable export would let a caller replace the check rather than pass it.
|
|
604
276
|
module.exports = _freezeExports({
|
|
605
277
|
accept: accept, acceptDerived: acceptDerived,
|
|
606
278
|
fromTrustedSource: fromTrustedSource, recordingParser: recordingParser,
|
package/lib/guard-range.js
CHANGED
|
@@ -1,67 +1,22 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
// Copyright (c) blamejs contributors
|
|
3
3
|
"use strict";
|
|
4
|
-
//
|
|
5
|
-
// @internal -- no operator-facing namespace. The documented surface is the
|
|
6
|
-
// parsers whose decoded-integer bounds compose this guard (pki.schema.x509 /
|
|
7
|
-
// crl / pkcs12 / cmp, pki.path.validate).
|
|
8
|
-
//
|
|
9
|
-
// guard-range -- fail-closed bound of an untrusted decoded integer before it is
|
|
10
|
-
// narrowed to a JS Number. Parse-time (Tier-2): the value is malformed content,
|
|
11
|
-
// so it throws the caller's typed PkiError, distinct from guard-limits, which
|
|
12
|
-
// validates an operator-supplied option at config time (Tier-1, TypeError).
|
|
13
|
-
//
|
|
14
|
-
// Defends the silent-narrowing class (a decoded ASN.1 INTEGER past 2^53 rounds
|
|
15
|
-
// when coerced to a Number, so a caller acting on the result, a pathLen,
|
|
16
|
-
// skipCerts, saltLength or iteration counter, acts on the wrong number;
|
|
17
|
-
// CWE-190 integer overflow / CWE-681 incorrect conversion). The bound and the
|
|
18
|
-
// narrow are atomic here: a caller cannot obtain the Number without the range
|
|
19
|
-
// having been enforced, so the value can never round silently and be acted on.
|
|
4
|
+
// @internal
|
|
20
5
|
|
|
21
|
-
// 2^31 - 1, the shared skip-cert / path-length / salt / iteration ceiling.
|
|
22
|
-
// This literal lives only here (a re-inline anywhere else is flagged: the
|
|
23
|
-
// guard-shape walk anchors on the literal). 2^53 - 1 is the safe-narrow ceiling.
|
|
24
|
-
// The safe-integer test, taken at load. It decides whether a decoded value may be narrowed to a
|
|
25
|
-
// Number at all, so one answering true for an unsafe magnitude reopens the precision loss this
|
|
26
|
-
// guard exists to prevent. See guard-intrinsic for the whole captured set.
|
|
27
6
|
var _isSafeInteger = require("./guard-intrinsic").isSafeInteger;
|
|
28
7
|
var _freeze = require("./guard-intrinsic").freeze;
|
|
29
|
-
// The narrow itself. This module exists to turn a bounded BigInt into a Number without losing the
|
|
30
|
-
// bound, and `Number` is a writable property of globalThis, so a replacement returns whatever it
|
|
31
|
-
// likes from a value this function has just certified as in range.
|
|
32
8
|
var _Number = require("./guard-intrinsic").Number;
|
|
33
|
-
// The widening half of the same job. A replacement returns a value the bound below is then applied
|
|
34
|
-
// to instead of the caller's, so the number that passes the range test is not the number that came
|
|
35
|
-
// in and is not the number handed back.
|
|
36
9
|
var _BigInt = require("./guard-intrinsic").BigInt;
|
|
37
10
|
|
|
38
11
|
var UINT31_MAX = 2147483647n;
|
|
39
12
|
var SAFE_MAX = 9007199254740991n;
|
|
40
13
|
var SAFE_MIN = -9007199254740991n;
|
|
41
|
-
// 2^64 - 1 (also spelled 0xffffffffffffffffn). The uint64 ceiling lives only here
|
|
42
14
|
// (both spellings are flagged as a re-inline; see uint64's @guard-shape lines).
|
|
43
15
|
var UINT64_MAX = 18446744073709551615n;
|
|
44
16
|
|
|
45
|
-
// int(value, min, max, E, code, label) -> a Number in [min, max].
|
|
46
|
-
// value : the BigInt result of an ASN.1 INTEGER / ENUMERATED read.
|
|
47
|
-
// min, max : inclusive BigInt bounds.
|
|
48
|
-
// E : the (code, message, cause) typed-error factory in scope at the call
|
|
49
|
-
// site (ns.E / ctx.E / the module-local E) -- guard-range injects the
|
|
50
|
-
// caller's typed error through it, the tier-appropriate currency (a
|
|
51
|
-
// parse boundary carries a factory, not a bare ErrorClass).
|
|
52
|
-
// code : the frozen domain/reason code this field rejects under.
|
|
53
|
-
// label : field phrase (+ optional RFC cite) for the message.
|
|
54
17
|
// @enforced-by guard-shape-reinlined
|
|
55
18
|
// @guard-shape 2147483647n
|
|
56
19
|
function int(value, min, max, E, code, label) {
|
|
57
|
-
// Config-time authoring guard (Tier-1): the bounds must be BigInt. A Number
|
|
58
|
-
// bound (worst: NaN, which compares false against every BigInt) silently
|
|
59
|
-
// disables the range so any decoded value narrows. Then: narrowing to Number
|
|
60
|
-
// is lossless only when the whole range sits inside the safe-integer band
|
|
61
|
-
// [-(2^53-1), 2^53-1]. Both ends bind, since a min below the floor rounds a
|
|
62
|
-
// decoded value near it just as a max above the ceiling does. A wider range
|
|
63
|
-
// needs a BigInt-preserving guard, not this one, which is why the uint64
|
|
64
|
-
// Merkle-coordinate domain must not route here.
|
|
65
20
|
if (typeof min !== "bigint" || typeof max !== "bigint") {
|
|
66
21
|
throw new TypeError("guard.range.int: the min/max bounds must be BigInt");
|
|
67
22
|
}
|
|
@@ -71,30 +26,18 @@ function int(value, min, max, E, code, label) {
|
|
|
71
26
|
if (min < SAFE_MIN) {
|
|
72
27
|
throw new TypeError("guard.range.int: min " + min + " is below the safe-integer floor; use a BigInt-preserving guard");
|
|
73
28
|
}
|
|
74
|
-
// Parse-time reject (Tier-2): malformed / out-of-range decoded content.
|
|
75
29
|
if (typeof value !== "bigint" || value < min || value > max) {
|
|
76
30
|
throw E(code, label + " must be an integer within " + min + ".." + max);
|
|
77
31
|
}
|
|
78
32
|
return _Number(value);
|
|
79
33
|
}
|
|
80
34
|
|
|
81
|
-
// uint31(value, E, code, label) -> Number in [0, 2^31-1]. The DER-INTEGER
|
|
82
|
-
// non-negative skip/path/salt shape (RFC 5280 / 4211 / 9810).
|
|
83
35
|
// @enforced-by guard-shape-reinlined (shares the 2147483647n shape declared on int)
|
|
84
36
|
function uint31(value, E, code, label) { return int(value, 0n, UINT31_MAX, E, code, label); }
|
|
85
37
|
|
|
86
|
-
// positiveInt31(value, E, code, label) -> Number in [1, 2^31-1]. The PKCS#12
|
|
87
|
-
// PBKDF2 / MAC iteration-count shape (a count of at least one).
|
|
88
38
|
// @enforced-by guard-shape-reinlined (shares the 2147483647n shape declared on int)
|
|
89
39
|
function positiveInt31(value, E, code, label) { return int(value, 1n, UINT31_MAX, E, code, label); }
|
|
90
40
|
|
|
91
|
-
// uint64(value, E, code, label) -> a BigInt in [0, 2^64-1], without narrowing to
|
|
92
|
-
// Number. Accepts a non-negative safe-integer Number (widened to BigInt) or a
|
|
93
|
-
// BigInt; a Number past 2^53 (precision already lost), a negative, a non-integer,
|
|
94
|
-
// or a non-number-non-bigint throws the caller's typed verdict. The uint64 domain
|
|
95
|
-
// (a Merkle tree coordinate RFC 9162, an SCT timestamp RFC 6962) must not route
|
|
96
|
-
// through int(), since narrowing to Number would corrupt the very value it guards, so
|
|
97
|
-
// this is the BigInt-preserving sibling int()'s authoring guard points callers to.
|
|
98
41
|
// @enforced-by guard-shape-reinlined
|
|
99
42
|
// @guard-shape 18446744073709551615n
|
|
100
43
|
// @guard-shape 0xffffffffffffffffn
|
|
@@ -113,17 +56,6 @@ function uint64(value, E, code, label) {
|
|
|
113
56
|
return v;
|
|
114
57
|
}
|
|
115
58
|
|
|
116
|
-
// authoredInteger(value, E, code, label) -> BigInt. The AUTHORING counterpart to
|
|
117
|
-
// int(): a value a CALLER supplies for an ASN.1 INTEGER with no upper bound (a CMC
|
|
118
|
-
// Transaction Identifier, a CRMF certReqId), normalized to the BigInt the builders
|
|
119
|
-
// take. Accepts a bigint, or a Number that is a safe integer (isSafeInteger, not
|
|
120
|
-
// isInteger, because a Number above 2^53 has already lost precision by the time it
|
|
121
|
-
// arrives), so a large identifier MUST come as a bigint and cannot be silently
|
|
122
|
-
// rounded to a neighbor. Everything else is a config-time reject.
|
|
123
|
-
//
|
|
124
|
-
// Distinct from int() on purpose: int() BOUNDS a value the wire decoded and narrows
|
|
125
|
-
// it to Number; this one is unbounded and returns BigInt, because the field has no
|
|
126
|
-
// ceiling to check against and the encoder wants the wide type.
|
|
127
59
|
// @enforced-by guard-shape-reinlined
|
|
128
60
|
// @guard-shape Number\.isSafeInteger\(\s*[A-Za-z_$][\w$]*\s*\)\s*\)\s*return BigInt
|
|
129
61
|
function authoredInteger(value, E, code, label) {
|
|
@@ -132,8 +64,6 @@ function authoredInteger(value, E, code, label) {
|
|
|
132
64
|
throw E(code, label + " must be an integer (a safe-integer number, or a bigint for a large value)");
|
|
133
65
|
}
|
|
134
66
|
|
|
135
|
-
// Frozen for the reason the family header gives: a boundary reads its guard off this object at the
|
|
136
|
-
// call, so a writable export would let a caller replace the check rather than pass it.
|
|
137
67
|
module.exports = _freeze({
|
|
138
68
|
int: int, uint31: uint31, positiveInt31: positiveInt31, uint64: uint64,
|
|
139
69
|
authoredInteger: authoredInteger,
|