@blamejs/pki 0.5.6 → 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 +419 -378
  2. package/MIGRATING.md +65 -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 +24 -20
  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 +104 -104
  11. package/lib/cmc-verify.js +108 -32
  12. package/lib/cmp-build.js +31 -26
  13. package/lib/cmp-session.js +74 -72
  14. package/lib/cmp-verify.js +72 -58
  15. package/lib/cms-compress.js +8 -9
  16. package/lib/cms-decrypt.js +92 -76
  17. package/lib/cms-encrypt.js +33 -34
  18. package/lib/cms-sign.js +100 -54
  19. package/lib/cms-verify.js +141 -82
  20. package/lib/composite-sig.js +13 -13
  21. package/lib/constants.js +4 -4
  22. package/lib/crl-sign.js +31 -25
  23. package/lib/crl-verify.js +7 -6
  24. package/lib/crmf-sign.js +19 -15
  25. package/lib/csr-sign.js +13 -9
  26. package/lib/ct.js +37 -37
  27. package/lib/edwards-point.js +7 -7
  28. package/lib/est.js +103 -59
  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 +378 -15
  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 +144 -75
  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 +32 -20
  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 +71 -56
  63. package/lib/pki-build.js +23 -22
  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 +169 -36
  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 +19 -16
  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 +22 -18
  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 +21 -20
  99. package/lib/webauthn-mds.js +67 -67
  100. package/lib/webauthn.js +34 -34
  101. package/lib/webcrypto.js +15 -15
  102. package/lib/x509-sign.js +24 -15
  103. package/package.json +3 -2
  104. package/sbom.cdx.json +6 -6
package/lib/schema-cms.js CHANGED
@@ -12,13 +12,13 @@
12
12
  * CMS handling per RFC 5652 (sec. 3 ContentInfo envelope). `parse` turns a DER or PEM
13
13
  * (`CMS`) message into a structured object and is an OID-dispatch envelope --
14
14
  * ContentInfo reads its `contentType` and structurally decodes SignedData (sec. 5),
15
- * EnvelopedData (sec. 6, with all five RecipientInfo kinds -- key-transport,
15
+ * EnvelopedData (sec. 6, with all five RecipientInfo kinds: key-transport,
16
16
  * key-agreement per RFC 5753, KEK, password, and other, including the RFC 9629
17
17
  * KEMRecipientInfo carried under `id-ori-kem` with ML-KEM per RFC 9936),
18
18
  * EncryptedData (sec. 8), AuthenticatedData (sec. 9), and AuthEnvelopedData (RFC 5083,
19
19
  * with RFC 5084 AES-GCM/CCM parameter validation); the remaining PKCS#7 content
20
20
  * types are recognized and rejected with a precise `cms/unsupported-content-type`
21
- * rather than a generic unknown-format error. A SignedData surfaces its version,
21
+ * in place of a generic unknown-format error. A SignedData surfaces its version,
22
22
  * digest algorithms, encapsulated content, certificate / CRL sets, and signer
23
23
  * infos; an EnvelopedData its recipient infos and encrypted content info; an
24
24
  * EncryptedData its encrypted content info; an AuthenticatedData its MAC
@@ -27,11 +27,11 @@
27
27
  * content, validated AEAD parameters, and raw `mac`.
28
28
  *
29
29
  * CMS is a signed container: the bytes an external verifier must hash are
30
- * surfaced RAW and never re-serialized. `encapContentInfo.eContent` is the raw
30
+ * surfaced raw and never re-serialized. `encapContentInfo.eContent` is the raw
31
31
  * content (or `null` for a detached signature); each SignerInfo's `signature` is
32
32
  * raw, and `signedAttrsBytes` is the on-wire `[0]` SignedAttributes TLV so a
33
33
  * verifier can re-tag it to the universal SET the signature is computed over
34
- * (sec. 5.4) -- `authAttrsBytes` plays the same role for the sec. 9.2 MAC input and the
34
+ * (sec. 5.4); `authAttrsBytes` plays the same role for the sec. 9.2 MAC input and the
35
35
  * RFC 5083 sec. 2.2 AAD. Embedded certificates and CRLs are surfaced as raw DER +
36
36
  * their outer tag, validated against the closed CertificateChoices /
37
37
  * RevocationInfoChoice tag sets, so an obsolete alternative never fails the
@@ -63,7 +63,7 @@ var ALGORITHM_IDENTIFIER = pkix.algorithmIdentifier(NS);
63
63
  var ATTRIBUTE = pkix.attribute(NS);
64
64
  var NAME = pkix.name(NS);
65
65
 
66
- // CMSVersion ::= INTEGER -- the SignedData version is {1,3,4,5} and the SignerInfo
66
+ // CMSVersion ::= INTEGER, where the SignedData version is {1,3,4,5} and the SignerInfo
67
67
  // version is {1,3} (RFC 5652 sec. 5.1, sec. 5.3). Wider accept maps than any other format.
68
68
  var SIGNED_DATA_VERSION = pkix.versionReader(NS, { "1": 1, "3": 3, "4": 4, "5": 5 });
69
69
  var SIGNER_VERSION = pkix.versionReader(NS, { "1": 1, "3": 3 });
@@ -188,7 +188,7 @@ function _checkContentBindingAttrs(attrs, mode) {
188
188
  // content-type attribute (what it signs has no content type).
189
189
  if (mode === "countersig") throw NS.E("cms/misplaced-attr", "a countersignature's signedAttrs must not carry a content-type attribute (RFC 5652 sec. 11.4)");
190
190
  if (a.values.length !== 1) throw NS.E("cms/bad-content-type-attr", "the content-type attribute must be single-valued");
191
- // ContentType ::= OBJECT IDENTIFIER (RFC 5652 sec. 11.1) -- validate the value's
191
+ // ContentType ::= OBJECT IDENTIFIER (RFC 5652 sec. 11.1): validate the value's
192
192
  // full syntax (tag AND minimal base-128 OID content), not just the tag, so a
193
193
  // truncated / non-minimal subidentifier is rejected here, not at verify time.
194
194
  try { asn1.read.oid(asn1.decode(a.values[0])); }
@@ -196,7 +196,7 @@ function _checkContentBindingAttrs(attrs, mode) {
196
196
  } else if (a.type === OID_MESSAGE_DIGEST) {
197
197
  md += 1;
198
198
  if (a.values.length !== 1) throw NS.E("cms/bad-message-digest-attr", "the message-digest attribute must be single-valued");
199
- // MessageDigest ::= OCTET STRING (RFC 5652 sec. 11.2) -- validate the full syntax.
199
+ // MessageDigest ::= OCTET STRING (RFC 5652 sec. 11.2): validate the full syntax.
200
200
  try { asn1.read.octetString(asn1.decode(a.values[0])); }
201
201
  catch (e) { throw NS.E("cms/bad-message-digest-attr", "the message-digest attribute value must be an OCTET STRING", e); }
202
202
  } else if (a.type === OID_SIGNING_TIME) {
@@ -212,17 +212,149 @@ function _checkContentBindingAttrs(attrs, mode) {
212
212
  if (md === 0) throw NS.E("cms/missing-message-digest", "the attribute set must contain a message-digest attribute (RFC 5652 sec. 11.2)");
213
213
  }
214
214
 
215
- // RFC 5652 sec. 5.3 / sec. 9.3 -- when a content-type attribute is present, it MUST
215
+ // looksLikeSignedAttributes(bytes) -> boolean.
216
+ //
217
+ // Does `bytes` parse as a DER SignedAttributes block, a SET OF Attribute carrying both the
218
+ // content-type and message-digest attributes RFC 5652 sec. 5.3 makes mandatory whenever signed
219
+ // attributes are present?
220
+ //
221
+ // This is the detector for the signed-attribute stripping forgery
222
+ // (draft-vangeest-lamps-cms-euf-cma-signeddata, Attack Type 1). A CMS signature does not commit to
223
+ // WHETHER signed attributes were present, so a signature made over a SignedAttributes block can be
224
+ // re-presented as one made over content: drop the signedAttrs field and set the encapsulated
225
+ // content to the DER of those same attributes. Sec. 5.4 then says the signature is over the content
226
+ // itself, which is exactly what it covers, and with no attributes there is no message-digest or
227
+ // content-type attribute left to disagree. The proposed standards fixes are protocol changes (a
228
+ // context string naming which mode was signed) that no verifier can apply on its own.
229
+ //
230
+ // What a verifier CAN do is refuse the shape. Every message produced by the attack has, as its
231
+ // content, the encoded SignedAttributes of a real message, and sec. 5.3 requires those to carry
232
+ // both attributes named above. That makes their presence a NECESSARY condition of the attack rather
233
+ // than a guess, and the shape is one ordinary content does not have: a certificate, a JSON payload,
234
+ // arbitrary bytes, and even a SET OF other attributes all fail it. The cost is a message whose
235
+ // legitimate content really is an encoded SignedAttributes block signed without attributes, which
236
+ // is refused as genuinely ambiguous -- sign it with attributes and it is unambiguous again.
237
+ //
238
+ // One mandatory attribute value, held to all three conditions a real SignedAttributes meets:
239
+ // exactly one value (RFC 5652 sec. 11.1 / sec. 11.2 make both single-valued), the right tag, and a
240
+ // body that actually reads as that type. All three together, for each attribute; checking the
241
+ // cardinality and the tag while letting an undecodable body through would refuse content the real
242
+ // SignedAttributes parser could never have produced, which is the false positive this whole
243
+ // detector is shaped to avoid.
244
+ function _readsAs(vals, tagNumber, reader) {
245
+ if (vals.length !== 1 || !schema.isUniversal(vals[0], tagNumber)) return false;
246
+ var reads = true;
247
+ try { reader(vals[0]); }
248
+ catch (_e) {
249
+ reads = false; // right tag, unreadable body -- not a preimage anything signed
250
+ }
251
+ return reads;
252
+ }
253
+ // SigningTime ::= Time is a CHOICE of UTCTime and GeneralizedTime, so the tag is one of two and the
254
+ // reader settles which -- _readsAs pins a single tag and cannot express it.
255
+ function _readsAsTime(vals) {
256
+ if (vals.length !== 1) return false;
257
+ var reads = true;
258
+ try { asn1.read.time(vals[0]); }
259
+ catch (_e) {
260
+ reads = false;
261
+ }
262
+ return reads;
263
+ }
264
+
265
+ // Deliberately a total function: it answers about arbitrary attacker bytes and must never throw.
266
+ // The X.690 sec. 11.6 SET OF ordering rule, as the schema engine applies it: each component's
267
+ // encoding is greater than or equal to the one before it.
268
+ function _ascendingDer(nodes) {
269
+ for (var i = 1; i < nodes.length; i++) {
270
+ if (Buffer.compare(nodes[i - 1].bytes, nodes[i].bytes) > 0) return false;
271
+ }
272
+ return true;
273
+ }
274
+
275
+ function looksLikeSignedAttributes(bytes) {
276
+ if (!bytes || !bytes.length) return false;
277
+ var node;
278
+ try { node = asn1.decode(bytes); }
279
+ catch (_e) { return false; } // not DER at all -- not this shape
280
+ if (!schema.isUniversal(node, asn1.TAGS.SET) || !node.constructed) return false;
281
+ var kids = node.children || [];
282
+ if (!kids.length) return false;
283
+ // Every rule _checkContentBindingAttrs applies to a real SignedAttributes is applied here, and
284
+ // for one reason: the detector must match only what a conforming block can be. A set the real
285
+ // parser would have rejected cannot be the preimage of any signature, so matching it would refuse
286
+ // content no attack could have produced. Enumerated against that function, never discovered
287
+ // one rule at a time -- no duplicate attribute types (sec. 5.3), content-type single-valued and a
288
+ // readable OID (sec. 11.1), message-digest single-valued and a readable OCTET STRING (sec. 11.2),
289
+ // signing-time when present single-valued and a readable Time (sec. 11.3).
290
+ // X.690 sec. 11.6: the components of a SET OF appear in ascending DER order, and the schema
291
+ // engine enforces exactly that on the real SignedAttributes. An out-of-order set is one the
292
+ // walker refuses, so it is not the preimage of any signature either.
293
+ if (!_ascendingDer(kids)) return false;
294
+ var sawContentType = false, sawMessageDigest = false, seenTypes = Object.create(null);
295
+ for (var i = 0; i < kids.length; i++) {
296
+ var a = kids[i];
297
+ // Attribute ::= SEQUENCE { attrType OBJECT IDENTIFIER, attrValues SET OF ANY }
298
+ if (!schema.isUniversal(a, asn1.TAGS.SEQUENCE)) return false;
299
+ if (!a.children || a.children.length !== 2) return false;
300
+ var t = a.children[0], vs = a.children[1];
301
+ if (!schema.isUniversal(t, asn1.TAGS.OBJECT_IDENTIFIER)) return false;
302
+ if (!schema.isUniversal(vs, asn1.TAGS.SET)) return false;
303
+ var attrOid;
304
+ try { attrOid = asn1.read.oid(t); }
305
+ catch (_e2) { return false; }
306
+ if (seenTypes[attrOid]) return false; // a repeated attribute type -- sec. 5.3 forbids it
307
+ seenTypes[attrOid] = true;
308
+ // The sec. 11 PLACEMENT rows, from the same table the real parser reads. An attribute the
309
+ // parser refuses to see in signedAttrs -- id-countersignature is the one sec. 11.4 names --
310
+ // cannot appear in a conforming SignedAttributes, so a set containing it is not a preimage any
311
+ // signature covers. Missing this row was the difference between a necessary condition and a
312
+ // guess: it would have refused ordinary content that merely carried that attribute encoding.
313
+ var placement = ATTR_FORBIDDEN_IN[attrOid];
314
+ if (placement && placement.signed) return false;
315
+ // RFC 5652 gives every AttributeValue set SIZE (1..MAX), so an attribute with an EMPTY value
316
+ // set is one no conforming signer produced and no signature covers. The upper bound is
317
+ // deliberately not applied: this decoder caps values per attribute as a resource limit of its
318
+ // own, and a limit this implementation chose is not a fact about what a signature can cover.
319
+ // An external signer may sign a conforming set larger than that cap, and the stripped message
320
+ // presents those bytes as opaque content where the cap never applies, so refusing to recognize
321
+ // it because of a local limit would miss exactly the preimage the attack reuses.
322
+ var n = (vs.children || []).length;
323
+ if (n < 1) return false;
324
+ if (!_ascendingDer(vs.children || [])) return false; // the inner SET OF is ordered too
325
+ // The two mandatory attributes are checked down to their VALUES, not just their type OIDs.
326
+ // RFC 5652 sec. 11.1 makes content-type a single OBJECT IDENTIFIER and sec. 11.2 makes
327
+ // message-digest a single OCTET STRING, so a set carrying those OIDs over an empty or
328
+ // wrongly-typed value cannot be the preimage of a real signature, and refusing it would be a
329
+ // false positive on content that merely resembles the shape. The detector has to stay a
330
+ // necessary condition of the attack; anything broader costs a legitimate caller.
331
+ var vals = vs.children || [];
332
+ if (attrOid === OID_CONTENT_TYPE) {
333
+ if (!_readsAs(vals, asn1.TAGS.OBJECT_IDENTIFIER, asn1.read.oid)) return false;
334
+ sawContentType = true;
335
+ } else if (attrOid === OID_MESSAGE_DIGEST) {
336
+ if (!_readsAs(vals, asn1.TAGS.OCTET_STRING, asn1.read.octetString)) return false;
337
+ sawMessageDigest = true;
338
+ } else if (attrOid === OID_SIGNING_TIME) {
339
+ // Not mandatory, but when present sec. 11.3 constrains it the same way, so a set carrying an
340
+ // unreadable signing-time is one the real parser would have refused.
341
+ if (!_readsAsTime(vals)) return false;
342
+ }
343
+ }
344
+ return sawContentType && sawMessageDigest;
345
+ }
346
+
347
+ // RFC 5652 sec. 5.3 / sec. 9.3: when a content-type attribute is present, it MUST
216
348
  // be single-valued (sec. 11.1) and its value MUST equal the eContentType (a
217
349
  // cross-field consistency both parsed here). Shared by SignedData signedAttrs,
218
- // AuthenticatedData authAttrs, and AuthEnvelopedData authAttrs -- the single-value
350
+ // AuthenticatedData authAttrs, and AuthEnvelopedData authAttrs, where the single-value
219
351
  // rule holds even where content-type is not REQUIRED (RFC 5083), so an
220
352
  // expected-first-value-plus-extra set can never surface as ambiguous.
221
353
  function _assertContentTypeMatchesAttrs(attrs, eContentType) {
222
354
  for (var i = 0; i < attrs.length; i++) {
223
355
  if (attrs[i].type !== OID_CONTENT_TYPE) continue;
224
356
  if (attrs[i].values.length !== 1) throw NS.E("cms/bad-content-type-attr", "the content-type attribute must be single-valued (RFC 5652 sec. 11.1)");
225
- // ContentType ::= OBJECT IDENTIFIER -- validate the value's full syntax (tag AND
357
+ // ContentType ::= OBJECT IDENTIFIER: validate the value's full syntax (tag and
226
358
  // minimal base-128 OID content) with the CMS typed verdict, so a malformed value
227
359
  // on a path that does not run _checkContentBindingAttrs (AuthEnvelopedData,
228
360
  // RFC 5083 sec. 2.1) surfaces cms/bad-content-type-attr, not the raw asn1/* error.
@@ -234,8 +366,8 @@ function _assertContentTypeMatchesAttrs(attrs, eContentType) {
234
366
  }
235
367
 
236
368
  // RFC 5652 sec. 5.3 -- an attribute set MUST NOT include multiple instances of the
237
- // same attribute type. AuthEnvelopedData authAttrs (RFC 5083 sec. 2.1) gets ONLY this
238
- // duplicate check -- NOT the content-type/message-digest presence rules (RFC 5652
369
+ // same attribute type. AuthEnvelopedData authAttrs (RFC 5083 sec. 2.1) gets only this
370
+ // duplicate check, and not the content-type/message-digest presence rules (RFC 5652
239
371
  // sec. 11.1/sec. 11.2 bind only signed-data and authenticated-data), so it must not reuse
240
372
  // _checkContentBindingAttrs, which would over-enforce.
241
373
  function _checkNoDuplicateAttrs(attrs) {
@@ -286,7 +418,7 @@ function _validateAeadParams(alg, macLen) {
286
418
  }
287
419
  var legal = kind === "gcm" ? AEAD_GCM_ICVLENS : AEAD_CCM_ICVLENS;
288
420
  if (!legal.has(icvLen)) throw NS.E("cms/bad-aead-params", "the AES-" + K + " aes-ICVlen " + icvLen + " is not an allowed value (RFC 5084)");
289
- // X.690 sec. 11.5: a DEFAULT value MUST be omitted in DER -- an encoded ICVlen equal
421
+ // X.690 sec. 11.5: a DEFAULT value MUST be omitted in DER, so an encoded ICVlen equal
290
422
  // to the default 12 is non-canonical.
291
423
  if (icvEncoded && icvLen === 12) throw NS.E("cms/non-canonical-default", "the AEAD aes-ICVlen equal to the DEFAULT 12 MUST be omitted (X.690 sec. 11.5)");
292
424
  // RFC 5084 sec. 3.1/sec. 3.2: aes-ICVlen MUST match the AuthEnvelopedData mac length.
@@ -294,7 +426,7 @@ function _validateAeadParams(alg, macLen) {
294
426
  return { kind: kind, nonce: nonce, icvLen: icvLen };
295
427
  }
296
428
 
297
- // RFC 5652 sec. 5.3 / sec. 9.1 + RFC 5083 sec. 2.1 -- a signed / authenticated attribute SET
429
+ // RFC 5652 sec. 5.3 / sec. 9.1 + RFC 5083 sec. 2.1: a signed / authenticated attribute SET
298
430
  // MUST be DER encoded even when the enclosing structure was decoded as BER (its
299
431
  // bytes feed the sec. 5.4 / sec. 9.2 re-tagged hash / MAC input, so a non-DER TLV makes
300
432
  // the surfaced raw bytes unusable for verification). Strictly re-decoding the
@@ -305,8 +437,8 @@ function _assertDerEncodedAttrs(node, code) {
305
437
  catch (e) { throw NS.E(code, "the attribute set must be DER encoded even inside a BER envelope (RFC 5652 sec. 5.3)", e); }
306
438
  }
307
439
 
308
- // A CertificateChoices / RevocationInfoChoice element, surfaced RAW (its DER +
309
- // outer tag) rather than recursively parsed -- the obsolete CHOICE alternatives
440
+ // A CertificateChoices / RevocationInfoChoice element, surfaced raw (its DER +
441
+ // outer tag) and never recursively parsed: the obsolete CHOICE alternatives
310
442
  // (extendedCertificate, attribute certs, otherRevocationInfo) never fail the
311
443
  // parse, and a caller re-parses a `certificate`/`CertificateList` element itself.
312
444
  function rawElement(item) {
@@ -383,7 +515,7 @@ var ISSUER_AND_SERIAL = schema.seq([
383
515
  });
384
516
 
385
517
  // SignerIdentifier ::= CHOICE { issuerAndSerialNumber IssuerAndSerialNumber,
386
- // subjectKeyIdentifier [0] IMPLICIT OCTET STRING } (RFC 5652 sec. 5.3) -- the arm is
518
+ // subjectKeyIdentifier [0] IMPLICIT OCTET STRING } (RFC 5652 sec. 5.3), where the arm is
387
519
  // disambiguated by tag (universal SEQUENCE vs context [0]).
388
520
  var SIGNER_IDENTIFIER = schema.choice([
389
521
  { when: { tagClass: "universal", tagNumber: asn1.TAGS.SEQUENCE }, schema: ISSUER_AND_SERIAL },
@@ -394,10 +526,10 @@ var SIGNER_IDENTIFIER = schema.choice([
394
526
  // signedAttrs [0] IMPLICIT OPTIONAL, signatureAlgorithm, signature OCTET STRING,
395
527
  // unsignedAttrs [1] IMPLICIT OPTIONAL } (RFC 5652 sec. 5.3). signedAttrs/unsignedAttrs
396
528
  // are positional optionals (a required signatureAlgorithm sits between them, so
397
- // they cannot be a trailing block). ONE definition drives both consumers -- the
529
+ // they cannot be a trailing block). One definition drives both consumers: the
398
530
  // signerInfos of a SignedData (mode "content") and a countersignature attribute
399
531
  // value (mode "countersig", RFC 5652 sec. 11.4: syntactically a SignerInfo whose
400
- // signedAttrs MUST carry message-digest and MUST NOT carry content-type) -- so
532
+ // signedAttrs MUST carry message-digest and MUST NOT carry content-type), so
401
533
  // the two can never diverge structurally.
402
534
  function makeSignerInfo(mode) {
403
535
  return schema.seq([
@@ -442,7 +574,7 @@ function makeSignerInfo(mode) {
442
574
  // sec. 11.4 -- every countersignature value IS a SignerInfo (validated by
443
575
  // content, never accepted on the attribute type alone). Multiple
444
576
  // countersignature instances are explicitly permitted here, and a
445
- // countersignature's own unsignedAttrs may nest further ones -- the
577
+ // countersignature's own unsignedAttrs may nest further ones, so the
446
578
  // recursion is bounded by the decoder's depth cap.
447
579
  for (var u = 0; u < unsignedAttrs.length; u++) {
448
580
  if (unsignedAttrs[u].type !== OID_COUNTERSIGNATURE) continue;
@@ -473,7 +605,7 @@ var COUNTERSIGNATURE_SIGNER_INFO = makeSignerInfo("countersig");
473
605
  // SignedData ::= SEQUENCE { version CMSVersion, digestAlgorithms SET OF,
474
606
  // encapContentInfo, certificates [0] IMPLICIT OPTIONAL, crls [1] IMPLICIT
475
607
  // OPTIONAL, signerInfos SET OF } (RFC 5652 sec. 5.1). digestAlgorithms and
476
- // signerInfos are min:0 -- a degenerate certs-only SignedData carries neither.
608
+ // signerInfos are min:0, since a degenerate certs-only SignedData carries neither.
477
609
  var SIGNED_DATA = schema.seq([
478
610
  schema.field("version", SIGNED_DATA_VERSION),
479
611
  schema.field("digestAlgorithms", schema.setOf(ALGORITHM_IDENTIFIER, { min: 0, code: "cms/bad-digest-algorithms", what: "digestAlgorithms" })),
@@ -526,10 +658,10 @@ var T = asn1.TAGS;
526
658
 
527
659
  // EncryptedContentInfo ::= SEQUENCE { contentType OID, contentEncryptionAlgorithm
528
660
  // AlgorithmIdentifier, encryptedContent [0] IMPLICIT OCTET STRING OPTIONAL } (RFC
529
- // 5652 sec. 6.1). encryptedContent is [0] IMPLICIT (context PRIMITIVE) -- its content
661
+ // 5652 sec. 6.1). encryptedContent is [0] IMPLICIT (context primitive), so its content
530
662
  // octets ARE the ciphertext directly, so it reads through implicitOctetString(0),
531
- // NOT the [0] EXPLICIT shape ENCAP_CONTENT_INFO uses (which would double-strip a
532
- // length header). The ciphertext + algorithm parameters are surfaced RAW.
663
+ // and not the [0] EXPLICIT shape ENCAP_CONTENT_INFO uses (which would double-strip a
664
+ // length header). The ciphertext + algorithm parameters are surfaced raw.
533
665
  var ENCRYPTED_CONTENT_INFO = schema.seq([
534
666
  schema.field("contentType", schema.oidLeaf()),
535
667
  schema.field("contentEncryptionAlgorithm", ALGORITHM_IDENTIFIER),
@@ -546,7 +678,7 @@ var ENCRYPTED_CONTENT_INFO = schema.seq([
546
678
  });
547
679
 
548
680
  // RecipientIdentifier ::= CHOICE { issuerAndSerialNumber, subjectKeyIdentifier [0]
549
- // IMPLICIT OCTET STRING } (RFC 5652 sec. 6.2.1) -- structurally identical to
681
+ // IMPLICIT OCTET STRING } (RFC 5652 sec. 6.2.1), structurally identical to
550
682
  // SignerIdentifier; reuse ISSUER_AND_SERIAL + the implicitOctetString(0) leaf.
551
683
  var RECIPIENT_IDENTIFIER = schema.choice([
552
684
  { when: { tagClass: "universal", tagNumber: T.SEQUENCE }, schema: ISSUER_AND_SERIAL },
@@ -580,7 +712,7 @@ var KEY_TRANS_RECIPIENT_INFO = schema.seq([
580
712
  });
581
713
 
582
714
  // OriginatorPublicKey ::= SEQUENCE { algorithm, publicKey BIT STRING } (RFC 5753
583
- // sec. 3.1.1), reached as originatorKey [1] IMPLICIT -- SPKI-shaped but cannot reuse
715
+ // sec. 3.1.1), reached as originatorKey [1] IMPLICIT: SPKI-shaped but cannot reuse
584
716
  // pkix.spki (which asserts a universal SEQUENCE), so assert:"constructed".
585
717
  var ORIGINATOR_PUBLIC_KEY = schema.seq([
586
718
  schema.field("algorithm", ALGORITHM_IDENTIFIER),
@@ -600,7 +732,7 @@ var ORIGINATOR_IDENTIFIER_OR_KEY = schema.choice([
600
732
 
601
733
  // RecipientKeyIdentifier (RFC 5652 sec. 6.2.2) and KEKIdentifier (sec. 6.2.3) are one
602
734
  // shape -- { <keyId> OCTET STRING, date GeneralizedTime OPTIONAL, other
603
- // OtherKeyAttribute OPTIONAL } -- differing only in the key-id field's name and
735
+ // OtherKeyAttribute OPTIONAL }, differing only in the key-id field's name and
604
736
  // the enclosing tag form. One factory defines both so the OPTIONAL handling
605
737
  // (date and the raw-surfaced OtherKeyAttribute) cannot diverge between them.
606
738
  function keyIdentifierSchema(keyIdName, assert, code, what) {
@@ -620,7 +752,7 @@ function keyIdentifierSchema(keyIdName, assert, code, what) {
620
752
  });
621
753
  }
622
754
 
623
- // Reached as rKeyId [0] IMPLICIT (a SEQUENCE -- constructed, unlike ktri's [0] leaf).
755
+ // Reached as rKeyId [0] IMPLICIT (a SEQUENCE, constructed, unlike ktri's [0] leaf).
624
756
  var RECIPIENT_KEY_IDENTIFIER = keyIdentifierSchema("subjectKeyIdentifier",
625
757
  "constructed", "cms/bad-recipient-key-identifier", "RecipientKeyIdentifier");
626
758
 
@@ -813,7 +945,7 @@ var RECIPIENT_INFO = schema.choice([
813
945
  ], { code: "cms/bad-recipient-info", what: "RecipientInfo" });
814
946
 
815
947
  // OriginatorInfo ::= [0] IMPLICIT SEQUENCE { certs [0] IMPLICIT OPTIONAL, crls [1]
816
- // IMPLICIT OPTIONAL } (RFC 5652 sec. 6.1). Members surfaced RAW (their outer tag feeds
948
+ // IMPLICIT OPTIONAL } (RFC 5652 sec. 6.1). Members surfaced raw (their outer tag feeds
817
949
  // the version rule).
818
950
  var ORIGINATOR_INFO = schema.seq([
819
951
  schema.optional("certs", schema.implicitSetOf(0, schema.any(), { min: 1, code: "cms/bad-originator-certs", what: "certs" }), { tag: 0 }),
@@ -845,8 +977,8 @@ function _expectedEnvelopedDataVersion(originatorInfo, recipientInfos, hasUnprot
845
977
  }
846
978
 
847
979
  // RFC 5652 sec. 9.1 -- the exact AuthenticatedData CMSVersion, from originatorInfo's raw
848
- // cert/crl outer tags ONLY. Unlike EnvelopedData sec. 6.1, the recipient-info kinds do
849
- // NOT influence the version. IF originatorInfo present AND (other-cert [3] OR
980
+ // cert/crl outer tags only. Unlike EnvelopedData sec. 6.1, the recipient-info kinds do
981
+ // not influence the version. If originatorInfo is present and (other-cert [3] or
850
982
  // other-crl [1]) -> 3; ELSE IF originatorInfo present AND v2AttrCert [2] -> 1; ELSE 0.
851
983
  function _expectedAuthDataVersion(originatorInfo) {
852
984
  if (!originatorInfo) return 0;
@@ -859,7 +991,7 @@ function _expectedAuthDataVersion(originatorInfo) {
859
991
  // EnvelopedData ::= SEQUENCE { version, originatorInfo [0] IMPLICIT OPTIONAL,
860
992
  // recipientInfos RecipientInfos (SET SIZE 1..MAX), encryptedContentInfo,
861
993
  // unprotectedAttrs [1] IMPLICIT OPTIONAL } (RFC 5652 sec. 6.1). recipientInfos is
862
- // min:1 (an empty SET is non-conformant -- the INVERSE of SignedData's degenerate
994
+ // min:1 (an empty SET is non-conformant, the inverse of SignedData's degenerate
863
995
  // signerInfos).
864
996
  var ENVELOPED_DATA = schema.seq([
865
997
  schema.field("version", ENVELOPED_DATA_VERSION),
@@ -889,7 +1021,7 @@ var ENVELOPED_DATA = schema.seq([
889
1021
  });
890
1022
 
891
1023
  // EncryptedData ::= SEQUENCE { version, encryptedContentInfo, unprotectedAttrs [1]
892
- // IMPLICIT OPTIONAL } (RFC 5652 sec. 8) -- no recipients, no originatorInfo; the CEK is
1024
+ // IMPLICIT OPTIONAL } (RFC 5652 sec. 8): no recipients, no originatorInfo; the CEK is
893
1025
  // distributed out of band. version is 0, or 2 iff unprotectedAttrs are present.
894
1026
  var ENCRYPTED_DATA = schema.seq([
895
1027
  schema.field("version", ENCRYPTED_DATA_VERSION),
@@ -1018,7 +1150,7 @@ var AUTH_ENVELOPED_DATA = schema.seq([
1018
1150
  // sec. 2.2 AAD is the re-tagged DER SET). No content-type/message-digest
1019
1151
  // presence rules bind here (sec. 11.1/sec. 11.2 name signed-data and
1020
1152
  // authenticated-data; sec. 2.1 says message-digest SHOULD NOT appear), so only
1021
- // the duplicate rule, the sec. 11 placement rows, and -- when a content-type
1153
+ // the duplicate rule, the sec. 11 placement rows, and, when a content-type
1022
1154
  // attribute IS present -- the value == contentType coherence apply.
1023
1155
  _assertDerEncodedAttrs(m.fields.authAttrs.node, "cms/bad-auth-attrs");
1024
1156
  authAttrs = m.fields.authAttrs.value.items.map(function (it) { return it.value.result; });
@@ -1133,8 +1265,8 @@ var CONTENT_INFO = schema.seq([
1133
1265
  * `{ type: "ori", oriType, oriValue, kemri }` with the parsed KEMRecipientInfo in
1134
1266
  * `kemri`. Every result additionally carries `contentType` (the dotted OID) and
1135
1267
  * `contentTypeName` (its registry name) naming which of the five shapes was
1136
- * dispatched. Raw byte ranges an external verifier hashes -- `eContent`,
1137
- * `signature`, `signedAttrsBytes`, `authAttrsBytes`, `mac` -- are surfaced
1268
+ * dispatched. The raw byte ranges an external verifier hashes (`eContent`,
1269
+ * `signature`, `signedAttrsBytes`, `authAttrsBytes`, `mac`) are surfaced
1138
1270
  * exactly as on the wire. The remaining PKCS#7 types throw `cms/unsupported-content-type`; an
1139
1271
  * unrecognized OID throws `cms/unknown-content-type`; a malformed structure
1140
1272
  * throws a typed `CmsError` (`cms/*`) and a leaf-level codec fault surfaces as
@@ -1310,6 +1442,7 @@ module.exports = {
1310
1442
  walkSignedData: walkSignedData,
1311
1443
  walkEncryptedData: walkEncryptedData,
1312
1444
  walkCountersignature: walkCountersignature,
1445
+ looksLikeSignedAttributes: looksLikeSignedAttributes,
1313
1446
  assertAttachedCiphertext: assertAttachedCiphertext,
1314
1447
  // The structure's own algorithm tables, exported (like the walk* helpers) as the single source
1315
1448
  // of truth the crypto layer (cms-encrypt / cms-decrypt) shares -- so the wrap<->KEK-length, the
package/lib/schema-crl.js CHANGED
@@ -19,7 +19,7 @@
19
19
  * rules, and the raw `tbsCertList` bytes are returned for signature checking.
20
20
  *
21
21
  * @card
22
- * Parse DER / PEM X.509 CRLs into structured, validated fields -- revoked
22
+ * Parse DER / PEM X.509 CRLs into structured, validated fields: revoked
23
23
  * serials with real-`Date` revocation times, named extensions, fail-closed.
24
24
  */
25
25
 
@@ -34,14 +34,14 @@ var PemError = frameworkError.PemError;
34
34
  var TAGS = asn1.TAGS;
35
35
 
36
36
 
37
- // CRLReason ::= ENUMERATED (RFC 5280 sec. 5.3.1) -- value 7 is unused/reserved.
37
+ // CRLReason ::= ENUMERATED (RFC 5280 sec. 5.3.1). Value 7 is unused/reserved.
38
38
  // The legal set lives once in pkix.CRL_REASON_NAMES (shared with the OCSP
39
39
  // RevokedInfo decoder, which surfaces the name; this decoder the numeric code).
40
40
  var CRL_REASONS = pkix.CRL_REASON_NAMES;
41
41
 
42
- // Extension-value decoding is keyed off the STABLE dotted OID (resolved once at
43
- // load from the canonical name), not the mutable display name -- a caller's
44
- // pki.oid.register() display override must not change parse behaviour.
42
+ // Extension-value decoding is keyed off the stable dotted OID (resolved once at
43
+ // load from the canonical name) and not the mutable display name, so a caller's
44
+ // pki.oid.register() display override cannot change parse behaviour.
45
45
  var OID_CRL_NUMBER = oid.byName("cRLNumber");
46
46
  var OID_REASON_CODE = oid.byName("reasonCode");
47
47
  var OID_INVALIDITY_DATE = oid.byName("invalidityDate");
@@ -58,9 +58,9 @@ var EXTENSIONS = pkix.extensions(NS);
58
58
  // UTCTime). invalidityDate stays GeneralizedTime-only (sec. 5.3.2, decodeExt).
59
59
  var TIME = pkix.time(NS);
60
60
 
61
- // CRL Version ::= INTEGER { v1(0), v2(1) } -- a BARE INTEGER (not [0] EXPLICIT).
61
+ // CRL Version ::= INTEGER { v1(0), v2(1) }, a bare INTEGER (not [0] EXPLICIT).
62
62
  // A CRL is at most v2; reject an explicit v1 (DER forbids the default) and any
63
- // value >= 2. Do NOT reuse the certificate readVersion (it maps 2 -> v3).
63
+ // value >= 2. Do not reuse the certificate readVersion (it maps 2 -> v3).
64
64
  var CRL_VERSION = pkix.versionReader(NS, { "1": 2 });
65
65
 
66
66
  // The three cheap, high-value CRL extension values are decoded from their raw
@@ -24,13 +24,13 @@
24
24
  * when supplied, MUST be 2 (RFC 4211 sec. 5); certReqId is an unbounded signed
25
25
  * INTEGER (the RFC 9483 `-1` sentinel is legal). The `CertRequest` byte range
26
26
  * the proof-of-possession signature covers, and each `poposkInput`, are surfaced
27
- * RAW for a downstream verifier; registration controls / info values and the
28
- * keyEncipherment / keyAgreement POP arms are surfaced RAW rather than recursed.
27
+ * raw for a downstream verifier; registration controls / info values and the
28
+ * keyEncipherment / keyAgreement POP arms are surfaced raw and never recursed.
29
29
  * DER-only, fail-closed.
30
30
  *
31
31
  * @card
32
32
  * Parse DER / PEM RFC 4211 CertReqMessages into requested-certificate templates,
33
- * proof-of-possession, and registration controls -- dual-accepted names, raw
33
+ * proof-of-possession, and registration controls: dual-accepted names, raw
34
34
  * verifier inputs, fail-closed.
35
35
  */
36
36
 
@@ -102,7 +102,7 @@ var OPTIONAL_VALIDITY = schema.seq([
102
102
  });
103
103
 
104
104
  // ---- ProofOfPossession (sec. 4) ------------------------------------------
105
- // raVerified [0] NULL -- the RA has already verified POP out of band.
105
+ // raVerified [0] NULL: the RA has already verified POP out of band.
106
106
  var POPO_RAVERIFIED = schema.decode(function (n, ctx) {
107
107
  try { asn1.read.nullImplicit(n, 0); }
108
108
  catch (e) { throw ctx.E("crmf/bad-popo", "raVerified [0] must be an IMPLICIT NULL", e); }
@@ -110,9 +110,9 @@ var POPO_RAVERIFIED = schema.decode(function (n, ctx) {
110
110
  });
111
111
 
112
112
  // When poposkInput is present the POP signature is over the DER of the
113
- // POPOSigningKeyInput VALUE (RFC 4211 sec. 4.1), which is a SEQUENCE -- but on the wire
113
+ // POPOSigningKeyInput value (RFC 4211 sec. 4.1), which is a SEQUENCE. But on the wire
114
114
  // poposkInput is an IMPLICIT [0] field, so its bytes lead with the context tag
115
- // (0xA0) rather than the SEQUENCE tag a verifier hashes. `asn1.sequenceTlv` recovers
115
+ // (0xA0) and not the SEQUENCE tag a verifier hashes. `asn1.sequenceTlv` recovers
116
116
  // the SEQUENCE-tagged signed region from the field's content; `signedBytes` is that
117
117
  // region and `bytes` keeps the raw wire [0] TLV, so a verifier can use either
118
118
  // encoding its peer produced.
@@ -120,7 +120,7 @@ var POPO_RAVERIFIED = schema.decode(function (n, ctx) {
120
120
  // POPOSigningKeyInput ::= SEQUENCE { authInfo CHOICE { sender [0] GeneralName,
121
121
  // publicKeyMAC PKMACValue }, publicKey SubjectPublicKeyInfo } (sec. 4.1). Structurally
122
122
  // validated (not deferred raw), so an empty or malformed poposkInput fails closed
123
- // rather than surfacing a signed region that was never a well-formed POP input.
123
+ // and never surfaces a signed region that was not a well-formed POP input.
124
124
  // PKMACValue ::= SEQUENCE { algId AlgorithmIdentifier, value BIT STRING }. The
125
125
  // MAC value is octet-string algorithm output handed to an external verifier;
126
126
  // with no in-tree POP verify layer, octet alignment is enforced at parse.
@@ -130,7 +130,7 @@ var PKMAC_VALUE = schema.seq([
130
130
  ], {
131
131
  assert: "sequence", arity: { exact: 2 }, code: "crmf/bad-popo", what: "PKMACValue",
132
132
  build: function (m, ctx) {
133
- // The parsed BIT STRING structure { unusedBits, bytes } -- a structural
133
+ // The parsed BIT STRING structure { unusedBits, bytes }, a structural
134
134
  // octet-alignment check on the unused-bit COUNT, not a comparison of the
135
135
  // MAC secret (there is no in-tree POP verify), so no timing oracle exists.
136
136
  var macBitString = m.fields.value.value;
@@ -142,7 +142,7 @@ var PKMAC_VALUE = schema.seq([
142
142
  });
143
143
 
144
144
  // authInfo CHOICE: sender [0] GeneralName is EXPLICIT (GeneralName is itself a
145
- // CHOICE); publicKeyMAC is a bare PKMACValue SEQUENCE -- disjoint by tag class.
145
+ // CHOICE); publicKeyMAC is a bare PKMACValue SEQUENCE, disjoint by tag class.
146
146
  var POPOSK_AUTH_INFO = schema.choice([
147
147
  { when: { tagClass: "context", tagNumber: 0 }, schema: schema.explicit(0, pkix.generalName(NS, { code: "crmf/bad-popo" }), { code: "crmf/bad-popo" }) },
148
148
  { when: { tagClass: "universal", tagNumber: TAGS.SEQUENCE }, schema: PKMAC_VALUE },
@@ -154,7 +154,7 @@ var POPOSK_INPUT = schema.seq([
154
154
  ], { assert: "implicit", implicitTag: 0, arity: { exact: 2 }, code: "crmf/bad-popo", what: "POPOSigningKeyInput" });
155
155
 
156
156
  // POPOSigningKey ::= SEQUENCE { poposkInput [0] POPOSigningKeyInput OPTIONAL,
157
- // algorithmIdentifier AlgorithmIdentifier, signature BIT STRING } -- IMPLICIT [1].
157
+ // algorithmIdentifier AlgorithmIdentifier, signature BIT STRING }, IMPLICIT [1].
158
158
  var POPO_SIGNING_KEY = schema.seq([
159
159
  schema.optional("poposkInput", schema.decode(function (n, ctx) {
160
160
  // Validate the full POPOSigningKeyInput structure fail-closed, then surface the
@@ -187,9 +187,9 @@ var POPO_SIGNING_KEY = schema.seq([
187
187
  // keyEncipherment [2] / keyAgreement [3] POPOPrivKey (sec. 4). POPOPrivKey is a
188
188
  // 5-arm CHOICE { thisMessage [0], subsequentMessage [1], dhMAC [2], agreeMAC [3],
189
189
  // encryptedKey [4] }, so X.680 sec. 31.2.7 forces the outer [2]/[3] tag to be EXPLICIT
190
- // (a CHOICE has no single tag to replace). Validate that shell -- a constructed
191
- // wrapper around exactly one inner context [0]..[4] alternative -- before surfacing
192
- // the arm RAW, so a primitive / empty / mis-tagged node is rejected rather than
190
+ // (a CHOICE has no single tag to replace). Validate that shell, a constructed
191
+ // wrapper around exactly one inner context [0]..[4] alternative, before surfacing
192
+ // the arm raw, so a primitive, empty or mis-tagged node is rejected and never
193
193
  // reported to a verifier as a well-formed POP. The inner alternative's own value
194
194
  // (incl. the encryptedKey [4] EnvelopedData) stays deferred to the verify layer.
195
195
  // POPOPrivKey ::= CHOICE { thisMessage [0] BIT STRING (deprecated), subsequentMessage
@@ -240,7 +240,7 @@ function popoPrivKey(type) {
240
240
  try { schema.embeddedDer(PKMAC_VALUE, asn1.sequenceTlv(inner), NS, { code: "crmf/bad-popo", what: "agreeMAC [3] PKMACValue" }); }
241
241
  catch (e) { throw ctx.E("crmf/bad-popo", "agreeMAC [3] must be a PKMACValue SEQUENCE { algId, BIT STRING } (RFC 4211 sec. 4.2)", e); }
242
242
  }
243
- // encryptedKey [4] EnvelopedData -- validate the CMS structure fail-closed by
243
+ // encryptedKey [4] EnvelopedData: validate the CMS structure fail-closed by
244
244
  // composing the shared EnvelopedData decoder (retag the IMPLICIT [4] to the
245
245
  // universal SEQUENCE it decodes as). The EncKeyWithID identity check lives inside
246
246
  // the ENCRYPTED content, so it stays a verify-layer concern.
@@ -248,12 +248,12 @@ function popoPrivKey(type) {
248
248
  var env;
249
249
  try { env = cms.walkEnvelopedData(asn1.decode(asn1.sequenceTlv(inner))); }
250
250
  catch (e) { throw ctx.E("crmf/bad-popo", "encryptedKey [4] must be a well-formed EnvelopedData (RFC 4211 sec. 4.2, RFC 5652 sec. 6.1)", e); }
251
- // RFC 4211 sec. 4.2 -- the enveloped content type MUST be id-ct-encKeyWithID (the
251
+ // RFC 4211 sec. 4.2: the enveloped content type MUST be id-ct-encKeyWithID (the
252
252
  // ContentType OID is in the clear even though the content itself is encrypted).
253
253
  if (env.encryptedContentInfo.contentType !== oid.byName("encKeyWithID")) {
254
254
  throw ctx.E("crmf/bad-popo", "encryptedKey [4] EnvelopedData content type MUST be id-ct-encKeyWithID (RFC 4211 sec. 4.2)");
255
255
  }
256
- // The encrypted key material MUST be present -- CMS allows a detached
256
+ // The encrypted key material MUST be present. CMS allows a detached
257
257
  // EnvelopedData (encryptedContent OPTIONAL), but a POP with no key to verify
258
258
  // or archive is meaningless. The shared CMS assert rejects a null OR
259
259
  // zero-length attached ciphertext (the rule the EST / PKCS#12 siblings hold).
@@ -275,7 +275,7 @@ var PROOF_OF_POSSESSION = schema.choice([
275
275
 
276
276
  // ---- Controls / regInfo (sec. 5, sec. 6, sec. 7) ---------------------------------
277
277
  // AttributeTypeAndValue ::= SEQUENCE { type OBJECT IDENTIFIER, value ANY DEFINED BY
278
- // type }. The list is decoded; each value stays RAW (the per-OID value semantics
278
+ // type }. The list is decoded; each value stays raw (the per-OID value semantics
279
279
  // are deferred). Shared shape for both controls and regInfo.
280
280
  var CONTROL = schema.seq([
281
281
  schema.field("type", schema.oidLeaf()),
@@ -293,10 +293,10 @@ var CONTROLS = schema.seqOf(CONTROL, { assert: "sequence", min: 1, code: "crmf/b
293
293
  // regInfo ::= SEQUENCE SIZE(1..MAX) OF AttributeTypeAndValue (sec. 7).
294
294
  var REG_INFO = schema.seqOf(CONTROL, { assert: "sequence", min: 1, code: "crmf/bad-reg-info", what: "regInfo", build: mapControls });
295
295
 
296
- // ---- CertTemplate (sec. 5) -- the IMPLICIT-TAGS core ----------------------
296
+ // ---- CertTemplate (sec. 5), the IMPLICIT-TAGS core ------------------------
297
297
  // CertTemplate ::= SEQUENCE { version [0], serialNumber [1], signingAlg [2],
298
298
  // issuer [3] Name, validity [4] OptionalValidity, subject [5] Name, publicKey [6]
299
- // SubjectPublicKeyInfo, issuerUID [7], subjectUID [8], extensions [9] } -- every
299
+ // SubjectPublicKeyInfo, issuerUID [7], subjectUID [8], extensions [9] }, where every
300
300
  // field IMPLICIT and OPTIONAL, so the whole body is one ascending [0..9] trailing
301
301
  // run. issuer/subject Name and the [4] validity times are the CHOICE exceptions.
302
302
  var CERT_TEMPLATE = schema.seq([
@@ -323,9 +323,9 @@ var CERT_TEMPLATE = schema.seq([
323
323
  // existing certificate -- serialNumber and issuer present -- inside a CMP
324
324
  // RevDetails (RFC 9810 sec. 5.3.9). Every field is surfaced so both the request
325
325
  // omission check and the revocation consumer read them off one result.
326
- // The ONE profile override: RFC 9810 Appendix D.6 profiles the CMP
326
+ // The single profile override: RFC 9810 Appendix D.6 profiles the CMP
327
327
  // cross-certification (ccr) template as "version v1 or v3", so the caller
328
- // opts in to v1 (0) via ctx.allowV1Version -- the same base-section-vs-
328
+ // opts in to v1 (0) via ctx.allowV1Version, the same base-section-vs-
329
329
  // profile-appendix override mechanism as the signingAlg relaxation below.
330
330
  if (f.version.present && f.version.value !== 2n &&
331
331
  !(ctx.allowV1Version && f.version.value === 0n)) {
@@ -365,7 +365,7 @@ var CERT_REQUEST = schema.seq([
365
365
  // hazard), so a request template that sets any of them is rejected
366
366
  // fail-closed. The check lives HERE, not in CERT_TEMPLATE, because the same
367
367
  // structure legitimately carries serialNumber/issuer in a CMP RevDetails.
368
- // A cross-certification request (ccr) is the ONE exception: RFC 9810 Appendix
368
+ // A cross-certification request (ccr) is the one exception: RFC 9810 Appendix
369
369
  // D.6 profiles its template with signingAlg PRESENT (the requesting CA states
370
370
  // the algorithm it wants the cross-certificate signed with), so the caller
371
371
  // relaxes that single field via ctx.allowSigningAlg; serialNumber and the UID
@@ -406,7 +406,7 @@ var CERT_REQ_MSG = schema.seq([
406
406
  // to sign / MAC over, and a verifier would be bound to the wrong identity/key.
407
407
  var complete = certReq.certTemplate.subject !== null && certReq.certTemplate.publicKey !== null;
408
408
  // RFC 4211 sec. 4.1 -- for a signature POP, poposkInput's presence is fixed by the
409
- // CertTemplate: it MUST be omitted when the template carries BOTH subject and
409
+ // CertTemplate: it MUST be omitted when the template carries both subject and
410
410
  // publicKey (the signature is then over the DER of the CertRequest), and MUST be
411
411
  // present otherwise.
412
412
  if (popo && popo.type === "signature") {
@@ -438,7 +438,7 @@ var CERT_REQ_MSG = schema.seq([
438
438
  },
439
439
  });
440
440
 
441
- // CertReqMessages ::= SEQUENCE SIZE(1..MAX) OF CertReqMsg (sec. 3) -- an empty sequence
441
+ // CertReqMessages ::= SEQUENCE SIZE(1..MAX) OF CertReqMsg (sec. 3), where an empty sequence
442
442
  // is malformed. The top-level reject code predates the
443
443
  // `<prefix>/not-a-<structure>` convention and stays frozen on the public
444
444
  // error-code surface.
@@ -461,16 +461,16 @@ var CERT_REQ_MESSAGES = schema.seqOf(CERT_REQ_MSG, {
461
461
  * `{ messages: [ { certReq, popo, regInfo } ] }`. Each `certReq` is
462
462
  * `{ certReqId, certReqIdHex, certTemplate, controls, certReqBytes }`, and
463
463
  * `certTemplate` carries the requestable certificate fields (`version`, `issuer`,
464
- * `validity`, `subject`, `publicKey`, `extensions` -- each `null` when absent). The
464
+ * `validity`, `subject`, `publicKey`, `extensions`, each `null` when absent). The
465
465
  * CA-assigned / deprecated fields RFC 4211 sec. 5 requires a request to omit
466
466
  * (`serialNumber`, `signingAlg`, `issuerUID`, `subjectUID`) are rejected, not
467
467
  * surfaced. `popo` is
468
468
  * `null`, `{ type: "raVerified" }`, `{ type: "signature", poposkInput,
469
469
  * algorithmIdentifier, signature }`, or `{ type: "keyEncipherment" |
470
470
  * "keyAgreement", method, bytes }` (where `method` is the POPOPrivKey alternative --
471
- * `thisMessage` / `subsequentMessage` / `dhMAC` / `agreeMAC` / `encryptedKey` -- each
471
+ * `thisMessage` / `subsequentMessage` / `dhMAC` / `agreeMAC` / `encryptedKey`, each
472
472
  * structurally validated, the `encryptedKey` EnvelopedData included). When present, `poposkInput` is
473
- * `{ bytes, signedBytes, publicKey }` -- `bytes` is the raw wire `[0]` TLV,
473
+ * `{ bytes, signedBytes, publicKey }`, where `bytes` is the raw wire `[0]` TLV,
474
474
  * `signedBytes` is the `POPOSigningKeyInput` re-tagged to the `SEQUENCE` DER the
475
475
  * signature actually covers, and `publicKey` is the canonical
476
476
  * `SubjectPublicKeyInfo` DER the RFC 4211 sec. 4.1 template-match check compares
@@ -503,7 +503,7 @@ var parse = pkix.makeParser({
503
503
  * @related pki.schema.crmf.parse
504
504
  *
505
505
  * Extract the DER bytes from a PEM block (RFC 4211 registers no RFC 7468 label, so
506
- * the first block is taken unless `label` is given -- CRMF rides inside CMP / EST as
506
+ * the first block is taken unless `label` is given; CRMF rides inside CMP / EST as
507
507
  * DER in practice). Throws `PemError` on a missing envelope or a non-base64 body.
508
508
  *
509
509
  * @example