@blamejs/pki 0.6.14 → 0.6.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -1
- package/lib/acme.js +59 -815
- package/lib/asn1-der.js +7 -245
- package/lib/attrcert-sign.js +12 -260
- package/lib/attrcert-verify.js +1 -7
- package/lib/byte-reader.js +0 -8
- package/lib/byte-writer.js +0 -11
- package/lib/cbor-det.js +0 -54
- package/lib/cmc-build.js +15 -357
- package/lib/cmc-verify.js +6 -239
- package/lib/cmp-build.js +41 -317
- package/lib/cmp-session.js +73 -760
- package/lib/cmp-verify.js +32 -488
- package/lib/cms-compress.js +3 -32
- package/lib/cms-decrypt.js +29 -294
- package/lib/cms-encrypt.js +14 -234
- package/lib/cms-sign.js +29 -324
- package/lib/cms-verify.js +9 -500
- package/lib/composite-kem.js +2 -102
- package/lib/composite-sig.js +1 -77
- package/lib/constants.js +0 -249
- package/lib/crl-sign.js +9 -237
- package/lib/crl-verify.js +1 -10
- package/lib/crmf-sign.js +19 -198
- package/lib/crmf-verify.js +1 -12
- package/lib/csr-sign.js +3 -34
- package/lib/csr-verify.js +1 -12
- package/lib/ct.js +28 -253
- package/lib/edwards-point.js +10 -44
- package/lib/est.js +28 -764
- package/lib/framework-error.js +4 -182
- package/lib/guard-all.js +1 -58
- package/lib/guard-async.js +1 -36
- package/lib/guard-bytes.js +6 -650
- package/lib/guard-compress.js +5 -102
- package/lib/guard-crypto.js +1 -40
- package/lib/guard-der.js +2 -93
- package/lib/guard-encoding.js +2 -60
- package/lib/guard-header.js +1 -36
- package/lib/guard-identifier.js +7 -531
- package/lib/guard-intrinsic.js +1 -150
- package/lib/guard-json.js +10 -73
- package/lib/guard-limits.js +1 -77
- package/lib/guard-list.js +1 -49
- package/lib/guard-name.js +3 -209
- package/lib/guard-parsed.js +7 -335
- package/lib/guard-range.js +1 -71
- package/lib/guard-secret.js +1 -102
- package/lib/guard-text.js +1 -64
- package/lib/guard-time.js +1 -97
- package/lib/hpke.js +3 -105
- package/lib/http-digest.js +14 -142
- package/lib/http-retry-after.js +1 -47
- package/lib/http-transport.js +21 -131
- package/lib/inspect.js +30 -284
- package/lib/ip-utils.js +6 -47
- package/lib/jose.js +7 -142
- package/lib/key.js +2 -67
- package/lib/lint.js +9 -194
- package/lib/merkle.js +1 -27
- package/lib/mime.js +9 -82
- package/lib/ocsp-request-verify.js +1 -14
- package/lib/ocsp-verify.js +1 -82
- package/lib/ocsp.js +11 -169
- package/lib/oid.js +5 -337
- package/lib/path-validate.js +103 -1492
- package/lib/pbes2.js +2 -89
- package/lib/pkcs12-build.js +31 -379
- package/lib/pki-build.js +64 -290
- package/lib/rc2.js +2 -18
- package/lib/rfc3339.js +4 -27
- package/lib/scep.js +8 -252
- package/lib/schema-all.js +0 -122
- package/lib/schema-attrcert.js +4 -146
- package/lib/schema-c509.js +153 -802
- package/lib/schema-cmc.js +5 -220
- package/lib/schema-cmp.js +2 -223
- package/lib/schema-cms.js +13 -427
- package/lib/schema-crl.js +5 -68
- package/lib/schema-crmf.js +6 -167
- package/lib/schema-csr.js +1 -54
- package/lib/schema-csrattrs.js +2 -87
- package/lib/schema-engine.js +0 -152
- package/lib/schema-ocsp.js +0 -117
- package/lib/schema-pkcs12.js +2 -93
- package/lib/schema-pkcs8.js +3 -35
- package/lib/schema-pkix.js +24 -655
- package/lib/schema-smime.js +0 -54
- package/lib/schema-tsp.js +3 -99
- package/lib/schema-x509.js +4 -63
- package/lib/shbs.js +11 -95
- package/lib/sign-scheme.js +2 -97
- package/lib/sigstore.js +9 -169
- package/lib/sleep.js +2 -8
- package/lib/smime.js +15 -408
- package/lib/tls-cert-compress.js +0 -62
- package/lib/trust.js +19 -185
- package/lib/tsp-sign.js +16 -270
- package/lib/validator-all.js +1 -30
- package/lib/validator-attcert.js +2 -46
- package/lib/validator-cose.js +2 -153
- package/lib/validator-keydesc.js +1 -37
- package/lib/validator-sig.js +1 -46
- package/lib/validator-tls.js +1 -88
- package/lib/validator-tpm.js +19 -122
- package/lib/verify-seam.js +1 -24
- package/lib/webauthn-mds.js +3 -400
- package/lib/webauthn.js +12 -833
- package/lib/webcrypto.js +5 -485
- package/lib/x509-sign.js +4 -108
- package/package.json +1 -1
- 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 });
|
|
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");
|
|
94
|
-
var OID_AUTH_ENVELOPED_DATA = oid.byName("authEnvelopedData");
|
|
95
|
-
var OID_COMPRESSED_DATA = oid.byName("compressedData");
|
|
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");
|
|
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;
|
|
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; }
|
|
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;
|
|
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;
|
|
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;
|
|
479
|
-
if (certificates.some(function (c) { return schema.isContext(c, 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
|
-
|
|
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;
|
|
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,
|