@blamejs/pki 0.8.3 → 0.8.5

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/lib/schema-cms.js CHANGED
@@ -127,7 +127,7 @@ function _checkAttrPlacement(attrs, place) {
127
127
  }
128
128
  }
129
129
 
130
- function _checkContentBindingAttrs(attrs, mode) {
130
+ function _checkContentBindingAttrs(attrs, mode, ctx) {
131
131
  var ct = 0, md = 0, seen = Object.create(null);
132
132
  for (var i = 0; i < attrs.length; i++) {
133
133
  var a = attrs[i];
@@ -141,16 +141,16 @@ function _checkContentBindingAttrs(attrs, mode) {
141
141
  ct += 1;
142
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)");
143
143
  if (a.values.length !== 1) throw NS.E("cms/bad-content-type-attr", "the content-type attribute must be single-valued");
144
- try { asn1.read.oid(asn1.decode(a.values[0])); }
144
+ try { asn1.read.oid(pkix.decodeNested(a.values[0], ctx, "a content-type attribute value")); }
145
145
  catch (e) { throw NS.E("cms/bad-content-type-attr", "the content-type attribute value must be a valid OBJECT IDENTIFIER", e); }
146
146
  } else if (a.type === OID_MESSAGE_DIGEST) {
147
147
  md += 1;
148
148
  if (a.values.length !== 1) throw NS.E("cms/bad-message-digest-attr", "the message-digest attribute must be single-valued");
149
- try { asn1.read.octetString(asn1.decode(a.values[0])); }
149
+ try { asn1.read.octetString(pkix.decodeNested(a.values[0], ctx, "a message-digest attribute value")); }
150
150
  catch (e) { throw NS.E("cms/bad-message-digest-attr", "the message-digest attribute value must be an OCTET STRING", e); }
151
151
  } else if (a.type === OID_SIGNING_TIME) {
152
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)");
153
- try { asn1.read.time(asn1.decode(a.values[0])); }
153
+ try { asn1.read.time(pkix.decodeNested(a.values[0], ctx, "a signing-time attribute value")); }
154
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); }
155
155
  }
156
156
  }
@@ -187,6 +187,7 @@ function _ascendingDer(nodes) {
187
187
  function looksLikeSignedAttributes(bytes) {
188
188
  if (!bytes || !bytes.length) return false;
189
189
  var node;
190
+ // allow:bare-decode a shape probe on bytes a caller hands the signer or verifier, outside any parse.
190
191
  try { node = asn1.decode(bytes); }
191
192
  catch (_e) { return false; }
192
193
  if (!schema.isUniversal(node, asn1.TAGS.SET) || !node.constructed) return false;
@@ -225,12 +226,12 @@ function looksLikeSignedAttributes(bytes) {
225
226
  return sawContentType && sawMessageDigest;
226
227
  }
227
228
 
228
- function _assertContentTypeMatchesAttrs(attrs, eContentType) {
229
+ function _assertContentTypeMatchesAttrs(attrs, eContentType, ctx) {
229
230
  for (var i = 0; i < attrs.length; i++) {
230
231
  if (attrs[i].type !== OID_CONTENT_TYPE) continue;
231
232
  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)");
232
233
  var ctv;
233
- try { ctv = asn1.read.oid(asn1.decode(attrs[i].values[0])); }
234
+ try { ctv = asn1.read.oid(pkix.decodeNested(attrs[i].values[0], ctx, "a content-type attribute value")); }
234
235
  catch (e) { throw NS.E("cms/bad-content-type-attr", "the content-type attribute value must be a valid OBJECT IDENTIFIER", e); }
235
236
  if (ctv !== eContentType) throw NS.E("cms/content-type-mismatch", "the content-type attribute (" + ctv + ") must equal the eContentType (" + eContentType + ") (RFC 5652 sec. 5.3)");
236
237
  }
@@ -244,13 +245,13 @@ function _checkNoDuplicateAttrs(attrs) {
244
245
  }
245
246
  }
246
247
 
247
- function _validateAeadParams(alg, macLen) {
248
+ function _validateAeadParams(alg, macLen, ctx) {
248
249
  var kind = AEAD_ALGS[alg.oid];
249
250
  if (!kind) return null;
250
251
  var K = kind.toUpperCase();
251
252
  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") + ")");
252
253
  var node;
253
- try { node = asn1.decode(alg.parameters); }
254
+ try { node = pkix.decodeNested(alg.parameters, ctx, "AES-" + K + " content-encryption parameters"); }
254
255
  catch (e) { throw NS.E("cms/bad-aead-params", "malformed AES-" + K + " parameters", e); }
255
256
  if (node.tagClass !== "universal" || node.tagNumber !== T.SEQUENCE || !node.children || node.children.length < 1 || node.children.length > 2) {
256
257
  throw NS.E("cms/bad-aead-params", "AES-" + K + " parameters must be a SEQUENCE { aes-nonce, aes-ICVlen DEFAULT 12 } (RFC 5084)");
@@ -275,8 +276,8 @@ function _validateAeadParams(alg, macLen) {
275
276
  return { kind: kind, nonce: nonce, icvLen: icvLen };
276
277
  }
277
278
 
278
- function _assertDerEncodedAttrs(node, code) {
279
- try { asn1.decode(node.bytes); }
279
+ function _assertDerEncodedAttrs(node, code, ctx) {
280
+ try { pkix.decodeNested(node.bytes, ctx, "an attribute set"); }
280
281
  catch (e) { throw NS.E(code, "the attribute set must be DER encoded even inside a BER envelope (RFC 5652 sec. 5.3)", e); }
281
282
  }
282
283
 
@@ -373,10 +374,10 @@ function makeSignerInfo(mode) {
373
374
  }
374
375
  var signedAttrs = null, signedAttrsBytes = null;
375
376
  if (m.fields.signedAttrs.present) {
376
- _assertDerEncodedAttrs(m.fields.signedAttrs.node, "cms/bad-signed-attrs");
377
+ _assertDerEncodedAttrs(m.fields.signedAttrs.node, "cms/bad-signed-attrs", ctx);
377
378
  signedAttrs = m.fields.signedAttrs.value.items.map(function (it) { return it.value.result; });
378
379
  _checkAttrPlacement(signedAttrs, "signed");
379
- _checkContentBindingAttrs(signedAttrs, mode);
380
+ _checkContentBindingAttrs(signedAttrs, mode, ctx);
380
381
  signedAttrsBytes = m.fields.signedAttrs.node.bytes;
381
382
  }
382
383
  var unsignedAttrs = null;
@@ -387,7 +388,7 @@ function makeSignerInfo(mode) {
387
388
  if (unsignedAttrs[u].type !== OID_COUNTERSIGNATURE) continue;
388
389
  for (var v = 0; v < unsignedAttrs[u].values.length; v++) {
389
390
  var csNode;
390
- try { csNode = asn1.decode(unsignedAttrs[u].values[v]); }
391
+ try { csNode = pkix.decodeNested(unsignedAttrs[u].values[v], ctx, "a countersignature attribute value"); }
391
392
  catch (e) { throw NS.E("cms/bad-countersignature", "a countersignature attribute value must be DER (RFC 5652 sec. 11.4)", e); }
392
393
  schema.walk(COUNTERSIGNATURE_SIGNER_INFO, csNode, ctx);
393
394
  }
@@ -418,7 +419,7 @@ var SIGNED_DATA = schema.seq([
418
419
  schema.field("signerInfos", schema.setOf(SIGNER_INFO, { min: 0, code: "cms/bad-signer-infos", what: "signerInfos" })),
419
420
  ], {
420
421
  assert: "sequence", code: "cms/bad-signed-data", what: "SignedData",
421
- build: function (m) {
422
+ build: function (m, ctx) {
422
423
  var version = m.fields.version.value;
423
424
  var encapContentInfo = m.fields.encapContentInfo.value.result;
424
425
  var certificates = m.fields.certificates.present ? m.fields.certificates.value.items.map(rawChoiceElement(CERT_CHOICE_TAGS, "cms/bad-certificates", "certificates")) : [];
@@ -432,7 +433,7 @@ var SIGNED_DATA = schema.seq([
432
433
  }
433
434
 
434
435
  for (var si = 0; si < signerInfos.length; si++) {
435
- if (signerInfos[si].signedAttrs) _assertContentTypeMatchesAttrs(signerInfos[si].signedAttrs, encapContentInfo.eContentType);
436
+ if (signerInfos[si].signedAttrs) _assertContentTypeMatchesAttrs(signerInfos[si].signedAttrs, encapContentInfo.eContentType, ctx);
436
437
  }
437
438
 
438
439
  var expected = _expectedSignedDataVersion(certificates, crls, signerInfos, encapContentInfo.eContentType);
@@ -771,7 +772,7 @@ var AUTHENTICATED_DATA = schema.seq([
771
772
  schema.optional("unauthAttrs", schema.implicitSetOf(3, ATTRIBUTE, { min: 1, code: "cms/bad-unauth-attrs", what: "unauthAttrs" }), { tag: 3 }),
772
773
  ], {
773
774
  assert: "sequence", code: "cms/bad-auth-data", what: "AuthenticatedData",
774
- build: function (m) {
775
+ build: function (m, ctx) {
775
776
  var version = m.fields.version.value;
776
777
  var originatorInfo = m.fields.originatorInfo.present ? m.fields.originatorInfo.value.result : null;
777
778
  var encapContentInfo = m.fields.encapContentInfo.value.result;
@@ -789,11 +790,11 @@ var AUTHENTICATED_DATA = schema.seq([
789
790
 
790
791
  var authAttrs = null, authAttrsBytes = null;
791
792
  if (hasAuthAttrs) {
792
- _assertDerEncodedAttrs(m.fields.authAttrs.node, "cms/bad-auth-attrs");
793
+ _assertDerEncodedAttrs(m.fields.authAttrs.node, "cms/bad-auth-attrs", ctx);
793
794
  authAttrs = m.fields.authAttrs.value.items.map(function (it) { return it.value.result; });
794
795
  _checkAttrPlacement(authAttrs, "auth");
795
- _checkContentBindingAttrs(authAttrs, "content");
796
- _assertContentTypeMatchesAttrs(authAttrs, encapContentInfo.eContentType);
796
+ _checkContentBindingAttrs(authAttrs, "content", ctx);
797
+ _assertContentTypeMatchesAttrs(authAttrs, encapContentInfo.eContentType, ctx);
797
798
  authAttrsBytes = m.fields.authAttrs.node.bytes;
798
799
  }
799
800
  var unauthAttrs = null;
@@ -826,10 +827,10 @@ var AUTH_ENVELOPED_DATA = schema.seq([
826
827
  schema.optional("unauthAttrs", schema.implicitSetOf(2, ATTRIBUTE, { min: 1, code: "cms/bad-unauth-attrs", what: "unauthAttrs" }), { tag: 2 }),
827
828
  ], {
828
829
  assert: "sequence", code: "cms/bad-auth-enveloped-data", what: "AuthEnvelopedData",
829
- build: function (m) {
830
+ build: function (m, ctx) {
830
831
  var encryptedContentInfo = m.fields.authEncryptedContentInfo.value.result;
831
832
  var mac = m.fields.mac.value;
832
- var aead = _validateAeadParams(encryptedContentInfo.contentEncryptionAlgorithm, mac.length);
833
+ var aead = _validateAeadParams(encryptedContentInfo.contentEncryptionAlgorithm, mac.length, ctx);
833
834
 
834
835
  var hasAuthAttrs = m.fields.authAttrs.present;
835
836
  if (!hasAuthAttrs && encryptedContentInfo.contentType !== OID_DATA) {
@@ -837,11 +838,11 @@ var AUTH_ENVELOPED_DATA = schema.seq([
837
838
  }
838
839
  var authAttrs = null, authAttrsBytes = null;
839
840
  if (hasAuthAttrs) {
840
- _assertDerEncodedAttrs(m.fields.authAttrs.node, "cms/bad-auth-attrs");
841
+ _assertDerEncodedAttrs(m.fields.authAttrs.node, "cms/bad-auth-attrs", ctx);
841
842
  authAttrs = m.fields.authAttrs.value.items.map(function (it) { return it.value.result; });
842
843
  _checkAttrPlacement(authAttrs, "auth");
843
844
  _checkNoDuplicateAttrs(authAttrs);
844
- _assertContentTypeMatchesAttrs(authAttrs, encryptedContentInfo.contentType);
845
+ _assertContentTypeMatchesAttrs(authAttrs, encryptedContentInfo.contentType, ctx);
845
846
  authAttrsBytes = m.fields.authAttrs.node.bytes;
846
847
  }
847
848
  var unauthAttrs = null;
@@ -1030,13 +1031,23 @@ function matches(root) {
1030
1031
  return true;
1031
1032
  }
1032
1033
 
1033
- function walkEnvelopedData(node) { return schema.walk(ENVELOPED_DATA, node, NS).result; }
1034
+ /** @internal `ctx` is the walk context of the parse that reached this structure, when another
1035
+ * format carries it. Only its caps and budget cross over: a cap the caller named at that
1036
+ * format's door has to reach this walk, and a fault in this structure is still CMS's to report. */
1037
+ function walkEnvelopedData(node, ctx) {
1038
+ return schema.walk(ENVELOPED_DATA, node, schema.withResources(NS, ctx)).result;
1039
+ }
1034
1040
 
1035
- var walkSignedData = guard.parsed.recordingWalker("cms", function (node) {
1036
- return schema.walk(SIGNED_DATA, node, NS).result;
1041
+ var walkSignedData = guard.parsed.recordingWalker("cms", function (node, ctx) {
1042
+ return schema.walk(SIGNED_DATA, node, schema.withResources(NS, ctx)).result;
1043
+ // allow:bare-decode the re-decode a recording walker performs on raw DER a caller hands it, outside any parse.
1037
1044
  }, function (der) { return asn1.decode(der, { ber: true }); });
1038
- function walkEncryptedData(node) { return schema.walk(ENCRYPTED_DATA, node, NS).result; }
1039
- function walkCountersignature(node) { return schema.walk(COUNTERSIGNATURE_SIGNER_INFO, node, NS).result; }
1045
+ function walkEncryptedData(node, ctx) {
1046
+ return schema.walk(ENCRYPTED_DATA, node, schema.withResources(NS, ctx)).result;
1047
+ }
1048
+ function walkCountersignature(node, ctx) {
1049
+ return schema.walk(COUNTERSIGNATURE_SIGNER_INFO, node, schema.withResources(NS, ctx)).result;
1050
+ }
1040
1051
 
1041
1052
  function assertAttachedCiphertext(eci, E, code, label) {
1042
1053
  if (!eci || eci.encryptedContent === null || eci.encryptedContent.length === 0) {
package/lib/schema-crl.js CHANGED
@@ -51,20 +51,20 @@ var TIME = pkix.time(NS);
51
51
 
52
52
  var CRL_VERSION = pkix.versionReader(NS, { "1": 2 });
53
53
 
54
- function decodeExt(ext, scope) {
54
+ function decodeExt(ext, scope, ctx) {
55
55
  var value = ext.value;
56
56
  var atCrl = scope !== "entry";
57
57
  var atEntry = scope !== "crl";
58
58
  try {
59
59
  if (ext.oid === OID_CRL_NUMBER && atCrl) {
60
- value = asn1.read.integer(asn1.decode(ext.value));
60
+ value = asn1.read.integer(pkix.decodeNested(ext.value, ctx, "a cRLNumber extension value"));
61
61
  if (value < 0n) throw new Error("cRLNumber must be non-negative (INTEGER 0..MAX)");
62
62
  } else if (ext.oid === OID_REASON_CODE && atEntry) {
63
- var reason = asn1.read.enumerated(asn1.decode(ext.value));
63
+ var reason = asn1.read.enumerated(pkix.decodeNested(ext.value, ctx, "a reasonCode extension value"));
64
64
  if (!_hasOwn(CRL_REASONS, reason.toString())) throw new Error("undefined CRLReason " + reason.toString());
65
65
  value = Number(reason);
66
66
  } else if (ext.oid === OID_INVALIDITY_DATE && atEntry) {
67
- var n = asn1.decode(ext.value);
67
+ var n = pkix.decodeNested(ext.value, ctx, "an invalidityDate extension value");
68
68
  if (n.tagClass !== "universal" || n.tagNumber !== TAGS.GENERALIZED_TIME) {
69
69
  throw new Error("invalidityDate must be a GeneralizedTime");
70
70
  }
@@ -82,13 +82,13 @@ var REVOKED_ENTRY = schema.seq([
82
82
  schema.optional("crlEntryExtensions", EXTENSIONS, { whenUniversal: [TAGS.SEQUENCE] }),
83
83
  ], {
84
84
  assert: "sequence", arity: { min: 2 }, code: "crl/bad-revoked-entry", what: "RevokedCertificate",
85
- build: function (m) {
85
+ build: function (m, ctx) {
86
86
  return {
87
87
  serialNumber: m.fields.userCertificate.value,
88
88
  serialNumberHex: m.fields.userCertificate.node.content.toString("hex"),
89
89
  revocationDate: m.fields.revocationDate.value,
90
90
  crlEntryExtensions: m.fields.crlEntryExtensions.present
91
- ? m.fields.crlEntryExtensions.value.result.map(function (e) { return decodeExt(e, "entry"); })
91
+ ? m.fields.crlEntryExtensions.value.result.map(function (e) { return decodeExt(e, "entry", ctx); })
92
92
  : [],
93
93
  };
94
94
  },
@@ -111,7 +111,7 @@ var TBS_CERTLIST = schema.seq([
111
111
  { minTag: 0, maxTag: 0, unexpectedCode: "crl/bad-tbs", orderCode: "crl/bad-tbs" }),
112
112
  ], {
113
113
  assert: "sequence", code: "crl/bad-tbs", what: "tbsCertList",
114
- build: function (m) {
114
+ build: function (m, ctx) {
115
115
  return {
116
116
  version: m.fields.version.present ? m.fields.version.value : 1,
117
117
  issuer: m.fields.issuer.value.result,
@@ -119,7 +119,7 @@ var TBS_CERTLIST = schema.seq([
119
119
  nextUpdate: m.fields.nextUpdate.present ? m.fields.nextUpdate.value : null,
120
120
  revokedCertificates: m.fields.revokedCertificates.present ? m.fields.revokedCertificates.value.result : [],
121
121
  crlExtensions: m.fields.crlExtensions.present
122
- ? m.fields.crlExtensions.value.result.map(function (e) { return decodeExt(e, "crl"); })
122
+ ? m.fields.crlExtensions.value.result.map(function (e) { return decodeExt(e, "crl", ctx); })
123
123
  : [],
124
124
  crlExtensionsPresent: m.fields.crlExtensions.present,
125
125
  };
@@ -265,10 +265,10 @@ function entryExtensionCriticality(oidRegistry) {
265
265
  * raw. Returns the record with its decoded `value`; throws `crl/bad-extension-value`. */
266
266
  var OID_CERTIFICATE_ISSUER = oid.byName("certificateIssuer");
267
267
  var CERTIFICATE_ISSUER_NAMES = pkix.generalNames(NS, { code: "crl/bad-extension-value" });
268
- function readEntryExtension(ext) {
269
- if (ext.oid !== OID_CERTIFICATE_ISSUER) return decodeExt(ext);
268
+ function readEntryExtension(ext, ctx) {
269
+ if (ext.oid !== OID_CERTIFICATE_ISSUER) return decodeExt(ext, undefined, ctx);
270
270
  var names;
271
- try { names = schema.walk(CERTIFICATE_ISSUER_NAMES, asn1.decode(ext.value), NS).result; }
271
+ try { names = schema.walk(CERTIFICATE_ISSUER_NAMES, pkix.decodeNested(ext.value, ctx, "a certificateIssuer extension value"), ctx || NS).result; }
272
272
  catch (e) {
273
273
  throw NS.E("crl/bad-extension-value", "malformed certificateIssuer extension value: " + ((e && e.message) || intrinsic.String(e)), e);
274
274
  }
@@ -170,12 +170,12 @@ function popoPrivKey(type) {
170
170
  }
171
171
  }
172
172
  if (inner.tagNumber === 3) {
173
- try { schema.embeddedDer(PKMAC_VALUE, asn1.sequenceTlv(inner), NS, { code: "crmf/bad-popo", what: "agreeMAC [3] PKMACValue" }); }
173
+ try { schema.embeddedDer(PKMAC_VALUE, asn1.sequenceTlv(inner), schema.withResources(NS, ctx), { code: "crmf/bad-popo", what: "agreeMAC [3] PKMACValue" }); }
174
174
  catch (e) { throw ctx.E("crmf/bad-popo", "agreeMAC [3] must be a PKMACValue SEQUENCE { algId, BIT STRING } (RFC 4211 sec. 4.2)", e); }
175
175
  }
176
176
  if (inner.tagNumber === 4) {
177
177
  var env;
178
- try { env = cms.walkEnvelopedData(asn1.decode(asn1.sequenceTlv(inner))); }
178
+ try { env = cms.walkEnvelopedData(pkix.decodeNested(asn1.sequenceTlv(inner), ctx, "an encryptedKey [4] EnvelopedData"), ctx); }
179
179
  catch (e) { throw ctx.E("crmf/bad-popo", "encryptedKey [4] must be a well-formed EnvelopedData (RFC 4211 sec. 4.2, RFC 5652 sec. 6.1)", e); }
180
180
  if (env.encryptedContentInfo.contentType !== oid.byName("encKeyWithID")) {
181
181
  throw ctx.E("crmf/bad-popo", "encryptedKey [4] EnvelopedData content type MUST be id-ct-encKeyWithID (RFC 4211 sec. 4.2)");
@@ -414,16 +414,21 @@ function matches(root) {
414
414
  schema.isUniversal(tpl, TAGS.SEQUENCE) && !!tpl.children;
415
415
  }
416
416
 
417
- function walkCertReqMessages(node, opts) {
418
- var ctx = NS;
417
+ /** @internal `outer` is the walk context of the parse that reached these messages, when another
418
+ * format carries them. Only its caps and budget cross over: the messages are still read in this
419
+ * namespace, so a fault in one keeps the CRMF error class and code. */
420
+ function walkCertReqMessages(node, opts, outer) {
421
+ var ctx = schema.withResources(NS, outer);
419
422
  if (opts && (opts.allowSigningAlg || opts.allowV1Version)) {
420
- ctx = Object.assign({}, NS);
423
+ ctx = Object.assign({}, ctx);
421
424
  if (opts.allowSigningAlg) ctx.allowSigningAlg = true;
422
425
  if (opts.allowV1Version) ctx.allowV1Version = true;
423
426
  }
424
427
  return schema.walk(CERT_REQ_MESSAGES, node, ctx).result;
425
428
  }
426
- function walkCertTemplate(node) { return schema.walk(CERT_TEMPLATE, node, NS).result; }
429
+ function walkCertTemplate(node, outer) {
430
+ return schema.walk(CERT_TEMPLATE, node, schema.withResources(NS, outer)).result;
431
+ }
427
432
 
428
433
  module.exports = {
429
434
  parse: parse,
@@ -158,12 +158,13 @@ var CERT_REQ_INFO_TEMPLATE = schema.seq([
158
158
  hasExtReq = true;
159
159
  extReqs += 1;
160
160
  if (a.values.length !== 1) throw NS.E("csrattrs/bad-template-attrs", "a template id-ExtensionReq values must be a SET of exactly one Extensions (RFC 9908 sec. 3.2)");
161
- extensions = schema.walk(EXTENSIONS, asn1.decode(a.values[0]), ctx).result;
161
+ extensions = schema.walk(EXTENSIONS, pkix.decodeNested(a.values[0], ctx, "a template id-ExtensionReq value"), ctx).result;
162
162
  }
163
163
  if (a.type === OID_EXT_REQ_TEMPLATE) {
164
164
  extReqTemplates += 1;
165
165
  if (a.values.length !== 1) throw NS.E("csrattrs/bad-template-attrs", "an id-aa-extensionReqTemplate values must be a SET of exactly one ExtensionReqTemplate (RFC 9908 sec. 3.4)");
166
- extensionTemplates = extensionTemplates.concat(schema.walk(EXTENSION_REQ_TEMPLATE, asn1.decode(a.values[0]), ctx).result);
166
+ extensionTemplates = extensionTemplates.concat(schema.walk(EXTENSION_REQ_TEMPLATE,
167
+ pkix.decodeNested(a.values[0], ctx, "an id-aa-extensionReqTemplate value"), ctx).result);
167
168
  }
168
169
  }
169
170
  if (extReqs > 1) throw NS.E("csrattrs/bad-template-attrs", "a template must not carry more than one id-ExtensionReq (RFC 9908 sec. 3.2)");
@@ -185,25 +186,26 @@ function _enrichAttribute(item, ctx) {
185
186
  if (KEY_TYPE_OIDS.has(item.oid)) item.isKeyType = true;
186
187
  if (item.oid === OID_EXTENSION_REQUEST) {
187
188
  if (item.values.length !== 1) throw ctx.E("csrattrs/bad-extension-req", "an id-ExtensionReq attribute values must be a SET of exactly one Extensions (RFC 9908 sec. 3.2)");
188
- item.extensions = schema.walk(EXTENSIONS, asn1.decode(item.values[0]), ctx).result;
189
+ item.extensions = schema.walk(EXTENSIONS, pkix.decodeNested(item.values[0], ctx, "an id-ExtensionReq value"), ctx).result;
189
190
  } else if (item.oid === OID_EC_PUBLIC_KEY) {
190
191
  if (item.values.length > 1) throw ctx.E("csrattrs/bad-key-type-attr", "an ecPublicKey attribute values must be empty or a SET of exactly one named-curve OBJECT IDENTIFIER (RFC 9908 sec. 3.2)");
191
192
  if (item.values.length === 1) {
192
- try { item.curve = asn1.read.oid(asn1.decode(item.values[0])); }
193
+ try { item.curve = asn1.read.oid(pkix.decodeNested(item.values[0], ctx, "an ecPublicKey attribute value")); }
193
194
  catch (e) { throw ctx.E("csrattrs/bad-key-type-attr", "an ecPublicKey attribute value must be a named-curve OBJECT IDENTIFIER (RFC 9908 sec. 3.2)", e); }
194
195
  }
195
196
  } else if (item.oid === OID_RSA_ENCRYPTION) {
196
197
  if (item.values.length > 1) throw ctx.E("csrattrs/bad-key-type-attr", "an rsaEncryption attribute values must be empty or a SET of exactly one INTEGER key size (RFC 9908 sec. 3.2)");
197
198
  if (item.values.length === 1) {
198
199
  var sz;
199
- try { sz = asn1.read.integer(asn1.decode(item.values[0])); }
200
+ try { sz = asn1.read.integer(pkix.decodeNested(item.values[0], ctx, "an rsaEncryption attribute value")); }
200
201
  catch (e) { throw ctx.E("csrattrs/bad-key-type-attr", "an rsaEncryption attribute value must be an INTEGER key size (RFC 9908 sec. 3.2)", e); }
201
202
  if (sz < 1n || sz > 65536n) throw ctx.E("csrattrs/bad-key-type-attr", "the rsaEncryption key size " + sz + " is out of range (1..65536 bits)");
202
203
  item.keySize = Number(sz);
203
204
  }
204
205
  } else if (item.oid === OID_TEMPLATE) {
205
206
  if (item.values.length !== 1) throw ctx.E("csrattrs/bad-template", "a certificationRequestInfoTemplate attribute values must be a SET of exactly one template (RFC 9908 sec. 3.4)");
206
- item.template = schema.walk(CERT_REQ_INFO_TEMPLATE, asn1.decode(item.values[0]), ctx).result;
207
+ item.template = schema.walk(CERT_REQ_INFO_TEMPLATE,
208
+ pkix.decodeNested(item.values[0], ctx, "a certificationRequestInfoTemplate value"), ctx).result;
207
209
  }
208
210
  }
209
211
 
@@ -75,6 +75,66 @@ function _assertArity(schema, kids, ctx) {
75
75
  }
76
76
 
77
77
 
78
+ var RESOURCE_REFUSALS = Object.assign(Object.create(null), {
79
+ "asn1/too-large": 1, "asn1/too-deep": 1, "asn1/too-many-items": 1,
80
+ });
81
+
82
+ /** @internal The codec's resource refusals, as distinct from its malformed-input refusals. A
83
+ * caller who tightened a cap has to be able to tell "your bytes are wrong" from "your cap is
84
+ * too small", and only the second is answered by raising the cap. */
85
+ function isResourceRefusal(e) {
86
+ return !!(e && typeof e.code === "string" && RESOURCE_REFUSALS[e.code] === 1);
87
+ }
88
+
89
+ /** @internal Carries a parse's caps and budget onto another namespace. A format whose structure
90
+ * is carried inside another format walks it in its OWN namespace, so a refusal names the domain
91
+ * that owns the fault and keeps its own error class; the caps and the budget are the only things
92
+ * that cross the boundary. Returns `ns` unchanged when there is nothing to carry. */
93
+ function withResources(ns, source) {
94
+ if (!source || (source.caps == null && source.budget == null)) return ns;
95
+ var out = Object.create(null);
96
+ var keys = Object.keys(ns);
97
+ for (var i = 0; i < keys.length; i++) out[keys[i]] = ns[keys[i]];
98
+ out.caps = source.caps;
99
+ out.budget = source.budget;
100
+ return out;
101
+ }
102
+
103
+ /** @internal The decode a parse performs on a value it has already read out of its input: an
104
+ * embedded OCTET STRING payload, an encapsulated protocol body. `ctx` is the walk context the
105
+ * door built, so the caps a caller named at the door hold at every level rather than at the
106
+ * root alone, and a resource refusal down here trips the parse budget before the original
107
+ * error is re-thrown. The error itself is re-thrown unchanged, because a format's existing
108
+ * code for a malformed embedded value is one callers pin.
109
+ *
110
+ * `opts.ber` is read once into a local and carries the relaxed acceptance a format whose door
111
+ * is BER-tolerant already applies at the root. The decode options are built fresh per call. */
112
+ function decodeNested(bytes, ctx, label, opts) {
113
+ var caps = ctx ? ctx.caps : null;
114
+ var ber = !!(opts && opts.ber === true);
115
+ var decodeOpts = Object.create(null);
116
+ if (caps) {
117
+ decodeOpts.maxBytes = caps.maxBytes;
118
+ decodeOpts.maxDepth = caps.maxDepth;
119
+ decodeOpts.maxItems = caps.maxItems;
120
+ }
121
+ if (ber) decodeOpts.ber = true;
122
+ try {
123
+ // allow:bare-decode this IS the primitive every other nested decode routes through.
124
+ return asn1.decode(bytes, decodeOpts);
125
+ } catch (e) {
126
+ if (isResourceRefusal(e) && ctx && ctx.budget) {
127
+ try {
128
+ ctx.budget.trip(label || "an embedded value");
129
+ } catch (_b) {
130
+ /* allow:swallow-unverified the latch is the point; the original error is re-thrown below */
131
+ }
132
+ }
133
+ throw e;
134
+ }
135
+ }
136
+
137
+ // allow:bare-decode a round trip through the codec on an integer this module just encoded.
78
138
  function _implicitIntContent(v) { return asn1.decode(asn1.build.integer(v)).content; }
79
139
  function oidLeaf() { return { kind: "leaf", read: asn1.read.oid, write: function (v) { return asn1.build.oid(v); } }; }
80
140
  function integerLeaf() { return { kind: "leaf", read: asn1.read.integer, write: function (v) { return asn1.build.integer(v); } }; }
@@ -436,6 +496,7 @@ function _encodeRepeat(schema, items, ctx) {
436
496
  if (schema.unique) {
437
497
  var seen = new Set();
438
498
  parts.forEach(function (tlv) {
499
+ // allow:bare-decode a round trip through the codec on a TLV this encoder just produced.
439
500
  var item = { node: asn1.decode(tlv) };
440
501
  item.value = walk(schema.item, item.node, ctx);
441
502
  var key = schema.unique(item);
@@ -500,7 +561,7 @@ function embeddedDer(schema, bytes, ctx, opts) {
500
561
  }
501
562
  var node;
502
563
  try {
503
- node = asn1.decode(bytes, opts.ber ? { ber: true } : undefined);
564
+ node = decodeNested(bytes, ctx, opts.what || "embedded DER", { ber: !!opts.ber });
504
565
  } catch (e) {
505
566
  throw ctx.E(opts.code, (opts.what || "embedded DER") + " did not decode: " + e.message, e);
506
567
  }
@@ -540,7 +601,9 @@ module.exports = {
540
601
  bitString: bitString, implicitBitString: implicitBitString, implicitOctetString: implicitOctetString,
541
602
  implicitNull: implicitNull, implicitInteger: implicitInteger, implicitBoolean: implicitBoolean,
542
603
  any: any, decode: decode, time: time,
543
- walk: walk, encode: encode, embeddedDer: embeddedDer, assertMinimalNamedBits: assertMinimalNamedBits,
604
+ walk: walk, encode: encode, embeddedDer: embeddedDer, decodeNested: decodeNested,
605
+ isResourceRefusal: isResourceRefusal, withResources: withResources,
606
+ assertMinimalNamedBits: assertMinimalNamedBits,
544
607
  isUniversal: isUniversal, isContext: isContext,
545
608
  isUniversalOneOf: isUniversalOneOf, isContextOneOf: isContextOneOf, isContextInRange: isContextInRange,
546
609
  };
@@ -98,9 +98,9 @@ function _rawSignature(field) {
98
98
  var CERTS = schema.seqOf(certificateBytes(), { assert: "sequence", code: "ocsp/bad-certs", what: "certs", max: constants.LIMITS.OCSP_MAX_CERTS, maxCode: "ocsp/too-many-certs" });
99
99
 
100
100
  /** @internal RFC 6960 sec. 4.4.4: ArchiveCutoff ::= GeneralizedTime, carried as a singleExtension. */
101
- function _readArchiveCutoff(ext) {
101
+ function _readArchiveCutoff(ext, ctx) {
102
102
  var node;
103
- try { node = asn1.decode(ext.value); }
103
+ try { node = pkix.decodeNested(ext.value, ctx, "an archiveCutoff extension value"); }
104
104
  catch (e) { throw NS.E("ocsp/bad-archive-cutoff", "the archiveCutoff extension value did not decode: " + ((e && e.message) || _String(e)), e); }
105
105
  if (!(node.tagClass === "universal" && node.tagNumber === asn1.TAGS.GENERALIZED_TIME)) {
106
106
  throw NS.E("ocsp/bad-archive-cutoff", "the archiveCutoff extension value must be a GeneralizedTime (RFC 6960 sec. 4.4.4)");
@@ -112,10 +112,10 @@ function _readArchiveCutoff(ext) {
112
112
  /** @internal RFC 6960 sec. 4.4.2: CrlID ::= SEQUENCE { crlUrl [0] EXPLICIT IA5String OPTIONAL,
113
113
  * crlNum [1] EXPLICIT INTEGER OPTIONAL, crlTime [2] EXPLICIT GeneralizedTime OPTIONAL }. Every
114
114
  * field is optional and each may appear at most once, in tag order. */
115
- function _readCrlId(ext) {
115
+ function _readCrlId(ext, ctx) {
116
116
  var C = "ocsp/bad-crl-id";
117
117
  var node;
118
- try { node = asn1.decode(ext.value); }
118
+ try { node = pkix.decodeNested(ext.value, ctx, "a crlID extension value"); }
119
119
  catch (e) { throw NS.E(C, "the CRL references extension value did not decode: " + ((e && e.message) || _String(e)), e); }
120
120
  if (!(node.tagClass === "universal" && node.tagNumber === asn1.TAGS.SEQUENCE && node.children)) {
121
121
  throw NS.E(C, "CrlID must be a SEQUENCE (RFC 6960 sec. 4.4.2)");
@@ -154,15 +154,15 @@ function _readCrlId(ext) {
154
154
  /** @internal The extensions this profile reads are decoded onto their entry beside the raw value,
155
155
  * so a consumer gets the value by name and a malformed one is refused here rather than passed
156
156
  * through as opaque bytes. The nonce is RFC 9654; the other two are RFC 6960 sec. 4.4. */
157
- function _validateOcspExtensions(list) {
157
+ function _validateOcspExtensions(list, ctx) {
158
158
  if (!list) return list;
159
159
  for (var i = 0; i < list.length; i++) {
160
160
  var ext = list[i];
161
- if (ext.oid === OID_OCSP_ARCHIVE_CUTOFF) { ext.archiveCutoff = _readArchiveCutoff(ext); continue; }
162
- if (ext.oid === OID_OCSP_CRL) { ext.crlReferences = _readCrlId(ext); continue; }
161
+ if (ext.oid === OID_OCSP_ARCHIVE_CUTOFF) { ext.archiveCutoff = _readArchiveCutoff(ext, ctx); continue; }
162
+ if (ext.oid === OID_OCSP_CRL) { ext.crlReferences = _readCrlId(ext, ctx); continue; }
163
163
  if (ext.oid !== OID_OCSP_NONCE) continue;
164
164
  var node;
165
- try { node = asn1.decode(ext.value); }
165
+ try { node = pkix.decodeNested(ext.value, ctx, "a nonce extension value"); }
166
166
  catch (e) { throw NS.E("ocsp/bad-nonce", "the nonce extension value did not decode: " + ((e && e.message) || String(e)), e); }
167
167
  if (!(node.tagClass === "universal" && node.tagNumber === asn1.TAGS.OCTET_STRING && node.content)) {
168
168
  throw NS.E("ocsp/bad-nonce", "the nonce extension value must be an OCTET STRING (RFC 9654 sec. 2.1)");
@@ -244,13 +244,13 @@ var SINGLE_RESPONSE = schema.seq([
244
244
  ], { minTag: 0, maxTag: 1, unexpectedCode: "ocsp/bad-single-response", orderCode: "ocsp/bad-single-response" }),
245
245
  ], {
246
246
  assert: "sequence", code: "ocsp/bad-single-response", what: "SingleResponse",
247
- build: function (m) {
247
+ build: function (m, ctx) {
248
248
  return {
249
249
  certID: m.fields.certID.value.result,
250
250
  certStatus: _shapeCertStatus(m.fields.certStatus),
251
251
  thisUpdate: m.fields.thisUpdate.value,
252
252
  nextUpdate: m.fields.nextUpdate.present ? m.fields.nextUpdate.value : null,
253
- singleExtensions: m.fields.singleExtensions.present ? _validateOcspExtensions(m.fields.singleExtensions.value.result) : null,
253
+ singleExtensions: m.fields.singleExtensions.present ? _validateOcspExtensions(m.fields.singleExtensions.value.result, ctx) : null,
254
254
  };
255
255
  },
256
256
  });
@@ -277,13 +277,13 @@ var RESPONSE_DATA = schema.seq([
277
277
  ], { minTag: 1, maxTag: 1, unexpectedCode: "ocsp/bad-response-data", orderCode: "ocsp/bad-response-data" }),
278
278
  ], {
279
279
  assert: "sequence", code: "ocsp/bad-response-data", what: "ResponseData",
280
- build: function (m) {
280
+ build: function (m, ctx) {
281
281
  return {
282
282
  version: m.fields.version.value,
283
283
  responderID: _shapeResponderID(m.fields.responderID),
284
284
  producedAt: m.fields.producedAt.value,
285
285
  responses: m.fields.responses.value.items.map(function (it) { return it.value.result; }),
286
- responseExtensions: m.fields.responseExtensions.present ? _validateOcspExtensions(m.fields.responseExtensions.value.result) : null,
286
+ responseExtensions: m.fields.responseExtensions.present ? _validateOcspExtensions(m.fields.responseExtensions.value.result, ctx) : null,
287
287
  };
288
288
  },
289
289
  });
@@ -362,10 +362,10 @@ var REQUEST = schema.seq([
362
362
  ], { minTag: 0, maxTag: 0, unexpectedCode: "ocsp/bad-request", orderCode: "ocsp/bad-request" }),
363
363
  ], {
364
364
  assert: "sequence", code: "ocsp/bad-request", what: "Request",
365
- build: function (m) {
365
+ build: function (m, ctx) {
366
366
  return {
367
367
  certID: m.fields.reqCert.value.result,
368
- singleRequestExtensions: m.fields.singleRequestExtensions.present ? _validateOcspExtensions(m.fields.singleRequestExtensions.value.result) : null,
368
+ singleRequestExtensions: m.fields.singleRequestExtensions.present ? _validateOcspExtensions(m.fields.singleRequestExtensions.value.result, ctx) : null,
369
369
  };
370
370
  },
371
371
  });
@@ -396,13 +396,13 @@ var TBS_REQUEST = schema.seq([
396
396
  ], { minTag: 2, maxTag: 2, unexpectedCode: "ocsp/bad-tbs-request", orderCode: "ocsp/bad-tbs-request" }),
397
397
  ], {
398
398
  assert: "sequence", code: "ocsp/bad-tbs-request", what: "TBSRequest",
399
- build: function (m) {
399
+ build: function (m, ctx) {
400
400
  var rn = m.fields.requestorName;
401
401
  return {
402
402
  version: m.fields.version.value,
403
403
  requestorName: rn.present ? { bytes: rn.value.bytes, tagClass: rn.value.tagClass, tagNumber: rn.value.tagNumber, value: rn.value.value } : null,
404
404
  requestList: m.fields.requestList.value.items.map(function (it) { return it.value.result; }),
405
- requestExtensions: m.fields.requestExtensions.present ? _validateOcspExtensions(m.fields.requestExtensions.value.result) : null,
405
+ requestExtensions: m.fields.requestExtensions.present ? _validateOcspExtensions(m.fields.requestExtensions.value.result, ctx) : null,
406
406
  };
407
407
  },
408
408
  });
@@ -592,6 +592,7 @@ module.exports = {
592
592
  * reads. Throws the parser's own ocsp/* code for a malformed entry. */
593
593
  readSingleExtensionsDer: function (derList) {
594
594
  var node;
595
+ // allow:bare-decode reached from the signer, not from a parse, so no parse context carries caps here.
595
596
  try { node = asn1.decode(asn1.build.sequence(intrinsic.map(derList, function (d) { return asn1.build.raw(d); }))); }
596
597
  catch (e) { throw NS.E("ocsp/bad-single-response", "a pre-encoded singleExtension is not DER", e); }
597
598
  return _validateOcspExtensions(schema.walk(EXTENSIONS, node, NS).result);
@@ -188,7 +188,8 @@ var SAFE_BAG = schema.seq([
188
188
  }
189
189
  if (a.type === OID_LOCAL_KEY_ID) {
190
190
  if (seen[a.type] || a.values.length !== 1) throw ctx.E("pkcs12/bad-local-key-id", "localKeyId carries exactly one OCTET STRING value (PKCS#9 SINGLE VALUE)");
191
- localKeyId = _octetContent(asn1.decode(a.values[0], { ber: true }), ctx, "a localKeyId value", "pkcs12/bad-local-key-id");
191
+ localKeyId = _octetContent(pkix.decodeNested(a.values[0], ctx, "a localKeyId value", { ber: true }), ctx,
192
+ "a localKeyId value", "pkcs12/bad-local-key-id");
192
193
  }
193
194
  seen[a.type] = true;
194
195
  }
@@ -255,7 +256,7 @@ var PFX = schema.seq([
255
256
  }
256
257
  if (authSafe.contentType === OID_SIGNED_DATA) {
257
258
  if (macPresent) throw ctx.E("pkcs12/bad-integrity-mode", "an id-signedData authSafe (public-key integrity) must omit macData (RFC 7292 sec. 4)");
258
- var signed = cms.walkSignedData(authSafe.innerNode);
259
+ var signed = cms.walkSignedData(authSafe.innerNode, ctx);
259
260
  if (signed.encapContentInfo.eContentType !== OID_DATA) {
260
261
  throw ctx.E("pkcs12/bad-authsafe", "a public-key-integrity authSafe must encapsulate id-data carrying the AuthenticatedSafe (RFC 7292 sec. 4.1)");
261
262
  }
@@ -296,7 +297,7 @@ function _privacySafe(content, ctx) {
296
297
  }
297
298
 
298
299
  function _bmpValue(bytes, ctx) {
299
- var node = asn1.decode(bytes, { ber: true });
300
+ var node = pkix.decodeNested(bytes, ctx, "a friendlyName value", { ber: true });
300
301
  if (!(node.tagClass === "universal" && node.tagNumber === TAGS.BMP_STRING)) {
301
302
  throw ctx.E("pkcs12/bad-friendly-name", "a friendlyName value must be a BMPString (RFC 7292 sec. 4.2)");
302
303
  }
@@ -310,12 +311,15 @@ function _walkAuthenticatedSafe(bytes, state, ctx) {
310
311
  });
311
312
  }
312
313
 
313
- // @internal
314
- function walkSafeContents(der) {
315
- var walked = schema.walk(SAFE_CONTENTS, asn1.decode(der, { ber: true }), NS);
314
+ /** @internal Reads a decrypted SafeContents. `ctx` is the parse context of the door that produced
315
+ * the bytes when there is one, so its caps and budget reach the bags; the decrypt path in
316
+ * pki.pkcs12.open has none and takes the built-in ceilings. */
317
+ function walkSafeContents(der, ctx) {
318
+ var ns = schema.withResources(NS, ctx);
319
+ var walked = schema.walk(SAFE_CONTENTS, pkix.decodeNested(der, ns, "a SafeContents", { ber: true }), ns);
316
320
  var state = { budget: { remaining: C.LIMITS.PKCS12_MAX_REDECODES }, bagDepth: 0 };
317
321
  var bags = [];
318
- for (var i = 0; i < walked.items.length; i++) bags.push(_buildBag(walked.items[i].value.result, state, NS));
322
+ for (var i = 0; i < walked.items.length; i++) bags.push(_buildBag(walked.items[i].value.result, state, ns));
319
323
  return bags;
320
324
  }
321
325
 
@@ -333,9 +337,9 @@ function _dispatchSafes(asMatch, state, ctx) {
333
337
  safeBags.push(_buildBag(contents.items[j].value.result, state, ctx));
334
338
  }
335
339
  } else if (el.contentType === OID_ENCRYPTED_DATA) {
336
- encryptedSafes.push({ type: "encryptedData", content: _privacySafe(cms.walkEncryptedData(el.innerNode), ctx) });
340
+ encryptedSafes.push({ type: "encryptedData", content: _privacySafe(cms.walkEncryptedData(el.innerNode, ctx), ctx) });
337
341
  } else if (el.contentType === OID_ENVELOPED_DATA) {
338
- encryptedSafes.push({ type: "envelopedData", content: _privacySafe(cms.walkEnvelopedData(el.innerNode), ctx), envelopedDataDer: el.innerNode.bytes });
342
+ encryptedSafes.push({ type: "envelopedData", content: _privacySafe(cms.walkEnvelopedData(el.innerNode, ctx), ctx), envelopedDataDer: el.innerNode.bytes });
339
343
  } else {
340
344
  throw ctx.E("pkcs12/bad-safe-contentinfo-type", "an AuthenticatedSafe element must be id-data, id-encryptedData, or id-envelopedData (RFC 7292 sec. 4.1), got " +
341
345
  (ctx.oid.name(el.contentType) || el.contentType));
@@ -351,13 +355,13 @@ function _buildBag(rec, state, ctx) {
351
355
  };
352
356
  if (rec.bagId === OID_KEY_BAG) {
353
357
  bag.type = "keyBag";
354
- bag.key = pkcs8.walkPrivateKeyInfo(rec.valueNode);
358
+ bag.key = pkcs8.walkPrivateKeyInfo(rec.valueNode, ctx);
355
359
  bag.keyDer = rec.valueNode.bytes;
356
360
  return bag;
357
361
  }
358
362
  if (rec.bagId === OID_SHROUDED_KEY_BAG) {
359
363
  bag.type = "pkcs8ShroudedKeyBag";
360
- bag.encrypted = pkcs8.walkEncryptedPrivateKeyInfo(rec.valueNode);
364
+ bag.encrypted = pkcs8.walkEncryptedPrivateKeyInfo(rec.valueNode, ctx);
361
365
  return bag;
362
366
  }
363
367
  if (rec.bagId === OID_CERT_BAG) {