@blamejs/pki 0.6.13 → 0.6.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/CHANGELOG.md +22 -1
  2. package/README.md +1 -1
  3. package/index.js +1 -1
  4. package/lib/acme.js +59 -815
  5. package/lib/asn1-der.js +7 -245
  6. package/lib/attrcert-sign.js +12 -260
  7. package/lib/attrcert-verify.js +1 -7
  8. package/lib/byte-reader.js +0 -8
  9. package/lib/byte-writer.js +0 -11
  10. package/lib/cbor-det.js +0 -54
  11. package/lib/cmc-build.js +15 -357
  12. package/lib/cmc-verify.js +6 -239
  13. package/lib/cmp-build.js +41 -317
  14. package/lib/cmp-session.js +73 -760
  15. package/lib/cmp-verify.js +32 -488
  16. package/lib/cms-compress.js +3 -32
  17. package/lib/cms-decrypt.js +29 -294
  18. package/lib/cms-encrypt.js +14 -234
  19. package/lib/cms-sign.js +29 -324
  20. package/lib/cms-verify.js +9 -500
  21. package/lib/composite-kem.js +2 -102
  22. package/lib/composite-sig.js +1 -77
  23. package/lib/constants.js +0 -249
  24. package/lib/crl-sign.js +9 -237
  25. package/lib/crl-verify.js +1 -10
  26. package/lib/crmf-sign.js +19 -198
  27. package/lib/crmf-verify.js +1 -12
  28. package/lib/csr-sign.js +3 -34
  29. package/lib/csr-verify.js +1 -12
  30. package/lib/ct.js +28 -253
  31. package/lib/edwards-point.js +10 -44
  32. package/lib/est.js +28 -764
  33. package/lib/framework-error.js +4 -182
  34. package/lib/guard-all.js +1 -58
  35. package/lib/guard-async.js +1 -36
  36. package/lib/guard-bytes.js +6 -650
  37. package/lib/guard-compress.js +5 -102
  38. package/lib/guard-crypto.js +1 -40
  39. package/lib/guard-der.js +2 -93
  40. package/lib/guard-encoding.js +2 -60
  41. package/lib/guard-header.js +1 -36
  42. package/lib/guard-identifier.js +7 -531
  43. package/lib/guard-intrinsic.js +1 -150
  44. package/lib/guard-json.js +10 -73
  45. package/lib/guard-limits.js +1 -77
  46. package/lib/guard-list.js +1 -49
  47. package/lib/guard-name.js +3 -209
  48. package/lib/guard-parsed.js +7 -335
  49. package/lib/guard-range.js +1 -71
  50. package/lib/guard-secret.js +1 -102
  51. package/lib/guard-text.js +1 -64
  52. package/lib/guard-time.js +1 -97
  53. package/lib/hpke.js +3 -105
  54. package/lib/http-digest.js +14 -142
  55. package/lib/http-retry-after.js +1 -47
  56. package/lib/http-transport.js +21 -131
  57. package/lib/inspect.js +30 -284
  58. package/lib/ip-utils.js +6 -47
  59. package/lib/jose.js +7 -142
  60. package/lib/key.js +2 -67
  61. package/lib/lint.js +9 -194
  62. package/lib/merkle.js +1 -27
  63. package/lib/mime.js +9 -82
  64. package/lib/ocsp-request-verify.js +1 -14
  65. package/lib/ocsp-verify.js +1 -82
  66. package/lib/ocsp.js +11 -169
  67. package/lib/oid.js +5 -337
  68. package/lib/path-validate.js +103 -1492
  69. package/lib/pbes2.js +2 -89
  70. package/lib/pkcs12-build.js +31 -379
  71. package/lib/pki-build.js +64 -290
  72. package/lib/rc2.js +2 -18
  73. package/lib/rfc3339.js +4 -27
  74. package/lib/scep.js +59 -233
  75. package/lib/schema-all.js +0 -122
  76. package/lib/schema-attrcert.js +4 -146
  77. package/lib/schema-c509.js +153 -802
  78. package/lib/schema-cmc.js +5 -220
  79. package/lib/schema-cmp.js +2 -223
  80. package/lib/schema-cms.js +13 -427
  81. package/lib/schema-crl.js +5 -68
  82. package/lib/schema-crmf.js +6 -167
  83. package/lib/schema-csr.js +1 -54
  84. package/lib/schema-csrattrs.js +2 -87
  85. package/lib/schema-engine.js +0 -152
  86. package/lib/schema-ocsp.js +0 -117
  87. package/lib/schema-pkcs12.js +2 -93
  88. package/lib/schema-pkcs8.js +3 -35
  89. package/lib/schema-pkix.js +24 -655
  90. package/lib/schema-smime.js +0 -54
  91. package/lib/schema-tsp.js +3 -99
  92. package/lib/schema-x509.js +4 -63
  93. package/lib/shbs.js +11 -95
  94. package/lib/sign-scheme.js +2 -97
  95. package/lib/sigstore.js +9 -169
  96. package/lib/sleep.js +2 -8
  97. package/lib/smime.js +15 -408
  98. package/lib/tls-cert-compress.js +0 -62
  99. package/lib/trust.js +19 -185
  100. package/lib/tsp-sign.js +16 -270
  101. package/lib/validator-all.js +1 -30
  102. package/lib/validator-attcert.js +2 -46
  103. package/lib/validator-cose.js +2 -153
  104. package/lib/validator-keydesc.js +1 -37
  105. package/lib/validator-sig.js +1 -46
  106. package/lib/validator-tls.js +1 -88
  107. package/lib/validator-tpm.js +19 -122
  108. package/lib/verify-seam.js +1 -24
  109. package/lib/webauthn-mds.js +3 -400
  110. package/lib/webauthn.js +12 -833
  111. package/lib/webcrypto.js +5 -485
  112. package/lib/x509-sign.js +4 -108
  113. package/package.json +1 -1
  114. package/sbom.cdx.json +6 -6
@@ -1,127 +1,30 @@
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 KEM
6
- // key-establishment paths that compose this guard (pki.cms.encrypt / decrypt).
7
- //
8
- // guard-secret -- wipe a secret buffer the TOOLKIT ALLOCATED at the moment it
9
- // stops being needed. NIST SP 800-227 RS5 / sec. 4.2 requires that a KEM shared
10
- // secret and every intermediate value be destroyed as soon as they are no longer
11
- // needed; RFC 9629 sec. 7 says the same of the KEK a KEMRecipientInfo derives.
12
- //
13
- // The defended class is secret lifetime, not secret disclosure: a shared secret
14
- // or KEK left readable in the heap widens the window in which a later memory
15
- // disclosure -- a core dump, a swapped page, a same-process read primitive --
16
- // yields key material for traffic that was already decrypted.
17
- //
18
- // SCOPE, stated honestly because the docstring is the only place a reader learns
19
- // it: this is BEST EFFORT. The runtime copies buffers into places no JS can
20
- // reach (node's decapsulate return is copied on the way out; importKey("raw")
21
- // copies into a KeyObject), and V8 may relocate a backing store, leaving the
22
- // original bytes behind. Wiping the copies the toolkit holds shortens the
23
- // window. It does not support a claim that a secret never persists in memory,
24
- // and no operator-facing text may imply that it does.
25
- //
26
- // Ownership is the contract. Only a buffer the toolkit allocated may be wiped:
27
- // never a caller's opts.key / opts.cert / opts.kek / opts.password, and never the
28
- // input DER. Silently destroying a caller's own memory is a worse defect than
29
- // leaving a secret readable, and it is the failure mode a zeroization patch
30
- // reaches for first, so the call sites pass only their own intermediates.
4
+ // @internal
31
5
 
32
6
  var bytes = require("./guard-bytes");
33
7
  var intrinsic = require("./guard-intrinsic");
34
8
 
35
- // The fill this guard performs, taken from the typed-array prototype at module load and uncurried
36
- // so the wipe site reads no property at all. Buffer overrides the fill with a writable
37
- // `Buffer.prototype.fill`, so reading the method off the value would let a caller decide what
38
- // wiping means; reading `call` off the captured function would let them decide it again, since
39
- // that function is still the one sitting on the prototype (see guard-intrinsic).
40
9
  var _fill = intrinsic.uncurry(Uint8Array.prototype.fill);
41
- // The join that produces the buffer cipherFinish hands back. Captured for the same reason as
42
- // the fill: a replaced `Buffer.concat` decides what the caller receives, and it would receive it
43
- // while the two intermediates this module is responsible for clearing were cleared as promised.
44
10
  var _concat = intrinsic.bufferConcat;
45
11
 
46
- // zeroize(value, ErrorClass, code, label) -> the same object, cleared.
47
- // value : a Buffer / TypedArray the TOOLKIT allocated, or null / undefined
48
- // (absent is a no-op so a `finally` needs no branch around it).
49
- // ErrorClass : the caller's typed error CONSTRUCTOR, declared with
50
- // `{ withCause: true }`. The guard family carries two currencies --
51
- // most guards take a (code, message) factory and call it without
52
- // `new`, while guard-bytes / guard-header take the class and
53
- // construct it. This module's one throw is the delegated re-view
54
- // below, so it must pass what guard-bytes expects: the class, and
55
- // one that accepts a cause, because guard-bytes threads the raw
56
- // detach fault through as one. A plain class fails to construct at
57
- // the single moment the caller needs a real error.
58
- // code : the frozen domain/reason code a detached buffer rejects under.
59
- // label : field phrase for the message.
60
- //
61
- // A detached ArrayBuffer cannot be written, and reaching one here means a caller
62
- // handed over memory that was transferred away. That is a real fault and not
63
- // something to swallow, so it routes through the shared re-view guard and throws
64
- // typed.
65
- //
66
- // The `.fill(0)` shape lives only in this module. A wipe re-inlined anywhere in
67
- // lib/, including a module not yet written, is flagged, so the safe implementation
68
- // is also the tripwire that stops the next consumer from rolling its own partial
69
- // one.
70
12
  // @enforced-by guard-shape-reinlined
71
13
  // @guard-shape \.fill\s*\(\s*0\s*[,)]
72
14
  function zeroize(value, ErrorClass, code, label) {
73
15
  if (value === null || value === undefined) return value;
74
- // Re-view through the shared bytes guard: it is the single place that decides
75
- // what counts as a writable BufferSource and rejects a detached one typed.
76
16
  var view = bytes.view(value, ErrorClass, code, label);
77
- // Through the fill captured at module load, never `view.fill(0)`. Buffer overrides the typed-array
78
- // fill with its own, and that property is writable, so a caller who replaces it with a no-op turns
79
- // every wipe in the toolkit into a silent nothing -- and nothing observable changes, because the
80
- // wipe still runs, still walks the right buffer, and still returns. A secret that survives a call
81
- // whose whole purpose was to clear it is the failure this guard exists to prevent, so the one
82
- // operation it performs is the one operation a caller must not be able to reach.
83
17
  _fill(view, 0);
84
18
  return value;
85
19
  }
86
20
 
87
- // zeroizeAll(list, ErrorClass, code, label) wipes every present member, tolerating
88
- // holes so a `finally` can name intermediates that may not have been reached.
89
- //
90
21
  // @enforced-by behavioral -- this is a loop over zeroize, which carries the family's only
91
- // rename-proof shape (the `.fill(0)` above). It introduces no shape of its own, so a lexical
92
- // detector here would anchor on a renameable symbol and go silently green (drift rule sec. 3).
93
- // The behavioral guards are guard-secret.test.js (holes tolerated, every member cleared) and the
94
- // CMS vectors that assert the shared secret and KEK are wiped on both the success and failure paths.
95
22
  function zeroizeAll(list, ErrorClass, code, label) {
96
23
  if (!list) return list;
97
24
  for (var i = 0; i < list.length; i++) zeroize(list[i], ErrorClass, code, label);
98
25
  return list;
99
26
  }
100
27
 
101
- // cipherFinish(transform, input, ErrorClass, code, label) -> the output, as one fresh buffer
102
- // transform : a node:crypto Cipher or Decipher the CALLER built and configured (key, iv, AAD,
103
- // auth tag, padding mode). This guard performs the two calls and nothing else.
104
- // input : the bytes to feed it.
105
- //
106
- // The single place this toolkit runs a configured cipher to completion, because
107
- // `Buffer.concat([c.update(x), c.final()])` abandons a copy on BOTH of its exits and the leak is
108
- // invisible at the call site.
109
- //
110
- // On a DECIPHER the abandoned copy is plaintext. `update` returns the recovered plaintext while
111
- // `final` is what decides whether the message was authentic at all -- a GCM tag mismatch, a CBC
112
- // padding failure -- so on a forged message the whole plaintext exists before anything has judged
113
- // it, and the `concat` never runs: that buffer becomes unreferenced garbage holding readable
114
- // plaintext. RFC 5083 sec. 1 requires a receiver whose integrity check fails to destroy the
115
- // plaintext, and withholding it from the caller is not destroying it. On the success path `concat`
116
- // COPIES, so the update buffer is a second full copy the caller never receives and nothing else
117
- // will ever clear.
118
- //
119
- // It covers the ENCRYPT direction too, where the intermediates are ciphertext and clearing them
120
- // buys little. That is deliberate: any rule for which of the two directions must clear is a
121
- // judgment made at each call site, and the sites where it is wrong are the ones nobody re-reads.
122
- // One shape, cleared everywhere, needs no such judgment -- and the encrypt intermediates of a
123
- // key-wrap are not obviously worthless to an attacker either.
124
- //
125
28
  // @enforced-by guard-shape-reinlined
126
29
  // @guard-shape \.update\s*\([\s\S]{0,300}?\)\s*,\s*[A-Za-z_$][\w$]*\s*\.\s*final\s*\(\s*\)
127
30
  function cipherFinish(transform, input, ErrorClass, code, label) {
@@ -129,8 +32,6 @@ function cipherFinish(transform, input, ErrorClass, code, label) {
129
32
  try {
130
33
  head = transform.update(input);
131
34
  tail = transform.final();
132
- // Always a fresh copy, never `head` itself: the returned buffer belongs to the caller and the
133
- // two intermediates have to be clearable independently of it.
134
35
  return _concat([head, tail]);
135
36
  } finally {
136
37
  zeroize(head, ErrorClass, code, label);
@@ -138,6 +39,4 @@ function cipherFinish(transform, input, ErrorClass, code, label) {
138
39
  }
139
40
  }
140
41
 
141
- // Frozen for the reason the family header gives: a boundary reads its guard off this object at the
142
- // call, so a writable export would let a caller replace the check rather than pass it.
143
42
  module.exports = intrinsic.freeze({ zeroize: zeroize, zeroizeAll: zeroizeAll, cipherFinish: cipherFinish });
package/lib/guard-text.js CHANGED
@@ -1,86 +1,27 @@
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
- // primitives whose text boundaries compose this guard (pki.jose.parseJson, the
7
- // PEM decoders, the EST transfer decoders).
8
- //
9
- // guard-text -- fail-closed decode of an untrusted byte-source input to a
10
- // string, capping the raw byte length before the string is materialized.
11
- // Enforced choke point for the byte->string boundary.
12
- //
13
- // Defends the parser-DoS string-allocation class (CWE-770 allocation without
14
- // limits, CWE-400 uncontrolled resource consumption; MITRE T1499.001). The
15
- // ORDERING is the invariant: cap the byte length, THEN decode. Decoding a Buffer
16
- // to a string first allocates a full-size string for input the cap is about to
17
- // reject; worse, a buffer above Node's max string length escapes the decode as
18
- // an untyped ERR_STRING_TOO_LONG instead of the caller's typed <tooLarge>. This
19
- // guard defines the cap-before-copy ordering and the fatal-UTF-8 decode once so
20
- // no boundary re-derives it (and drifts to cap-after, as some had).
4
+ // @internal
21
5
 
22
6
  var bytes = require("./guard-bytes");
23
7
  var intrinsic = require("./guard-intrinsic");
24
8
 
25
- // Everything this guard does to a caller's input, taken at module load. Each name below is an
26
- // ordinary writable property of a global, so reading it at call time would let a caller decide what
27
- // the guard does at the moment it does it. Between them these four cover both of the guard's
28
- // contracts, and both of its input arms:
29
- //
30
- // _toString / _decode produce the guard's entire output on the Buffer arm. Replaced with a
31
- // function returning a constant, they hand a PEM header, a JOSE segment or a
32
- // DN attribute value a string the caller chose in place of the bytes that
33
- // were checked -- while the cap, the detached-view refusal and the strict
34
- // UTF-8 rule all still run and all still pass.
35
- // _byteLength measures the string arm against the cap. One returning 0 admits a string
36
- // of any size, which is the allocation-DoS this guard exists to bound.
37
- // _isInteger decides whether the cap itself is usable; guard.bytes.isByteSource decides
38
- // whether an input takes the byte arm or the string arm.
39
- // The two method captures are uncurried, so their call sites read no property of them; see
40
- // guard-intrinsic for why capturing a method and then reading `.call` on it reopens the hole.
41
9
  var _toString = intrinsic.uncurry(Buffer.prototype.toString);
42
10
  var _decode = intrinsic.uncurry(TextDecoder.prototype.decode);
43
- // Already plain functions rather than methods, so these are invoked directly.
44
11
  var _byteLength = Buffer.byteLength;
45
12
  var _isInteger = Number.isInteger;
46
13
  var _TextDecoder = TextDecoder;
47
14
 
48
15
  var LATIN1 = "latin1";
49
16
 
50
- // decode(input, maxBytes, ErrorClass, spec) -> string.
51
- // input : a Buffer (decoded per spec.charset) or a string (taken as-is).
52
- // maxBytes : the raw byte-length ceiling, checked before any string copy.
53
- // ErrorClass: a PkiError subclass (withCause where spec.fatal is set).
54
- // spec : { charset: "latin1"|"utf-8" (default latin1),
55
- // fatal: boolean, strict UTF-8 (a lone continuation /
56
- // truncated sequence throws, never substitutes U+FFFD),
57
- // tooLarge: code for the over-cap reject,
58
- // badDecode: code for a fatal-charset (bad-UTF-8) reject
59
- // (required only when fatal is set),
60
- // badInput: code for a non-byte-source/non-string reject,
61
- // label: human phrase for the message }
62
17
  // @enforced-by behavioral -- cap-before-copy has no rename-proof code shape; the
63
- // over-cap RED vectors + the detached re-view through guard.bytes.source (itself
64
- // enforced) are the guard.
65
18
  function decode(input, maxBytes, ErrorClass, spec) {
66
- // maxBytes is an authoring input: an undefined / NaN / fractional cap makes
67
- // the `length > maxBytes` comparison always false, silently disabling the size
68
- // cap on the guard whose whole contract is cap-before-copy. Config-time
69
- // TypeError (every composing boundary passes a C.LIMITS constant).
70
19
  if (!_isInteger(maxBytes) || maxBytes < 0) {
71
20
  throw new TypeError("guard.text.decode: maxBytes must be a non-negative integer");
72
21
  }
73
22
  var charset = spec.charset || LATIN1;
74
23
  if (bytes.isByteSource(input)) {
75
- // Re-view through the byte guard first so any byte source (a Buffer, a Uint8Array or other typed array, a
76
- // DataView, or a raw ArrayBuffer -- a caller-injected transport may return any of these) is normalized to a
77
- // usable view, and a detached backing ArrayBuffer (a transferred / structuredClone'd view, which reads as
78
- // zero-length) fails closed here instead of being decoded as an empty string. Then cap, then decode.
79
24
  input = bytes.source(input, ErrorClass, spec.badInput, spec.label);
80
- // Measured through the byte guard's captured intrinsic getter, never `input.length`. On a typed
81
- // array `length` is an accessor on the prototype and it is configurable, so a replacement
82
- // returning 0 lets a buffer of any size past the cap and straight into the decode -- the
83
- // allocation this guard exists to bound, defeated by the one read that bounds it.
84
25
  if (bytes.lengthOf(input) > maxBytes) throw new ErrorClass(spec.tooLarge, spec.label + " exceeds the size cap");
85
26
  if (spec.fatal) {
86
27
  try { return _decode(new _TextDecoder(charset, { fatal: true }), input); }
@@ -89,8 +30,6 @@ function decode(input, maxBytes, ErrorClass, spec) {
89
30
  return _toString(input, charset);
90
31
  }
91
32
  if (typeof input === "string") {
92
- // A latin1 string's char length equals its byte length; a UTF-8 string's
93
- // does not, so bound the encoded byte length.
94
33
  var byteLen = charset === LATIN1 ? input.length : _byteLength(input, "utf8");
95
34
  if (byteLen > maxBytes) throw new ErrorClass(spec.tooLarge, spec.label + " exceeds the size cap");
96
35
  return input;
@@ -98,6 +37,4 @@ function decode(input, maxBytes, ErrorClass, spec) {
98
37
  throw new ErrorClass(spec.badInput, spec.label + " expects a string or a byte source (Buffer / TypedArray / DataView / ArrayBuffer)");
99
38
  }
100
39
 
101
- // Frozen for the reason the family header gives: a boundary reads its guard off this object at the
102
- // call, so a writable export would let a caller replace the check rather than pass it.
103
40
  module.exports = intrinsic.freeze({ decode: decode });
package/lib/guard-time.js CHANGED
@@ -1,138 +1,42 @@
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
- // verifiers whose validity / currency / temporal gates compose this guard
7
- // (pki.path.validate, pki.ocsp.verify, pki.tsp.*, pki.ct.*, pki.cms.sign).
8
- //
9
- // guard-time -- fail-closed Date-instant validation before a temporal comparison.
10
- //
11
- // Defends the NaN-Date fail-open class (CWE-20 improper input validation feeding
12
- // a silent security-gate bypass). An Invalid Date is still `instanceof Date`, and
13
- // every relational comparison against its NaN getTime() (NaN < x, NaN >= x) is
14
- // false, so a validity / not-before / not-after / currency window built on an
15
- // unvalidated Date silently accepts when it should reject. The class recurred at
16
- // three independent boundaries (TSP genTime, OCSP producedAt/thisUpdate, CT
17
- // temporal-interval) before it was centralized here; the sibling
18
- // nan-date-comparison-unguarded codebase-patterns detector flags any lib function
19
- // that re-inlines a `.getTime()` comparison without first rejecting a NaN, so a
20
- // new boundary is routed to this guard instead of re-growing the bug.
21
- //
22
- // Tier split: `value instanceof Date` with a NaN time is malformed caller input
23
- // (a config-time or entry-point Date, such as an operator-supplied distrustAfter
24
- // or a caller `opts.time`), so assertValid throws the caller's typed error instead
25
- // of returning a silent default. within() is the fail-closed window primitive: it
26
- // THROWS on a malformed operand but RETURNS a boolean for in/out-of-window, so the
27
- // OCSP / CRL currency callers that treat out-of-window as a `continue` skip keep
28
- // their control flow while a NaN operand can never slip through as a false.
4
+ // @internal
29
5
 
30
- // The slot predicates and the NaN test, snapshotted at load. `util.types` is an ordinary object,
31
- // so a later `util.types.isDate = () => true` would otherwise decide what this guard calls a Date;
32
- // the global `isNaN` is writable, and one returning false makes an invalid Date pass the check
33
- // below, which is the fail-open direction.
34
6
  var util = { types: require("./guard-intrinsic").types };
35
7
  var _freeze = require("./guard-intrinsic").freeze;
36
8
  var _isNaN = Number.isNaN;
37
9
 
38
- // assertValid(value, E, code, label) -> the same Date, once proven valid.
39
- // value : a Date (or an alleged one) from a caller boundary.
40
- // E : the (code, message[, cause]) typed-error FACTORY in scope at the call
41
- // site (ns.E / the module-local _err), never a defineClass class, which
42
- // would crash `class cannot be invoked without new` on the error path.
43
- // code : the frozen domain/reason code this boundary rejects malformed time under.
44
- // label : field phrase for the message.
45
- // The instant a Date holds, asked of the language rather than of the Date.
46
- //
47
- // `getTime` is an ordinary method on `Date.prototype`, so `class T extends Date { getTime() {} }`
48
- // and an own `getTime` on any instance both answer it. Reading it off the value let a caller throw
49
- // a raw exception out of a guard whose contract is a typed error, and let one return a fabricated
50
- // instant that passed the check while the real one did not. It also made the read repeatable: a
51
- // method asked twice may answer twice, so a window check could validate one instant and compare
52
- // another.
53
- //
54
- // The intrinsic is called against the value, which reaches the internal slot whatever the value's
55
- // own class says, and refuses a receiver with no slot behind it.
56
- //
57
- // The capture is of whatever `Date.prototype.getTime` holds when this module first loads, so it
58
- // is the language's own function only if nothing replaced it earlier in the process. Code that
59
- // runs before this module is inside the trust boundary already and can reach further than this
60
- // one method, so there is no defense here to add; what the capture buys is immunity to every
61
- // replacement that happens afterwards, which is the case a caller's argument can reach.
62
- //
63
- // The capture is uncurried through guard-intrinsic, so `instantOf` reads no property of it either.
64
- // The captured function is still the one on `Date.prototype`, and an own `call` assigned to it
65
- // would shadow `Function.prototype.call` and decide every instant this toolkit evaluates against.
66
10
  var _dateGetTime = require("./guard-intrinsic").uncurry(Date.prototype.getTime);
67
11
 
68
- // instantOf(value) -> the epoch milliseconds a Date holds, read through the intrinsic.
69
- //
70
- // The shape is the method call itself, wherever it appears. There is no boundary at which reading
71
- // `getTime` off a value is the safe spelling: a caller's Date reaches the read through an override,
72
- // and this toolkit's own Date reaches it through whatever `Date.prototype.getTime` has been made
73
- // to be, which a dependency loaded before this module gets to decide. So the rule is that lib/
74
- // calls no `getTime` at all, and the one capture above is where it lives.
75
- //
76
12
  // @enforced-by guard-shape-reinlined
77
13
  // @guard-shape \.getTime\s*\(
78
14
  function instantOf(value) { return _dateGetTime(value); }
79
15
 
80
16
  // @enforced-by nan-date-comparison-unguarded
81
17
  function assertValid(value, E, code, label) {
82
- // The slot, never the prototype. `instanceof Date` asks what a value inherits from, and
83
- // `Object.create(Date.prototype)` inherits everything while holding no instant, so it passes
84
- // that test and `getTime()` then throws a raw TypeError out of this guard. `util.types.isDate`
85
- // reads the internal slot, which a caller cannot fake and which answers the same across realms.
86
18
  if (!util.types.isDate(value) || _isNaN(instantOf(value))) {
87
19
  throw E(code, (label || "value") + " must be a valid Date");
88
20
  }
89
21
  return value;
90
22
  }
91
23
 
92
- // within(instant, lower, upper, E, code, label, opts) -> boolean (instant in window).
93
- // Rejects a malformed instant / lower / upper by THROWING (assertValid), then
94
- // answers containment as a boolean. Half-open [lower, upper) by default (a CT
95
- // temporal-interval / an OCSP-CRL currency window); pass opts.upperInclusive for
96
- // the closed [notBefore, notAfter] certificate-validity shape.
97
24
  // @enforced-by nan-date-comparison-unguarded
98
25
  function within(instant, lower, upper, E, code, label, opts) {
99
- // assertValid rejects a non-Date / Invalid Date operand up front, so t/lo/hi below are
100
- // guaranteed non-NaN -- the containment comparisons can never be a silent NaN-false. The
101
- // getTime() results are bound to locals (not compared inline), so this home carries no
102
- // unguarded `.getTime()`-in-a-comparison shape for the nan-date-comparison-unguarded detector.
103
26
  assertValid(instant, E, code, label);
104
27
  assertValid(lower, E, code, (label || "window") + " lower bound");
105
28
  assertValid(upper, E, code, (label || "window") + " upper bound");
106
- // Read through the intrinsic, so the instants compared here are the ones assertValid just
107
- // proved. A caller's `getTime` override answers each call afresh, which made the value checked
108
- // and the value compared two different reads of the same argument.
109
29
  var t = instantOf(instant);
110
30
  var lo = instantOf(lower);
111
31
  var hi = instantOf(upper);
112
32
  return t >= lo && (opts && opts.upperInclusive ? t <= hi : t < hi);
113
33
  }
114
34
 
115
- // isDate(value) -> boolean: does this value actually hold an instant?
116
- //
117
- // The predicate half of assertValid, for the boundaries that choose a default rather than refuse
118
- // (`opts.genTime instanceof Date ? opts.genTime : new Date()`). `instanceof` asks what a value
119
- // inherits from, so `Object.create(Date.prototype)` passes it while holding no instant, and the
120
- // `.getTime()` that follows throws a raw TypeError out of a verb whose contract is a typed error.
121
- // A caller who supplied one and got the default instead would be worse served still: the value
122
- // they passed would have been silently replaced. This reads the slot, which cannot be faked and
123
- // answers the same for a Date built in another realm.
124
- //
125
- // An Invalid Date has the slot and a NaN instant, so this says yes to one. A boundary that
126
- // compares instants wants assertValid, which rejects both shapes.
127
35
  // @enforced-by behavioral -- a prototype test in place of a slot test has no rename-proof code
128
- // shape; the RED vectors (a slot-less Date lookalike at cms.verify / cms.sign / tsp.sign) are
129
- // the guard.
130
36
  function isDate(value) {
131
37
  return util.types.isDate(value);
132
38
  }
133
39
 
134
- // Frozen for the reason the family header gives: a boundary reads its guard off this object at the
135
- // call, so a writable export would let a caller replace the check rather than pass it.
136
40
  module.exports = _freeze({
137
41
  assertValid: assertValid,
138
42
  instantOf: instantOf,