@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.
- package/CHANGELOG.md +41 -0
- package/lib/guard-limits.js +48 -1
- package/lib/guard-parsed.js +4 -4
- package/lib/schema-all.js +29 -18
- package/lib/schema-attrcert.js +52 -35
- package/lib/schema-c509.js +92 -46
- package/lib/schema-cmc.js +75 -36
- package/lib/schema-cmp.js +14 -9
- package/lib/schema-cms.js +45 -29
- package/lib/schema-crl.js +17 -12
- package/lib/schema-crmf.js +17 -7
- package/lib/schema-csr.js +6 -1
- package/lib/schema-csrattrs.js +14 -7
- package/lib/schema-engine.js +65 -2
- package/lib/schema-ocsp.js +29 -18
- package/lib/schema-pkcs12.js +21 -12
- package/lib/schema-pkcs8.js +21 -4
- package/lib/schema-pkix.js +183 -10
- package/lib/schema-smime.js +12 -2
- package/lib/schema-tsp.js +54 -9
- package/lib/schema-x509.js +6 -1
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/schema-c509.js
CHANGED
|
@@ -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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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 {
|
|
1590
|
-
|
|
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
|
-
|
|
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 =
|
|
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(
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
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
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
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
|
-
|
|
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
|
|
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;
|