@blamejs/pki 0.6.14 → 0.6.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/CHANGELOG.md +27 -1
  2. package/lib/acme.js +59 -815
  3. package/lib/asn1-der.js +7 -245
  4. package/lib/attrcert-sign.js +12 -260
  5. package/lib/attrcert-verify.js +1 -7
  6. package/lib/byte-reader.js +0 -8
  7. package/lib/byte-writer.js +0 -11
  8. package/lib/cbor-det.js +0 -54
  9. package/lib/cmc-build.js +15 -357
  10. package/lib/cmc-verify.js +6 -239
  11. package/lib/cmp-build.js +41 -317
  12. package/lib/cmp-session.js +73 -760
  13. package/lib/cmp-verify.js +32 -488
  14. package/lib/cms-compress.js +3 -32
  15. package/lib/cms-decrypt.js +29 -294
  16. package/lib/cms-encrypt.js +14 -234
  17. package/lib/cms-sign.js +29 -324
  18. package/lib/cms-verify.js +9 -500
  19. package/lib/composite-kem.js +2 -102
  20. package/lib/composite-sig.js +1 -77
  21. package/lib/constants.js +0 -249
  22. package/lib/crl-sign.js +9 -237
  23. package/lib/crl-verify.js +1 -10
  24. package/lib/crmf-sign.js +19 -198
  25. package/lib/crmf-verify.js +1 -12
  26. package/lib/csr-sign.js +3 -34
  27. package/lib/csr-verify.js +1 -12
  28. package/lib/ct.js +28 -253
  29. package/lib/edwards-point.js +10 -44
  30. package/lib/est.js +28 -764
  31. package/lib/framework-error.js +4 -182
  32. package/lib/guard-all.js +1 -58
  33. package/lib/guard-async.js +1 -36
  34. package/lib/guard-bytes.js +6 -650
  35. package/lib/guard-compress.js +5 -102
  36. package/lib/guard-crypto.js +1 -40
  37. package/lib/guard-der.js +2 -93
  38. package/lib/guard-encoding.js +2 -60
  39. package/lib/guard-header.js +1 -36
  40. package/lib/guard-identifier.js +7 -531
  41. package/lib/guard-intrinsic.js +1 -150
  42. package/lib/guard-json.js +10 -73
  43. package/lib/guard-limits.js +1 -77
  44. package/lib/guard-list.js +1 -49
  45. package/lib/guard-name.js +3 -209
  46. package/lib/guard-parsed.js +7 -335
  47. package/lib/guard-range.js +1 -71
  48. package/lib/guard-secret.js +1 -102
  49. package/lib/guard-text.js +1 -64
  50. package/lib/guard-time.js +1 -97
  51. package/lib/hpke.js +3 -105
  52. package/lib/http-digest.js +14 -142
  53. package/lib/http-retry-after.js +1 -47
  54. package/lib/http-transport.js +21 -131
  55. package/lib/inspect.js +30 -284
  56. package/lib/ip-utils.js +6 -47
  57. package/lib/jose.js +7 -142
  58. package/lib/key.js +2 -67
  59. package/lib/lint.js +9 -194
  60. package/lib/merkle.js +1 -27
  61. package/lib/mime.js +9 -82
  62. package/lib/ocsp-request-verify.js +1 -14
  63. package/lib/ocsp-verify.js +1 -82
  64. package/lib/ocsp.js +11 -169
  65. package/lib/oid.js +5 -337
  66. package/lib/path-validate.js +103 -1492
  67. package/lib/pbes2.js +2 -89
  68. package/lib/pkcs12-build.js +31 -379
  69. package/lib/pki-build.js +64 -290
  70. package/lib/rc2.js +2 -18
  71. package/lib/rfc3339.js +4 -27
  72. package/lib/scep.js +140 -286
  73. package/lib/schema-all.js +0 -122
  74. package/lib/schema-attrcert.js +4 -146
  75. package/lib/schema-c509.js +153 -802
  76. package/lib/schema-cmc.js +5 -220
  77. package/lib/schema-cmp.js +2 -223
  78. package/lib/schema-cms.js +13 -427
  79. package/lib/schema-crl.js +5 -68
  80. package/lib/schema-crmf.js +6 -167
  81. package/lib/schema-csr.js +1 -54
  82. package/lib/schema-csrattrs.js +2 -87
  83. package/lib/schema-engine.js +0 -152
  84. package/lib/schema-ocsp.js +0 -117
  85. package/lib/schema-pkcs12.js +2 -93
  86. package/lib/schema-pkcs8.js +3 -35
  87. package/lib/schema-pkix.js +24 -655
  88. package/lib/schema-smime.js +0 -54
  89. package/lib/schema-tsp.js +3 -99
  90. package/lib/schema-x509.js +4 -63
  91. package/lib/shbs.js +11 -95
  92. package/lib/sign-scheme.js +2 -97
  93. package/lib/sigstore.js +9 -169
  94. package/lib/sleep.js +2 -8
  95. package/lib/smime.js +15 -408
  96. package/lib/tls-cert-compress.js +0 -62
  97. package/lib/trust.js +19 -185
  98. package/lib/tsp-sign.js +16 -270
  99. package/lib/validator-all.js +1 -30
  100. package/lib/validator-attcert.js +2 -46
  101. package/lib/validator-cose.js +2 -153
  102. package/lib/validator-keydesc.js +1 -37
  103. package/lib/validator-sig.js +1 -46
  104. package/lib/validator-tls.js +1 -88
  105. package/lib/validator-tpm.js +19 -122
  106. package/lib/verify-seam.js +1 -24
  107. package/lib/webauthn-mds.js +3 -400
  108. package/lib/webauthn.js +12 -833
  109. package/lib/webcrypto.js +5 -485
  110. package/lib/x509-sign.js +4 -108
  111. package/package.json +1 -1
  112. package/sbom.cdx.json +6 -6
package/lib/schema-cms.js CHANGED
@@ -56,65 +56,42 @@ var schemaCrl = require("./schema-crl");
56
56
  var CmsError = frameworkError.CmsError;
57
57
  var PemError = frameworkError.PemError;
58
58
 
59
- // The cms error namespace the schema engine walks under.
60
59
  var NS = pkix.makeNS("cms", CmsError, oid);
61
60
 
62
61
  var ALGORITHM_IDENTIFIER = pkix.algorithmIdentifier(NS);
63
62
  var ATTRIBUTE = pkix.attribute(NS);
64
63
  var NAME = pkix.name(NS);
65
64
 
66
- // CMSVersion ::= INTEGER, where the SignedData version is {1,3,4,5} and the SignerInfo
67
- // version is {1,3} (RFC 5652 sec. 5.1, sec. 5.3). Wider accept maps than any other format.
68
65
  var SIGNED_DATA_VERSION = pkix.versionReader(NS, { "1": 1, "3": 3, "4": 4, "5": 5 });
69
66
  var SIGNER_VERSION = pkix.versionReader(NS, { "1": 1, "3": 3 });
70
- // EnvelopedData sec. 6.1 (0/2/3/4), EncryptedData sec. 8 (0/2), and the per-RecipientInfo
71
- // versions (RFC 5652 sec. 6.2.1-sec. 6.2.4): ktri {0,2}, kari {3}, kekri {4}, pwri {0}.
72
67
  var ENVELOPED_DATA_VERSION = pkix.versionReader(NS, { "0": 0, "2": 2, "3": 3, "4": 4 });
73
68
  var ENCRYPTED_DATA_VERSION = pkix.versionReader(NS, { "0": 0, "2": 2 });
74
69
  var KTRI_VERSION = pkix.versionReader(NS, { "0": 0, "2": 2 });
75
70
  var KARI_VERSION = pkix.versionReader(NS, { "3": 3 });
76
71
  var KEKRI_VERSION = pkix.versionReader(NS, { "4": 4 });
77
72
  var PWRI_VERSION = pkix.versionReader(NS, { "0": 0 });
78
- // AuthenticatedData sec. 9.1: computed {0,1,3} (see _expectedAuthDataVersion). The
79
- // AuthEnvelopedData version (RFC 5083 sec. 2.1) and the KEMRecipientInfo version
80
- // (RFC 9629 sec. 3) are both a fixed 0.
81
73
  var AUTHDATA_VERSION = pkix.versionReader(NS, { "0": 0, "1": 1, "3": 3 });
82
- var COMPRESSED_DATA_VERSION = pkix.versionReader(NS, { "0": 0 }); // RFC 3274 sec. 1.1 -- version MUST be 0
74
+ var COMPRESSED_DATA_VERSION = pkix.versionReader(NS, { "0": 0 });
83
75
  var AUTHENV_VERSION = pkix.versionReader(NS, { "0": 0 });
84
76
  var KEMRI_VERSION = pkix.versionReader(NS, { "0": 0 });
85
77
 
86
- // id-signedData / id-envelopedData / id-encryptedData are the content types this
87
- // build structurally decodes; the rest are recognized-and-deferred (a precise
88
- // diagnostic, not a silent unknown-format). OIDs resolve from the registry (pkcs7 /
89
- // smimeCt families), never dotted literals.
90
78
  var OID_SIGNED_DATA = oid.byName("signedData");
91
79
  var OID_ENVELOPED_DATA = oid.byName("envelopedData");
92
80
  var OID_ENCRYPTED_DATA = oid.byName("encryptedData");
93
- var OID_AUTH_DATA = oid.byName("authData"); // RFC 5652 sec. 9
94
- var OID_AUTH_ENVELOPED_DATA = oid.byName("authEnvelopedData"); // RFC 5083
95
- var OID_COMPRESSED_DATA = oid.byName("compressedData"); // RFC 3274
81
+ var OID_AUTH_DATA = oid.byName("authData");
82
+ var OID_AUTH_ENVELOPED_DATA = oid.byName("authEnvelopedData");
83
+ var OID_COMPRESSED_DATA = oid.byName("compressedData");
96
84
  var OID_DATA = oid.byName("data");
97
- var OID_ORI_KEM = oid.byName("kem"); // id-ori-kem (RFC 9629)
85
+ var OID_ORI_KEM = oid.byName("kem");
98
86
 
99
- // AES key-wrap OID -> the KEK length in octets it wraps (RFC 3565). RFC 9629 sec. 3:
100
- // a KEMRecipientInfo kekLength MUST be consistent with the wrap algorithm, so a
101
- // recognized wrap pins the exact length; an unrecognized wrap carries no rule.
102
87
  var WRAP_KEK_LENGTHS = {};
103
88
  WRAP_KEK_LENGTHS[oid.byName("aes128-wrap")] = 16;
104
89
  WRAP_KEK_LENGTHS[oid.byName("aes192-wrap")] = 24;
105
90
  WRAP_KEK_LENGTHS[oid.byName("aes256-wrap")] = 32;
106
91
 
107
- // ML-KEM OID -> the exact ciphertext (kemct) length in octets (FIPS 203). A
108
- // recognized ML-KEM kem carries a fixed-size ciphertext; any other length can
109
- // never decapsulate. (The params-absent rule rides the shared oid registry.)
110
- // Ciphertext lengths come from the shared ML-KEM parameter registry (FIPS 203 Table 3) rather
111
- // than a local copy, so this codec and the crypto engine cannot disagree about a parameter set.
112
92
  var KEM_CT_LENGTHS = {};
113
93
  ["id-ml-kem-512", "id-ml-kem-768", "id-ml-kem-1024"].forEach(function (n) { KEM_CT_LENGTHS[oid.byName(n)] = oid.kemParams(n).ct; });
114
94
 
115
- // Recognized AEAD content-encryption OIDs -> the AES-GCM/CCM parameter shape + the
116
- // legal ICVlen set (RFC 5084). An unrecognized content-encryption OID surfaces its
117
- // parameters raw with no AEAD validation (registry, not switch).
118
95
  var AEAD_GCM_ICVLENS = new Set([12, 13, 14, 15, 16]);
119
96
  var AEAD_CCM_ICVLENS = new Set([4, 6, 8, 10, 12, 14, 16]);
120
97
  var AEAD_ALGS = {};
@@ -125,16 +102,11 @@ var DEFERRED = new Set([
125
102
  oid.byName("data"), oid.byName("signedAndEnvelopedData"),
126
103
  oid.byName("digestedData"),
127
104
  ]);
128
- // The RFC 5652 sec. 11 attribute types with per-context placement + value rules.
129
105
  var OID_CONTENT_TYPE = oid.byName("contentType");
130
106
  var OID_MESSAGE_DIGEST = oid.byName("messageDigest");
131
107
  var OID_SIGNING_TIME = oid.byName("signingTime");
132
108
  var OID_COUNTERSIGNATURE = oid.byName("countersignature");
133
109
 
134
- // RFC 5652 sec. 11.1-sec. 11.4 -- where each sec. 11 attribute type may appear. Keyed by
135
- // attribute OID; each row names the attribute-set contexts the type MUST NOT
136
- // appear in: content-type / message-digest / signing-time are signed-or-
137
- // authenticated only; countersignature is unsigned only.
138
110
  var ATTR_FORBIDDEN_IN = {};
139
111
  ATTR_FORBIDDEN_IN[OID_CONTENT_TYPE] = { unsigned: true, unauth: true, unprotected: true };
140
112
  ATTR_FORBIDDEN_IN[OID_MESSAGE_DIGEST] = { unsigned: true, unauth: true, unprotected: true };
@@ -145,37 +117,20 @@ var ATTR_PLACE_LABELS = {
145
117
  unauth: "an unauthenticated attribute", unprotected: "an unprotected attribute",
146
118
  };
147
119
 
148
- // Enforce the sec. 11 placement rows on one parsed attribute set. `place` names the
149
- // context the set occupies (signed / unsigned / auth / unauth / unprotected).
150
120
  function _checkAttrPlacement(attrs, place) {
151
121
  for (var i = 0; i < attrs.length; i++) {
152
122
  var row = ATTR_FORBIDDEN_IN[attrs[i].type];
153
123
  if (row && row[place]) {
154
- // Coverage residual -- reached only when the OID is a registry entry (the guard above
155
- // guarantees it), so oid.name() never returns undefined and this fallback is a
156
- // defensive belt, not selectable through the public parse path.
157
124
  throw NS.E("cms/misplaced-attr", "the " + (oid.name(attrs[i].type) || attrs[i].type) +
158
125
  " attribute must not be " + ATTR_PLACE_LABELS[place] + " (RFC 5652 sec. 11)");
159
126
  }
160
127
  }
161
128
  }
162
129
 
163
- // Enforce the content-binding-attribute value constraints (RFC 5652 sec. 11.1-sec. 11.3)
164
- // shared by SignedData signedAttrs (sec. 5.3), AuthenticatedData authAttrs (sec. 9.1),
165
- // and a countersignature value's signedAttrs (sec. 11.4): the set MUST contain
166
- // exactly one message-digest attribute, each sec. 11 attribute is single-valued with
167
- // valid syntax, and no attribute type repeats. `mode` selects the content-type
168
- // presence rule -- "content" (sec. 5.3/sec. 9.1) REQUIRES one; "countersig" (sec. 11.4)
169
- // FORBIDS it (there is no content type for what a countersignature signs). The
170
- // content-type value == eContentType cross-check is a separate per-build step
171
- // (it needs the eContentType in scope); messageDigest == the actual content
172
- // hash is a sec. 5.6 VERIFICATION concern surfaced, not a structural-parse concern.
173
130
  function _checkContentBindingAttrs(attrs, mode) {
174
131
  var ct = 0, md = 0, seen = Object.create(null);
175
132
  for (var i = 0; i < attrs.length; i++) {
176
133
  var a = attrs[i];
177
- // RFC 5652 sec. 5.3 -- a signerInfo MUST NOT include multiple instances of the
178
- // same signed-attribute type (specific codes for the two mandatory types).
179
134
  if (seen[a.type]) {
180
135
  if (a.type === OID_CONTENT_TYPE) throw NS.E("cms/duplicate-content-type", "the attribute set must not repeat the content-type attribute");
181
136
  if (a.type === OID_MESSAGE_DIGEST) throw NS.E("cms/duplicate-message-digest", "the attribute set must not repeat the message-digest attribute");
@@ -184,74 +139,34 @@ function _checkContentBindingAttrs(attrs, mode) {
184
139
  seen[a.type] = true;
185
140
  if (a.type === OID_CONTENT_TYPE) {
186
141
  ct += 1;
187
- // RFC 5652 sec. 11.4 -- a countersignature's signedAttrs MUST NOT carry a
188
- // content-type attribute (what it signs has no content type).
189
142
  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
143
  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
192
- // full syntax (tag AND minimal base-128 OID content), not just the tag, so a
193
- // truncated / non-minimal subidentifier is rejected here, not at verify time.
194
144
  try { asn1.read.oid(asn1.decode(a.values[0])); }
195
145
  catch (e) { throw NS.E("cms/bad-content-type-attr", "the content-type attribute value must be a valid OBJECT IDENTIFIER", e); }
196
146
  } else if (a.type === OID_MESSAGE_DIGEST) {
197
147
  md += 1;
198
148
  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.
200
149
  try { asn1.read.octetString(asn1.decode(a.values[0])); }
201
150
  catch (e) { throw NS.E("cms/bad-message-digest-attr", "the message-digest attribute value must be an OCTET STRING", e); }
202
151
  } else if (a.type === OID_SIGNING_TIME) {
203
- // RFC 5652 sec. 11.3 -- signing-time MUST be single-valued and its value is
204
- // SigningTime ::= Time (UTCTime | GeneralizedTime, full syntax validated).
205
152
  if (a.values.length !== 1) throw NS.E("cms/bad-signing-time-attr", "the signing-time attribute must be single-valued (RFC 5652 sec. 11.3)");
206
153
  try { asn1.read.time(asn1.decode(a.values[0])); }
207
154
  catch (e) { throw NS.E("cms/bad-signing-time-attr", "the signing-time attribute value must be a Time (RFC 5652 sec. 11.3)", e); }
208
155
  }
209
156
  }
210
- // Duplicates are rejected in the loop (the seen-set); here only presence.
211
157
  if (mode !== "countersig" && ct === 0) throw NS.E("cms/missing-content-type", "the attribute set must contain a content-type attribute (RFC 5652 sec. 11.1)");
212
158
  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
159
  }
214
160
 
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
161
  function _readsAs(vals, tagNumber, reader) {
245
162
  if (vals.length !== 1 || !schema.isUniversal(vals[0], tagNumber)) return false;
246
163
  var reads = true;
247
164
  try { reader(vals[0]); }
248
165
  catch (_e) {
249
- reads = false; // right tag, unreadable body -- not a preimage anything signed
166
+ reads = false;
250
167
  }
251
168
  return reads;
252
169
  }
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
170
  function _readsAsTime(vals) {
256
171
  if (vals.length !== 1) return false;
257
172
  var reads = true;
@@ -262,9 +177,6 @@ function _readsAsTime(vals) {
262
177
  return reads;
263
178
  }
264
179
 
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
180
  function _ascendingDer(nodes) {
269
181
  for (var i = 1; i < nodes.length; i++) {
270
182
  if (Buffer.compare(nodes[i - 1].bytes, nodes[i].bytes) > 0) return false;
@@ -276,25 +188,14 @@ function looksLikeSignedAttributes(bytes) {
276
188
  if (!bytes || !bytes.length) return false;
277
189
  var node;
278
190
  try { node = asn1.decode(bytes); }
279
- catch (_e) { return false; } // not DER at all -- not this shape
191
+ catch (_e) { return false; }
280
192
  if (!schema.isUniversal(node, asn1.TAGS.SET) || !node.constructed) return false;
281
193
  var kids = node.children || [];
282
194
  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
195
  if (!_ascendingDer(kids)) return false;
294
196
  var sawContentType = false, sawMessageDigest = false, seenTypes = Object.create(null);
295
197
  for (var i = 0; i < kids.length; i++) {
296
198
  var a = kids[i];
297
- // Attribute ::= SEQUENCE { attrType OBJECT IDENTIFIER, attrValues SET OF ANY }
298
199
  if (!schema.isUniversal(a, asn1.TAGS.SEQUENCE)) return false;
299
200
  if (!a.children || a.children.length !== 2) return false;
300
201
  var t = a.children[0], vs = a.children[1];
@@ -303,31 +204,13 @@ function looksLikeSignedAttributes(bytes) {
303
204
  var attrOid;
304
205
  try { attrOid = asn1.read.oid(t); }
305
206
  catch (_e2) { return false; }
306
- if (seenTypes[attrOid]) return false; // a repeated attribute type -- sec. 5.3 forbids it
207
+ if (seenTypes[attrOid]) return false;
307
208
  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
209
  var placement = ATTR_FORBIDDEN_IN[attrOid];
314
210
  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
211
  var n = (vs.children || []).length;
323
212
  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.
213
+ if (!_ascendingDer(vs.children || [])) return false;
331
214
  var vals = vs.children || [];
332
215
  if (attrOid === OID_CONTENT_TYPE) {
333
216
  if (!_readsAs(vals, asn1.TAGS.OBJECT_IDENTIFIER, asn1.read.oid)) return false;
@@ -336,28 +219,16 @@ function looksLikeSignedAttributes(bytes) {
336
219
  if (!_readsAs(vals, asn1.TAGS.OCTET_STRING, asn1.read.octetString)) return false;
337
220
  sawMessageDigest = true;
338
221
  } 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
222
  if (!_readsAsTime(vals)) return false;
342
223
  }
343
224
  }
344
225
  return sawContentType && sawMessageDigest;
345
226
  }
346
227
 
347
- // RFC 5652 sec. 5.3 / sec. 9.3: when a content-type attribute is present, it MUST
348
- // be single-valued (sec. 11.1) and its value MUST equal the eContentType (a
349
- // cross-field consistency both parsed here). Shared by SignedData signedAttrs,
350
- // AuthenticatedData authAttrs, and AuthEnvelopedData authAttrs, where the single-value
351
- // rule holds even where content-type is not REQUIRED (RFC 5083), so an
352
- // expected-first-value-plus-extra set can never surface as ambiguous.
353
228
  function _assertContentTypeMatchesAttrs(attrs, eContentType) {
354
229
  for (var i = 0; i < attrs.length; i++) {
355
230
  if (attrs[i].type !== OID_CONTENT_TYPE) continue;
356
231
  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)");
357
- // ContentType ::= OBJECT IDENTIFIER: validate the value's full syntax (tag and
358
- // minimal base-128 OID content) with the CMS typed verdict, so a malformed value
359
- // on a path that does not run _checkContentBindingAttrs (AuthEnvelopedData,
360
- // RFC 5083 sec. 2.1) surfaces cms/bad-content-type-attr, not the raw asn1/* error.
361
232
  var ctv;
362
233
  try { ctv = asn1.read.oid(asn1.decode(attrs[i].values[0])); }
363
234
  catch (e) { throw NS.E("cms/bad-content-type-attr", "the content-type attribute value must be a valid OBJECT IDENTIFIER", e); }
@@ -365,11 +236,6 @@ function _assertContentTypeMatchesAttrs(attrs, eContentType) {
365
236
  }
366
237
  }
367
238
 
368
- // RFC 5652 sec. 5.3 -- an attribute set MUST NOT include multiple instances of the
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
371
- // sec. 11.1/sec. 11.2 bind only signed-data and authenticated-data), so it must not reuse
372
- // _checkContentBindingAttrs, which would over-enforce.
373
239
  function _checkNoDuplicateAttrs(attrs) {
374
240
  var seen = Object.create(null);
375
241
  for (var i = 0; i < attrs.length; i++) {
@@ -378,22 +244,12 @@ function _checkNoDuplicateAttrs(attrs) {
378
244
  }
379
245
  }
380
246
 
381
- // RFC 5084 -- validate the AES-GCM/CCM content-encryption parameters when the OID
382
- // is a recognized AEAD (an unknown OID surfaces its parameters raw with no AEAD
383
- // check). Returns { kind, nonce, icvLen } or null. `macLen` (the AuthEnvelopedData
384
- // mac length) is cross-checked against the effective ICVlen. The bounded nonce read
385
- // is the pre-auth surface the oversized-IV class (CVE-2025-15467) targets.
386
247
  function _validateAeadParams(alg, macLen) {
387
248
  var kind = AEAD_ALGS[alg.oid];
388
249
  if (!kind) return null;
389
250
  var K = kind.toUpperCase();
390
- // sec. 3.1/sec. 3.2: the parameters field MUST be present and carry the Parameters SEQUENCE.
391
251
  if (alg.parameters === null) throw NS.E("cms/bad-aead-params", "an AES-" + K + " content-encryption algorithm MUST carry its parameters (RFC 5084 sec. 3." + (kind === "gcm" ? "2" : "1") + ")");
392
252
  var node;
393
- // Coverage residual -- alg.parameters is an AlgorithmIdentifier child already fully
394
- // validated by the eager-recursive asn1.decode during the enclosing SEQUENCE parse;
395
- // re-decoding the same TLV cannot fault, so this catch is an unreachable fail-closed
396
- // belt (malformed parameters are rejected as asn1/* before _validateAeadParams runs).
397
253
  try { node = asn1.decode(alg.parameters); }
398
254
  catch (e) { throw NS.E("cms/bad-aead-params", "malformed AES-" + K + " parameters", e); }
399
255
  if (node.tagClass !== "universal" || node.tagNumber !== T.SEQUENCE || !node.children || node.children.length < 1 || node.children.length > 2) {
@@ -402,12 +258,8 @@ function _validateAeadParams(alg, macLen) {
402
258
  var nonce;
403
259
  try { nonce = asn1.read.octetString(node.children[0]); }
404
260
  catch (e) { throw NS.E("cms/bad-aead-params", "the AEAD aes-nonce must be an OCTET STRING", e); }
405
- // CCM: aes-nonce is SIZE(7..13) (RFC 5084 sec. 3.1). GCM has no ASN.1 bound; the raw
406
- // nonce is surfaced (a subarray, never copied into a fixed buffer), bounded by the
407
- // DER size cap -- a nonzero length is the only structural floor.
408
261
  if (kind === "ccm" && (nonce.length < 7 || nonce.length > 13)) throw NS.E("cms/bad-aead-params", "the AES-CCM aes-nonce must be 7..13 octets (RFC 5084 sec. 3.1)");
409
262
  if (nonce.length < 1) throw NS.E("cms/bad-aead-params", "the AEAD aes-nonce must be non-empty");
410
- // aes-ICVlen INTEGER DEFAULT 12.
411
263
  var icvLen = 12, icvEncoded = false;
412
264
  if (node.children.length === 2) {
413
265
  var iv;
@@ -418,40 +270,20 @@ function _validateAeadParams(alg, macLen) {
418
270
  }
419
271
  var legal = kind === "gcm" ? AEAD_GCM_ICVLENS : AEAD_CCM_ICVLENS;
420
272
  if (!legal.has(icvLen)) throw NS.E("cms/bad-aead-params", "the AES-" + K + " aes-ICVlen " + icvLen + " is not an allowed value (RFC 5084)");
421
- // X.690 sec. 11.5: a DEFAULT value MUST be omitted in DER, so an encoded ICVlen equal
422
- // to the default 12 is non-canonical.
423
273
  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)");
424
- // RFC 5084 sec. 3.1/sec. 3.2: aes-ICVlen MUST match the AuthEnvelopedData mac length.
425
274
  if (macLen != null && icvLen !== macLen) throw NS.E("cms/mac-length-mismatch", "the AEAD aes-ICVlen " + icvLen + " must equal the mac length " + macLen + " (RFC 5084)");
426
275
  return { kind: kind, nonce: nonce, icvLen: icvLen };
427
276
  }
428
277
 
429
- // RFC 5652 sec. 5.3 / sec. 9.1 + RFC 5083 sec. 2.1: a signed / authenticated attribute SET
430
- // MUST be DER encoded even when the enclosing structure was decoded as BER (its
431
- // bytes feed the sec. 5.4 / sec. 9.2 re-tagged hash / MAC input, so a non-DER TLV makes
432
- // the surfaced raw bytes unusable for verification). Strictly re-decoding the
433
- // on-wire TLV rejects any indefinite-length or non-minimal encoding in the
434
- // subtree; on the strict parse path this always passes.
435
278
  function _assertDerEncodedAttrs(node, code) {
436
279
  try { asn1.decode(node.bytes); }
437
280
  catch (e) { throw NS.E(code, "the attribute set must be DER encoded even inside a BER envelope (RFC 5652 sec. 5.3)", e); }
438
281
  }
439
282
 
440
- // A CertificateChoices / RevocationInfoChoice element, surfaced raw (its DER +
441
- // outer tag) and never recursively parsed: the obsolete CHOICE alternatives
442
- // (extendedCertificate, attribute certs, otherRevocationInfo) never fail the
443
- // parse, and a caller re-parses a `certificate`/`CertificateList` element itself.
444
283
  function rawElement(item) {
445
284
  return { bytes: item.node.bytes, tagClass: item.node.tagClass, tagNumber: item.node.tagNumber };
446
285
  }
447
286
 
448
- // CertificateChoices (RFC 5652 sec. 10.2.2) and RevocationInfoChoice (sec. 10.2.1) are
449
- // CLOSED CHOICE sets -- Certificate / CertificateList (a universal SEQUENCE) or
450
- // the listed context tags, every alternative constructed. An element outside
451
- // the set is structurally malformed even though members are surfaced raw: the
452
- // [3] / [1] `other` arms ARE the RFC's extension points for unknown formats,
453
- // so an open tag set is never needed. The tag sets also feed the sec. 5.1 / sec. 6.1
454
- // version rules, which must never be computed over undefined-type elements.
455
287
  var CERT_CHOICE_TAGS = { universal: {}, context: { 0: true, 1: true, 2: true, 3: true } };
456
288
  var CRL_CHOICE_TAGS = { universal: {}, context: { 1: true } };
457
289
  CERT_CHOICE_TAGS.universal[asn1.TAGS.SEQUENCE] = true;
@@ -467,24 +299,17 @@ function rawChoiceElement(allowed, code, what) {
467
299
  };
468
300
  }
469
301
 
470
- // RFC 5652 sec. 5.1 -- the exact SignedData CMSVersion, computed from the raw
471
- // CertificateChoices / RevocationInfoChoice outer tags (a certificate `other` is
472
- // [3], a v2AttrCert [2], a v1AttrCert [1]; a RevocationInfoChoice `other` is [1]),
473
- // the SignerInfo versions, and whether the content type is id-data.
474
302
  function _expectedSignedDataVersion(certificates, crls, signerInfos, eContentType) {
475
303
  var otherCert = certificates.some(function (c) { return schema.isContext(c, 3); });
476
304
  var otherCrl = crls.some(function (c) { return schema.isContext(c, 1); });
477
305
  if (otherCert || otherCrl) return 5;
478
- if (certificates.some(function (c) { return schema.isContext(c, 2); })) return 4; // v2AttrCert [2]
479
- if (certificates.some(function (c) { return schema.isContext(c, 1); }) || // v1AttrCert [1]
306
+ if (certificates.some(function (c) { return schema.isContext(c, 2); })) return 4;
307
+ if (certificates.some(function (c) { return schema.isContext(c, 1); }) ||
480
308
  signerInfos.some(function (si) { return si.version === 3; }) ||
481
309
  eContentType !== OID_DATA) return 3;
482
310
  return 1;
483
311
  }
484
312
 
485
- // EncapsulatedContentInfo ::= SEQUENCE { eContentType OID,
486
- // eContent [0] EXPLICIT OCTET STRING OPTIONAL } (RFC 5652 sec. 5.2). Absent eContent
487
- // is a detached signature (surfaced as null); present is the raw content bytes.
488
313
  var ENCAP_CONTENT_INFO = schema.seq([
489
314
  schema.field("eContentType", schema.oidLeaf()),
490
315
  schema.optional("eContent", schema.octetString(), { tag: 0, explicit: true, emptyCode: "cms/bad-econtent" }),
@@ -498,8 +323,6 @@ var ENCAP_CONTENT_INFO = schema.seq([
498
323
  },
499
324
  });
500
325
 
501
- // IssuerAndSerialNumber ::= SEQUENCE { issuer Name, serialNumber INTEGER }
502
- // (RFC 5652 sec. 10.2.4). serialNumberHex preserves the DER sign padding.
503
326
  var ISSUER_AND_SERIAL = schema.seq([
504
327
  schema.field("issuer", NAME),
505
328
  schema.field("serialNumber", schema.integerLeaf()),
@@ -514,23 +337,11 @@ var ISSUER_AND_SERIAL = schema.seq([
514
337
  },
515
338
  });
516
339
 
517
- // SignerIdentifier ::= CHOICE { issuerAndSerialNumber IssuerAndSerialNumber,
518
- // subjectKeyIdentifier [0] IMPLICIT OCTET STRING } (RFC 5652 sec. 5.3), where the arm is
519
- // disambiguated by tag (universal SEQUENCE vs context [0]).
520
340
  var SIGNER_IDENTIFIER = schema.choice([
521
341
  { when: { tagClass: "universal", tagNumber: asn1.TAGS.SEQUENCE }, schema: ISSUER_AND_SERIAL },
522
342
  { when: { tagClass: "context", tagNumber: 0 }, schema: schema.implicitOctetString(0) },
523
343
  ], { code: "cms/bad-signer-identifier", what: "SignerIdentifier" });
524
344
 
525
- // SignerInfo ::= SEQUENCE { version, sid SignerIdentifier, digestAlgorithm,
526
- // signedAttrs [0] IMPLICIT OPTIONAL, signatureAlgorithm, signature OCTET STRING,
527
- // unsignedAttrs [1] IMPLICIT OPTIONAL } (RFC 5652 sec. 5.3). signedAttrs/unsignedAttrs
528
- // are positional optionals (a required signatureAlgorithm sits between them, so
529
- // they cannot be a trailing block). One definition drives both consumers: the
530
- // signerInfos of a SignedData (mode "content") and a countersignature attribute
531
- // value (mode "countersig", RFC 5652 sec. 11.4: syntactically a SignerInfo whose
532
- // signedAttrs MUST carry message-digest and MUST NOT carry content-type), so
533
- // the two can never diverge structurally.
534
345
  function makeSignerInfo(mode) {
535
346
  return schema.seq([
536
347
  schema.field("version", SIGNER_VERSION),
@@ -548,34 +359,24 @@ function makeSignerInfo(mode) {
548
359
  var isSkid = sidNode.tagClass === "context" && sidNode.tagNumber === 0;
549
360
  var sid;
550
361
  if (isSkid) {
551
- // RFC 5652 sec. 5.3 -- a subjectKeyIdentifier sid forces SignerInfo version 3.
552
362
  if (version !== 3) throw NS.E("cms/bad-signer-version", "a subjectKeyIdentifier signer identifier requires SignerInfo version 3");
553
363
  sid = { subjectKeyIdentifier: m.fields.sid.value };
554
364
  } else {
555
- // RFC 5652 sec. 5.3 -- an issuerAndSerialNumber sid forces SignerInfo version 1.
556
365
  if (version !== 1) throw NS.E("cms/bad-signer-version", "an issuerAndSerialNumber signer identifier requires SignerInfo version 1");
557
366
  sid = m.fields.sid.value.result;
558
367
  }
559
368
  var signedAttrs = null, signedAttrsBytes = null;
560
369
  if (m.fields.signedAttrs.present) {
561
- // sec. 5.3 -- signedAttrs MUST be DER even in a BER envelope (feeds the sec. 5.4 hash).
562
370
  _assertDerEncodedAttrs(m.fields.signedAttrs.node, "cms/bad-signed-attrs");
563
371
  signedAttrs = m.fields.signedAttrs.value.items.map(function (it) { return it.value.result; });
564
372
  _checkAttrPlacement(signedAttrs, "signed");
565
373
  _checkContentBindingAttrs(signedAttrs, mode);
566
- // The raw on-wire signedAttrs bytes (leading 0xA0) so a verifier can re-tag to
567
- // a universal SET and reproduce the signed hash (RFC 5652 sec. 5.4).
568
374
  signedAttrsBytes = m.fields.signedAttrs.node.bytes;
569
375
  }
570
376
  var unsignedAttrs = null;
571
377
  if (m.fields.unsignedAttrs.present) {
572
378
  unsignedAttrs = m.fields.unsignedAttrs.value.items.map(function (it) { return it.value.result; });
573
379
  _checkAttrPlacement(unsignedAttrs, "unsigned");
574
- // sec. 11.4 -- every countersignature value IS a SignerInfo (validated by
575
- // content, never accepted on the attribute type alone). Multiple
576
- // countersignature instances are explicitly permitted here, and a
577
- // countersignature's own unsignedAttrs may nest further ones, so the
578
- // recursion is bounded by the decoder's depth cap.
579
380
  for (var u = 0; u < unsignedAttrs.length; u++) {
580
381
  if (unsignedAttrs[u].type !== OID_COUNTERSIGNATURE) continue;
581
382
  for (var v = 0; v < unsignedAttrs[u].values.length; v++) {
@@ -602,10 +403,6 @@ function makeSignerInfo(mode) {
602
403
  var SIGNER_INFO = makeSignerInfo("content");
603
404
  var COUNTERSIGNATURE_SIGNER_INFO = makeSignerInfo("countersig");
604
405
 
605
- // SignedData ::= SEQUENCE { version CMSVersion, digestAlgorithms SET OF,
606
- // encapContentInfo, certificates [0] IMPLICIT OPTIONAL, crls [1] IMPLICIT
607
- // OPTIONAL, signerInfos SET OF } (RFC 5652 sec. 5.1). digestAlgorithms and
608
- // signerInfos are min:0, since a degenerate certs-only SignedData carries neither.
609
406
  var SIGNED_DATA = schema.seq([
610
407
  schema.field("version", SIGNED_DATA_VERSION),
611
408
  schema.field("digestAlgorithms", schema.setOf(ALGORITHM_IDENTIFIER, { min: 0, code: "cms/bad-digest-algorithms", what: "digestAlgorithms" })),
@@ -622,23 +419,16 @@ var SIGNED_DATA = schema.seq([
622
419
  var crls = m.fields.crls.present ? m.fields.crls.value.items.map(rawChoiceElement(CRL_CHOICE_TAGS, "cms/bad-crls", "crls")) : [];
623
420
  var signerInfos = m.fields.signerInfos.value.items.map(function (it) { return it.value.result; });
624
421
 
625
- // RFC 5652 sec. 5.3 -- signedAttrs MAY be omitted only when the content type is
626
- // id-data; any other eContentType requires each SignerInfo to carry signedAttrs
627
- // (so the content-type + message-digest attributes bind the signature).
628
422
  if (encapContentInfo.eContentType !== OID_DATA) {
629
423
  for (var s = 0; s < signerInfos.length; s++) {
630
424
  if (signerInfos[s].signedAttrs === null) throw NS.E("cms/missing-signed-attrs", "a SignerInfo must carry signedAttrs when the content type is not id-data (RFC 5652 sec. 5.3)");
631
425
  }
632
426
  }
633
427
 
634
- // RFC 5652 sec. 5.3 -- when signedAttrs are present, the content-type attribute's
635
- // value MUST equal the eContentType (a cross-field consistency both parsed
636
- // here; a mismatch is an internally-inconsistent SignedData).
637
428
  for (var si = 0; si < signerInfos.length; si++) {
638
429
  if (signerInfos[si].signedAttrs) _assertContentTypeMatchesAttrs(signerInfos[si].signedAttrs, encapContentInfo.eContentType);
639
430
  }
640
431
 
641
- // RFC 5652 sec. 5.1 -- the SignedData CMSVersion is determined by its contents.
642
432
  var expected = _expectedSignedDataVersion(certificates, crls, signerInfos, encapContentInfo.eContentType);
643
433
  if (version !== expected) throw NS.E("cms/bad-version", "SignedData version " + version + " does not match its contents (RFC 5652 sec. 5.1 requires v" + expected + ")");
644
434
 
@@ -653,15 +443,8 @@ var SIGNED_DATA = schema.seq([
653
443
  },
654
444
  });
655
445
 
656
- // ==== EnvelopedData / EncryptedData (RFC 5652 sec. 6/sec. 8, RFC 5753) ========
657
446
  var T = asn1.TAGS;
658
447
 
659
- // EncryptedContentInfo ::= SEQUENCE { contentType OID, contentEncryptionAlgorithm
660
- // AlgorithmIdentifier, encryptedContent [0] IMPLICIT OCTET STRING OPTIONAL } (RFC
661
- // 5652 sec. 6.1). encryptedContent is [0] IMPLICIT (context primitive), so its content
662
- // octets ARE the ciphertext directly, so it reads through implicitOctetString(0),
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.
665
448
  var ENCRYPTED_CONTENT_INFO = schema.seq([
666
449
  schema.field("contentType", schema.oidLeaf()),
667
450
  schema.field("contentEncryptionAlgorithm", ALGORITHM_IDENTIFIER),
@@ -677,16 +460,11 @@ var ENCRYPTED_CONTENT_INFO = schema.seq([
677
460
  },
678
461
  });
679
462
 
680
- // RecipientIdentifier ::= CHOICE { issuerAndSerialNumber, subjectKeyIdentifier [0]
681
- // IMPLICIT OCTET STRING } (RFC 5652 sec. 6.2.1), structurally identical to
682
- // SignerIdentifier; reuse ISSUER_AND_SERIAL + the implicitOctetString(0) leaf.
683
463
  var RECIPIENT_IDENTIFIER = schema.choice([
684
464
  { when: { tagClass: "universal", tagNumber: T.SEQUENCE }, schema: ISSUER_AND_SERIAL },
685
465
  { when: { tagClass: "context", tagNumber: 0 }, schema: schema.implicitOctetString(0) },
686
466
  ], { code: "cms/bad-recipient-identifier", what: "RecipientIdentifier" });
687
467
 
688
- // KeyTransRecipientInfo ::= SEQUENCE { version(0|2), rid RecipientIdentifier,
689
- // keyEncryptionAlgorithm, encryptedKey OCTET STRING } (RFC 5652 sec. 6.2.1).
690
468
  var KEY_TRANS_RECIPIENT_INFO = schema.seq([
691
469
  schema.field("version", KTRI_VERSION),
692
470
  schema.field("rid", RECIPIENT_IDENTIFIER),
@@ -698,7 +476,6 @@ var KEY_TRANS_RECIPIENT_INFO = schema.seq([
698
476
  var version = m.fields.version.value;
699
477
  var ridNode = m.fields.rid.node;
700
478
  var isSkid = ridNode.tagClass === "context" && ridNode.tagNumber === 0;
701
- // RFC 5652 sec. 6.2.1 -- rid <=> version: issuerAndSerialNumber => 0, subjectKeyIdentifier => 2.
702
479
  if (isSkid && version !== 2) throw NS.E("cms/bad-recipient-version", "a subjectKeyIdentifier recipient identifier requires KeyTransRecipientInfo version 2 (RFC 5652 sec. 6.2.1)");
703
480
  if (!isSkid && version !== 0) throw NS.E("cms/bad-recipient-version", "an issuerAndSerialNumber recipient identifier requires KeyTransRecipientInfo version 0 (RFC 5652 sec. 6.2.1)");
704
481
  return {
@@ -711,9 +488,6 @@ var KEY_TRANS_RECIPIENT_INFO = schema.seq([
711
488
  },
712
489
  });
713
490
 
714
- // OriginatorPublicKey ::= SEQUENCE { algorithm, publicKey BIT STRING } (RFC 5753
715
- // sec. 3.1.1), reached as originatorKey [1] IMPLICIT: SPKI-shaped but cannot reuse
716
- // pkix.spki (which asserts a universal SEQUENCE), so assert:"constructed".
717
491
  var ORIGINATOR_PUBLIC_KEY = schema.seq([
718
492
  schema.field("algorithm", ALGORITHM_IDENTIFIER),
719
493
  schema.field("publicKey", schema.bitString()),
@@ -722,19 +496,12 @@ var ORIGINATOR_PUBLIC_KEY = schema.seq([
722
496
  build: function (m) { return { algorithm: m.fields.algorithm.value.result, publicKey: m.fields.publicKey.value }; },
723
497
  });
724
498
 
725
- // OriginatorIdentifierOrKey ::= CHOICE { issuerAndSerialNumber, subjectKeyIdentifier
726
- // [0] IMPLICIT OCTET STRING, originatorKey [1] IMPLICIT OriginatorPublicKey }.
727
499
  var ORIGINATOR_IDENTIFIER_OR_KEY = schema.choice([
728
500
  { when: { tagClass: "universal", tagNumber: T.SEQUENCE }, schema: ISSUER_AND_SERIAL },
729
501
  { when: { tagClass: "context", tagNumber: 0 }, schema: schema.implicitOctetString(0) },
730
502
  { when: { tagClass: "context", tagNumber: 1 }, schema: ORIGINATOR_PUBLIC_KEY },
731
503
  ], { code: "cms/bad-originator-identifier", what: "OriginatorIdentifierOrKey" });
732
504
 
733
- // RecipientKeyIdentifier (RFC 5652 sec. 6.2.2) and KEKIdentifier (sec. 6.2.3) are one
734
- // shape -- { <keyId> OCTET STRING, date GeneralizedTime OPTIONAL, other
735
- // OtherKeyAttribute OPTIONAL }, differing only in the key-id field's name and
736
- // the enclosing tag form. One factory defines both so the OPTIONAL handling
737
- // (date and the raw-surfaced OtherKeyAttribute) cannot diverge between them.
738
505
  function keyIdentifierSchema(keyIdName, assert, code, what) {
739
506
  return schema.seq([
740
507
  schema.field(keyIdName, schema.octetString()),
@@ -752,19 +519,14 @@ function keyIdentifierSchema(keyIdName, assert, code, what) {
752
519
  });
753
520
  }
754
521
 
755
- // Reached as rKeyId [0] IMPLICIT (a SEQUENCE, constructed, unlike ktri's [0] leaf).
756
522
  var RECIPIENT_KEY_IDENTIFIER = keyIdentifierSchema("subjectKeyIdentifier",
757
523
  "constructed", "cms/bad-recipient-key-identifier", "RecipientKeyIdentifier");
758
524
 
759
- // KeyAgreeRecipientIdentifier ::= CHOICE { issuerAndSerialNumber, rKeyId [0] IMPLICIT
760
- // RecipientKeyIdentifier } (RFC 5652 sec. 6.2.2).
761
525
  var KEY_AGREE_RECIPIENT_IDENTIFIER = schema.choice([
762
526
  { when: { tagClass: "universal", tagNumber: T.SEQUENCE }, schema: ISSUER_AND_SERIAL },
763
527
  { when: { tagClass: "context", tagNumber: 0 }, schema: RECIPIENT_KEY_IDENTIFIER },
764
528
  ], { code: "cms/bad-kari-identifier", what: "KeyAgreeRecipientIdentifier" });
765
529
 
766
- // RecipientEncryptedKey ::= SEQUENCE { rid KeyAgreeRecipientIdentifier, encryptedKey
767
- // OCTET STRING } (RFC 5652 sec. 6.2.2).
768
530
  var RECIPIENT_ENCRYPTED_KEY = schema.seq([
769
531
  schema.field("rid", KEY_AGREE_RECIPIENT_IDENTIFIER),
770
532
  schema.field("encryptedKey", schema.octetString()),
@@ -775,18 +537,12 @@ var RECIPIENT_ENCRYPTED_KEY = schema.seq([
775
537
  var isRkid = ridNode.tagClass === "context" && ridNode.tagNumber === 0;
776
538
  return {
777
539
  rid: m.fields.rid.value.result,
778
- // Which KeyAgreeRecipientIdentifier arm was taken -- the recipient-matching
779
- // form differs (mirrors ktri.ridType / kari.originator.form).
780
540
  ridType: isRkid ? "rKeyId" : "issuerAndSerialNumber",
781
541
  encryptedKey: m.fields.encryptedKey.value,
782
542
  };
783
543
  },
784
544
  });
785
545
 
786
- // KeyAgreeRecipientInfo ::= [1] IMPLICIT SEQUENCE { version(3), originator [0]
787
- // EXPLICIT OriginatorIdentifierOrKey, ukm [1] EXPLICIT OPTIONAL,
788
- // keyEncryptionAlgorithm, recipientEncryptedKeys SEQUENCE OF } (RFC 5652 sec. 6.2.2 +
789
- // RFC 5753 sec. 3.1.1). originator [0] is EXPLICIT (wraps a CHOICE).
790
546
  var KEY_AGREE_RECIPIENT_INFO = schema.seq([
791
547
  schema.field("version", KARI_VERSION),
792
548
  schema.field("originator", schema.explicit(0, ORIGINATOR_IDENTIFIER_OR_KEY, { code: "cms/bad-kari" })),
@@ -809,8 +565,6 @@ var KEY_AGREE_RECIPIENT_INFO = schema.seq([
809
565
  },
810
566
  });
811
567
 
812
- // KEKRecipientInfo ::= [2] IMPLICIT SEQUENCE { version(4), kekid KEKIdentifier,
813
- // keyEncryptionAlgorithm, encryptedKey } (RFC 5652 sec. 6.2.3).
814
568
  var KEK_IDENTIFIER = keyIdentifierSchema("keyIdentifier",
815
569
  "sequence", "cms/bad-kek-identifier", "KEKIdentifier");
816
570
  var KEK_RECIPIENT_INFO = schema.seq([
@@ -830,10 +584,6 @@ var KEK_RECIPIENT_INFO = schema.seq([
830
584
  },
831
585
  });
832
586
 
833
- // PasswordRecipientInfo ::= [3] IMPLICIT SEQUENCE { version(0), keyDerivationAlgorithm
834
- // [0] IMPLICIT AlgorithmIdentifier OPTIONAL, keyEncryptionAlgorithm, encryptedKey }
835
- // (RFC 5652 sec. 6.2.4). keyDerivationAlgorithm [0] IMPLICIT is the one field needing
836
- // the implicitTag AlgorithmIdentifier; present iff the first post-version node is [0].
837
587
  var PASSWORD_RECIPIENT_INFO = schema.seq([
838
588
  schema.field("version", PWRI_VERSION),
839
589
  schema.optional("keyDerivationAlgorithm", pkix.algorithmIdentifier(NS, { implicitTag: 0 }), { tag: 0 }),
@@ -851,12 +601,6 @@ var PASSWORD_RECIPIENT_INFO = schema.seq([
851
601
  },
852
602
  });
853
603
 
854
- // KEMRecipientInfo ::= SEQUENCE { version CMSVersion (0), rid RecipientIdentifier,
855
- // kem KEMAlgorithmIdentifier, kemct OCTET STRING, kdf KeyDerivationAlgorithm-
856
- // Identifier, kekLength INTEGER (1..65535), ukm [0] EXPLICIT OPTIONAL, wrap
857
- // KeyEncryptionAlgorithmIdentifier, encryptedKey OCTET STRING } (RFC 9629 sec. 3).
858
- // Reached through the ori [4] arm under id-ori-kem. The kem / kdf params-absent
859
- // rules (ML-KEM, HKDF) ride the shared AlgorithmIdentifier registry guard.
860
604
  var KEM_RECIPIENT_INFO = schema.seq([
861
605
  schema.field("version", KEMRI_VERSION),
862
606
  schema.field("rid", RECIPIENT_IDENTIFIER),
@@ -875,26 +619,15 @@ var KEM_RECIPIENT_INFO = schema.seq([
875
619
  var kem = m.fields.kem.value.result;
876
620
  var kemct = m.fields.kemct.value;
877
621
  var wrap = m.fields.wrap.value.result;
878
- // kekLength INTEGER (1..65535) -- bound the unbounded INTEGER before narrowing.
879
622
  var kl = m.fields.kekLength.value;
880
623
  if (kl < 1n || kl > 65535n) throw NS.E("cms/bad-kek-length", "KEMRecipientInfo kekLength must be 1..65535 (RFC 9629 sec. 3)");
881
624
  var kekLength = Number(kl);
882
- // RFC 9629 sec. 3 -- kekLength MUST be consistent with the wrap algorithm; a
883
- // recognized AES key-wrap pins the exact KEK size (registry, not switch).
884
625
  var wrapLen = WRAP_KEK_LENGTHS[wrap.oid];
885
626
  if (wrapLen !== undefined && kekLength !== wrapLen) {
886
- // Coverage residual -- reached only when the OID is a registry entry (the guard above
887
- // guarantees it), so oid.name() never returns undefined and this fallback is a
888
- // defensive belt, not selectable through the public parse path.
889
627
  throw NS.E("cms/kek-length-mismatch", "kekLength " + kekLength + " does not match the " + (oid.name(wrap.oid) || wrap.oid) + " KEK size " + wrapLen + " (RFC 9629 sec. 3)");
890
628
  }
891
- // FIPS 203 -- a recognized ML-KEM kem produces a fixed-size ciphertext; any
892
- // other kemct length can never decapsulate.
893
629
  var ctLen = KEM_CT_LENGTHS[kem.oid];
894
630
  if (ctLen !== undefined && kemct.length !== ctLen) {
895
- // Coverage residual -- reached only when the OID is a registry entry (the guard above
896
- // guarantees it), so oid.name() never returns undefined and this fallback is a
897
- // defensive belt, not selectable through the public parse path.
898
631
  throw NS.E("cms/bad-kem-ciphertext", "the " + (oid.name(kem.oid) || kem.oid) + " kemct must be exactly " + ctLen + " octets (FIPS 203)");
899
632
  }
900
633
  return {
@@ -912,11 +645,6 @@ var KEM_RECIPIENT_INFO = schema.seq([
912
645
  },
913
646
  });
914
647
 
915
- // OtherRecipientInfo ::= [4] IMPLICIT SEQUENCE { oriType OID, oriValue ANY } (RFC
916
- // 5652 sec. 6.2.5). A RECOGNIZED oriType is validated by content, never accepted on
917
- // the type OID alone: id-ori-kem walks KEMRecipientInfo (RFC 9629) and surfaces
918
- // the parsed structure as `kemri` alongside the raw oriValue. An unrecognized
919
- // oriType stays raw-opaque (the ORI extension point), kemri null.
920
648
  var OTHER_RECIPIENT_INFO = schema.seq([
921
649
  schema.field("oriType", schema.oidLeaf()),
922
650
  schema.field("oriValue", schema.any()),
@@ -933,9 +661,6 @@ var OTHER_RECIPIENT_INFO = schema.seq([
933
661
  },
934
662
  });
935
663
 
936
- // RecipientInfo ::= CHOICE { ktri KeyTransRecipientInfo, kari [1], kekri [2], pwri
937
- // [3], ori [4] } (RFC 5652 sec. 6.2). A bare universal SEQUENCE is ktri (the untagged
938
- // alternative). An unknown context tag -> no arm -> cms/bad-recipient-info.
939
664
  var RECIPIENT_INFO = schema.choice([
940
665
  { when: { tagClass: "universal", tagNumber: T.SEQUENCE }, schema: KEY_TRANS_RECIPIENT_INFO },
941
666
  { when: { tagClass: "context", tagNumber: 1 }, schema: KEY_AGREE_RECIPIENT_INFO },
@@ -944,9 +669,6 @@ var RECIPIENT_INFO = schema.choice([
944
669
  { when: { tagClass: "context", tagNumber: 4 }, schema: OTHER_RECIPIENT_INFO },
945
670
  ], { code: "cms/bad-recipient-info", what: "RecipientInfo" });
946
671
 
947
- // OriginatorInfo ::= [0] IMPLICIT SEQUENCE { certs [0] IMPLICIT OPTIONAL, crls [1]
948
- // IMPLICIT OPTIONAL } (RFC 5652 sec. 6.1). Members surfaced raw (their outer tag feeds
949
- // the version rule).
950
672
  var ORIGINATOR_INFO = schema.seq([
951
673
  schema.optional("certs", schema.implicitSetOf(0, schema.any(), { min: 1, code: "cms/bad-originator-certs", what: "certs" }), { tag: 0 }),
952
674
  schema.optional("crls", schema.implicitSetOf(1, schema.any(), { min: 1, code: "cms/bad-originator-crls", what: "crls" }), { tag: 1 }),
@@ -960,10 +682,6 @@ var ORIGINATOR_INFO = schema.seq([
960
682
  },
961
683
  });
962
684
 
963
- // RFC 5652 sec. 6.1 -- the exact EnvelopedData CMSVersion, from originatorInfo's raw
964
- // cert/crl outer tags and the recipient arms (a cert `other` is [3], a v2AttrCert
965
- // [2]; a crl `other` is [1]; a pwri or ori forces v3; all-ktri-IAS with no
966
- // originatorInfo/unprotectedAttrs is v0; everything else v2).
967
685
  function _expectedEnvelopedDataVersion(originatorInfo, recipientInfos, hasUnprotectedAttrs) {
968
686
  var hasOrig = !!originatorInfo;
969
687
  var certs = hasOrig ? originatorInfo.certs : [];
@@ -976,10 +694,6 @@ function _expectedEnvelopedDataVersion(originatorInfo, recipientInfos, hasUnprot
976
694
  return 2;
977
695
  }
978
696
 
979
- // RFC 5652 sec. 9.1 -- the exact AuthenticatedData CMSVersion, from originatorInfo's raw
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
982
- // other-crl [1]) -> 3; ELSE IF originatorInfo present AND v2AttrCert [2] -> 1; ELSE 0.
983
697
  function _expectedAuthDataVersion(originatorInfo) {
984
698
  if (!originatorInfo) return 0;
985
699
  var certs = originatorInfo.certs, crls = originatorInfo.crls;
@@ -988,11 +702,6 @@ function _expectedAuthDataVersion(originatorInfo) {
988
702
  return 0;
989
703
  }
990
704
 
991
- // EnvelopedData ::= SEQUENCE { version, originatorInfo [0] IMPLICIT OPTIONAL,
992
- // recipientInfos RecipientInfos (SET SIZE 1..MAX), encryptedContentInfo,
993
- // unprotectedAttrs [1] IMPLICIT OPTIONAL } (RFC 5652 sec. 6.1). recipientInfos is
994
- // min:1 (an empty SET is non-conformant, the inverse of SignedData's degenerate
995
- // signerInfos).
996
705
  var ENVELOPED_DATA = schema.seq([
997
706
  schema.field("version", ENVELOPED_DATA_VERSION),
998
707
  schema.optional("originatorInfo", ORIGINATOR_INFO, { tag: 0 }),
@@ -1020,9 +729,6 @@ var ENVELOPED_DATA = schema.seq([
1020
729
  },
1021
730
  });
1022
731
 
1023
- // EncryptedData ::= SEQUENCE { version, encryptedContentInfo, unprotectedAttrs [1]
1024
- // IMPLICIT OPTIONAL } (RFC 5652 sec. 8): no recipients, no originatorInfo; the CEK is
1025
- // distributed out of band. version is 0, or 2 iff unprotectedAttrs are present.
1026
732
  var ENCRYPTED_DATA = schema.seq([
1027
733
  schema.field("version", ENCRYPTED_DATA_VERSION),
1028
734
  schema.field("encryptedContentInfo", ENCRYPTED_CONTENT_INFO),
@@ -1044,11 +750,6 @@ var ENCRYPTED_DATA = schema.seq([
1044
750
  },
1045
751
  });
1046
752
 
1047
- // AuthenticatedData ::= SEQUENCE { version, originatorInfo [0] IMPLICIT OPTIONAL,
1048
- // recipientInfos RecipientInfos, macAlgorithm, digestAlgorithm [1] OPTIONAL,
1049
- // encapContentInfo, authAttrs [2] IMPLICIT OPTIONAL, mac OCTET STRING,
1050
- // unauthAttrs [3] IMPLICIT OPTIONAL } (RFC 5652 sec. 9.1). digestAlgorithm [1] is
1051
- // IMPLICIT (module default) -- the same shape as pwri's keyDerivationAlgorithm.
1052
753
  var AUTHENTICATED_DATA = schema.seq([
1053
754
  schema.field("version", AUTHDATA_VERSION),
1054
755
  schema.optional("originatorInfo", ORIGINATOR_INFO, { tag: 0 }),
@@ -1068,13 +769,9 @@ var AUTHENTICATED_DATA = schema.seq([
1068
769
  var hasDigestAlg = m.fields.digestAlgorithm.present;
1069
770
  var hasAuthAttrs = m.fields.authAttrs.present;
1070
771
 
1071
- // RFC 5652 sec. 9.1 -- the version is computed from originatorInfo's raw cert/crl
1072
- // tags only (recipient kinds never influence it, unlike EnvelopedData sec. 6.1).
1073
772
  var expected = _expectedAuthDataVersion(originatorInfo);
1074
773
  if (version !== expected) throw NS.E("cms/bad-version", "AuthenticatedData version " + version + " does not match its contents (RFC 5652 sec. 9.1 requires v" + expected + ")");
1075
774
 
1076
- // RFC 5652 sec. 9.1 -- authAttrs MUST be present when the content type is not
1077
- // id-data, and digestAlgorithm <=> authAttrs is a strict biconditional.
1078
775
  if (!hasAuthAttrs && encapContentInfo.eContentType !== OID_DATA) {
1079
776
  throw NS.E("cms/missing-auth-attrs", "AuthenticatedData must carry authAttrs when the content type is not id-data (RFC 5652 sec. 9.1)");
1080
777
  }
@@ -1083,16 +780,11 @@ var AUTHENTICATED_DATA = schema.seq([
1083
780
 
1084
781
  var authAttrs = null, authAttrsBytes = null;
1085
782
  if (hasAuthAttrs) {
1086
- // sec. 9.1 -- AuthAttributes MUST be DER even in a BER envelope (feeds the sec. 9.2 MAC).
1087
783
  _assertDerEncodedAttrs(m.fields.authAttrs.node, "cms/bad-auth-attrs");
1088
784
  authAttrs = m.fields.authAttrs.value.items.map(function (it) { return it.value.result; });
1089
785
  _checkAttrPlacement(authAttrs, "auth");
1090
- // sec. 9.1 -- authAttrs MUST contain content-type + message-digest (sec. 11.1/sec. 11.2),
1091
- // and the content-type value MUST equal the eContentType.
1092
786
  _checkContentBindingAttrs(authAttrs, "content");
1093
787
  _assertContentTypeMatchesAttrs(authAttrs, encapContentInfo.eContentType);
1094
- // The raw on-wire authAttrs bytes (leading 0xA2) so a verifier can re-tag to
1095
- // a universal SET and reproduce the MAC input (RFC 5652 sec. 9.2).
1096
788
  authAttrsBytes = m.fields.authAttrs.node.bytes;
1097
789
  }
1098
790
  var unauthAttrs = null;
@@ -1115,12 +807,6 @@ var AUTHENTICATED_DATA = schema.seq([
1115
807
  },
1116
808
  });
1117
809
 
1118
- // AuthEnvelopedData ::= SEQUENCE { version CMSVersion (0), originatorInfo [0]
1119
- // IMPLICIT OPTIONAL, recipientInfos RecipientInfos, authEncryptedContentInfo
1120
- // EncryptedContentInfo, authAttrs [1] IMPLICIT OPTIONAL, mac OCTET STRING,
1121
- // unauthAttrs [2] IMPLICIT OPTIONAL } (RFC 5083 sec. 2.1). The version is a fixed 0
1122
- // -- originatorInfo contents never change it. The authAttrs tags are SHIFTED
1123
- // ([1]/[2]) relative to AuthenticatedData's ([2]/[3]).
1124
810
  var AUTH_ENVELOPED_DATA = schema.seq([
1125
811
  schema.field("version", AUTHENV_VERSION),
1126
812
  schema.optional("originatorInfo", ORIGINATOR_INFO, { tag: 0 }),
@@ -1134,24 +820,14 @@ var AUTH_ENVELOPED_DATA = schema.seq([
1134
820
  build: function (m) {
1135
821
  var encryptedContentInfo = m.fields.authEncryptedContentInfo.value.result;
1136
822
  var mac = m.fields.mac.value;
1137
- // RFC 5084 -- a recognized AES-GCM/CCM content-encryption algorithm carries
1138
- // validated parameters, and its aes-ICVlen MUST equal the mac length.
1139
823
  var aead = _validateAeadParams(encryptedContentInfo.contentEncryptionAlgorithm, mac.length);
1140
824
 
1141
- // RFC 5083 sec. 2.1 -- authAttrs MUST be present when the content type carried in
1142
- // EncryptedContentInfo is not id-data.
1143
825
  var hasAuthAttrs = m.fields.authAttrs.present;
1144
826
  if (!hasAuthAttrs && encryptedContentInfo.contentType !== OID_DATA) {
1145
827
  throw NS.E("cms/missing-auth-attrs", "AuthEnvelopedData must carry authAttrs when the content type is not id-data (RFC 5083 sec. 2.1)");
1146
828
  }
1147
829
  var authAttrs = null, authAttrsBytes = null;
1148
830
  if (hasAuthAttrs) {
1149
- // RFC 5083 sec. 2.1 -- AuthAttributes MUST be DER even in a BER envelope (the
1150
- // sec. 2.2 AAD is the re-tagged DER SET). No content-type/message-digest
1151
- // presence rules bind here (sec. 11.1/sec. 11.2 name signed-data and
1152
- // authenticated-data; sec. 2.1 says message-digest SHOULD NOT appear), so only
1153
- // the duplicate rule, the sec. 11 placement rows, and, when a content-type
1154
- // attribute IS present -- the value == contentType coherence apply.
1155
831
  _assertDerEncodedAttrs(m.fields.authAttrs.node, "cms/bad-auth-attrs");
1156
832
  authAttrs = m.fields.authAttrs.value.items.map(function (it) { return it.value.result; });
1157
833
  _checkAttrPlacement(authAttrs, "auth");
@@ -1178,19 +854,6 @@ var AUTH_ENVELOPED_DATA = schema.seq([
1178
854
  },
1179
855
  });
1180
856
 
1181
- // ContentInfo ::= SEQUENCE { contentType OID, content [0] EXPLICIT ANY DEFINED BY
1182
- // contentType } (RFC 5652 sec. 3). The content is captured raw (explicit(0, any()))
1183
- // and re-dispatched by contentType inside the build: id-signedData walks
1184
- // SIGNED_DATA; id-envelopedData / id-encryptedData / id-ct-authData /
1185
- // id-ct-authEnvelopedData walk their schemas; the other PKCS#7 types are
1186
- // recognized-and-deferred; unknown OIDs are rejected.
1187
- // CompressedData ::= SEQUENCE { version CMSVersion (0), compressionAlgorithm
1188
- // CompressionAlgorithmIdentifier, encapContentInfo EncapsulatedContentInfo }
1189
- // (RFC 3274 sec. 1.1). The eContent is an RFC 1950 ZLIB stream of the inner
1190
- // content. Parse decodes the structure and surfaces the raw compressionAlgorithm
1191
- // + eContent; the zlib-only algorithm check + the bounded inflate live in the
1192
- // pki.cms.decompress consuming operation, not the parser (mirroring how the
1193
- // content-encryption algorithm is validated by decrypt, not the envelope parse).
1194
857
  var COMPRESSED_DATA = schema.seq([
1195
858
  schema.field("version", COMPRESSED_DATA_VERSION),
1196
859
  schema.field("compressionAlgorithm", ALGORITHM_IDENTIFIER),
@@ -1222,21 +885,12 @@ var CONTENT_INFO = schema.seq([
1222
885
  else if (ct === OID_COMPRESSED_DATA) inner = COMPRESSED_DATA;
1223
886
  if (inner === null) {
1224
887
  if (DEFERRED.has(ct)) {
1225
- // Coverage residual -- reached only when the OID is a registry entry (the guard above
1226
- // guarantees it), so oid.name() never returns undefined and this fallback is a
1227
- // defensive belt, not selectable through the public parse path.
1228
888
  throw NS.E("cms/unsupported-content-type", (ctx.oid.name(ct) || ct) + " is recognized but not parsed by this build");
1229
889
  }
1230
890
  throw NS.E("cms/unknown-content-type", "unrecognized ContentInfo content type " + ct);
1231
891
  }
1232
- // The dispatched ContentInfo type rides the result (mirroring the ocsp
1233
- // responseType/responseTypeName pair and the RecipientInfo `type` tags), so
1234
- // a consumer branches on contentTypeName instead of duck-typing the shape.
1235
892
  var result = schema.walk(inner, m.fields.content.value, ctx).result;
1236
893
  result.contentType = ct;
1237
- // Coverage residual -- reached only when the OID is a registry entry (the guard above
1238
- // guarantees it), so oid.name() never returns undefined and this fallback is a
1239
- // defensive belt, not selectable through the public parse path.
1240
894
  result.contentTypeName = ctx.oid.name(ct) || null;
1241
895
  return result;
1242
896
  },
@@ -1283,14 +937,6 @@ var CONTENT_INFO = schema.seq([
1283
937
  * cms.signerInfos[0].sid.serialNumberHex; // -> "0a1b"
1284
938
  * cms.encapContentInfo.eContent; // -> Buffer | null (detached)
1285
939
  */
1286
- // Recording, for the reason the certificate and CRL parsers are: a SignedData is one or more
1287
- // signatures over byte ranges, and a parsed one presents those ranges (`signedAttrsBytes`, the
1288
- // encapsulated `eContent`), the signatures, and the certificates that verify them as separate
1289
- // properties of one object. Keep a genuine signer's `signature` and `signedAttrsBytes` and replace
1290
- // the `eContent` beside them, and every part of the check still passes for content that signer never
1291
- // signed -- which is exactly the forgery pki.cms.verify's own block claims to defend. The verdict
1292
- // verbs re-derive from what is recorded here, so the object a caller passes names bytes rather than
1293
- // asserting facts.
1294
940
  var parse = pkix.makeRecordingParser({ pemLabel: "CMS", PemError: PemError, ErrorClass: CmsError, prefix: "cms", what: "CMS ContentInfo", topSchema: CONTENT_INFO, ns: NS }, "cms");
1295
941
 
1296
942
  /**
@@ -1334,11 +980,6 @@ function pemDecode(text, label) { return pkix.pemDecode(text, label || "CMS", Pe
1334
980
  */
1335
981
  function pemEncode(der, label) { return pkix.pemEncode(der, label || "CMS", PemError); }
1336
982
 
1337
- // A CMS ContentInfo root is the only registered structure whose root leads with an
1338
- // OBJECT IDENTIFIER child: a SEQUENCE of exactly 2 whose first child is an OID and
1339
- // second a context [0] constructed wrapper. x509/crl/csr lead with a tbs SEQUENCE
1340
- // and pkcs8 with an INTEGER, so the detectors are mutually exclusive regardless of
1341
- // registry order.
1342
983
  function matches(root) {
1343
984
  var TAGS = asn1.TAGS;
1344
985
  var k = pkix.rootSequenceChildren(root, 2, 2);
@@ -1348,41 +989,14 @@ function matches(root) {
1348
989
  return true;
1349
990
  }
1350
991
 
1351
- // Validate a bare EnvelopedData (RFC 5652 sec. 6.1) -- a universal SEQUENCE node --
1352
- // returning its structured value; throws a typed cms/* error on a malformed
1353
- // structure. Exposed for a composer that carries a bare EnvelopedData OUTSIDE a
1354
- // ContentInfo (an RFC 4211 CRMF encryptedKey [4] POPOPrivKey arm), which the
1355
- // content-type dispatch in `parse` cannot reach. The node MUST already be a
1356
- // universal SEQUENCE (an IMPLICIT [n] EnvelopedData is retagged by the caller).
1357
992
  function walkEnvelopedData(node) { return schema.walk(ENVELOPED_DATA, node, NS).result; }
1358
993
 
1359
- // Validate a bare SignedData / EncryptedData node the same way -- for a composer
1360
- // that holds an already-decoded content node and must not re-decode its bytes
1361
- // (an RFC 7292 PFX authSafe or encrypted safe, whose wire encoding may be BER
1362
- // that the strict `parse` entry would refuse). Same contract as
1363
- // walkEnvelopedData: the node is the bare structure, typed cms/* on rejection.
1364
- // Records provenance, like parse: the structure a consumer hands to pki.cms.verify must be
1365
- // re-derivable from the bytes it was walked from, and a PFX authSafe reaches verify this way. The
1366
- // walker's own BER-tolerant decode is what replays it -- the strict `parse` entry would refuse the
1367
- // indefinite-length encoding real stores carry.
1368
994
  var walkSignedData = guard.parsed.recordingWalker("cms", function (node) {
1369
995
  return schema.walk(SIGNED_DATA, node, NS).result;
1370
996
  }, function (der) { return asn1.decode(der, { ber: true }); });
1371
997
  function walkEncryptedData(node) { return schema.walk(ENCRYPTED_DATA, node, NS).result; }
1372
- // Validate + surface one countersignature value (RFC 5652 sec. 11.4, Countersignature ::=
1373
- // SignerInfo) into the same parsed shape parse() gives a top-level SignerInfo -- so pki.cms.verify
1374
- // can verify a countersignature through the ONE SignerInfo definition (its sec. 11.4 rules
1375
- // enforced: message-digest present, content-type FORBIDDEN via mode "countersig"). The node is the
1376
- // bare id-countersignature value; a malformed / content-type-carrying value throws a typed cms/*.
1377
998
  function walkCountersignature(node) { return schema.walk(COUNTERSIGNATURE_SIGNER_INFO, node, NS).result; }
1378
999
 
1379
- // assertAttachedCiphertext(eci, E, code, label): reject an EnvelopedData /
1380
- // EncryptedData encryptedContentInfo whose encryptedContent (the payload itself)
1381
- // is absent OR zero-length. RFC 5652 sec. 6.1 / RFC 5083 permit DETACHED content,
1382
- // so this is NOT enforced in the base walker -- it is a per-PROFILE opt-in for a
1383
- // consumer that requires the ciphertext to be present (an EST server-generated
1384
- // key, a PKCS#12 shrouded bag): a valid envelope that delivers NOTHING is
1385
- // verification-of-nothing (CWE-20). E is the caller's (code, message) factory.
1386
1000
  function assertAttachedCiphertext(eci, E, code, label) {
1387
1001
  if (!eci || eci.encryptedContent === null || eci.encryptedContent.length === 0) {
1388
1002
  throw E(code, (label || "encrypted content") + " must carry a non-empty attached ciphertext (RFC 5652 sec. 6.1), not be detached or empty");
@@ -1390,21 +1004,6 @@ function assertAttachedCiphertext(eci, E, code, label) {
1390
1004
  return eci;
1391
1005
  }
1392
1006
 
1393
- // The "certs-only" Simple PKI Response (RFC 5272 sec. 4.1): a degenerate SignedData carrying certificates (and
1394
- // optionally CRLs) but NO signed content -- id-data with no eContent and EMPTY signerInfos. It is how RFC 7030
1395
- // EST returns a CA chain (/cacerts) and how an RFC 5280 sec. 4.2.2.1 AIA caIssuers URL may serve issuers
1396
- // (application/pkcs7-mime). Returns { certificates: [raw DER], crls: [raw DER] } -- the RAW bytes only; the
1397
- // caller validates each cert/CRL for its own purpose (EST re-parses; path building runs each through validate).
1398
- // Shared so the EST client and the path builder decode an identical shape; the caller passes its typed-error
1399
- // factory `E(code, msg, cause)` and domain `prefix` so a fault surfaces the caller's own `prefix/*` code.
1400
- // `maxCerts` (optional) caps how many certificates / CRLs are validated + returned, BEFORE the per-element
1401
- // X.509 / CRL parse -- so an untrusted source (an AIA caIssuers URL) cannot force tens of thousands of parses
1402
- // with one in-cap-bytes bundle; the EST client passes no cap (a /cacerts chain is small and fully returned).
1403
- // `requireCert` (default true) sets the profile floor: the RFC 5272 sec. 4.1 Simple PKI Response the
1404
- // EST client and path builder read REQUIRES at least one certificate, so they pass nothing and get the
1405
- // strict floor. The general RFC 8551 sec. 3.8 / RFC 5652 sec. 5.1 certificate-management message
1406
- // permits "certificates and/or CRLs", so pki.cms.parseCertsOnly passes `requireCert: false` and a
1407
- // CRL-only message is accepted; a message with neither is still refused.
1408
1007
  function parseCertsOnly(der, E, prefix, maxCerts, requireCert) {
1409
1008
  var needCert = requireCert !== false;
1410
1009
  var r;
@@ -1415,29 +1014,20 @@ function parseCertsOnly(der, E, prefix, maxCerts, requireCert) {
1415
1014
  throw E(prefix + "/not-certs-only", "a certs-only Simple PKI Response must carry id-data with no eContent (RFC 5272 sec. 4.1)");
1416
1015
  }
1417
1016
  if (r.signerInfos.length !== 0) throw E(prefix + "/not-certs-only", "a certs-only Simple PKI Response must have empty signerInfos (RFC 5272 sec. 4.1)");
1418
- // digestAlgorithms is NOT required to be empty: RFC 5652 sec. 5.1 permits any number of elements
1419
- // (including algorithms no SignerInfo uses), so a valid EST /cacerts or AIA caIssuers bundle may
1420
- // carry a non-empty set. The builder emits an empty one (the canonical form), but the reader
1421
- // accepts a non-empty one rather than rejecting valid CMS.
1422
- var allCerts = r.certificates; // parse always surfaces certificates and crls as arrays (empty when the field is absent)
1017
+ var allCerts = r.certificates;
1423
1018
  var allCrls = r.crls;
1424
1019
  if (needCert) {
1425
1020
  if (allCerts.length === 0) throw E(prefix + "/no-certificates", "a certs-only response must contain at least one certificate (RFC 5272 sec. 4.1)");
1426
1021
  } else if (allCerts.length === 0 && allCrls.length === 0) {
1427
1022
  throw E(prefix + "/no-certificates", "a certs-only message must contain at least one certificate or CRL (RFC 8551 sec. 3.8)");
1428
1023
  }
1429
- // Cap BEFORE the per-element parse: bound parse work by count, not only by the decoded byte size. The
1430
- // cap is on the COMBINED number of certificates and CRLs, so a bundle carrying both cannot force up to
1431
- // 2*maxCerts parses: certificates are counted first and CRLs draw only from the remaining budget.
1432
1024
  var certs = (maxCerts != null && allCerts.length > maxCerts) ? allCerts.slice(0, maxCerts) : allCerts;
1433
1025
  for (var i = 0; i < certs.length; i++) {
1434
- // A universal-SEQUENCE CertificateChoice must be a well-formed X.509 Certificate, not merely any SEQUENCE;
1435
- // a tagged CertificateChoices alternative (attribute cert / other) is not a plain certificate.
1436
1026
  if (certs[i].tagClass !== "universal") throw E(prefix + "/bad-certificate-choice", "a certs-only response exchanges plain X.509 certificates; a tagged CertificateChoices alternative is not permitted (RFC 5272)");
1437
1027
  try { schemaX509.parse(certs[i].bytes); }
1438
1028
  catch (e) { throw E(prefix + "/bad-certificate", "a certs-only response carried a non-certificate in its certificates field (RFC 5272 sec. 4.1)", e); }
1439
1029
  }
1440
- var crlCap = maxCerts != null ? (maxCerts - certs.length) : null; // the budget certificates left for CRLs
1030
+ var crlCap = maxCerts != null ? (maxCerts - certs.length) : null;
1441
1031
  var crls = (crlCap != null && allCrls.length > crlCap) ? allCrls.slice(0, crlCap) : allCrls;
1442
1032
  for (var j = 0; j < crls.length; j++) {
1443
1033
  if (crls[j].tagClass !== "universal") throw E(prefix + "/bad-crl", "a certs-only response CRL must be a plain X.509 CertificateList, not a tagged otherRevInfo alternative (RFC 5652 sec. 10.2.1)");
@@ -1462,10 +1052,6 @@ module.exports = {
1462
1052
  walkCountersignature: walkCountersignature,
1463
1053
  looksLikeSignedAttributes: looksLikeSignedAttributes,
1464
1054
  assertAttachedCiphertext: assertAttachedCiphertext,
1465
- // The structure's own algorithm tables, exported (like the walk* helpers) as the single source
1466
- // of truth the crypto layer (cms-encrypt / cms-decrypt) shares -- so the wrap<->KEK-length, the
1467
- // ML-KEM ciphertext-length, and the AEAD-mode/ICV-length rules the parser enforces cannot drift
1468
- // from what the producer/consumer emit and accept.
1469
1055
  WRAP_KEK_LENGTHS: WRAP_KEK_LENGTHS,
1470
1056
  KEM_CT_LENGTHS: KEM_CT_LENGTHS,
1471
1057
  AEAD_ALGS: AEAD_ALGS,