@blamejs/pki 0.5.7 → 0.5.9

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 (105) hide show
  1. package/CHANGELOG.md +418 -386
  2. package/MIGRATING.md +43 -0
  3. package/README.md +12 -12
  4. package/lib/acme.js +31 -31
  5. package/lib/asn1-der.js +10 -10
  6. package/lib/attrcert-sign.js +19 -19
  7. package/lib/byte-reader.js +6 -6
  8. package/lib/byte-writer.js +5 -5
  9. package/lib/cbor-det.js +27 -24
  10. package/lib/cmc-build.js +97 -92
  11. package/lib/cmc-verify.js +106 -30
  12. package/lib/cmp-build.js +25 -25
  13. package/lib/cmp-session.js +70 -70
  14. package/lib/cmp-verify.js +71 -57
  15. package/lib/cms-compress.js +7 -7
  16. package/lib/cms-decrypt.js +90 -72
  17. package/lib/cms-encrypt.js +32 -32
  18. package/lib/cms-sign.js +74 -55
  19. package/lib/cms-verify.js +97 -75
  20. package/lib/composite-sig.js +13 -13
  21. package/lib/constants.js +4 -4
  22. package/lib/crl-sign.js +22 -22
  23. package/lib/crl-verify.js +7 -6
  24. package/lib/crmf-sign.js +14 -14
  25. package/lib/csr-sign.js +8 -8
  26. package/lib/ct.js +37 -37
  27. package/lib/edwards-point.js +7 -7
  28. package/lib/est.js +98 -55
  29. package/lib/framework-error.js +5 -5
  30. package/lib/guard-all.js +5 -3
  31. package/lib/guard-async.js +4 -4
  32. package/lib/guard-bytes.js +79 -79
  33. package/lib/guard-compress.js +17 -17
  34. package/lib/guard-crypto.js +1 -1
  35. package/lib/guard-der.js +152 -0
  36. package/lib/guard-encoding.js +15 -15
  37. package/lib/guard-header.js +3 -3
  38. package/lib/guard-identifier.js +16 -16
  39. package/lib/guard-json.js +15 -15
  40. package/lib/guard-limits.js +7 -7
  41. package/lib/guard-name.js +81 -16
  42. package/lib/guard-parsed.js +80 -80
  43. package/lib/guard-range.js +19 -19
  44. package/lib/guard-secret.js +11 -10
  45. package/lib/guard-text.js +6 -6
  46. package/lib/guard-time.js +10 -10
  47. package/lib/hpke.js +18 -17
  48. package/lib/http-digest.js +35 -35
  49. package/lib/http-retry-after.js +13 -13
  50. package/lib/http-transport.js +20 -19
  51. package/lib/inspect.js +53 -53
  52. package/lib/ip-utils.js +2 -2
  53. package/lib/jose.js +13 -13
  54. package/lib/key.js +16 -16
  55. package/lib/lint.js +51 -51
  56. package/lib/merkle.js +51 -36
  57. package/lib/mime.js +18 -18
  58. package/lib/ocsp-verify.js +10 -10
  59. package/lib/ocsp.js +13 -13
  60. package/lib/oid.js +29 -29
  61. package/lib/path-validate.js +114 -113
  62. package/lib/pbes2.js +16 -16
  63. package/lib/pkcs12-build.js +53 -53
  64. package/lib/pki-build.js +48 -20
  65. package/lib/rc2.js +1 -1
  66. package/lib/rfc3339.js +5 -5
  67. package/lib/schema-all.js +31 -31
  68. package/lib/schema-attrcert.js +12 -12
  69. package/lib/schema-c509.js +149 -197
  70. package/lib/schema-cmc.js +58 -58
  71. package/lib/schema-cmp.js +43 -43
  72. package/lib/schema-cms.js +45 -45
  73. package/lib/schema-crl.js +7 -7
  74. package/lib/schema-crmf.js +28 -28
  75. package/lib/schema-csr.js +12 -12
  76. package/lib/schema-csrattrs.js +16 -16
  77. package/lib/schema-engine.js +18 -18
  78. package/lib/schema-ocsp.js +15 -15
  79. package/lib/schema-pkcs12.js +20 -20
  80. package/lib/schema-pkcs8.js +2 -2
  81. package/lib/schema-pkix.js +131 -126
  82. package/lib/schema-smime.js +19 -19
  83. package/lib/schema-tsp.js +12 -12
  84. package/lib/schema-x509.js +3 -3
  85. package/lib/shbs.js +18 -18
  86. package/lib/sign-scheme.js +13 -13
  87. package/lib/sigstore.js +10 -11
  88. package/lib/sleep.js +1 -1
  89. package/lib/smime.js +308 -96
  90. package/lib/tls-cert-compress.js +18 -18
  91. package/lib/trust.js +27 -27
  92. package/lib/tsp-sign.js +17 -17
  93. package/lib/validator-all.js +1 -1
  94. package/lib/validator-attcert.js +1 -1
  95. package/lib/validator-cose.js +43 -44
  96. package/lib/validator-keydesc.js +3 -3
  97. package/lib/validator-sig.js +13 -13
  98. package/lib/validator-tls.js +11 -11
  99. package/lib/validator-tpm.js +20 -19
  100. package/lib/webauthn-mds.js +66 -66
  101. package/lib/webauthn.js +33 -33
  102. package/lib/webcrypto.js +15 -15
  103. package/lib/x509-sign.js +13 -13
  104. package/package.json +3 -2
  105. package/sbom.cdx.json +6 -6
@@ -7,21 +7,21 @@
7
7
  //
8
8
  // validator-sig -- the SINGLE home for "is this a conformant DER ECDSA-Sig-Value", and
9
9
  // its conversion to the raw r||s (IEEE P1363) form a WebCrypto verify expects. Sibling to
10
- // the guard family: a validator owns a decoded TYPE's COMPLETE conformance rule set once,
11
- // so a format module composes it rather than hand-decoding a 2-INTEGER SEQUENCE into r/s
10
+ // the guard family: a validator owns a decoded type's complete conformance rule set once,
11
+ // so a format module composes it instead of hand-decoding a two-INTEGER SEQUENCE into r/s
12
12
  // and forgetting a strict-DER check (the drift a validator exists to prevent).
13
13
  //
14
14
  // Interface mirrors the guard family: (subject, ..., E, code) where E is the caller's
15
15
  // typed error CONSTRUCTOR and code its domain code.
16
16
  //
17
17
  // Rule set (gap-checked verbatim against RFC 3279 sec. 2.2.3 + X.690 sec. 8.3 + SEC1):
18
- // - ECDSA-Sig-Value ::= SEQUENCE { r INTEGER, s INTEGER } -- a universal SEQUENCE with
19
- // EXACTLY two children.
20
- // - r and s are each a PRIMITIVE, MINIMALLY-ENCODED (X.690 sec. 8.3.2 -- no redundant
21
- // leading 0x00/0xFF), POSITIVE integer. Reading through the strict DER integer reader
22
- // enforces the primitive + minimal-encoding rules; the value is then range-checked
18
+ // - ECDSA-Sig-Value ::= SEQUENCE { r INTEGER, s INTEGER }, a universal SEQUENCE with
19
+ // exactly two children.
20
+ // - r and s are each a primitive, minimally-encoded (X.690 sec. 8.3.2, no redundant
21
+ // leading 0x00/0xFF), positive integer. Reading through the strict DER integer reader
22
+ // enforces the primitive and minimal-encoding rules; the value is then range-checked
23
23
  // positive (r,s >= 1) and bounded by the curve field size. A non-minimal, zero,
24
- // negative, or over-size coordinate fails closed -- never normalized-and-accepted.
24
+ // negative, or over-size coordinate fails closed, never normalized-and-accepted.
25
25
 
26
26
  var asn1 = require("./asn1-der");
27
27
 
@@ -52,8 +52,8 @@ var CURVE_ORDER = {
52
52
 
53
53
  // ecdsaDerToP1363(der, curve, E, code) -> the DER ECDSA-Sig-Value converted to raw r||s (P1363),
54
54
  // each coordinate left-padded to the curve field width, rejecting r or s outside [1, n-1] against
55
- // the curve ORDER (CVE-2022-21449 "Psychic Signatures" -- the r/s = 0 case AND the >= n upper
56
- // bound). `curve` is a WebCrypto namedCurve (P-256/384/521). This is the SINGLE ECDSA-Sig-Value
55
+ // the curve order (CVE-2022-21449 "Psychic Signatures": the r/s = 0 case and the >= n upper
56
+ // bound). `curve` is a WebCrypto namedCurve (P-256/384/521). This is the single ECDSA-Sig-Value
57
57
  // conformance gate every curve-aware verifier routes through: it enforces the complete strict-DER
58
58
  // rule set (2-INTEGER SEQUENCE, minimal encoding) AND the order bound, so a verifier never
59
59
  // hand-decodes the SEQUENCE (skipping minimality) nor bounds r/s only by the field size (missing
@@ -72,9 +72,9 @@ function ecdsaDerToP1363(der, curve, E, code) {
72
72
  if (n.tagClass !== "universal" || n.tagNumber !== asn1.TAGS.SEQUENCE || !n.children || n.children.length !== 2) {
73
73
  throw new E(code, "ECDSA signature must be a SEQUENCE of exactly two INTEGERs");
74
74
  }
75
- // Wrap the strict DER integer reads (they enforce PRIMITIVE + MINIMAL encoding) so a non-minimal /
76
- // constructed / empty INTEGER surfaces the CALLER's typed code, not a raw asn1/* -- the complete DER
77
- // conformance rule set, gated together with the order bound below.
75
+ // Wrap the strict DER integer reads (they enforce primitive and minimal encoding) so a non-minimal,
76
+ // constructed or empty INTEGER surfaces the caller's typed code and not a raw asn1/*. That is the
77
+ // complete DER conformance rule set, gated together with the order bound below.
78
78
  var r, s;
79
79
  try { r = asn1.read.integer(n.children[0]); } catch (e) { throw new E(code, "ECDSA signature r is not a minimally-encoded DER INTEGER", e); }
80
80
  try { s = asn1.read.integer(n.children[1]); } catch (e) { throw new E(code, "ECDSA signature s is not a minimally-encoded DER INTEGER", e); }
@@ -14,12 +14,12 @@
14
14
  // Every field is unsigned big-endian, packed with no padding, in the TLS presentation
15
15
  // language: `uintN` is a fixed-width integer and `opaque<min..max>` is a length prefix wide
16
16
  // enough for `max` followed by exactly that many bytes. The caller supplies its typed error
17
- // CONSTRUCTOR E (a validator takes a class -- `new E(code, msg)` -- where the guard family
17
+ // constructor E (a validator takes a class, `new E(code, msg)`, where the guard family
18
18
  // takes a factory) plus the `codes` its domain uses for each fault.
19
19
  //
20
20
  // Rule set (verbatim against RFC 8879 sec. 4 and RFC 8446 sec. 4.4.2):
21
21
  // - compressedCertificate: uint16 algorithm, uint24 uncompressed_length, then
22
- // opaque compressed_certificate_message<1..2^24-1> -- the minimum of 1 makes an EMPTY
22
+ // opaque compressed_certificate_message<1..2^24-1>, where the minimum of 1 makes an empty
23
23
  // compressed body a framing violation, which matters because the three decompressors do
24
24
  // not agree on what empty input means. Bytes after the vector are rejected: one chain
25
25
  // must have exactly one encoding.
@@ -34,19 +34,19 @@
34
34
  // codec's policy and stay with the codec.
35
35
  //
36
36
  // The RFC 8446 sec. 4.4.2 / sec. 4.2 rules that bind a Certificate-message DECODER, and where
37
- // each one lands -- enumerated so the ones deliberately NOT enforced are a recorded decision
38
- // rather than an oversight:
37
+ // each one lands, enumerated so the ones deliberately left unenforced are a recorded decision
38
+ // and not an oversight:
39
39
  // ENFORCED here:
40
40
  // - RawPublicKey carries at most one CertificateEntry (sec. 4.4.2).
41
41
  // - No extension type appears twice in one extension block (sec. 4.2).
42
42
  // - Every vector's framing, its declared minimum, and no trailing bytes at either level.
43
- // NOT ENFORCED, deliberately:
43
+ // Not enforced, deliberately:
44
44
  // - "The sender's certificate MUST come in the first CertificateEntry" is about which
45
45
  // certificate a consumer treats as the leaf, not about framing. Entries are surfaced in
46
- // wire order and never reordered, so the caller sees exactly what arrived -- and the same
46
+ // wire order and never reordered, so the caller sees exactly what arrived, and the same
47
47
  // section tells implementations to tolerate extraneous certificates and arbitrary
48
48
  // orderings beyond the first, so refusing any order here would be wrong.
49
- // - "The server's certificate_list MUST always be non-empty" is conditioned on the SENDER's
49
+ // - "The server's certificate_list MUST always be non-empty" is conditioned on the sender's
50
50
  // role: a client legitimately sends an empty list when it has no certificate to offer.
51
51
  // This decoder has no role, so an empty list decodes to zero entries and the caller, which
52
52
  // does know the role, decides.
@@ -54,7 +54,7 @@
54
54
  // extension's own contents (a status_request body being a CertificateStatus) need the
55
55
  // negotiation state and the extension's semantics. Neither exists here: extension values
56
56
  // are surfaced raw.
57
- // - The OpenPGP certificate type is unreachable rather than checked -- the only accepted
57
+ // - The OpenPGP certificate type is unreachable, not checked: the only accepted
58
58
  // types are X509 and RawPublicKey, and anything else is refused at the entry point.
59
59
 
60
60
  var C = require("./constants");
@@ -78,8 +78,8 @@ function compressedCertificate(view, E, codes) {
78
78
  var r = new ByteReader(view, 0, view.length, E, codes.truncated);
79
79
  var algorithm = r.u16(codes.truncated);
80
80
  var uncompressedLength = r.u24(codes.truncated);
81
- // opaque compressed_certificate_message<1..2^24-1> -- minimum 1, so an empty body is a
82
- // framing violation rather than something a decompressor gets to interpret.
81
+ // opaque compressed_certificate_message<1..2^24-1>, minimum 1, so an empty body is a
82
+ // framing violation and not something a decompressor gets to interpret.
83
83
  var body = r.vector(3, 1, MAX_VECTOR_24, codes.framing);
84
84
  if (!r.atEnd()) {
85
85
  throw new E(codes.trailing, "the CompressedCertificate carries " + r.remaining() +
@@ -92,7 +92,7 @@ function compressedCertificate(view, E, codes) {
92
92
  // Extension a uint16 type plus a uint16-prefixed value, so the vector is a whole number of them and
93
93
  // the smallest is 4 bytes. Walking it is what keeps a malformed Certificate message from passing as
94
94
  // structurally valid: a one-byte vector cannot be an Extension, and accepting the bytes opaquely
95
- // would report it as well-formed. Each record's value is surfaced raw -- this decodes the framing,
95
+ // would report it as well-formed. Each record's value is surfaced raw: this decodes the framing,
96
96
  // never the extension's own contents.
97
97
  function _extensionRecords(view, E, codes) {
98
98
  var r = new ByteReader(view, 0, view.length, E, codes.truncated);
@@ -112,7 +112,7 @@ function parsePubArea(buf, E, code) {
112
112
  throw new E(code, "unsupported TPMT_PUBLIC type 0x" + type.toString(16));
113
113
  }
114
114
  // TPMT_PUBLIC ends with `unique`; trailing bytes mean a malformed pubArea (and would
115
- // perturb the TPM Name hash), so fail closed rather than silently ignore them.
115
+ // perturb the TPM Name hash), so the decode fails closed on them.
116
116
  if (!r.atEnd()) throw new E(code, "pubArea has trailing bytes after the unique field (WebAuthn 8.3)");
117
117
  return pub;
118
118
  }
@@ -148,7 +148,7 @@ function pubKeyEqualsCose(pub, cose, E, mismatchCode, code) {
148
148
  return;
149
149
  }
150
150
  if (pub.type === TPM_ALG.RSA) {
151
- // Compare the exponent as an unsigned integer over its FULL width (a UINT32 up to
151
+ // Compare the exponent as an unsigned integer over its full width (a UINT32 up to
152
152
  // 0xFFFFFFFF); a fixed 3-byte re-encode would silently truncate an exponent > 0xFFFFFF
153
153
  // and let a mismatched key pass (WebAuthn 8.3 item 22).
154
154
  var e = _uintBytes(pub.exponent >>> 0);
@@ -161,7 +161,7 @@ function pubKeyEqualsCose(pub, cose, E, mismatchCode, code) {
161
161
  }
162
162
 
163
163
  // The one defined preset: the six TPMA_OBJECT bits every genuine attestation observed sets the same
164
- // way. `decrypt`, `noDA` and `userWithAuth` are deliberately absent -- they DIFFER across real
164
+ // way. `decrypt`, `noDA` and `userWithAuth` are deliberately absent: they differ across real
165
165
  // Windows Hello statements, so requiring any of them rejects working hardware. A second preset
166
166
  // would be policy invention; a caller who wants different bits spells them out.
167
167
  var TPM_POLICY_PROFILES = Object.assign(Object.create(null), {
@@ -171,7 +171,7 @@ var _TPM_POLICY_KEYS = Object.assign(Object.create(null), { profile: 1, objectAt
171
171
  var _AUTH_POLICY_KEYS = Object.assign(Object.create(null), { present: 1, allow: 1 });
172
172
 
173
173
  // Config-time validation of opts.tpmPolicy: a typo must never silently disable a check, so an
174
- // unknown key or attribute name throws at the boundary rather than being ignored.
174
+ // unknown key or attribute name throws at the boundary.
175
175
  // @enforced-by behavioral -- an opts-shape validator has no rename-proof code shape; the RED
176
176
  // vectors (unknown key, unknown profile, unknown attribute, non-boolean value, and the
177
177
  // sensitiveDataOrigin-without-fixedTPM rule) are the guard.
@@ -196,8 +196,8 @@ function normalizeObjectAttributePolicy(policy, E, code) {
196
196
  });
197
197
  }
198
198
  // sec. 8.3.3.5 NOTE 1: sensitiveDataOrigin only asserts the TPM generated the key when fixedTPM
199
- // is also SET -- otherwise the object could have been imported. Demanding it alone asserts
200
- // nothing, so say so at config time rather than letting a caller believe it is protected.
199
+ // is also SET; otherwise the object could have been imported. Demanding it alone asserts
200
+ // nothing, and a caller who thinks otherwise should hear it at config time.
201
201
  if (want.sensitiveDataOrigin === true && want.fixedTPM !== true) {
202
202
  throw new E(code, "opts.tpmPolicy requires sensitiveDataOrigin without fixedTPM; on its own it does not establish that the TPM generated the key (TPM 2.0 Part 2 sec. 8.3.3.5)");
203
203
  }
@@ -215,15 +215,16 @@ function normalizeObjectAttributePolicy(policy, E, code) {
215
215
  if (ap.present !== undefined && typeof ap.present !== "boolean") throw new E(code, "opts.tpmPolicy.authPolicy.present must be a boolean");
216
216
  if (ap.allow !== undefined) {
217
217
  if (!Array.isArray(ap.allow)) throw new E(code, "opts.tpmPolicy.authPolicy.allow must be an array");
218
- // Decode every entry HERE, at config time, rather than inside the comparison. Node's hex
219
- // decoder is permissive: it stops at the first character that is not a hex digit, so
220
- // "<digest>zz" decodes back to the digest and a non-string decodes to an empty buffer --
221
- // either of which could match a key this policy was written to exclude, including the
218
+ // Every entry is decoded here, at config time, so the comparison only ever sees bytes.
219
+ // Node's hex decoder is permissive: it stops at the first character that is not a hex
220
+ // digit, so "<digest>zz" decodes back to the digest and a non-string decodes to an empty
221
+ // buffer. Either could match a key this policy was written to exclude, including the
222
222
  // Empty Policy. An entry that is not a Buffer or a canonical even-length hex string is a
223
- // caller error, and it fails here rather than becoming a digest nobody intended.
224
- // Through guard.encoding.hex, which owns the alphabet, the even-length rule and the
225
- // canonical round-trip -- the same three checks written here by hand, and now written
226
- // once. It also decodes, so the hex path cannot validate one string and decode another.
223
+ // caller error; it fails here, before it can become a digest nobody intended.
224
+ // The decoding runs through guard.encoding.hex, which owns the alphabet, the even-length
225
+ // rule and the canonical round-trip. Those three checks were once written out here by hand
226
+ // and now live in one place. It also decodes, so the hex path cannot validate one string
227
+ // and decode another.
227
228
  allow = ap.allow.map(function (entry, i) {
228
229
  if (Buffer.isBuffer(entry)) return guard.bytes.snapshot(entry, E, code, "opts.tpmPolicy.authPolicy.allow[" + i + "]");
229
230
  var label = "opts.tpmPolicy.authPolicy.allow[" + i + "]";
@@ -240,8 +241,8 @@ function normalizeObjectAttributePolicy(policy, E, code) {
240
241
  authPolicy: ap ? { present: ap.present === true, allow: allow } : null };
241
242
  }
242
243
 
243
- // Apply a normalized policy to a parsed pubArea. Nothing here is a WebAuthn sec. 8.3 requirement --
244
- // that section constrains only pubArea's `parameters` and `unique` fields -- so every rule runs
244
+ // Apply a normalized policy to a parsed pubArea. Nothing here is a WebAuthn sec. 8.3 requirement,
245
+ // since that section constrains only pubArea's `parameters` and `unique` fields. Every rule runs
245
246
  // only because a caller asked for it by name.
246
247
  // @enforced-by behavioral -- an opt-gated policy gate has no rename-proof code shape, and there is
247
248
  // no general vector shape to detect: the RED vectors (each required bit in both directions, the
@@ -251,7 +252,7 @@ function assertObjectAttributePolicy(pub, policy, E, policyCode, structuralCode)
251
252
  if (!policy) return;
252
253
  var oa = pub.objectAttributes >>> 0;
253
254
  // Bit 31 lies inside the reserved mask, so the AND must be coerced back to unsigned: a bare
254
- // `oa & 0xfff0f009` is a signed int32 and would read negative rather than "a bit is set".
255
+ // `oa & 0xfff0f009` is a signed int32, and a negative result would not read as "a bit is set".
255
256
  if (policy.reservedBitsClear && ((oa & TPMA_OBJECT_RESERVED) >>> 0) !== 0) {
256
257
  throw new E(structuralCode, "the TPMT_PUBLIC objectAttributes sets a reserved bit (TPM 2.0 Part 2 sec. 8.3.2 Table 33: shall be zero)");
257
258
  }
@@ -271,8 +272,8 @@ function assertObjectAttributePolicy(pub, policy, E, policyCode, structuralCode)
271
272
  if (pub.authPolicy.length > 64) {
272
273
  throw new E(structuralCode, "the TPMT_PUBLIC authPolicy is " + pub.authPolicy.length + " octets, above the 64-octet TPM2B_DIGEST maximum");
273
274
  }
274
- // The REMAINING sec. 8.3.3 "shall" statements are all parent-relative -- fixedTPM, stClear and
275
- // encryptedDuplication are each constrained against the value the object's PARENT carries. An
275
+ // The remaining sec. 8.3.3 "shall" statements are all parent-relative. fixedTPM, stClear and
276
+ // encryptedDuplication are each constrained against the value the object's parent carries. An
276
277
  // attestation presents one public area and no parent, so a verifier cannot evaluate them at
277
278
  // all; they are not omitted by oversight, they are unverifiable from what is on the wire.
278
279
  }