@blamejs/pki 0.5.7 → 0.5.8

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 (104) hide show
  1. package/CHANGELOG.md +405 -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 +3 -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-encoding.js +15 -15
  36. package/lib/guard-header.js +3 -3
  37. package/lib/guard-identifier.js +16 -16
  38. package/lib/guard-json.js +15 -15
  39. package/lib/guard-limits.js +7 -7
  40. package/lib/guard-name.js +81 -16
  41. package/lib/guard-parsed.js +80 -80
  42. package/lib/guard-range.js +19 -19
  43. package/lib/guard-secret.js +11 -10
  44. package/lib/guard-text.js +6 -6
  45. package/lib/guard-time.js +10 -10
  46. package/lib/hpke.js +18 -17
  47. package/lib/http-digest.js +35 -35
  48. package/lib/http-retry-after.js +13 -13
  49. package/lib/http-transport.js +20 -19
  50. package/lib/inspect.js +53 -53
  51. package/lib/ip-utils.js +2 -2
  52. package/lib/jose.js +13 -13
  53. package/lib/key.js +16 -16
  54. package/lib/lint.js +51 -51
  55. package/lib/merkle.js +51 -36
  56. package/lib/mime.js +18 -18
  57. package/lib/ocsp-verify.js +10 -10
  58. package/lib/ocsp.js +13 -13
  59. package/lib/oid.js +29 -29
  60. package/lib/path-validate.js +114 -113
  61. package/lib/pbes2.js +16 -16
  62. package/lib/pkcs12-build.js +53 -53
  63. package/lib/pki-build.js +21 -19
  64. package/lib/rc2.js +1 -1
  65. package/lib/rfc3339.js +5 -5
  66. package/lib/schema-all.js +31 -31
  67. package/lib/schema-attrcert.js +12 -12
  68. package/lib/schema-c509.js +144 -142
  69. package/lib/schema-cmc.js +58 -58
  70. package/lib/schema-cmp.js +43 -43
  71. package/lib/schema-cms.js +45 -45
  72. package/lib/schema-crl.js +7 -7
  73. package/lib/schema-crmf.js +28 -28
  74. package/lib/schema-csr.js +12 -12
  75. package/lib/schema-csrattrs.js +16 -16
  76. package/lib/schema-engine.js +18 -18
  77. package/lib/schema-ocsp.js +15 -15
  78. package/lib/schema-pkcs12.js +20 -20
  79. package/lib/schema-pkcs8.js +2 -2
  80. package/lib/schema-pkix.js +131 -126
  81. package/lib/schema-smime.js +19 -19
  82. package/lib/schema-tsp.js +12 -12
  83. package/lib/schema-x509.js +3 -3
  84. package/lib/shbs.js +18 -18
  85. package/lib/sign-scheme.js +13 -13
  86. package/lib/sigstore.js +10 -11
  87. package/lib/sleep.js +1 -1
  88. package/lib/smime.js +308 -96
  89. package/lib/tls-cert-compress.js +18 -18
  90. package/lib/trust.js +27 -27
  91. package/lib/tsp-sign.js +17 -17
  92. package/lib/validator-all.js +1 -1
  93. package/lib/validator-attcert.js +1 -1
  94. package/lib/validator-cose.js +43 -44
  95. package/lib/validator-keydesc.js +3 -3
  96. package/lib/validator-sig.js +13 -13
  97. package/lib/validator-tls.js +11 -11
  98. package/lib/validator-tpm.js +20 -19
  99. package/lib/webauthn-mds.js +66 -66
  100. package/lib/webauthn.js +33 -33
  101. package/lib/webcrypto.js +15 -15
  102. package/lib/x509-sign.js +13 -13
  103. package/package.json +3 -2
  104. package/sbom.cdx.json +6 -6
@@ -15,18 +15,18 @@
15
15
  * largest thing a handshake sends and matters most for post-quantum chains,
16
16
  * where certificates grow by kilobytes. `parseCertificateMessage` decodes the
17
17
  * RFC 8446 sec. 4.4.2 `Certificate` message itself, so a compressed chain
18
- * arrives as certificate DER ready for `pki.schema.x509.parse` rather than as
18
+ * arrives as certificate DER ready for `pki.schema.x509.parse` instead of as
19
19
  * an opaque blob.
20
20
  *
21
- * These are encoded in the TLS presentation language -- positional, tag-less,
22
- * fixed-width big-endian integers and length-prefixed opaque vectors -- NOT
23
- * ASN.1/DER, so this module composes the toolkit's bounded big-endian cursor
24
- * rather than the DER schema engine.
21
+ * These are encoded in the TLS presentation language (positional, tag-less,
22
+ * fixed-width big-endian integers and length-prefixed opaque vectors) and not
23
+ * in ASN.1/DER, so this module composes the toolkit's bounded big-endian cursor
24
+ * in place of the DER schema engine.
25
25
  *
26
26
  * Decompression is the attack surface and is fail-closed on both sides of the
27
27
  * bound RFC 8879 sec. 5 requires: the decompressor is capped at the message's
28
- * OWN declared uncompressed length (so a bomb is refused mid-stream, never
29
- * allocated), and the output must then equal that length EXACTLY (which catches
28
+ * own declared uncompressed length (so a bomb is refused mid-stream, never
29
+ * allocated), and the output must then equal that length exactly (which catches
30
30
  * the under-length direction a cap cannot see). The caller's policy cap applies
31
31
  * independently, so a peer declaring 16 MiB does not get 16 MiB. This module
32
32
  * decodes structure only -- it never verifies a certificate, builds a path, or
@@ -83,14 +83,14 @@ var LEVEL_OPT = {
83
83
  zstd: function (n) { var p = {}; p[zlib.constants.ZSTD_c_compressionLevel] = n; return { params: p }; },
84
84
  };
85
85
 
86
- // The names that are BOTH registered by RFC 8879 and reachable through the decompression
86
+ // The names that are both registered by RFC 8879 and reachable through the decompression
87
87
  // guard on this runtime. An algorithm the guard cannot decompress must never be offered
88
- // or accepted -- advertising one would mean accepting a message we then cannot open.
88
+ // or accepted: advertising one would mean accepting a message we then cannot open.
89
89
  var SUPPORTED = guard.compress.algorithms().filter(function (n) {
90
90
  return Object.prototype.hasOwnProperty.call(ALG_BY_NAME, n) && typeof COMPRESS[n] === "function";
91
91
  });
92
92
 
93
- // The minimum Certificate message size and the widest opaque<..2^24-1> vector -- both owned
93
+ // The minimum Certificate message size and the widest opaque<..2^24-1> vector, both owned
94
94
  // by validator-tls, alongside the rest of the framing rule set. A declared
95
95
  // uncompressed_length below the minimum cannot be a Certificate message, and refusing it
96
96
  // also keeps a zero-length declaration away from the decompression cap, which is a positive
@@ -147,12 +147,12 @@ function _resolveAllowed(opts) {
147
147
  *
148
148
  * Decode an RFC 8879 sec. 4 `CompressedCertificate`: the `algorithm` code point, the
149
149
  * declared `uncompressedLength`, the recovered `certificateMessage` (the raw RFC 8446
150
- * sec. 4.4.2 `Certificate` message bytes, surfaced verbatim), and `certificate` -- that
150
+ * sec. 4.4.2 `Certificate` message bytes, surfaced verbatim), and `certificate`, that
151
151
  * message already decoded into its request context and per-entry certificate DER.
152
152
  *
153
153
  * The decompression bound is two-sided, as RFC 8879 sec. 5 requires. The decompressor is
154
154
  * capped at `min(uncompressedLength, policy cap)`, so a bomb is refused the moment its
155
- * output would exceed what the message itself declared -- it is never allocated. The
155
+ * output would exceed what the message itself declared, so it is never allocated. The
156
156
  * recovered length must then equal `uncompressedLength` exactly, which catches the
157
157
  * under-length direction no cap can see. A message that fails either way is refused;
158
158
  * RFC 8879 sec. 5 maps both to the `bad_certificate` alert, but they keep distinct codes
@@ -160,7 +160,7 @@ function _resolveAllowed(opts) {
160
160
  * diagnoses.
161
161
  *
162
162
  * An algorithm outside the RFC 8879 registry, one this runtime cannot decompress, or one
163
- * absent from `opts.allowedAlgorithms` is refused before any decompressor runs -- the
163
+ * absent from `opts.allowedAlgorithms` is refused before any decompressor runs, since the
164
164
  * algorithm MUST be one the receiver advertised (RFC 8879 sec. 4). An empty compressed
165
165
  * body is a framing violation (`opaque<1..2^24-1>`), not an empty certificate list.
166
166
  *
@@ -263,18 +263,18 @@ function decompressCertificate(bytes, opts) {
263
263
  * Decode an RFC 8446 sec. 4.4.2 `Certificate` message into its
264
264
  * `certificateRequestContext` (raw, empty in a server's handshake certificate) and its
265
265
  * `entries`. Each entry surfaces `certData` -- the certificate DER exactly as it appeared
266
- * on the wire, ready for `pki.schema.x509.parse` and never re-serialized -- plus the raw
266
+ * on the wire, ready for `pki.schema.x509.parse` and never re-serialized, plus the raw
267
267
  * `extensions` bytes that follow it and `extensionList`, those bytes decoded to their
268
268
  * RFC 8446 sec. 4.2 records (`type` and raw `data` per extension). The vector's framing is
269
- * validated rather than accepted opaquely, so a vector that cannot be a whole number of
269
+ * validated and not accepted opaquely, so a vector that cannot be a whole number of
270
270
  * Extensions is refused instead of being reported as a well-formed message.
271
271
  *
272
- * `certificate_type` is negotiated by a separate extension (RFC 7250) and is NOT present
272
+ * `certificate_type` is negotiated by a separate extension (RFC 7250) and is not present
273
273
  * in this message, so it cannot be inferred from the bytes. It is declared through
274
274
  * `opts.certificateType` and defaults to X509; under `"RawPublicKey"` the same slot is a
275
275
  * `SubjectPublicKeyInfo` and is surfaced as `spki` instead of `certData`.
276
276
  *
277
- * Throws `TlsError` with a stable `tls/*` code on any framing violation -- a lying vector
277
+ * Throws `TlsError` with a stable `tls/*` code on any framing violation: a lying vector
278
278
  * length, a field past its bound, or bytes trailing the entry list.
279
279
  *
280
280
  * @opts
@@ -330,7 +330,7 @@ function parseCertificateMessage(bytes, opts) {
330
330
  * through `decompressCertificate` and the recovered bytes compared to the input, so a
331
331
  * message this toolkit produces can never be one this toolkit's own decoder refuses.
332
332
  *
333
- * Throws `TlsError` with a stable `tls/*` code -- for an unknown algorithm, a message
333
+ * Throws `TlsError` with a stable `tls/*` code for an unknown algorithm, or a message
334
334
  * that cannot be framed (empty, or past the 2^24-1 ceiling either compressed or not).
335
335
  *
336
336
  * @opts
package/lib/trust.js CHANGED
@@ -11,7 +11,7 @@
11
11
  * @intro
12
12
  * Mozilla / CCADB root-program ingestion into constraint-carrying trust
13
13
  * anchors. The bare root list (`tls.rootCertificates`) throws away exactly
14
- * the metadata that decides WHICH roots may vouch for WHAT: the per-purpose
14
+ * the metadata that decides which roots may vouch for what: the per-purpose
15
15
  * trust bits (a root trusted for TLS is not thereby trusted for S/MIME) and
16
16
  * the per-purpose distrust-after dates (a sunsetting root keeps validating
17
17
  * already-issued certificates while certificates issued after the cutoff are
@@ -57,7 +57,7 @@ var PURPOSE_ATTRS = [
57
57
  ["CKA_TRUST_CODE_SIGNING", "codeSigning"],
58
58
  ];
59
59
 
60
- // The recognized CK_TRUST vocabulary. ONLY CKT_NSS_TRUSTED_DELEGATOR grants a
60
+ // The recognized CK_TRUST vocabulary. Only CKT_NSS_TRUSTED_DELEGATOR grants a
61
61
  // purpose; the rest are recognized-but-untrusted (fail-closed: an absent or
62
62
  // non-delegator bit never defaults to trusted). A token outside this set is a
63
63
  // typed trust/bad-trust-value, never a silent false.
@@ -138,7 +138,7 @@ function _lexObjects(text) {
138
138
  var rawVal = m[3] !== undefined ? m[3].trim() : undefined;
139
139
  if (!type) throw E("trust/bad-block", "attribute line is missing its type token: " + name);
140
140
  if (!cur) {
141
- // Refuse the block bomb BEFORE lexing another block's payloads.
141
+ // Refuse the block bomb before lexing another block's payloads.
142
142
  if (objects.length >= LIMITS.TRUST_MAX_OBJECTS) throw E("trust/bad-block", "certdata object count exceeds LIMITS.TRUST_MAX_OBJECTS");
143
143
  cur = { attrs: Object.create(null) };
144
144
  }
@@ -167,8 +167,8 @@ function _lexObjects(text) {
167
167
  if (rawVal === undefined || /\s/.test(rawVal)) throw E("trust/bad-block", name + " " + type + " must carry a single token value");
168
168
  entry = { type: type, value: rawVal };
169
169
  } else {
170
- // An unknown value TYPE cannot be lexed safely (its value grammar is
171
- // unknown) -- fail closed rather than guess past it.
170
+ // An unknown value type cannot be lexed safely (its value grammar is
171
+ // unknown), so fail closed instead of guessing past it.
172
172
  throw E("trust/bad-block", "unrecognized attribute type " + JSON.stringify(type) + " on " + name);
173
173
  }
174
174
  cur.attrs[name] = entry;
@@ -190,8 +190,8 @@ function _requireOctal(obj, name, what) {
190
190
  return a.bytes;
191
191
  }
192
192
 
193
- // The NSS distrust-after payload is the BARE ASCII of a YYMMDDHHMMSSZ UTCTime
194
- // (13 bytes) or a YYYYMMDDHHMMSSZ GeneralizedTime (15 bytes) -- no DER tag or
193
+ // The NSS distrust-after payload is the bare ASCII of a YYMMDDHHMMSSZ UTCTime
194
+ // (13 bytes) or a YYYYMMDDHHMMSSZ GeneralizedTime (15 bytes), with no DER tag or
195
195
  // length. Synthesize the minimal TLV and route it through the strict asn1
196
196
  // time reader so the Z-terminator / mandatory-seconds / component-rollover /
197
197
  // RFC 5280 sec. 4.1.2.5.1 2050-pivot rules are enforced once, never re-derived.
@@ -300,14 +300,14 @@ function _mkAnchor(cert, meta) {
300
300
  label: meta.label,
301
301
  mozillaCaPolicy: meta.mozillaCaPolicy,
302
302
  };
303
- // A trust anchor IS the pair (name, key) -- RFC 5280 sec. 6.1.1 -- so those two fields are one
303
+ // A trust anchor is the pair (name, key) per RFC 5280 sec. 6.1.1, so those two fields are one
304
304
  // fact about one certificate, and they were derived here from one. `anchor()` re-derives them
305
- // rather than reading them back, so an entry rebuilt with a substituted key cannot carry the
305
+ // instead of reading them back, so an entry rebuilt with a substituted key cannot carry the
306
306
  // store's NAME and its per-purpose trust metadata over to a key the store never vouched for.
307
307
  // Recorded off the object, so rebuilding the entry loses the record along with the binding.
308
- // COPIED, not aliased. The record and the entry would otherwise hold the same Buffer and the same
309
- // name object, so overwriting the entry's publicKey in place -- otherSpki.copy(entry.publicKey)
310
- // for an equal-length key -- would overwrite the record with it, and re-deriving would hand back
308
+ // Copied, not aliased. The record and the entry would otherwise hold the same Buffer and the same
309
+ // name object, so overwriting the entry's publicKey in place (otherSpki.copy(entry.publicKey)
310
+ // for an equal-length key) would overwrite the record with it, and re-deriving would hand back
311
311
  // exactly the substituted key. A record that changes with the thing it is meant to pin is not one.
312
312
  //
313
313
  // The store's METADATA is recorded with them, and for the stronger reason: `purposes` IS the
@@ -334,7 +334,7 @@ var _DERIVED_FROM = new WeakMap();
334
334
  // The per-purpose distrust dates, with fresh Date objects. A Date is mutable, so handing back the
335
335
  // entry's own would let a consumer move the date this anchor is judged against.
336
336
  // The three trust bits, normalized to booleans. Read by the purpose gate and handed back on every
337
- // anchor, so it is built fresh from whichever source is authoritative rather than shared.
337
+ // anchor, so it is built fresh from whichever source is authoritative and never shared.
338
338
  function _copyPurposes(src) {
339
339
  return {
340
340
  serverAuth: !!src && src.serverAuth === true,
@@ -352,13 +352,13 @@ function _copyDistrustAfter(src) {
352
352
  return out;
353
353
  }
354
354
 
355
- // A parsed Name, copied FAITHFULLY: every field the parser assigns, at every level, with nothing
356
- // mutable shared. Used on both sides of the record -- writing it in and handing it out -- so
355
+ // A parsed Name, copied faithfully: every field the parser assigns, at every level, with nothing
356
+ // mutable shared. Used on both sides of the record, writing it in and handing it out, so
357
357
  // neither direction shares an object with the caller.
358
358
  //
359
- // Faithful rather than "the fields the path validator reads", because this Name is handed back on
359
+ // Faithful, not "the fields the path validator reads", because this Name is handed back on
360
360
  // pki.trust.anchor and is the same structure pki.schema.x509.parse produces. A copy that keeps the
361
- // subset one consumer needs silently degrades it for every other reader -- an attribute would lose
361
+ // subset one consumer needs silently degrades it for every other reader: an attribute would lose
362
362
  // its registry `name`, so a caller walking anchor.name.rdns would see a different shape depending
363
363
  // on whether the anchor came from a store entry or straight from the parser. Each attribute value
364
364
  // is a string and so needs no copy of its own; the DER does.
@@ -396,11 +396,11 @@ function _anchorsAgree(x, y) {
396
396
  _datesEqual(x.distrustAfter, y.distrustAfter);
397
397
  }
398
398
 
399
- // Dedup by (subjectDer, publicKey): the SAME root appearing twice (a
400
- // duplicated block, or certdata + CCADB merged by the operator) collapses to
401
- // one anchor; a DISTINCT root that merely shares a subject DN has a different
402
- // key and survives. Two entries for one root that DISAGREE on trust metadata
403
- // are ambiguous -- fail closed rather than silently pick one.
399
+ // Dedup by (subjectDer, publicKey): one root appearing twice (a duplicated
400
+ // block, or certdata + CCADB merged by the operator) collapses to one anchor,
401
+ // while a distinct root that merely shares a subject DN has a different key and
402
+ // survives. Two entries for one root that disagree on trust metadata are
403
+ // ambiguous, so they fail closed instead of silently picking one.
404
404
  function _dedupAnchors(anchors) {
405
405
  var seen = Object.create(null);
406
406
  var out = [];
@@ -443,7 +443,7 @@ function _dedupAnchors(anchors) {
443
443
  * dropped); a trust object with no certificate grants nothing. Malformed
444
444
  * octal, an oversized block, an unrecognized trust value, a mispaired or
445
445
  * ambiguous-duplicate block, or an undecodable distrust-after time throws a
446
- * typed `trust/*` error -- never a silently truncated or misattributed root.
446
+ * typed `trust/*` error, never a silently truncated or misattributed root.
447
447
  *
448
448
  * @example
449
449
  * // Real input is the NSS certdata.txt read from disk; a one-root stream is
@@ -653,9 +653,9 @@ function _pemCell(cell) {
653
653
  * @defends trust-metadata-misattribution (CWE-345), trust-store-parser-DoS (CWE-770)
654
654
  * @related pki.trust.parseCertdata, pki.trust.anchor, pki.path.validate
655
655
  *
656
- * Parse a CCADB certificate-records CSV export into the SAME `Anchor` shape
656
+ * Parse a CCADB certificate-records CSV export into the same `Anchor` shape
657
657
  * `parseCertdata` produces, so downstream enforcement is source-agnostic.
658
- * Columns are located by header NAME -- never by position -- and unknown,
658
+ * Columns are located by header name, never by position, and unknown,
659
659
  * reordered, or extra columns are tolerated; a MISSING required column
660
660
  * (`Common Name or Certificate Name`, `Trust Bits`, `Distrust for TLS After
661
661
  * Date`, `Distrust for S/MIME After Date`, `PEM Info`) fails closed with
@@ -737,7 +737,7 @@ function parseCcadbCsv(text) {
737
737
  *
738
738
  * Turn a parsed trust-store entry into the `trustAnchor` object
739
739
  * `pki.path.validate` consumes: `{ name, publicKey, algorithm, parameters,
740
- * distrustAfter, purposes }` -- a straight hand-off (validate reads
740
+ * distrustAfter, purposes }`, a straight hand-off (validate reads
741
741
  * `distrustAfter` as a per-purpose map and `purposes` as the delegator set,
742
742
  * selected by its own `opts.checkPurpose`). With `opts.purpose` it
743
743
  * fail-fasts: an entry that is not a trusted delegator for that purpose
@@ -746,7 +746,7 @@ function parseCcadbCsv(text) {
746
746
  * authoritative gate stays inside `validate`).
747
747
  *
748
748
  * @opts
749
- * purpose: string // "serverAuth" | "emailProtection" | "codeSigning" -- fail-fast purpose check
749
+ * purpose: string // "serverAuth" | "emailProtection" | "codeSigning"; fail-fast purpose check
750
750
  *
751
751
  * @example
752
752
  * var ca = await pki.key.generate("Ed25519");
package/lib/tsp-sign.js CHANGED
@@ -51,7 +51,7 @@ var HASH_LEN = { sha256: 32, sha384: 48, sha512: 64 };
51
51
  // checker ran at all"; "undetermined" is "one ran and could not answer"; "waived" is an
52
52
  // undetermined one a caller chose to pass; "determined" is an explicit good-or-revoked answer.
53
53
  // Ranking them lets a caller trying several candidate paths keep the most established outcome any
54
- // of them reached, rather than whichever happened to run last.
54
+ // of them reached. Order of evaluation then does not decide the answer.
55
55
  var _REVOCATION_RANK = { "false": 0, "undetermined": 1, "waived": 2, "determined": 3 };
56
56
  function _rankRevocation(v) {
57
57
  var r = _REVOCATION_RANK[String(v)];
@@ -61,7 +61,7 @@ function _moreEstablished(candidate, current) {
61
61
  return _rankRevocation(candidate) > _rankRevocation(current);
62
62
  }
63
63
 
64
- // A hash AlgorithmIdentifier { OID, NULL } -- messageImprint and ESSCertIDv2 hash algorithms
64
+ // A hash AlgorithmIdentifier { OID, NULL }: messageImprint and ESSCertIDv2 hash algorithms
65
65
  // carry an explicit NULL parameter (the form RFC 3161 / RFC 5035 producers emit).
66
66
  function _hashAlgId(name) {
67
67
  if (!NODE_DIGEST[name]) throw _err("tsp/unsupported-algorithm", "unsupported hash algorithm " + JSON.stringify(name));
@@ -114,7 +114,7 @@ function _signingCertV2(certDer, hashName) {
114
114
  * @opts serialNumber REQUIRED -- a unique token serial number (a number or BigInt).
115
115
  * @opts genTime The trusted time (a `Date`). Default: now.
116
116
  * @opts nonce The request nonce to echo (a number or BigInt), for replay protection.
117
- * @opts accuracy `{ seconds?, millis?, micros? }` -- the genTime +/- accuracy.
117
+ * @opts accuracy The genTime +/- accuracy, as `{ seconds?, millis?, micros? }`.
118
118
  * @opts ordering Whether tokens from this TSA are strictly ordered in time (boolean).
119
119
  * @opts certHashAlgorithm The ESSCertIDv2 hash algorithm name. Default `sha256`.
120
120
  * @opts sid / pem Passed through to `pki.cms.sign` (signer identifier, PEM output).
@@ -250,7 +250,7 @@ function _assertImprint(mi) {
250
250
  * @related pki.tsp.parseRequest, pki.tsp.sign
251
251
  *
252
252
  * Build an RFC 3161 `TimeStampReq` (sec. 2.4.1) over `messageImprint`
253
- * (`{ hashAlgorithm, hashedMessage }` -- the same shape `pki.tsp.sign` takes). `version` is 1;
253
+ * (`{ hashAlgorithm, hashedMessage }`, the same shape `pki.tsp.sign` takes). `version` is 1;
254
254
  * `certReq` is BOOLEAN DEFAULT FALSE, so it is emitted only when explicitly `true`. Returns DER
255
255
  * (or PEM when `opts.pem`).
256
256
  *
@@ -611,38 +611,38 @@ function _buildTsaChains(leaf, pool) {
611
611
  * @related pki.tsp.sign, pki.cms.verify, pki.path.validate
612
612
  *
613
613
  * Verify an RFC 3161 TimeStampToken against the data it should cover. `token` is the token DER /
614
- * PEM (never a parsed object -- every checked field is read from the CMS-verified eContent, so a
615
- * mutated parsed structure cannot desynchronize the checks from the signed bytes). `data` is the
614
+ * PEM, never a parsed object: every checked field is read from the CMS-verified eContent, so a
615
+ * mutated parsed structure cannot desynchronize the checks from the signed bytes. `data` is the
616
616
  * original bytes (hashed under the token's messageImprint algorithm) or a precomputed
617
617
  * `{ hashAlgorithm, hashedMessage }`. Returns `{ valid, trusted, revocationChecked,
618
618
  * anchorConstraints, genTime, accuracy, serialNumber,
619
619
  * serialNumberHex, policy, nonce, tsa, tstInfo, signer, code?, reason? }`. `valid` is true only
620
620
  * when the CMS signature, the imprint match, the eContentType, the ESSCertID(V2) binding, the
621
621
  * RFC 3161 sec. 2.3 critical single-`timeStamping` extendedKeyUsage rule, the requested nonce (when
622
- * supplied), and -- when a `trustAnchor` is supplied -- the full out-of-path TSA-cert path
622
+ * supplied), and, when a `trustAnchor` is supplied, the full out-of-path TSA-cert path
623
623
  * validation all pass. A conformance / trust failure of a well-formed token is a
624
624
  * `{ valid:false, code }` verdict; malformed or config input throws a typed `TspError`.
625
625
  *
626
626
  * `trusted` is the second claim and is kept apart from the first. `valid` says the token's
627
627
  * signature and structural bindings hold; `trusted` says the timestamp authority chained to an
628
628
  * anchor this caller named. Without `trustAnchor` there is nothing to chain to and `trusted` is
629
- * `false` -- a definite answer rather than a missing one, on the refusal branch as well as the
630
- * accepting one. A timestamp is archived precisely to be re-read years later, and one boolean
629
+ * `false`: every branch, refusing and accepting alike, carries the field with a definite
630
+ * value. A timestamp is archived precisely to be re-read years later, and one boolean
631
631
  * cannot answer both questions then.
632
632
  *
633
633
  * `revocationChecked` is the third claim, for the same reason. Revocation runs only when a
634
- * `revocationChecker` is supplied, so a `trusted` token whose TSA was never checked against a CRL
635
- * or an OCSP responder reads identically to one established un-revoked -- unless the verdict says
636
- * which. It is `false` whenever no path ran at all. `anchorConstraints` carries whatever the anchor
634
+ * `revocationChecker` is supplied. Without a field naming which happened, a `trusted` token whose
635
+ * TSA was never checked against a CRL or an OCSP responder reads identically to one established
636
+ * un-revoked. It is `false` whenever no path ran at all. `anchorConstraints` carries whatever the anchor
637
637
  * itself constrained, from `pki.path.validate`.
638
638
  *
639
- * @opts trustAnchor Anchor `{ name, publicKey, algorithm }` -- runs `pki.path.validate` on the
639
+ * @opts trustAnchor Anchor `{ name, publicKey, algorithm }`. Runs `pki.path.validate` on the
640
640
  * TSA certificate chain ordered from the token's embedded certificates
641
641
  * (validity at genTime, requiredEku timeStamping, and revocation when a
642
- * `revocationChecker` is supplied -- `revocationChecked` reports which), so a
643
- * TSA under an intermediate CA validates, not only one directly under the
644
- * anchor. Omit to verify signature + imprint + binding + EKU only and anchor
645
- * the cert yourself.
642
+ * `revocationChecker` is supplied, with `revocationChecked` reporting which),
643
+ * so a TSA under an intermediate CA validates, not only one directly under
644
+ * the anchor. Omit to verify signature + imprint + binding + EKU only and
645
+ * anchor the cert yourself.
646
646
  * @opts nonce Require the token's TSTInfo.nonce to equal this (a number/BigInt).
647
647
  * @opts reqPolicy Require the token's policy to equal this (an OID name or dotted string).
648
648
  * @opts certs Out-of-band TSA certificates (an array of DER `Buffer`s) added to the signer
@@ -8,7 +8,7 @@
8
8
  // validator-all -- the validator-family orchestrator (schema-all's role for the
9
9
  // validator family, guard-all's role for the guards). It assembles the per-type
10
10
  // conformance validators into one namespaced surface every format module composes,
11
- // so a format depends on the family rather than re-deriving a decoded type's rule set
11
+ // so a format depends on the family and no module re-derives a decoded type's rule set
12
12
  // inline (the drift that leaks a spec MUST out one review round at a time):
13
13
  //
14
14
  // validator.cose.credentialKey / .toSpki
@@ -9,7 +9,7 @@
9
9
  // attestation-certificate profile" (WebAuthn sec. 8.2.1 packed, sec. 8.3.1 TPM AIK).
10
10
  // Sibling to the guard family: a validator owns a decoded TYPE's COMPLETE conformance rule
11
11
  // set once, so the packed and AIK profiles cannot drift apart or lose a gate (e.g. the v3
12
- // check that previously lived split out in the tpm verifier rather than in the profile).
12
+ // check that previously lived split out in the tpm verifier, away from the profile).
13
13
  //
14
14
  // The caller supplies its parsed certificate + an extension-accessor object `exts`
15
15
  // { find(cert, oidName) -> rawExt|null, decode(cert, oidName) -> {critical, value}|null }
@@ -10,7 +10,7 @@
10
10
  // CTAP2 canonical-CBOR profile WebAuthn sec. 6.5.1 imposes). Sibling to the guard
11
11
  // family: where a guard owns a CVE-class fail-closed defence once, a validator owns a
12
12
  // decoded TYPE's COMPLETE conformance rule set once, so a format module composes the
13
- // family rather than re-deriving a partial subset inline (the drift that leaks MUSTs
13
+ // family instead of re-deriving a partial subset inline (the drift that leaks MUSTs
14
14
  // out one review round at a time). Enforced by the validator-shape-reinlined
15
15
  // codebase-patterns detector: a lib function that re-inlines COSE-key validation fires.
16
16
  //
@@ -20,7 +20,7 @@
20
20
  //
21
21
  // Rule set (gap-checked verbatim against RFC 9052 sec. 7 + RFC 9053 sec. 2/6 +
22
22
  // WebAuthn sec. 6.5.1 + the IANA COSE Key Type / Key Type Parameters registries):
23
- // - kty (label 1) REQUIRED; value an integer (CTAP2 canonical -- a tstr kty/alg is
23
+ // - kty (label 1) REQUIRED; value an integer (CTAP2 canonical, so a tstr kty/alg is
24
24
  // rejected as non-canonical for the WebAuthn profile).
25
25
  // - alg (label 3) REQUIRED (the RP needs it to verify the later assertion).
26
26
  // - EC2 (kty 2): crv (-1), x (-2), y (-3) all present; x/y length == the curve field
@@ -29,16 +29,16 @@
29
29
  // - CANONICAL: EXACTLY the type's parameters, nothing more (EC2 = 5, OKP/RSA = 4) --
30
30
  // rejects a padded key / a private "d" component / kid / key_ops (WebAuthn 6.5.1
31
31
  // CTAP2 canonical, stricter than open COSE `* label => values`).
32
- // - PROFILE: alg <-> kty (and, for EC2, alg <-> crv) consistent; -8 (EdDSA) is Ed25519
33
- // ONLY; the RFC 9864 fully-specified ids (-9/-51/-52/-19/-53) are accepted.
34
- // - COMPRESSED: an EC2 credential key MUST use the uncompressed point form (y is a full
35
- // coordinate byte string, never a CBOR bool sign bit) -- WebAuthn sec. alg identifier.
32
+ // - PROFILE: alg <-> kty (and, for EC2, alg <-> crv) consistent; -8 (EdDSA) is only
33
+ // Ed25519; the RFC 9864 fully-specified ids (-9/-51/-52/-19/-53) are accepted.
34
+ // - COMPRESSED: an EC2 credential key MUST use the uncompressed point form, per WebAuthn
35
+ // sec. alg identifier: y is a full coordinate byte string, never a CBOR bool sign bit.
36
36
  // - ON-CURVE: the public key point MUST be valid for its curve. For EC2 the SPKI is
37
37
  // imported via node:crypto so OpenSSL validates the point (an off-curve or identity
38
- // point fails to parse). For OKP, OpenSSL does NOT validate the Edwards point on
38
+ // point fails to parse). For OKP, OpenSSL does not validate the Edwards point on
39
39
  // import (an all-zeroes key parses, and even verifies a trivial signature), so the
40
40
  // point is checked explicitly via edwards-point (RFC 8032 decode + cofactor low-order
41
- // rejection) -- an off-curve, non-canonical, or low-order OKP key fails closed.
41
+ // rejection). An off-curve, non-canonical, or low-order OKP key fails closed.
42
42
 
43
43
  var cbor = require("./cbor-det");
44
44
  var asn1 = require("./asn1-der");
@@ -54,7 +54,7 @@ var EC2_CRV_OID = { 1: "prime256v1", 2: "secp384r1", 3: "secp521r1" };
54
54
  var OKP_CRV = { 6: { oid: "Ed25519", len: 32 }, 7: { oid: "Ed448", len: 57 } };
55
55
  // alg (label 3) -> the key type (and, for EC2, curve) it pins. WebAuthn (sec. alg
56
56
  // identifier) adds guarantees over the open COSE registry: an ECDSA alg fixes its curve,
57
- // -8 (EdDSA) is Ed25519 ONLY, and the RFC 9864 fully-specified ids (-9 ESP256, -51 ESP384,
57
+ // -8 (EdDSA) is only Ed25519, and the RFC 9864 fully-specified ids (-9 ESP256, -51 ESP384,
58
58
  // -52 ESP512, -19 Ed25519, -53 Ed448) each pin key type + curve. A verifier accepts the
59
59
  // fully-specified ids even though WebAuthn recommends against them for credential creation.
60
60
  // The RSA credential-key bounds. 2048 bits is the floor every current FIDO authenticator and
@@ -73,8 +73,8 @@ var ALG_PROFILE = {
73
73
  "-9": { kty: 2, crv: 1 }, "-51": { kty: 2, crv: 2 }, "-52": { kty: 2, crv: 3 },
74
74
  "-8": { kty: 1, crv: 6 }, "-19": { kty: 1, crv: 6 }, "-53": { kty: 1, crv: 7 },
75
75
  // RSASSA-PSS at all three strengths. PS256 alone left PS384/PS512 refused at PARSE time on a key
76
- // that is perfectly well-formed -- the same bytes accepted under -37 -- so the refusal blamed the
77
- // key rather than the algorithm, and a relying party migrating credential rows written by another
76
+ // that is perfectly well-formed (the same bytes accepted under -37), so the refusal blamed the
77
+ // key instead of the algorithm, and a relying party migrating credential rows written by another
78
78
  // implementation could not tell which of its stored keys this verifier would decline, or why.
79
79
  "-257": { kty: 3 }, "-258": { kty: 3 }, "-259": { kty: 3 },
80
80
  "-37": { kty: 3 }, "-38": { kty: 3 }, "-39": { kty: 3 }, "-65535": { kty: 3 },
@@ -89,12 +89,12 @@ var ALG_PROFILE = {
89
89
  // @validator-shape kty\s*===\s*2n
90
90
  // @validator-shape EC2_CRV_LEN|ALG_PROFILE
91
91
  // `unsupportedCode` is OPTIONAL and names the code raised when the key is well-formed but its
92
- // algorithm is not one this verifier implements -- a different fact from a malformed key. Omit it
92
+ // algorithm is not one this verifier implements, a different fact from a malformed key. Omit it
93
93
  // and that case keeps raising `code`, so an existing caller sees no change.
94
94
  function credentialKey(node, E, code, unsupportedCode) {
95
95
  function bad(msg, cause) { return new E(code, msg, cause); }
96
96
  if (!node || node.majorType !== 5) throw bad("a COSE_Key must be a CBOR map (RFC 9052 sec. 7)");
97
- // Every parameter read maps a wrong-type cbor/* fault to the caller's domain -- a
97
+ // Every parameter read maps a wrong-type cbor/* fault to the caller's domain: a
98
98
  // wrong-typed COSE label (x as an integer, kty as a string) is bad input, not a leak.
99
99
  function ib(label) { var n = cbor.read.mapGet(node, label); if (!n) return null; try { return cbor.read.byteString(n); } catch (e) { throw bad("COSE_Key parameter " + label + " must be a byte string", e); } }
100
100
  function ii(label) { var n = cbor.read.mapGet(node, label); if (!n) return null; try { return cbor.read.int(n); } catch (e) { throw bad("COSE_Key parameter " + label + " must be an integer", e); } }
@@ -122,17 +122,17 @@ function credentialKey(node, E, code, unsupportedCode) {
122
122
  } else if (kty === 3n) {
123
123
  key.n = ib(-1); key.e = ib(-2);
124
124
  if (!key.n || !key.n.length || !key.e || !key.e.length) throw bad("an RSA COSE_Key must carry n (-1) and e (-2)");
125
- // The MATERIAL, not merely its presence -- the same standard the other two key types are held
126
- // to, where EC2 pins x/y to the curve's field size and has the point validated on the curve,
125
+ // The material itself, not merely its presence: the same standard the other two key types are
126
+ // held to, where EC2 pins x/y to the curve's field size and has the point validated on the curve,
127
127
  // and OKP pins x to an exact length. Checking only presence let a 1-byte modulus and an
128
128
  // exponent of 1 through as conformant credential public keys, and both reach the WebCrypto
129
129
  // import, so they reach real signature verification. e = 1 makes RSA the identity function:
130
130
  // the "signature" is the message, and it verifies under any modulus.
131
- // BOTH values first, before either is judged. RFC 8230 sec. 4 encodes n and e as unsigned
132
- // big-endian integers with no leading zero, and every check below reads a byte LENGTH as though
131
+ // Both values first, before either is judged. RFC 8230 sec. 4 encodes n and e as unsigned
132
+ // big-endian integers with no leading zero, and every check below reads a byte length as though
133
133
  // it were a magnitude: the modulus floor, the exponent bound, and the exponent's value. A
134
134
  // padded encoding decouples the two, so `00 01` would be read as a two-byte exponent and skip
135
- // the value check that refuses 1 -- the degenerate key the whole check exists to catch. (An EC2
135
+ // the value check that refuses 1, the degenerate key the whole check exists to catch. (An EC2
136
136
  // coordinate is the opposite case, fixed-width and zero-padded by definition, which is why this
137
137
  // rule is stated for the RSA parameters and not for x/y.)
138
138
  if (key.n[0] === 0) throw bad("an RSA COSE_Key modulus (-1) must be minimally encoded, with no leading zero byte (RFC 8230 sec. 4)");
@@ -147,9 +147,9 @@ function credentialKey(node, E, code, unsupportedCode) {
147
147
  // so a caller cannot hand over a megabyte of exponent for the modular exponentiation to chew.
148
148
  if (key.e.length > RSA_MAX_EXPONENT_BYTES) throw bad("an RSA COSE_Key exponent (-2) is longer than " + RSA_MAX_EXPONENT_BYTES + " bytes");
149
149
  if ((key.e[key.e.length - 1] & 1) === 0) throw bad("an RSA COSE_Key exponent (-2) must be odd");
150
- // Minimal encoding above makes a one-byte e the ONLY way to express a value this small, so the
150
+ // Minimal encoding above makes a one-byte e the only way to express a value this small, so the
151
151
  // comparison is on the value and not on where it happens to sit.
152
- if (key.e.length === 1 && key.e[0] <= 1) throw bad("an RSA COSE_Key exponent (-2) must be greater than 1 -- e = 1 makes RSA the identity function");
152
+ if (key.e.length === 1 && key.e[0] <= 1) throw bad("an RSA COSE_Key exponent (-2) must be greater than 1; e = 1 makes RSA the identity function");
153
153
  } else {
154
154
  throw bad("unsupported COSE_Key kty " + Number(kty));
155
155
  }
@@ -170,40 +170,39 @@ function credentialKey(node, E, code, unsupportedCode) {
170
170
  // form their datastore round-trips, which is not a choice about how carefully their
171
171
  // credential is checked.
172
172
  //
173
- // Everything above this line is about the ENCODING -- a CBOR map, integer labels, byte
174
- // strings, the canonical parameter count -- and can only be asked of bytes. Everything
173
+ // Everything above this line is about the encoding (a CBOR map, integer labels, byte
174
+ // strings, the canonical parameter count) and can only be asked of bytes. Everything
175
175
  // here is about the key, and is asked of both.
176
176
  //
177
177
  // @enforced-by behavioral -- key-material rules have no rename-proof code shape distinct
178
- // from ordinary length and byte comparisons; the RED vectors that drive BOTH accepted
178
+ // from ordinary length and byte comparisons; the RED vectors that drive both accepted
179
179
  // forms of a stored credential key (the COSE bytes and the object) through
180
180
  // pki.webauthn.verifyAssertion with an undersized modulus, e = 1, a curve/length
181
181
  // mismatch and a short OKP x are the guard.
182
182
  function assertKeyMaterial(key, E, code, unsupportedCode) {
183
183
  function bad(msg, cause) { return new E(code, msg, cause); }
184
184
  if (!key || typeof key !== "object") throw bad("a credential key must be a decoded COSE_Key object");
185
- // ONE read of each field, into a plain object, before anything is checked or used.
185
+ // One read of each field, into a plain object, before anything is checked or used.
186
186
  //
187
- // The object form comes from the caller, so any of these can be an accessor. One that THROWS
188
- // turns a validation into a raw fault -- the thing this function exists to prevent -- and one
189
- // that returns DIFFERENT values on successive reads makes the field that was checked and the
190
- // field that is used two different values, which is the check defeated rather than merely
191
- // reported badly. Reading each exactly once settles both, and settles them for every field
192
- // rather than for the ones a particular branch happens to reach.
187
+ // The object form comes from the caller, so any of these can be an accessor. One that throws
188
+ // turns a validation into a raw fault, the thing this function exists to prevent, and one
189
+ // that returns different values on successive reads makes the field that was checked and the
190
+ // field that is used two different values, which defeats the check outright. Reading each
191
+ // exactly once settles both, for every field, not just the ones a particular branch reaches.
193
192
  try {
194
193
  key = { kty: key.kty, alg: key.alg, crv: key.crv, x: key.x, y: key.y, n: key.n, e: key.e };
195
194
  } catch (e) { throw bad("a credential key field could not be read", e); }
196
- // Then the TYPE and the VALUE. BigInt() throws a raw TypeError on a Symbol and on undefined, and
197
- // a raw RangeError on a fractional or non-finite number, so "it is a number" is not the check --
198
- // "it is an integer" is. EVERY integer label the branches below read, not the two the dispatch
199
- // happens to need first:
195
+ // Then the type and the value. BigInt() throws a raw TypeError on a Symbol and on undefined, and
196
+ // a raw RangeError on a fractional or non-finite number, so the check is not "it is a number";
197
+ // it is "it is an integer". Every integer label the branches below read, not the two the
198
+ // dispatch happens to need first:
200
199
  // crv indexes a lookup table, and a Symbol thrown at a property read is the same raw fault as a
201
200
  // Symbol thrown at BigInt(). The decoded form gets these from the CBOR reader, which has already
202
201
  // established them; the object form gets them from the caller, so this is where they are settled.
203
202
  // A BigInt is bounded too. COSE labels are small registry integers, and an unbounded one converts
204
- // to Infinity, which then throws a raw RangeError at the next conversion -- the same defeat as a
205
- // fractional number, reached by a value that IS an integer. "It is an integer" is not the whole
206
- // check either; "it is an integer this code can carry" is.
203
+ // to Infinity, which then throws a raw RangeError at the next conversion: the same defeat as a
204
+ // fractional number, reached by a value that is an integer. So "it is an integer" is not the
205
+ // whole check either; "it is an integer this code can carry" is.
207
206
  var MAX = BigInt(Number.MAX_SAFE_INTEGER);
208
207
  function _isInt(v) {
209
208
  if (typeof v === "bigint") return v <= MAX && v >= -MAX;
@@ -212,8 +211,8 @@ function assertKeyMaterial(key, E, code, unsupportedCode) {
212
211
  if (!_isInt(key.kty)) throw bad("a COSE_Key kty (label 1) must be an integer");
213
212
  if (!_isInt(key.alg)) throw bad("a COSE_Key alg (label 3) must be an integer");
214
213
  if (key.crv !== undefined && key.crv !== null && !_isInt(key.crv)) throw bad("a COSE_Key crv (label -1) must be an integer");
215
- // ONE representation from here down. A label may arrive as a Number or a BigInt -- a CBOR reader
216
- // hands out BigInt, an object built in JavaScript is likelier to hold Number -- and everything
214
+ // One representation from here down. A label may arrive as a Number or a BigInt (a CBOR reader
215
+ // hands out BigInt; an object built in JavaScript is likelier to hold Number), and everything
217
216
  // below compares with === against the Number-keyed profile table and the curve tables. Accepting
218
217
  // both forms at the gate and then comparing only one is a check that answers by how the caller
219
218
  // happened to spell the value; the decoded arm normalizes here too, for the same reason.
@@ -236,14 +235,14 @@ function assertKeyMaterial(key, E, code, unsupportedCode) {
236
235
  if (bits < RSA_MIN_MODULUS_BITS) throw bad("an RSA COSE_Key modulus (-1) is " + bits + " bits, below the " + RSA_MIN_MODULUS_BITS + "-bit minimum");
237
236
  if (key.e.length > RSA_MAX_EXPONENT_BYTES) throw bad("an RSA COSE_Key exponent (-2) is longer than " + RSA_MAX_EXPONENT_BYTES + " bytes");
238
237
  if ((key.e[key.e.length - 1] & 1) === 0) throw bad("an RSA COSE_Key exponent (-2) must be odd");
239
- if (key.e.length === 1 && key.e[0] <= 1) throw bad("an RSA COSE_Key exponent (-2) must be greater than 1 -- e = 1 makes RSA the identity function");
238
+ if (key.e.length === 1 && key.e[0] <= 1) throw bad("an RSA COSE_Key exponent (-2) must be greater than 1; e = 1 makes RSA the identity function");
240
239
  } else {
241
240
  throw bad("unsupported COSE_Key kty " + Number(key.kty));
242
241
  }
243
242
  // PROFILE: the declared alg must match the key type (and, for EC2, the curve).
244
243
  var prof = ALG_PROFILE[String(key.alg)];
245
- // An algorithm this verifier does not implement is NOT a malformed key. The key can be perfectly
246
- // well-formed -- the same bytes may parse under a neighbouring algorithm id -- and a relying
244
+ // An algorithm this verifier does not implement is not a malformed key. The key can be perfectly
245
+ // well-formed (the same bytes may parse under a neighbouring algorithm id), and a relying
247
246
  // party migrating credential rows written elsewhere needs to tell "I cannot check this
248
247
  // algorithm" from "these bytes are wrong", since only one of those is fixable by re-registering.
249
248
  // Callers that do not distinguish the two pass one code and keep the previous behaviour.
@@ -254,8 +253,8 @@ function assertKeyMaterial(key, E, code, unsupportedCode) {
254
253
  // x/y or the identity point fails to parse here.
255
254
  try { nodeCrypto.createPublicKey({ key: toSpki(key, E, code), format: "der", type: "spki" }); }
256
255
  catch (e) { throw bad("the credential public key point is not valid for its curve", e); }
257
- // OpenSSL does NOT validate an OKP (Ed25519/Ed448) point on import -- an all-zeroes key
258
- // parses, and even verifies a trivial signature -- so an OKP point needs an explicit
256
+ // OpenSSL does not validate an OKP (Ed25519/Ed448) point on import. An all-zeroes key
257
+ // parses, and even verifies a trivial signature, so an OKP point needs an explicit
259
258
  // on-curve + full-order (non-low-order) check (RFC 8032 decode + the cofactor check).
260
259
  if (Number(key.kty) === 1 && !edwardsPoint.validate(key.x, key.crv)) throw bad("the OKP credential public key is not a valid, full-order Edwards point");
261
260
  return key;
@@ -19,12 +19,12 @@
19
19
  // schema):
20
20
  // - KeyDescription is a positional SEQUENCE; attestationChallenge (position 4) MUST equal
21
21
  // clientDataHash.
22
- // - allApplications (tag 600) MUST be absent from BOTH the softwareEnforced (position 6)
22
+ // - allApplications (tag 600) MUST be absent from both the softwareEnforced (position 6)
23
23
  // and teeEnforced (position 7) AuthorizationLists (the key MUST be scoped to the RP).
24
24
  // - origin (tag 702) MUST equal KM_ORIGIN_GENERATED (0): at least one list declares it,
25
25
  // and every list that declares one says GENERATED (a mixed IMPORTED/GENERATED key is
26
26
  // contradictory and rejected).
27
- // - purpose (tag 1) MUST be EXACTLY { KM_PURPOSE_SIGN (2) } over the union of the lists.
27
+ // - purpose (tag 1) MUST be exactly { KM_PURPOSE_SIGN (2) } over the union of the lists.
28
28
 
29
29
  var asn1 = require("./asn1-der");
30
30
 
@@ -53,7 +53,7 @@ function _purposeUnion(a, b, E, code) {
53
53
  return out;
54
54
  }
55
55
 
56
- // androidKeyDescription(cert, clientDataHash, exts, E, code, failCode) -- the complete
56
+ // androidKeyDescription(cert, clientDataHash, exts, E, code, failCode) is the complete
57
57
  // WebAuthn 8.4.1 KeyDescription gate. A verifier MUST route the android-key description
58
58
  // through here, never re-derive a partial subset of the four checks inline.
59
59
  // @enforced-by behavioral -- the challenge / allApplications / origin / purpose checks are