@blamejs/pki 0.8.2 → 0.8.4

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.
@@ -413,16 +413,16 @@ function _generalNameToDer(intVal, valueNode, ipMode, isNative) {
413
413
  if (intVal === 0 || intVal === -1 || intVal === -2 || intVal === -3) return _otherNameToDer(valueNode, intVal);
414
414
  throw _err("c509/bad-extensions", "GeneralName int " + intVal + " has no C509 sec. 8.13 registry row");
415
415
  }
416
- function _generalNameFromDer(gn, ipMode) {
416
+ function _generalNameFromDer(gn, ipMode, ctx) {
417
417
  var t = gn.tagNumber;
418
418
  if (t === 1 || t === 2 || t === 6) return [cbor.build.int(BigInt(t)), cbor.build.textString(gn.value)];
419
- if (t === 4) { var nm = _dirNameToCbor(gn.bytes); return nm == null ? null : [cbor.build.int(4n), nm]; }
419
+ if (t === 4) { var nm = _dirNameToCbor(gn.bytes, ctx); return nm == null ? null : [cbor.build.int(4n), nm]; }
420
420
  if (t === 7) {
421
421
  if (!ipMode) return [cbor.build.int(7n), cbor.build.byteString(gn.value)];
422
422
  var ip = _ncIpFromDer(gn.value); return ip == null ? null : [cbor.build.int(7n), cbor.build.byteString(ip)];
423
423
  }
424
424
  if (t === 8) return [cbor.build.int(8n), _oidCbor(gn.value)];
425
- if (t === 0) return _otherNameFromDer(gn.value);
425
+ if (t === 0) return _otherNameFromDer(gn.value, ctx);
426
426
  return null;
427
427
  }
428
428
 
@@ -434,10 +434,10 @@ function _generalNamesToDer(node, isNative) {
434
434
  for (var i = 0; i + 1 < kids.length; i += 2) out.push(_generalNameToDer(Number(_cborIntVal(kids[i], "a GeneralName type")), kids[i + 1], false, isNative));
435
435
  return out;
436
436
  }
437
- function _generalNamesFromDer(gnNodes) {
437
+ function _generalNamesFromDer(gnNodes, ctx) {
438
438
  var out = [];
439
439
  for (var i = 0; i < gnNodes.length; i++) {
440
- var pair = _generalNameFromDer(schema.walk(GN_LEAF, gnNodes[i], NS), false);
440
+ var pair = _generalNameFromDer(schema.walk(GN_LEAF, gnNodes[i], schema.withResources(NS, ctx)), false, ctx);
441
441
  if (pair == null) return null;
442
442
  out.push(pair[0]); out.push(pair[1]);
443
443
  }
@@ -470,11 +470,11 @@ function _otherNameToDer(node, intVal) {
470
470
  }
471
471
  return b.contextConstructed(0, Buffer.concat([b.oid(typeId), b.explicit(0, inner)]));
472
472
  }
473
- function _otherNameFromDer(v) {
473
+ function _otherNameFromDer(v, ctx) {
474
474
  var onInt = ON_INT_BY_OID[v.typeId];
475
475
  if (onInt === undefined) return [cbor.build.int(0n), cbor.build.array([_oidCbor(v.typeId), cbor.build.byteString(v.valueBytes)])];
476
476
  var inner;
477
- try { inner = asn1.decode(v.valueBytes); } catch (_e) { return null; } // allow:swallow-unverified -- a malformed otherName value yields null, so the extension does not compact and is carried in C509 general form (raw bytes)
477
+ try { inner = pkix.decodeNested(v.valueBytes, ctx, "an otherName value"); } catch (_e) { return null; } // allow:swallow-unverified -- a malformed otherName value yields null, so the extension does not compact and is carried in C509 general form (raw bytes)
478
478
  if (onInt === -1) {
479
479
  if (inner.tagClass !== "universal" || inner.tagNumber !== asn1.TAGS.SEQUENCE || !inner.children || inner.children.length !== 2) return null;
480
480
  var hwType, hwSerial;
@@ -492,11 +492,11 @@ function _otherNameFromDer(v) {
492
492
  return [cbor.build.int(-3n), cbor.build.byteString(mac)];
493
493
  }
494
494
 
495
- function _dirNameToCbor(gnBytes) {
496
- var node = asn1.decode(gnBytes);
495
+ function _dirNameToCbor(gnBytes, ctx) {
496
+ var node = pkix.decodeNested(gnBytes, ctx, "a directoryName value");
497
497
  if (!node.children || node.children.length !== 1) return null;
498
498
  var name;
499
- try { name = _c509NameFromDer(node.children[0].bytes); } catch (_e) { return null; } // allow:swallow-unverified -- a malformed directoryName yields null, so the extension is carried in C509 general form instead
499
+ try { name = _c509NameFromDer(node.children[0].bytes, ctx); } catch (_e) { return null; } // allow:swallow-unverified -- a malformed directoryName yields null, so the extension is carried in C509 general form instead
500
500
  try { return _encName(name, true); } catch (_e2) { return null; } // allow:swallow-unverified -- a directoryName that does not re-encode yields null, so the extension is carried in C509 general form instead
501
501
  }
502
502
 
@@ -745,13 +745,13 @@ function _subtreesToDer(node, isNative) {
745
745
  for (var i = 0; i + 1 < kids.length; i += 2) out.push(b.sequence([_generalNameToDer(Number(_cborIntVal(kids[i], "a GeneralSubtree base type")), kids[i + 1], true, isNative)]));
746
746
  return Buffer.concat(out);
747
747
  }
748
- function _subtreesFromDer(subtreeNodes) {
748
+ function _subtreesFromDer(subtreeNodes, ctx) {
749
749
  if (!subtreeNodes.length) return null;
750
750
  var out = [];
751
751
  for (var i = 0; i < subtreeNodes.length; i++) {
752
752
  var st = subtreeNodes[i];
753
753
  if (st.tagClass !== "universal" || st.tagNumber !== asn1.TAGS.SEQUENCE || !st.children || st.children.length !== 1) return null;
754
- var pair = _generalNameFromDer(schema.walk(GN_LEAF_SUBTREE, st.children[0], NS), true);
754
+ var pair = _generalNameFromDer(schema.walk(GN_LEAF_SUBTREE, st.children[0], schema.withResources(NS, ctx)), true, ctx);
755
755
  if (pair == null) return null;
756
756
  out.push(pair[0]); out.push(pair[1]);
757
757
  }
@@ -836,7 +836,7 @@ function _dpToDer(dpNode, isNative) {
836
836
  if (!_isCborNull(crlIssuer)) fields.push(b.contextConstructed(2, b.explicit(4, _reconName(_name509(crlIssuer, true, isNative)))));
837
837
  return b.sequence(fields);
838
838
  }
839
- function _dpFromDer(dp) {
839
+ function _dpFromDer(dp, ctx) {
840
840
  if (dp.tagClass !== "universal" || dp.tagNumber !== asn1.TAGS.SEQUENCE || !dp.children) return null;
841
841
  var fullNameUris = null, reasonsVal = null, crlIssuerCbor = null, lastTag = -1;
842
842
  for (var i = 0; i < dp.children.length; i++) {
@@ -862,7 +862,7 @@ function _dpFromDer(dp) {
862
862
  if (!f.children || f.children.length !== 1) return null;
863
863
  var ci = f.children[0];
864
864
  if (ci.tagClass !== "context" || ci.tagNumber !== 4 || !ci.children || ci.children.length !== 1) return null;
865
- try { crlIssuerCbor = _encName(_c509NameFromDer(ci.children[0].bytes), true); } catch (_e2) { return null; } // allow:swallow-unverified -- a malformed CRL-issuer name yields null, so the distributionPoint is carried in C509 general form instead
865
+ try { crlIssuerCbor = _encName(_c509NameFromDer(ci.children[0].bytes, ctx), true); } catch (_e2) { return null; } // allow:swallow-unverified -- a malformed CRL-issuer name yields null, so the distributionPoint is carried in C509 general form instead
866
866
  } else return null;
867
867
  }
868
868
  if (fullNameUris == null) return null;
@@ -1054,15 +1054,15 @@ function _extValueToDer(name, node, isNative) {
1054
1054
  }
1055
1055
  }
1056
1056
 
1057
- function _extValueFromDer(name, der) {
1057
+ function _extValueFromDer(name, der, ctx) {
1058
1058
  var node;
1059
1059
  try {
1060
- node = asn1.decode(der);
1060
+ node = pkix.decodeNested(der, ctx, "an extension value");
1061
1061
  switch (name) {
1062
1062
  case "subjectKeyIdentifier":
1063
1063
  return cbor.build.byteString(asn1.read.octetString(node));
1064
1064
  case "keyUsage": {
1065
- var bits = _keyUsageBitsFromDer(der);
1065
+ var bits = _keyUsageBitsFromDer(der, ctx);
1066
1066
  return bits == null ? null : cbor.build.uint(BigInt(bits));
1067
1067
  }
1068
1068
  case "basicConstraints": {
@@ -1081,7 +1081,7 @@ function _extValueFromDer(name, der) {
1081
1081
  if (akids.length === 3 && akids[0].tagClass === "context" && akids[0].tagNumber === 0 &&
1082
1082
  akids[1].tagClass === "context" && akids[1].tagNumber === 1 && akids[1].children &&
1083
1083
  akids[2].tagClass === "context" && akids[2].tagNumber === 2) {
1084
- var akiIssuer = _generalNamesFromDer(akids[1].children);
1084
+ var akiIssuer = _generalNamesFromDer(akids[1].children, ctx);
1085
1085
  if (akiIssuer == null) return null;
1086
1086
  var akiSerial = asn1.read.integerImplicit(akids[2], 2);
1087
1087
  if (akiSerial < 0n) return null;
@@ -1112,7 +1112,7 @@ function _extValueFromDer(name, der) {
1112
1112
  if (sanMembers.length === 1 && sanMembers[0].tagClass === "context" && sanMembers[0].tagNumber === 2) {
1113
1113
  return cbor.build.textString(schema.walk(GN_LEAF, sanMembers[0], NS).value);
1114
1114
  }
1115
- var sanItems = _generalNamesFromDer(sanMembers);
1115
+ var sanItems = _generalNamesFromDer(sanMembers, ctx);
1116
1116
  return sanItems == null ? null : cbor.build.array(sanItems);
1117
1117
  }
1118
1118
  case "nameConstraints": {
@@ -1122,7 +1122,7 @@ function _extValueFromDer(name, der) {
1122
1122
  var ncf = node.children[nci];
1123
1123
  if (ncf.tagClass !== "context" || ncf.tagNumber <= ncLast) return null;
1124
1124
  ncLast = ncf.tagNumber;
1125
- var subItems = _subtreesFromDer(ncf.children || []);
1125
+ var subItems = _subtreesFromDer(ncf.children || [], ctx);
1126
1126
  if (subItems == null) return null;
1127
1127
  if (ncf.tagNumber === 0) { permCbor = cbor.build.array(subItems); sawNc = true; }
1128
1128
  else if (ncf.tagNumber === 1) { exclCbor = cbor.build.array(subItems); sawNc = true; }
@@ -1151,7 +1151,7 @@ function _extValueFromDer(name, der) {
1151
1151
  var dps = node.children || [];
1152
1152
  if (node.tagClass !== "universal" || node.tagNumber !== asn1.TAGS.SEQUENCE || dps.length < 1) return null;
1153
1153
  var dpResults = [];
1154
- for (var dpi = 0; dpi < dps.length; dpi++) { var dpr = _dpFromDer(dps[dpi]); if (dpr == null) return null; dpResults.push(dpr); }
1154
+ for (var dpi = 0; dpi < dps.length; dpi++) { var dpr = _dpFromDer(dps[dpi], ctx); if (dpr == null) return null; dpResults.push(dpr); }
1155
1155
  if (dpResults.length === 1 && dpResults[0].oneUri != null && dpResults[0].noReasons && dpResults[0].noIssuer) return cbor.build.textString(dpResults[0].oneUri);
1156
1156
  return cbor.build.array(dpResults.map(function (r) { return r.triple; }));
1157
1157
  }
@@ -1377,14 +1377,14 @@ function _sdaFromDer(node) {
1377
1377
  * the codec represents it, and one without a codec always, since its int form carries the same
1378
1378
  * bytes. An extension the codec cannot represent keeps the generic form on every certificate type.
1379
1379
  */
1380
- function _assertSpecificExtension(dotted, valContent, isNative) {
1380
+ function _assertSpecificExtension(dotted, valContent, isNative, ctx) {
1381
1381
  var canon = EXT_NAME_BY_OID[dotted];
1382
1382
  if (!isNative || canon === undefined) return;
1383
- if (EXT_COMPACT[canon] && (!Buffer.isBuffer(valContent) || _tryCompactExtValue(canon, valContent, isNative) == null)) return;
1383
+ if (EXT_COMPACT[canon] && (!Buffer.isBuffer(valContent) || _tryCompactExtValue(canon, valContent, isNative, ctx) == null)) return;
1384
1384
  throw _err("c509/non-specific-encoding", "extension " + canon + " has a C509 registry id that carries this value, and a natively signed certificate uses only the specific CBOR encoding (draft sec. 3.7)");
1385
1385
  }
1386
- function _tryCompactExtValue(name, der, isNative) {
1387
- var compact = _extValueFromDer(name, der);
1386
+ function _tryCompactExtValue(name, der, isNative, ctx) {
1387
+ var compact = _extValueFromDer(name, der, ctx);
1388
1388
  if (compact == null) return null;
1389
1389
  var recon;
1390
1390
  try {
@@ -1395,7 +1395,7 @@ function _tryCompactExtValue(name, der, isNative) {
1395
1395
  return recon.equals(der) ? compact : null;
1396
1396
  }
1397
1397
 
1398
- function _extensions(node, isNative) {
1398
+ function _extensions(node, isNative, ctx) {
1399
1399
  if (node.majorType === 0 || node.majorType === 1) {
1400
1400
  var iv = Number(cbor.read.int(node));
1401
1401
  return [{ name: "keyUsage", oid: oid.byName("keyUsage"), critical: iv < 0, keyUsageBits: Math.abs(iv) }];
@@ -1437,7 +1437,7 @@ function _extensions(node, isNative) {
1437
1437
  if (valNode.majorType !== 2) throw _err("c509/bad-extensions", "a non-critical ~oid extension value must be a byte string");
1438
1438
  valContent = valNode.content;
1439
1439
  }
1440
- _assertSpecificExtension(extOid, valContent, isNative);
1440
+ _assertSpecificExtension(extOid, valContent, isNative, ctx);
1441
1441
  }
1442
1442
  out.push({ name: name, oid: extOid, critical: critical, value: valContent || null });
1443
1443
  }
@@ -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);
@@ -1621,21 +1637,49 @@ function parse(input) {
1621
1637
  if (f[8].majorType !== 2) throw _err("c509/bad-spki", "subjectPublicKey must be a CBOR byte string");
1622
1638
  subjectPublicKey = f[8].content;
1623
1639
  }
1624
- var extensions = _extensions(f[9], type === 2);
1640
+ /** @internal A native certificate's extension values are opaque DER inside the CBOR, so reading
1641
+ * one is a second decode that restarts depth and item counting and takes the caps this door
1642
+ * was given. The latch is re-read before the result is returned. */
1643
+ var nested = pkix.nestedContext(NS, caps);
1644
+ var result;
1645
+ try {
1646
+ result = _finishParse(f, root, nested, {
1647
+ type: type, serial: serial, sHex: sHex, sigAlg: sigAlg, issuer: issuer,
1648
+ notBefore: notBefore, notAfter: notAfter, subject: subject, spkAlg: spkAlg,
1649
+ subjectPublicKey: subjectPublicKey, rsaKey: rsaKey,
1650
+ });
1651
+ } catch (e) {
1652
+ if (nested.budget.exhausted() && !(e && e.code === "c509/too-large")) throw _budgetRefusal(e);
1653
+ throw e;
1654
+ }
1655
+ if (nested.budget.exhausted()) throw _budgetRefusal(null);
1656
+ return result;
1657
+ }
1658
+
1659
+ /** @internal The refusal a cap produces, as distinct from a statement about the bytes. It is read
1660
+ * off the latch rather than off a throw, because the probe that reads an extension value absorbs
1661
+ * a failure by returning null. */
1662
+ function _budgetRefusal(cause) {
1663
+ return _err("c509/too-large", "the C509 certificate exceeds the decode budget for this parse", cause || undefined);
1664
+ }
1665
+
1666
+ function _finishParse(f, root, nested, fields) {
1667
+ var type = fields.type;
1668
+ var extensions = _extensions(f[9], type === 2, nested.ns);
1625
1669
  if (f[10].majorType !== 2) throw _err("c509/bad-signature", "issuerSignatureValue must be a CBOR byte string");
1626
1670
  var signatureValue = f[10].content;
1627
1671
 
1628
1672
  var result = {
1629
1673
  certificateType: type,
1630
- serialNumber: serial,
1631
- serialNumberHex: sHex,
1632
- signatureAlgorithm: sigAlg,
1633
- issuer: issuer,
1634
- validity: { notBefore: notBefore, notAfter: notAfter },
1635
- subject: subject,
1636
- subjectPublicKeyAlgorithm: spkAlg,
1637
- subjectPublicKey: subjectPublicKey,
1638
- rsaPublicKey: rsaKey,
1674
+ serialNumber: fields.serial,
1675
+ serialNumberHex: fields.sHex,
1676
+ signatureAlgorithm: fields.sigAlg,
1677
+ issuer: fields.issuer,
1678
+ validity: { notBefore: fields.notBefore, notAfter: fields.notAfter },
1679
+ subject: fields.subject,
1680
+ subjectPublicKeyAlgorithm: fields.spkAlg,
1681
+ subjectPublicKey: fields.subjectPublicKey,
1682
+ rsaPublicKey: fields.rsaKey,
1639
1683
  extensions: extensions,
1640
1684
  signatureValue: signatureValue,
1641
1685
  };
@@ -1644,7 +1688,6 @@ function parse(input) {
1644
1688
  if (type === 2) result.signedData = result._fieldBytes;
1645
1689
 
1646
1690
  if (type === 3) result.reconstructedDer = _reconstructDer(result, f[10]);
1647
-
1648
1691
  return result;
1649
1692
  }
1650
1693
 
@@ -1867,8 +1910,8 @@ function _euiFromCn(value) {
1867
1910
  if (bytes[3] === 0xff && bytes[4] === 0xfe) return Buffer.concat([bytes.subarray(0, 3), bytes.subarray(5)]);
1868
1911
  return bytes;
1869
1912
  }
1870
- function _c509NameFromDer(nameBytes) {
1871
- var node = asn1.decode(nameBytes);
1913
+ function _c509NameFromDer(nameBytes, ctx) {
1914
+ var node = pkix.decodeNested(nameBytes, ctx, "a Name value");
1872
1915
  var rdns = [];
1873
1916
  (node.children || []).forEach(function (rdnSet) {
1874
1917
  if (!rdnSet.children || rdnSet.children.length !== 1) throw _err("c509/non-invertible", "a C509 Name requires single-attribute RDNs");
@@ -1889,10 +1932,10 @@ function _c509NameFromDer(nameBytes) {
1889
1932
  });
1890
1933
  return { rdns: rdns };
1891
1934
  }
1892
- function _keyUsageBitsFromDer(extnValue) {
1935
+ function _keyUsageBitsFromDer(extnValue, ctx) {
1893
1936
  var bs;
1894
1937
  try {
1895
- bs = asn1.read.bitString(asn1.decode(extnValue));
1938
+ bs = asn1.read.bitString(pkix.decodeNested(extnValue, ctx, "a keyUsage extension value"));
1896
1939
  } catch (_e) {
1897
1940
  return null;
1898
1941
  }
@@ -1909,6 +1952,7 @@ function _magBytes(intNode) {
1909
1952
  }
1910
1953
  function _sigMagWidth(derSig) {
1911
1954
  var n;
1955
+ // allow:bare-decode on the DER -> C509 conversion, below the X.509 parse door that caps the input.
1912
1956
  try { n = asn1.decode(derSig); } catch (e) { throw _err("c509/bad-signature", "the ECDSA issuer signature is not valid DER", e); }
1913
1957
  if (n.tagNumber !== asn1.TAGS.SEQUENCE || n.tagClass !== "universal" || !n.children || n.children.length !== 2) throw _err("c509/bad-signature", "the ECDSA issuer signature must be a SEQUENCE of two INTEGERs");
1914
1958
  for (var i = 0; i < 2; i++) {
@@ -1953,12 +1997,14 @@ _derToType3 = function (input, opts) {
1953
1997
  var sigAlgName = sigCanon === undefined ? c.signatureAlgorithm.name : sigCanon;
1954
1998
  if (String(sigAlgName || "").toLowerCase().indexOf("ecdsa") !== 0) throw _err("c509/non-invertible", "type-3 C509 encoding covers only ECDSA-signed certificates; got " + (sigAlgName || "an unregistered algorithm"));
1955
1999
  if (c.subjectPublicKeyInfo.algorithm.oid !== oid.byName("ecPublicKey")) throw _err("c509/non-invertible", "type-3 C509 encoding covers only EC (ecPublicKey) certificates in v1; got " + (c.subjectPublicKeyInfo.algorithm.name || "an unregistered algorithm"));
2000
+ // allow:bare-decode on the DER -> C509 conversion, below the X.509 parse door that caps the input.
1956
2001
  var curveOid = asn1.read.oid(asn1.decode(c.subjectPublicKeyInfo.algorithm.parameters));
1957
2002
  var curveCanon = EC_CURVE_BY_OID[curveOid];
1958
2003
  var curve = curveCanon === undefined ? oid.name(curveOid) : curveCanon;
1959
2004
  var coordLen = EC_FIELD_BYTES[curve];
1960
2005
  if (!coordLen) throw _err("c509/non-invertible", "unsupported EC subject curve " + (curve || curveOid));
1961
2006
  var sigCurve = _resolveIssuerSigCurve(c, opts, sigAlgName);
2007
+ // allow:bare-decode on the DER -> C509 conversion, below the X.509 parse door that caps the input.
1962
2008
  var spkiNode = asn1.decode(c.subjectPublicKeyInfo.bytes);
1963
2009
  return {
1964
2010
  certificateType: 3,
package/lib/schema-cmc.js CHANGED
@@ -195,8 +195,8 @@ function readExtendedFailInfo(children, what) {
195
195
  return { failInfoOID: asn1.read.oid(children[0]), failInfoValueBytes: children[1].bytes };
196
196
  }
197
197
 
198
- function parseStatusInfo(valueDer, version, bodyPartID) {
199
- var root = asn1.decode(valueDer);
198
+ function parseStatusInfo(valueDer, version, bodyPartID, ctx) {
199
+ var root = pkix.decodeNested(valueDer, ctx, "a CMCStatusInfo value");
200
200
  if (root.tagClass !== "universal" || root.tagNumber !== TAGS.SEQUENCE || !root.constructed) {
201
201
  throw E("cmc/bad-status-info", "a CMCStatusInfo value must be a SEQUENCE");
202
202
  }
@@ -447,14 +447,15 @@ var PKI_RESPONSE = schema.seq([
447
447
  * that is not one could never be checked against anything. This is asked of a CHALLENGED request only.
448
448
  * A request body carries what its sender sent, and judging it is the enrollment's business, not the
449
449
  * decoder's. */
450
- function _assertAnswerableRequest(request) {
450
+ function _assertAnswerableRequest(request, ctx) {
451
451
  if (request.arm !== "tcr" && request.arm !== "crm") return;
452
452
  /** @internal A CertReqMessages of one, which is the shape the CRMF parser takes; built outside the
453
453
  * catch below so only the parse it wraps can be what fails. */
454
454
  var payload = request.arm === "tcr" ? request.certificationRequestBytes
455
455
  : asn1.build.sequence([asn1.build.raw(request.certReqMsgBytes)]);
456
456
  try {
457
- if (request.arm === "tcr") { csr.parse(payload); } else { crmf.parse(payload); }
457
+ var caps = ctx ? ctx.caps : undefined;
458
+ if (request.arm === "tcr") { csr.parse(payload, caps); } else { crmf.parse(payload, caps); }
458
459
  } catch (e) {
459
460
  throw E("cmc/bad-pop-challenge",
460
461
  "the request an EncryptedPOP quotes is not a " +
@@ -464,13 +465,13 @@ function _assertAnswerableRequest(request) {
464
465
  }
465
466
  }
466
467
 
467
- function parseEncryptedPop(node) {
468
+ function parseEncryptedPop(node, ctx) {
468
469
  if (node.tagClass !== "universal" || node.tagNumber !== TAGS.SEQUENCE || !node.constructed ||
469
470
  node.children.length !== 5) {
470
471
  throw E("cmc/bad-pop-challenge",
471
472
  "an EncryptedPOP is SEQUENCE { request, cms, thePOPAlgID, witnessAlgID, witness } (RFC 5272 sec. 6.7)");
472
473
  }
473
- var envelope = _assertEnvelopedChallenge(node.children[1]);
474
+ var envelope = _assertEnvelopedChallenge(node.children[1], ctx);
474
475
  var request;
475
476
  try { request = parseTaggedRequest(node.children[0]); }
476
477
  catch (e) {
@@ -478,7 +479,7 @@ function parseEncryptedPop(node) {
478
479
  "an EncryptedPOP quotes the request it challenges as a TaggedRequest: " +
479
480
  ((e && e.message) || "malformed") + " (RFC 5272 sec. 6.7)", e);
480
481
  }
481
- _assertAnswerableRequest(request);
482
+ _assertAnswerableRequest(request, ctx);
482
483
  return {
483
484
  request: request,
484
485
  /** @internal The TaggedRequest exactly as it arrived. A proof of possession is a MAC over these
@@ -526,7 +527,7 @@ function parseDecryptedPop(node) {
526
527
  * on unopened because opening them needs key material this parser does not hold. Unopened is not
527
528
  * unchecked: a field that is not a ContentInfo carrying an EnvelopedData cannot be a challenge, and
528
529
  * saying so here is what keeps a control that could never be answered from reading as one. */
529
- function _assertEnvelopedChallenge(node) {
530
+ function _assertEnvelopedChallenge(node, ctx) {
530
531
  var kids = pkix.rootSequenceChildren(node, 2, 2);
531
532
  if (!kids) {
532
533
  throw E("cmc/bad-pop-challenge",
@@ -557,7 +558,7 @@ function _assertEnvelopedChallenge(node) {
557
558
  * a challenge addressed only that way is refused here, and `pki.cms.decrypt` could not open it
558
559
  * either, so it is refused where the reason can be named rather than after a failed answer. */
559
560
  var enveloped;
560
- try { enveloped = cms.walkEnvelopedData(content.children[0]); }
561
+ try { enveloped = cms.walkEnvelopedData(content.children[0], ctx); }
561
562
  catch (e) {
562
563
  throw E("cmc/bad-pop-challenge",
563
564
  "an EncryptedPOP's ContentInfo does not carry an EnvelopedData a recipient could open: " +
@@ -594,26 +595,26 @@ function _popAlgorithm(node, what) {
594
595
  }
595
596
 
596
597
  /** @internal One AttributeValue, which is what a control carrying a single structure holds. */
597
- function _onlyValue(c, what) {
598
+ function _onlyValue(c, what, ctx) {
598
599
  if (c.values.length !== 1) {
599
600
  throw E("cmc/bad-pop-challenge",
600
601
  "a " + what + " control carries exactly one AttributeValue, got " + c.values.length);
601
602
  }
602
- return asn1.decode(c.values[0]);
603
+ return pkix.decodeNested(c.values[0], ctx, what + " control value");
603
604
  }
604
605
 
605
- function _decodePopChallenges(controls) {
606
+ function _decodePopChallenges(controls, ctx) {
606
607
  for (var i = 0; i < controls.length; i++) {
607
608
  var c = controls[i];
608
609
  if (c.attrType === OID_ENCRYPTED_POP) {
609
- c.encryptedPOP = parseEncryptedPop(_onlyValue(c, "an Encrypted POP"));
610
+ c.encryptedPOP = parseEncryptedPop(_onlyValue(c, "an Encrypted POP", ctx), ctx);
610
611
  } else if (c.attrType === OID_DECRYPTED_POP) {
611
- c.decryptedPOP = parseDecryptedPop(_onlyValue(c, "a Decrypted POP"));
612
+ c.decryptedPOP = parseDecryptedPop(_onlyValue(c, "a Decrypted POP", ctx));
612
613
  }
613
614
  }
614
615
  }
615
616
 
616
- function _collectStatuses(controls) {
617
+ function _collectStatuses(controls, ctx) {
617
618
  var out = [];
618
619
  for (var i = 0; i < controls.length; i++) {
619
620
  var c = controls[i];
@@ -622,15 +623,15 @@ function _collectStatuses(controls) {
622
623
  if (c.values.length !== 1) {
623
624
  throw E("cmc/bad-status-info", "a CMC status control carries exactly one AttributeValue, got " + c.values.length);
624
625
  }
625
- out.push(parseStatusInfo(c.values[0], version, c.bodyPartID));
626
+ out.push(parseStatusInfo(c.values[0], version, c.bodyPartID, ctx));
626
627
  }
627
628
  return out;
628
629
  }
629
630
 
630
- function _finishBody(body, where) {
631
+ function _finishBody(body, where, ctx) {
631
632
  for (var i = 0; i < body.controls.length; i++) assertControlPlacement(body.controls[i], where);
632
- _decodePopChallenges(body.controls);
633
- body.statuses = _collectStatuses(body.controls);
633
+ _decodePopChallenges(body.controls, ctx);
634
+ body.statuses = _collectStatuses(body.controls, ctx);
634
635
  body.unhandled = body.controls.filter(function (c) {
635
636
  return c.attrType !== OID_STATUS_INFO && c.attrType !== OID_STATUS_INFO_V2 && c.attrName === null;
636
637
  });
@@ -646,7 +647,7 @@ function _finishBody(body, where) {
646
647
 
647
648
  /**
648
649
  * @primitive pki.schema.cmc.parsePkiData
649
- * @signature pki.schema.cmc.parsePkiData(der) -> parsed
650
+ * @signature pki.schema.cmc.parsePkiData(der, caps?) -> parsed
650
651
  * @since 0.4.16
651
652
  * @status stable
652
653
  * @spec RFC 5272 sec. 3.2.1
@@ -664,17 +665,23 @@ function _finishBody(body, where) {
664
665
  * var der = b.sequence([b.sequence([]), b.sequence([]), b.sequence([]), b.sequence([])]);
665
666
  * var d = pki.schema.cmc.parsePkiData(der);
666
667
  * d.controls.length; // 0 -- present and empty, which is legal
668
+ * @opts
669
+ * - `maxBytes` / `maxDepth` / `maxItems` (number) -- decode caps for this parse. Each
670
+ * defaults to the matching `pki.C.LIMITS` figure and may only be set lower; a value
671
+ * above it, or an option outside this set, is refused. A parse that exceeds one is
672
+ * refused with the `/too-large` code of this format.
667
673
  */
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");
674
+ function parsePkiData(input, callerOpts) {
675
+ return pkix.runParseWith(input, _BODY_PARSE_OPTS, callerOpts, function (root, ns) {
676
+ var body = schema.walk(PKI_DATA, root, ns).result;
677
+ body.bytes = root.bytes;
678
+ return _finishBody(body, "pkiData", ns);
679
+ });
673
680
  }
674
681
 
675
682
  /**
676
683
  * @primitive pki.schema.cmc.parsePkiResponse
677
- * @signature pki.schema.cmc.parsePkiResponse(der) -> parsed
684
+ * @signature pki.schema.cmc.parsePkiResponse(der, caps?) -> parsed
678
685
  * @since 0.4.16
679
686
  * @status stable
680
687
  * @spec RFC 5272 sec. 4.2.1
@@ -690,17 +697,33 @@ function parsePkiData(input) {
690
697
  * var der = b.sequence([b.sequence([]), b.sequence([]), b.sequence([])]);
691
698
  * var r = pki.schema.cmc.parsePkiResponse(der);
692
699
  * r.statuses.length; // 0 -- no status control means success is assumed
700
+ * @opts
701
+ * - `maxBytes` / `maxDepth` / `maxItems` (number) -- decode caps for this parse. Each
702
+ * defaults to the matching `pki.C.LIMITS` figure and may only be set lower; a value
703
+ * above it, or an option outside this set, is refused. A parse that exceeds one is
704
+ * refused with the `/too-large` code of this format.
693
705
  */
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");
706
+ function parsePkiResponse(input, callerOpts) {
707
+ return pkix.runParseWith(input, _BODY_PARSE_OPTS, callerOpts, function (root, ns) {
708
+ var body = schema.walk(PKI_RESPONSE, root, ns).result;
709
+ body.bytes = root.bytes;
710
+ return _finishBody(body, "pkiResponse", ns);
711
+ });
699
712
  }
700
713
 
714
+ /** @internal The door options for the two body parsers, which read a bare DER body rather than
715
+ * a CMS carrier, so they take no PEM label. `rawDecodeErrors` keeps the codec's own code for a
716
+ * malformed body, which callers already pin. */
717
+ var _CARRIER_PARSE_OPTS = pkix.parseOpts({ ErrorClass: CmcError, prefix: "cmc", what: "the CMC message" });
718
+
719
+ var _BODY_PARSE_OPTS = pkix.parseOpts({
720
+ ns: NS, ErrorClass: CmcError, PemError: CmcError, prefix: "cmc",
721
+ what: "the CMC body", pemLabel: null, rawDecodeErrors: true,
722
+ });
723
+
701
724
  /**
702
725
  * @primitive pki.schema.cmc.parse
703
- * @signature pki.schema.cmc.parse(input) -> parsed
726
+ * @signature pki.schema.cmc.parse(input, caps?) -> parsed
704
727
  * @since 0.4.16
705
728
  * @status stable
706
729
  * @spec RFC 5272, RFC 6402
@@ -724,17 +747,31 @@ function parsePkiResponse(input) {
724
747
  * var sd = b.sequence([b.integer(3n), b.set([]), encap, b.set([])]);
725
748
  * var der = b.sequence([b.oid("1.2.840.113549.1.7.2"), b.explicit(0, sd)]);
726
749
  * pki.schema.cmc.parse(der).kind; // "pkiData"
750
+ * @opts
751
+ * - `maxBytes` / `maxDepth` / `maxItems` (number) -- decode caps for this parse. Each
752
+ * defaults to the matching `pki.C.LIMITS` figure and may only be set lower; a value
753
+ * above it, or an option outside this set, is refused. A parse that exceeds one is
754
+ * refused with the `/too-large` code of this format.
727
755
  */
728
- function parse(input) {
756
+ function parse(input, callerOpts) {
757
+ /** @internal Before the branch, because the already-parsed arm reaches no decode and would
758
+ * otherwise hold a caller to the option contract only when they passed bytes. The snapshot
759
+ * is what travels onward: handing the caller's own object to the CMS parser would read each
760
+ * cap a second time, and a value read twice need not answer the same way twice. */
761
+ var caps = pkix.parseCaps(callerOpts, _CARRIER_PARSE_OPTS);
729
762
  var alreadyParsed = input && typeof input === "object" && !ArrayBuffer.isView(input) &&
730
763
  !guard.bytes.isByteSource(input) && input.encapContentInfo != null;
731
764
  var parsedCms;
732
765
  if (alreadyParsed) { parsedCms = input; }
733
766
  else {
734
767
  /** @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); }
768
+ * other format parser answers for its own input in its own domain. A resource refusal is the one
769
+ * exception: it is not a statement about the bytes, so it keeps its own code. */
770
+ try { parsedCms = cms.parse(input, caps); }
737
771
  catch (e) {
772
+ if (e && typeof e.code === "string" && e.code.indexOf("/too-large") !== -1) {
773
+ throw E("cmc/too-large", "the CMC message exceeds the decode budget for this parse: " + (e.message || ""), e);
774
+ }
738
775
  throw E("cmc/bad-der",
739
776
  "a CMC message is carried in a CMS SignedData or AuthenticatedData, and these bytes are not " +
740
777
  "one: " + ((e && e.message) || "malformed") + " (RFC 5272 sec. 3.2 / 4.2)", e);
@@ -757,7 +794,9 @@ function parse(input) {
757
794
  throw E("cmc/no-content",
758
795
  "a Full PKI Request / Response carries its body as the encapsulated content; this SignedData is detached (RFC 5272 sec. 3.2)");
759
796
  }
760
- var body = eContentType === OID_PKI_DATA ? parsePkiData(encap.eContent) : parsePkiResponse(encap.eContent);
797
+ var body = eContentType === OID_PKI_DATA
798
+ ? parsePkiData(encap.eContent, caps)
799
+ : parsePkiResponse(encap.eContent, caps);
761
800
  body.cms = parsedCms;
762
801
  body.eContentType = eContentType;
763
802
  return body;