@blamejs/pki 0.8.1 → 0.8.3

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/oid.js CHANGED
@@ -46,8 +46,13 @@ var FAMILIES = Object.assign(Object.create(null), {
46
46
  attributeType: { base: [2, 5, 4], of: {
47
47
  commonName: 3, surname: 4, serialNumber: 5, countryName: 6,
48
48
  localityName: 7, stateOrProvinceName: 8, streetAddress: 9,
49
- organizationName: 10, organizationalUnitName: 11, title: 12, givenName: 42,
50
- clearance: 55, role: 72 } },
49
+ organizationName: 10, organizationalUnitName: 11, title: 12,
50
+ businessCategory: 15, postalCode: 17, name: 41, givenName: 42,
51
+ initials: 43, generationQualifier: 44, distinguishedNameQualifier: 46,
52
+ clearance: 55, pseudonym: 65, role: 72, organizationIdentifier: 97 } },
53
+
54
+ cabfJurisdiction: { base: [1, 3, 6, 1, 4, 1, 311, 60, 2, 1], of: {
55
+ jurisdictionLocalityName: 1, jurisdictionStateOrProvinceName: 2, jurisdictionCountryName: 3 } },
51
56
 
52
57
  certExtension: { base: [2, 5, 29], of: {
53
58
  subjectDirectoryAttributes: 9,
@@ -339,7 +344,7 @@ var FAMILIES = Object.assign(Object.create(null), {
339
344
  nistKem: { base: [2, 16, 840, 1, 101, 3, 4, 4], of: {
340
345
  "id-ml-kem-512": 1, "id-ml-kem-768": 2, "id-ml-kem-1024": 3 } },
341
346
 
342
- datatype: { base: [0, 9, 2342, 19200300, 100, 1], of: { domainComponent: 25 } },
347
+ datatype: { base: [0, 9, 2342, 19200300, 100, 1], of: { userId: 1, domainComponent: 25 } },
343
348
 
344
349
  fidoGenCe: { base: [1, 3, 6, 1, 4, 1, 45724, 1, 1], of: { idFidoGenCeAaguid: 4 } },
345
350
 
package/lib/pki-build.js CHANGED
@@ -265,6 +265,27 @@ function _isDottedOid(s) {
265
265
 
266
266
  var b = asn1.build;
267
267
 
268
+ /** @internal The distinguished-name attributes RFC 5280 App. A.1 types as something other than
269
+ * DirectoryString, with the SIZE constraint the module states. Every other attribute is a
270
+ * DirectoryString and takes UTF8String. A row here, not a branch, so a newly registered
271
+ * attribute whose module entry names a type is a data addition. */
272
+ var ATV_TYPE = intrinsic.assign(intrinsic.create(null), {
273
+ countryName: { encode: b.printable, tag: asn1.TAGS.PRINTABLE_STRING, size: [2, 2], what: "X520countryName, PrintableString SIZE(2)" },
274
+ jurisdictionCountryName: { encode: b.printable, tag: asn1.TAGS.PRINTABLE_STRING, size: [2, 2], what: "a jurisdiction country code, PrintableString SIZE(2)" },
275
+ distinguishedNameQualifier: { encode: b.printable, tag: asn1.TAGS.PRINTABLE_STRING, what: "X520dnQualifier, PrintableString" },
276
+ serialNumber: { encode: b.printable, tag: asn1.TAGS.PRINTABLE_STRING, size: [1, 64], what: "X520SerialNumber, PrintableString" },
277
+ emailAddress: { encode: b.ia5, tag: asn1.TAGS.IA5_STRING, size: [1, 255], what: "EmailAddress, IA5String" },
278
+ domainComponent: { encode: b.ia5, tag: asn1.TAGS.IA5_STRING, what: "DomainComponent, IA5String (RFC 4519 sec. 2.4)" },
279
+ });
280
+
281
+ /** @internal The DirectoryString CHOICE of RFC 5280 App. A.1. Every attribute the toolkit
282
+ * publishes a short name for and does not give its own row above is valued as one, so a
283
+ * pre-encoded value for one of them must be a member. An attribute with no published short
284
+ * name has a syntax this toolkit does not claim to know, and its value stays opaque. */
285
+ var DIRECTORY_STRING_TAGS = intrinsic.assign(intrinsic.create(null), {});
286
+ [asn1.TAGS.TELETEX_STRING, asn1.TAGS.PRINTABLE_STRING, asn1.TAGS.UNIVERSAL_STRING,
287
+ asn1.TAGS.UTF8_STRING, asn1.TAGS.BMP_STRING].forEach(function (t) { DIRECTORY_STRING_TAGS[t] = 1; });
288
+
268
289
  var KU_BIT = intrinsic.assign(intrinsic.create(null), {
269
290
  digitalSignature: 0, nonRepudiation: 1, contentCommitment: 1, keyEncipherment: 2,
270
291
  dataEncipherment: 3, keyAgreement: 4, keyCertSign: 5, cRLSign: 6, encipherOnly: 7, decipherOnly: 8,
@@ -360,19 +381,55 @@ function makeBuilder(ctx) {
360
381
  return (y >= 1950 && y <= 2049) ? b.utcTime(date) : b.generalizedTime(date);
361
382
  }
362
383
 
363
- function atvString(attrName, value) {
364
- if (attrName === "countryName") {
365
- if (String(value).length !== 2) throw E("bad-name", "countryName must be a two-letter ISO 3166 code (PrintableString SIZE(2))");
366
- return b.printable(value);
384
+ function assertAtvValue(attrName, value) {
385
+ if (value == null || value === "") throw E("bad-name", "the " + attrName + " attribute value must be a non-empty string");
386
+ var row = ATV_TYPE[attrName];
387
+ if (!row || !row.size) return;
388
+ var len = _String(value).length;
389
+ if (len < row.size[0] || len > row.size[1]) {
390
+ throw E("bad-name", attrName + " must be " + (row.size[0] === row.size[1]
391
+ ? "exactly " + row.size[0] + " characters" : row.size[0] + " to " + row.size[1] + " characters") +
392
+ " (" + row.what + ", RFC 5280 App. A.1)");
367
393
  }
368
- if (attrName === "emailAddress") return b.ia5(value);
369
- return b.utf8(value);
394
+ }
395
+ function atvString(attrName, value) {
396
+ assertAtvValue(attrName, value);
397
+ var row = ATV_TYPE[attrName];
398
+ if (!row) return b.utf8(value);
399
+ return row.encode(value);
370
400
  }
371
401
  function encodeAtv(attrName, value) {
372
402
  if (value == null || value === "") throw E("bad-name", "the " + attrName + " attribute value must be a non-empty string");
373
403
  var typeOid = O(attrName);
374
404
  if (typeOid == null) throw E("bad-name", "unknown distinguished-name attribute " + guard.text.showValue(attrName));
375
405
  var valueTlv;
406
+ if (typeof value === "string" && _charCodeAt(value, 0) === 0x23) {
407
+ var raw = guard.encoding.hex(_sliceStr(value, 1), null, rawErr, ctx.prefix + "/bad-name", "a #hex attribute value");
408
+ guard.der.tlv(raw, rawErr, ctx.prefix + "/bad-name", "a #hex attribute value");
409
+ var rawNode = asn1.decode(raw);
410
+ var typeRow = ATV_TYPE[attrName];
411
+ var universalPrimitive = rawNode.tagClass === "universal" && !rawNode.constructed;
412
+ if (typeRow && (!universalPrimitive || rawNode.tagNumber !== typeRow.tag)) {
413
+ throw E("bad-name", "the " + attrName + " #hex value must be encoded as " + typeRow.what);
414
+ }
415
+ if (!typeRow && intrinsic.hasOwn(C.NAMES.DN_SHORT, attrName) &&
416
+ (!universalPrimitive || !DIRECTORY_STRING_TAGS[rawNode.tagNumber])) {
417
+ throw E("bad-name", "the " + attrName + " #hex value must be a DirectoryString: a TeletexString, PrintableString, UniversalString, UTF8String or BMPString (RFC 5280 App. A.1)");
418
+ }
419
+ var rawText;
420
+ try {
421
+ rawText = asn1.read.string(rawNode);
422
+ } catch (_e) {
423
+ rawText = null;
424
+ }
425
+ if (rawText !== null) {
426
+ try { atvString(attrName, rawText); }
427
+ catch (e) { if (e instanceof ErrorClass) throw e; throw E("bad-name", "the " + attrName + " #hex value carries characters invalid for that attribute's string type", e); }
428
+ }
429
+ return b.sequence([b.oid(typeOid), raw]);
430
+ }
431
+ if (typeof value === "string" && _charCodeAt(value, 0) === 0x5c) value = _sliceStr(value, 1);
432
+ if (value === "") throw E("bad-name", "the " + attrName + " attribute value must be a non-empty string");
376
433
  try { valueTlv = atvString(attrName, value); }
377
434
  catch (e) { if (e instanceof ErrorClass) throw e; throw E("bad-name", "the " + attrName + " value has characters invalid for its string type", e); }
378
435
  return b.sequence([b.oid(typeOid), valueTlv]);
package/lib/schema-all.js CHANGED
@@ -60,85 +60,85 @@ var FORMATS = [
60
60
  name: "cms",
61
61
  module: cms,
62
62
  detect: cms.matches,
63
- parse: function (input) { return cms.parse(input); },
63
+ parse: function (input, caps) { return cms.parse(input, caps); },
64
64
  },
65
65
  {
66
66
  name: "tsp",
67
67
  module: tsp,
68
68
  detect: tsp.matches,
69
- parse: function (input) { return tsp.parse(input); },
69
+ parse: function (input, caps) { return tsp.parse(input, caps); },
70
70
  },
71
71
  {
72
72
  name: "crmf",
73
73
  module: crmf,
74
74
  detect: crmf.matches,
75
- parse: function (input) { return crmf.parse(input); },
75
+ parse: function (input, caps) { return crmf.parse(input, caps); },
76
76
  },
77
77
  {
78
78
  name: "cmp",
79
79
  module: cmp,
80
80
  detect: cmp.matches,
81
- parse: function (input) { return cmp.parse(input); },
81
+ parse: function (input, caps) { return cmp.parse(input, caps); },
82
82
  },
83
83
  {
84
84
  name: "csrattrs",
85
85
  module: csrattrs,
86
86
  detect: csrattrs.matches,
87
- parse: function (input) { return csrattrs.parse(input); },
87
+ parse: function (input, caps) { return csrattrs.parse(input, caps); },
88
88
  },
89
89
  {
90
90
  name: "ocsp-request",
91
91
  module: ocsp,
92
92
  detect: ocsp.matchesRequest,
93
- parse: function (input) { return ocsp.parseRequest(input); },
93
+ parse: function (input, caps) { return ocsp.parseRequest(input, caps); },
94
94
  },
95
95
  {
96
96
  name: "ocsp-response",
97
97
  module: ocsp,
98
98
  detect: ocsp.matchesResponse,
99
- parse: function (input) { return ocsp.parseResponse(input); },
99
+ parse: function (input, caps) { return ocsp.parseResponse(input, caps); },
100
100
  },
101
101
  {
102
102
  name: "pkcs12",
103
103
  module: pkcs12,
104
104
  detect: pkcs12.matches,
105
- parse: function (input) { return pkcs12.parse(input); },
105
+ parse: function (input, caps) { return pkcs12.parse(input, caps); },
106
106
  },
107
107
  {
108
108
  name: "pkcs8",
109
109
  module: pkcs8,
110
110
  detect: pkcs8.matches,
111
- parse: function (input) { return pkcs8.parse(input); },
111
+ parse: function (input, caps) { return pkcs8.parse(input, caps); },
112
112
  },
113
113
  {
114
114
  name: "csr",
115
115
  module: csr,
116
116
  detect: csr.matches,
117
- parse: function (input) { return csr.parse(input); },
117
+ parse: function (input, caps) { return csr.parse(input, caps); },
118
118
  },
119
119
  {
120
120
  name: "attrcert",
121
121
  module: attrcert,
122
122
  detect: attrcert.matches,
123
- parse: function (input) { return attrcert.parse(input); },
123
+ parse: function (input, caps) { return attrcert.parse(input, caps); },
124
124
  },
125
125
  {
126
126
  name: "attrcert-v1",
127
127
  module: attrcert,
128
128
  detect: attrcert.matchesV1,
129
- parse: function (input) { return attrcert.parseV1(input); },
129
+ parse: function (input, caps) { return attrcert.parseV1(input, caps); },
130
130
  },
131
131
  {
132
132
  name: "crl",
133
133
  module: crl,
134
134
  detect: crl.matches,
135
- parse: function (input) { return crl.parse(input); },
135
+ parse: function (input, caps) { return crl.parse(input, caps); },
136
136
  },
137
137
  {
138
138
  name: "x509",
139
139
  module: x509,
140
140
  detect: x509.matches,
141
- parse: function (input) { return x509.parse(input); },
141
+ parse: function (input, caps) { return x509.parse(input, caps); },
142
142
  },
143
143
  ];
144
144
 
@@ -159,7 +159,7 @@ function all() { return FORMATS.map(function (f) { return f.name; }); }
159
159
 
160
160
  /**
161
161
  * @primitive pki.schema.parse
162
- * @signature pki.schema.parse(input) -> parsed
162
+ * @signature pki.schema.parse(input, caps?) -> parsed
163
163
  * @since 0.1.7
164
164
  * @status stable
165
165
  * @spec RFC 5280
@@ -171,6 +171,13 @@ function all() { return FORMATS.map(function (f) { return f.name; }); }
171
171
  * the bytes match no registered format; the underlying decode / structural
172
172
  * errors of the matched format propagate unchanged.
173
173
  *
174
+ * @opts
175
+ * - `maxBytes` / `maxDepth` / `maxItems` (number) -- decode caps for this parse. Each
176
+ * defaults to the matching `pki.C.LIMITS` figure and may only be set lower; a value
177
+ * above it, or an option outside this set, is refused. They bound the detection decode
178
+ * and the decode of whichever format matches, and a parse that exceeds one is refused
179
+ * with the `/too-large` code of that format.
180
+ *
174
181
  * @example
175
182
  * var pair = await pki.key.generate("Ed25519");
176
183
  * var der = await pki.x509.sign({ subject: "example.com", subjectPublicKey: await pki.key.export(pair.publicKey),
@@ -178,11 +185,15 @@ function all() { return FORMATS.map(function (f) { return f.name; }); }
178
185
  * { key: await pki.key.export(pair.privateKey) });
179
186
  * var parsed = pki.schema.parse(der); // cert -> the pki.schema.x509 shape
180
187
  */
181
- function parse(input) {
188
+ function parse(input, callerOpts) {
189
+ /** @internal The caps are validated once here and the snapshot is what the chosen format
190
+ * receives, so the detection decode and the format's own decode are held to the same
191
+ * figures a caller named, read once. */
192
+ var caps = pkix.parseCaps(callerOpts, ENTRY);
182
193
  var der = pkix.coerceToDer(input, ENTRY);
183
- var root = pkix.decodeRoot(der, ENTRY);
194
+ var root = pkix.decodeRoot(der, ENTRY, caps);
184
195
  for (var i = 0; i < FORMATS.length; i++) {
185
- if (FORMATS[i].detect(root)) return FORMATS[i].parse(der);
196
+ if (FORMATS[i].detect(root)) return FORMATS[i].parse(der, caps);
186
197
  }
187
198
  throw new SchemaError("schema/unknown-format", "input does not match any registered PKI format (" + all().join(", ") + ")");
188
199
  }
@@ -248,7 +248,7 @@ var ATTRIBUTE_CERTIFICATE = pkix.signedEnvelope(NS, ACINFO, {
248
248
 
249
249
  /**
250
250
  * @primitive pki.schema.attrcert.parse
251
- * @signature pki.schema.attrcert.parse(input) -> attributeCertificate
251
+ * @signature pki.schema.attrcert.parse(input, caps?) -> attributeCertificate
252
252
  * @since 0.1.14
253
253
  * @status stable
254
254
  * @spec RFC 5755, X.509
@@ -279,25 +279,32 @@ var ATTRIBUTE_CERTIFICATE = pkix.signedEnvelope(NS, ACINFO, {
279
279
  * var ac = pki.schema.attrcert.parse(der);
280
280
  * ac.attributes[0].name; // "role"
281
281
  * ac.validity.notAfterTime;// Date
282
+ * @opts
283
+ * - `maxBytes` / `maxDepth` / `maxItems` (number) -- decode caps for this parse. Each
284
+ * defaults to the matching `pki.C.LIMITS` figure and may only be set lower; a value
285
+ * above it, or an option outside this set, is refused. A parse that exceeds one is
286
+ * refused with the `/too-large` code of this format.
282
287
  */
283
- var PARSE_OPTS = intrinsic.assign(intrinsic.create(null), { pemLabel: "ATTRIBUTE CERTIFICATE", PemError: PemError, ErrorClass: AttrCertError, prefix: "attrcert", what: "attribute certificate" });
288
+ var PARSE_OPTS = intrinsic.assign(intrinsic.create(null), { pemLabel: "ATTRIBUTE CERTIFICATE", PemError: PemError, ErrorClass: AttrCertError, prefix: "attrcert", what: "attribute certificate", ns: NS });
284
289
 
285
290
  function _legacyV1Error() {
286
291
  return new AttrCertError("attrcert/legacy-v1-not-supported", "AttributeCertificateV1 (X.509-1997) is obsolete and not parsed by this build (RFC 5755 sec. 1)");
287
292
  }
288
293
 
289
- function _parseAc(input) {
290
- var root = pkix.decodeRoot(pkix.coerceToDer(input, PARSE_OPTS), PARSE_OPTS);
291
- if (matchesV1(root)) throw _legacyV1Error();
292
- return schema.walk(ATTRIBUTE_CERTIFICATE, root, NS).result;
294
+ function _parseAc(input, callerOpts) {
295
+ return pkix.runParseWith(input, PARSE_OPTS, callerOpts, function (root, ns) {
296
+ if (matchesV1(root)) throw _legacyV1Error();
297
+ return schema.walk(ATTRIBUTE_CERTIFICATE, root, ns).result;
298
+ });
293
299
  }
294
300
 
295
301
  var parse = guard.parsed.recordingParser("attributeCertificate", _parseAc,
296
302
  AttrCertError, "attrcert/bad-input", "an attribute certificate");
297
303
 
298
- function parseV1(input) {
299
- pkix.decodeRoot(pkix.coerceToDer(input, PARSE_OPTS), PARSE_OPTS);
300
- throw _legacyV1Error();
304
+ function parseV1(input, callerOpts) {
305
+ return pkix.runParseWith(input, PARSE_OPTS, callerOpts, function () {
306
+ throw _legacyV1Error();
307
+ });
301
308
  }
302
309
 
303
310
  /**
@@ -1563,7 +1563,7 @@ function _reconstructDer(r, sigNode) {
1563
1563
 
1564
1564
  /**
1565
1565
  * @primitive pki.schema.c509.parse
1566
- * @signature pki.schema.c509.parse(bytes) -> { certificateType, serialNumber, serialNumberHex, ... }
1566
+ * @signature pki.schema.c509.parse(bytes, caps?) -> { certificateType, serialNumber, serialNumberHex, ... }
1567
1567
  * @since 0.2.30
1568
1568
  * @status stable
1569
1569
  * @spec draft-ietf-cose-cbor-encoded-cert, RFC 8949, RFC 9090, RFC 5280
@@ -1583,11 +1583,27 @@ function _reconstructDer(r, sigNode) {
1583
1583
  * "hex");
1584
1584
  * var c = pki.schema.c509.parse(bytes);
1585
1585
  * c.certificateType; // 3
1586
+ * @opts
1587
+ * - `maxBytes` / `maxDepth` / `maxItems` (number) -- decode caps for this parse. Each
1588
+ * defaults to the matching `pki.C.LIMITS` figure and may only be set lower; a value
1589
+ * above it, or an option outside this set, is refused. A parse that exceeds one is
1590
+ * refused with the `/too-large` code of this format.
1586
1591
  */
1587
- function parse(input) {
1592
+ var _PARSE_OPTS = pkix.cborParseOpts(C509Error, "c509", "the C509 certificate");
1593
+
1594
+ function parse(input, callerOpts) {
1595
+ var caps = pkix.parseCaps(callerOpts, _PARSE_OPTS);
1588
1596
  var root;
1589
- try { root = cbor.decode(input); }
1590
- catch (e) { throw _err("c509/not-a-certificate", "the input is not well-formed deterministic CBOR", e); }
1597
+ try {
1598
+ root = cbor.decode(input, { maxBytes: caps.maxBytes, maxDepth: caps.maxDepth, maxItems: caps.maxItems });
1599
+ } catch (e) {
1600
+ /** @internal A resource refusal from the CBOR reader says the cap was too small. Reading it
1601
+ * as "these bytes are not a certificate" would send a caller to look at the wrong thing. */
1602
+ if (pkix.isCborResourceRefusal(e)) {
1603
+ throw _err("c509/too-large", "the C509 certificate exceeds the decode budget for this parse: " + ((e && e.message) || ""), e);
1604
+ }
1605
+ throw _err("c509/not-a-certificate", "the input is not well-formed deterministic CBOR", e);
1606
+ }
1591
1607
  if (root.majorType !== 4 || !root.children) throw _err("c509/not-a-certificate", "a C509 certificate must be a CBOR array");
1592
1608
  var f = root.children;
1593
1609
  if (f.length !== 11) throw _err("c509/bad-tbs", "a C509 certificate must be an array of exactly 11 elements, got " + f.length);
package/lib/schema-cmc.js CHANGED
@@ -646,7 +646,7 @@ function _finishBody(body, where) {
646
646
 
647
647
  /**
648
648
  * @primitive pki.schema.cmc.parsePkiData
649
- * @signature pki.schema.cmc.parsePkiData(der) -> parsed
649
+ * @signature pki.schema.cmc.parsePkiData(der, caps?) -> parsed
650
650
  * @since 0.4.16
651
651
  * @status stable
652
652
  * @spec RFC 5272 sec. 3.2.1
@@ -664,17 +664,23 @@ function _finishBody(body, where) {
664
664
  * var der = b.sequence([b.sequence([]), b.sequence([]), b.sequence([]), b.sequence([])]);
665
665
  * var d = pki.schema.cmc.parsePkiData(der);
666
666
  * d.controls.length; // 0 -- present and empty, which is legal
667
+ * @opts
668
+ * - `maxBytes` / `maxDepth` / `maxItems` (number) -- decode caps for this parse. Each
669
+ * defaults to the matching `pki.C.LIMITS` figure and may only be set lower; a value
670
+ * above it, or an option outside this set, is refused. A parse that exceeds one is
671
+ * refused with the `/too-large` code of this format.
667
672
  */
668
- function parsePkiData(input) {
669
- var root = asn1.decode(guard.bytes.isByteSource(input) ? guard.bytes.source(input, E, "cmc/bad-input", "the CMC DER") : Buffer.from(input));
670
- var body = schema.walk(PKI_DATA, root, NS).result;
671
- body.bytes = root.bytes;
672
- return _finishBody(body, "pkiData");
673
+ function parsePkiData(input, callerOpts) {
674
+ return pkix.runParseWith(input, _BODY_PARSE_OPTS, callerOpts, function (root, ns) {
675
+ var body = schema.walk(PKI_DATA, root, ns).result;
676
+ body.bytes = root.bytes;
677
+ return _finishBody(body, "pkiData");
678
+ });
673
679
  }
674
680
 
675
681
  /**
676
682
  * @primitive pki.schema.cmc.parsePkiResponse
677
- * @signature pki.schema.cmc.parsePkiResponse(der) -> parsed
683
+ * @signature pki.schema.cmc.parsePkiResponse(der, caps?) -> parsed
678
684
  * @since 0.4.16
679
685
  * @status stable
680
686
  * @spec RFC 5272 sec. 4.2.1
@@ -690,17 +696,33 @@ function parsePkiData(input) {
690
696
  * var der = b.sequence([b.sequence([]), b.sequence([]), b.sequence([])]);
691
697
  * var r = pki.schema.cmc.parsePkiResponse(der);
692
698
  * r.statuses.length; // 0 -- no status control means success is assumed
699
+ * @opts
700
+ * - `maxBytes` / `maxDepth` / `maxItems` (number) -- decode caps for this parse. Each
701
+ * defaults to the matching `pki.C.LIMITS` figure and may only be set lower; a value
702
+ * above it, or an option outside this set, is refused. A parse that exceeds one is
703
+ * refused with the `/too-large` code of this format.
693
704
  */
694
- function parsePkiResponse(input) {
695
- var root = asn1.decode(guard.bytes.isByteSource(input) ? guard.bytes.source(input, E, "cmc/bad-input", "the CMC DER") : Buffer.from(input));
696
- var body = schema.walk(PKI_RESPONSE, root, NS).result;
697
- body.bytes = root.bytes;
698
- return _finishBody(body, "pkiResponse");
705
+ function parsePkiResponse(input, callerOpts) {
706
+ return pkix.runParseWith(input, _BODY_PARSE_OPTS, callerOpts, function (root, ns) {
707
+ var body = schema.walk(PKI_RESPONSE, root, ns).result;
708
+ body.bytes = root.bytes;
709
+ return _finishBody(body, "pkiResponse");
710
+ });
699
711
  }
700
712
 
713
+ /** @internal The door options for the two body parsers, which read a bare DER body rather than
714
+ * a CMS carrier, so they take no PEM label. `rawDecodeErrors` keeps the codec's own code for a
715
+ * malformed body, which callers already pin. */
716
+ var _CARRIER_PARSE_OPTS = pkix.parseOpts({ ErrorClass: CmcError, prefix: "cmc", what: "the CMC message" });
717
+
718
+ var _BODY_PARSE_OPTS = pkix.parseOpts({
719
+ ns: NS, ErrorClass: CmcError, PemError: CmcError, prefix: "cmc",
720
+ what: "the CMC body", pemLabel: null, rawDecodeErrors: true,
721
+ });
722
+
701
723
  /**
702
724
  * @primitive pki.schema.cmc.parse
703
- * @signature pki.schema.cmc.parse(input) -> parsed
725
+ * @signature pki.schema.cmc.parse(input, caps?) -> parsed
704
726
  * @since 0.4.16
705
727
  * @status stable
706
728
  * @spec RFC 5272, RFC 6402
@@ -724,17 +746,31 @@ function parsePkiResponse(input) {
724
746
  * var sd = b.sequence([b.integer(3n), b.set([]), encap, b.set([])]);
725
747
  * var der = b.sequence([b.oid("1.2.840.113549.1.7.2"), b.explicit(0, sd)]);
726
748
  * pki.schema.cmc.parse(der).kind; // "pkiData"
749
+ * @opts
750
+ * - `maxBytes` / `maxDepth` / `maxItems` (number) -- decode caps for this parse. Each
751
+ * defaults to the matching `pki.C.LIMITS` figure and may only be set lower; a value
752
+ * above it, or an option outside this set, is refused. A parse that exceeds one is
753
+ * refused with the `/too-large` code of this format.
727
754
  */
728
- function parse(input) {
755
+ function parse(input, callerOpts) {
756
+ /** @internal Before the branch, because the already-parsed arm reaches no decode and would
757
+ * otherwise hold a caller to the option contract only when they passed bytes. The snapshot
758
+ * is what travels onward: handing the caller's own object to the CMS parser would read each
759
+ * cap a second time, and a value read twice need not answer the same way twice. */
760
+ var caps = pkix.parseCaps(callerOpts, _CARRIER_PARSE_OPTS);
729
761
  var alreadyParsed = input && typeof input === "object" && !ArrayBuffer.isView(input) &&
730
762
  !guard.bytes.isByteSource(input) && input.encapContentInfo != null;
731
763
  var parsedCms;
732
764
  if (alreadyParsed) { parsedCms = input; }
733
765
  else {
734
766
  /** @internal Bytes that are not a CMS message at all are a CMC message this cannot read, and every
735
- * other format parser answers for its own input in its own domain. */
736
- try { parsedCms = cms.parse(input); }
767
+ * other format parser answers for its own input in its own domain. A resource refusal is the one
768
+ * exception: it is not a statement about the bytes, so it keeps its own code. */
769
+ try { parsedCms = cms.parse(input, caps); }
737
770
  catch (e) {
771
+ if (e && typeof e.code === "string" && e.code.indexOf("/too-large") !== -1) {
772
+ throw E("cmc/too-large", "the CMC message exceeds the decode budget for this parse: " + (e.message || ""), e);
773
+ }
738
774
  throw E("cmc/bad-der",
739
775
  "a CMC message is carried in a CMS SignedData or AuthenticatedData, and these bytes are not " +
740
776
  "one: " + ((e && e.message) || "malformed") + " (RFC 5272 sec. 3.2 / 4.2)", e);
package/lib/schema-cmp.js CHANGED
@@ -751,7 +751,7 @@ var readName = _valueParser("Name", pkix.name(NS));
751
751
 
752
752
  /**
753
753
  * @primitive pki.schema.cmp.parse
754
- * @signature pki.schema.cmp.parse(input) -> pkiMessage
754
+ * @signature pki.schema.cmp.parse(input, caps?) -> pkiMessage
755
755
  * @since 0.1.19
756
756
  * @status stable
757
757
  * @spec RFC 9810, RFC 9483, RFC 9481
@@ -805,6 +805,11 @@ var readName = _valueParser("Name", pkix.name(NS));
805
805
  * var m = pki.schema.cmp.parse(der);
806
806
  * m.body.arm; // "ir", "ip", "error", ...
807
807
  * m.header.transactionID; // raw Buffer or null
808
+ * @opts
809
+ * - `maxBytes` / `maxDepth` / `maxItems` (number) -- decode caps for this parse. Each
810
+ * defaults to the matching `pki.C.LIMITS` figure and may only be set lower; a value
811
+ * above it, or an option outside this set, is refused. A parse that exceeds one is
812
+ * refused with the `/too-large` code of this format.
808
813
  */
809
814
  var parse = pkix.makeParser({
810
815
  pemLabel: "CMP", PemError: PemError, ErrorClass: CmpError,
package/lib/schema-cms.js CHANGED
@@ -934,7 +934,7 @@ var CONTENT_INFO = schema.seq([
934
934
 
935
935
  /**
936
936
  * @primitive pki.schema.cms.parse
937
- * @signature pki.schema.cms.parse(input) -> content
937
+ * @signature pki.schema.cms.parse(input, caps?) -> content
938
938
  * @since 0.1.10
939
939
  * @status stable
940
940
  * @spec RFC 5652, RFC 5083, RFC 9629
@@ -972,6 +972,11 @@ var CONTENT_INFO = schema.seq([
972
972
  * var cms = pki.schema.cms.parse(der);
973
973
  * cms.signerInfos[0].sid.serialNumberHex; // -> "0a1b"
974
974
  * cms.encapContentInfo.eContent; // -> Buffer | null (detached)
975
+ * @opts
976
+ * - `maxBytes` / `maxDepth` / `maxItems` (number) -- decode caps for this parse. Each
977
+ * defaults to the matching `pki.C.LIMITS` figure and may only be set lower; a value
978
+ * above it, or an option outside this set, is refused. A parse that exceeds one is
979
+ * refused with the `/too-large` code of this format.
975
980
  */
976
981
  var parse = pkix.makeRecordingParser({ pemLabel: "CMS", PemError: PemError, ErrorClass: CmsError, prefix: "cms", what: "CMS ContentInfo", topSchema: CONTENT_INFO, ns: NS }, "cms");
977
982
 
package/lib/schema-crl.js CHANGED
@@ -157,7 +157,7 @@ var CERTIFICATE_LIST = pkix.signedEnvelope(NS, TBS_CERTLIST, {
157
157
 
158
158
  /**
159
159
  * @primitive pki.schema.crl.parse
160
- * @signature pki.schema.crl.parse(input) -> crl
160
+ * @signature pki.schema.crl.parse(input, caps?) -> crl
161
161
  * @since 0.1.7
162
162
  * @status stable
163
163
  * @spec RFC 5280
@@ -180,6 +180,11 @@ var CERTIFICATE_LIST = pkix.signedEnvelope(NS, TBS_CERTLIST, {
180
180
  * { cert: caCert, key: key });
181
181
  * var crl = pki.schema.crl.parse(der);
182
182
  * crl.revokedCertificates[0].serialNumberHex; // -> "0a3f"
183
+ * @opts
184
+ * - `maxBytes` / `maxDepth` / `maxItems` (number) -- decode caps for this parse. Each
185
+ * defaults to the matching `pki.C.LIMITS` figure and may only be set lower; a value
186
+ * above it, or an option outside this set, is refused. A parse that exceeds one is
187
+ * refused with the `/too-large` code of this format.
183
188
  */
184
189
  var parse = pkix.makeRecordingParser({ pemLabel: "X509 CRL", PemError: PemError, ErrorClass: CrlError, prefix: "crl", what: "CRL", topSchema: CERTIFICATE_LIST, ns: NS }, "crl");
185
190
 
@@ -311,7 +311,7 @@ var CERT_REQ_MESSAGES = schema.seqOf(CERT_REQ_MSG, {
311
311
 
312
312
  /**
313
313
  * @primitive pki.schema.crmf.parse
314
- * @signature pki.schema.crmf.parse(input) -> certReqMessages
314
+ * @signature pki.schema.crmf.parse(input, caps?) -> certReqMessages
315
315
  * @since 0.1.17
316
316
  * @status stable
317
317
  * @spec RFC 4211
@@ -348,6 +348,11 @@ var CERT_REQ_MESSAGES = schema.seqOf(CERT_REQ_MSG, {
348
348
  * { key: await pki.key.export(pair.privateKey) });
349
349
  * var m = pki.schema.crmf.parse(der);
350
350
  * m.messages[0].certReq.certTemplate.subject.dn; // "CN=req.example"
351
+ * @opts
352
+ * - `maxBytes` / `maxDepth` / `maxItems` (number) -- decode caps for this parse. Each
353
+ * defaults to the matching `pki.C.LIMITS` figure and may only be set lower; a value
354
+ * above it, or an option outside this set, is refused. A parse that exceeds one is
355
+ * refused with the `/too-large` code of this format.
351
356
  */
352
357
  var parse = pkix.makeRecordingParser({
353
358
  pemLabel: null, PemError: PemError, ErrorClass: CrmfError, prefix: "crmf",
package/lib/schema-csr.js CHANGED
@@ -121,7 +121,7 @@ var CERTIFICATION_REQUEST = pkix.signedEnvelope(NS, CERTIFICATION_REQUEST_INFO,
121
121
 
122
122
  /**
123
123
  * @primitive pki.schema.csr.parse
124
- * @signature pki.schema.csr.parse(input) -> csr
124
+ * @signature pki.schema.csr.parse(input, caps?) -> csr
125
125
  * @since 0.1.8
126
126
  * @status stable
127
127
  * @spec RFC 2986
@@ -147,6 +147,11 @@ var CERTIFICATION_REQUEST = pkix.signedEnvelope(NS, CERTIFICATION_REQUEST_INFO,
147
147
  * var csr = pki.schema.csr.parse(der);
148
148
  * csr.subject.dn; // -> "CN=req.example"
149
149
  * csr.attributes[0].type; // -> "1.2.840.113549.1.9.14"
150
+ * @opts
151
+ * - `maxBytes` / `maxDepth` / `maxItems` (number) -- decode caps for this parse. Each
152
+ * defaults to the matching `pki.C.LIMITS` figure and may only be set lower; a value
153
+ * above it, or an option outside this set, is refused. A parse that exceeds one is
154
+ * refused with the `/too-large` code of this format.
150
155
  */
151
156
  var parse = pkix.makeRecordingParser({ pemLabel: "CERTIFICATE REQUEST", PemError: PemError, ErrorClass: CsrError, prefix: "csr", what: "certification request", topSchema: CERTIFICATION_REQUEST, ns: NS }, "csr");
152
157
 
@@ -244,7 +244,7 @@ var CSR_ATTRS = schema.seqOf(ATTR_OR_OID, {
244
244
 
245
245
  /**
246
246
  * @primitive pki.schema.csrattrs.parse
247
- * @signature pki.schema.csrattrs.parse(der) -> { items }
247
+ * @signature pki.schema.csrattrs.parse(der, caps?) -> { items }
248
248
  * @since 0.1.24
249
249
  * @status stable
250
250
  * @spec RFC 8951, RFC 9908, RFC 7030
@@ -269,6 +269,11 @@ var CSR_ATTRS = schema.seqOf(ATTR_OR_OID, {
269
269
  * ]);
270
270
  * var a = pki.schema.csrattrs.parse(der);
271
271
  * a.items[0].kind; // -> "oid" | "attribute"
272
+ * @opts
273
+ * - `maxBytes` / `maxDepth` / `maxItems` (number) -- decode caps for this parse. Each
274
+ * defaults to the matching `pki.C.LIMITS` figure and may only be set lower; a value
275
+ * above it, or an option outside this set, is refused. A parse that exceeds one is
276
+ * refused with the `/too-large` code of this format.
272
277
  */
273
278
  var parse = pkix.makeParser({ pemLabel: null, PemError: frameworkError.PemError, ErrorClass: CsrattrsError, prefix: "csrattrs", what: "CsrAttrs", topSchema: CSR_ATTRS, ns: NS });
274
279
 
@@ -431,7 +431,7 @@ var OCSP_REQUEST = schema.seq([
431
431
 
432
432
  /**
433
433
  * @primitive pki.schema.ocsp.parseRequest
434
- * @signature pki.schema.ocsp.parseRequest(input) -> ocspRequest
434
+ * @signature pki.schema.ocsp.parseRequest(input, caps?) -> ocspRequest
435
435
  * @since 0.1.11
436
436
  * @status stable
437
437
  * @spec RFC 6960
@@ -458,12 +458,17 @@ var OCSP_REQUEST = schema.seq([
458
458
  * var der = await pki.ocsp.buildRequest({ cert: leafDer, issuer: caDer }, {});
459
459
  * var req = pki.schema.ocsp.parseRequest(der);
460
460
  * req.requestList[0].certID.serialNumberHex; // -> "1332"
461
+ * @opts
462
+ * - `maxBytes` / `maxDepth` / `maxItems` (number) -- decode caps for this parse. Each
463
+ * defaults to the matching `pki.C.LIMITS` figure and may only be set lower; a value
464
+ * above it, or an option outside this set, is refused. A parse that exceeds one is
465
+ * refused with the `/too-large` code of this format.
461
466
  */
462
467
  var parseRequest = pkix.makeParser({ pemLabel: "OCSP REQUEST", PemError: PemError, ErrorClass: OcspError, prefix: "ocsp", what: "OCSP request", topSchema: OCSP_REQUEST, ns: NS });
463
468
 
464
469
  /**
465
470
  * @primitive pki.schema.ocsp.parseResponse
466
- * @signature pki.schema.ocsp.parseResponse(input) -> ocspResponse
471
+ * @signature pki.schema.ocsp.parseResponse(input, caps?) -> ocspResponse
467
472
  * @since 0.1.11
468
473
  * @status stable
469
474
  * @spec RFC 6960
@@ -495,6 +500,11 @@ var parseRequest = pkix.makeParser({ pemLabel: "OCSP REQUEST", PemError: PemErro
495
500
  * var res = pki.schema.ocsp.parseResponse(der);
496
501
  * res.responseStatus.name; // -> "successful"
497
502
  * res.basicResponse.responses[0].certStatus.type; // -> "good" | "revoked" | "unknown"
503
+ * @opts
504
+ * - `maxBytes` / `maxDepth` / `maxItems` (number) -- decode caps for this parse. Each
505
+ * defaults to the matching `pki.C.LIMITS` figure and may only be set lower; a value
506
+ * above it, or an option outside this set, is refused. A parse that exceeds one is
507
+ * refused with the `/too-large` code of this format.
498
508
  */
499
509
  var parseResponse = pkix.makeRecordingParser({ pemLabel: "OCSP RESPONSE", PemError: PemError, ErrorClass: OcspError, prefix: "ocsp", what: "OCSP response", topSchema: OCSP_RESPONSE, ns: NS }, "ocspResponse");
500
510