@blamejs/pki 0.8.3 → 0.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -0
- package/README.md +27 -0
- package/lib/cms-sign.js +16 -6
- package/lib/cms-verify.js +2 -1
- package/lib/csr-sign.js +2 -1
- package/lib/guard-parsed.js +2 -2
- package/lib/ocsp.js +18 -7
- package/lib/pki-build.js +1 -1
- package/lib/schema-attrcert.js +36 -26
- package/lib/schema-c509.js +72 -42
- package/lib/schema-cmc.js +25 -22
- package/lib/schema-cmp.js +8 -8
- package/lib/schema-cms.js +39 -28
- package/lib/schema-crl.js +11 -11
- package/lib/schema-crmf.js +11 -6
- package/lib/schema-csrattrs.js +8 -6
- package/lib/schema-engine.js +65 -2
- package/lib/schema-ocsp.js +17 -16
- package/lib/schema-pkcs12.js +15 -11
- package/lib/schema-pkcs8.js +9 -2
- package/lib/schema-pkix.js +44 -17
- package/lib/schema-tsp.js +16 -3
- package/lib/sign-scheme.js +117 -5
- package/lib/x509-sign.js +7 -2
- 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
|
}
|
|
@@ -1637,21 +1637,49 @@ function parse(input, callerOpts) {
|
|
|
1637
1637
|
if (f[8].majorType !== 2) throw _err("c509/bad-spki", "subjectPublicKey must be a CBOR byte string");
|
|
1638
1638
|
subjectPublicKey = f[8].content;
|
|
1639
1639
|
}
|
|
1640
|
-
|
|
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);
|
|
1641
1669
|
if (f[10].majorType !== 2) throw _err("c509/bad-signature", "issuerSignatureValue must be a CBOR byte string");
|
|
1642
1670
|
var signatureValue = f[10].content;
|
|
1643
1671
|
|
|
1644
1672
|
var result = {
|
|
1645
1673
|
certificateType: type,
|
|
1646
|
-
serialNumber: serial,
|
|
1647
|
-
serialNumberHex: sHex,
|
|
1648
|
-
signatureAlgorithm: sigAlg,
|
|
1649
|
-
issuer: issuer,
|
|
1650
|
-
validity: { notBefore: notBefore, notAfter: notAfter },
|
|
1651
|
-
subject: subject,
|
|
1652
|
-
subjectPublicKeyAlgorithm: spkAlg,
|
|
1653
|
-
subjectPublicKey: subjectPublicKey,
|
|
1654
|
-
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,
|
|
1655
1683
|
extensions: extensions,
|
|
1656
1684
|
signatureValue: signatureValue,
|
|
1657
1685
|
};
|
|
@@ -1660,7 +1688,6 @@ function parse(input, callerOpts) {
|
|
|
1660
1688
|
if (type === 2) result.signedData = result._fieldBytes;
|
|
1661
1689
|
|
|
1662
1690
|
if (type === 3) result.reconstructedDer = _reconstructDer(result, f[10]);
|
|
1663
|
-
|
|
1664
1691
|
return result;
|
|
1665
1692
|
}
|
|
1666
1693
|
|
|
@@ -1883,8 +1910,8 @@ function _euiFromCn(value) {
|
|
|
1883
1910
|
if (bytes[3] === 0xff && bytes[4] === 0xfe) return Buffer.concat([bytes.subarray(0, 3), bytes.subarray(5)]);
|
|
1884
1911
|
return bytes;
|
|
1885
1912
|
}
|
|
1886
|
-
function _c509NameFromDer(nameBytes) {
|
|
1887
|
-
var node =
|
|
1913
|
+
function _c509NameFromDer(nameBytes, ctx) {
|
|
1914
|
+
var node = pkix.decodeNested(nameBytes, ctx, "a Name value");
|
|
1888
1915
|
var rdns = [];
|
|
1889
1916
|
(node.children || []).forEach(function (rdnSet) {
|
|
1890
1917
|
if (!rdnSet.children || rdnSet.children.length !== 1) throw _err("c509/non-invertible", "a C509 Name requires single-attribute RDNs");
|
|
@@ -1905,10 +1932,10 @@ function _c509NameFromDer(nameBytes) {
|
|
|
1905
1932
|
});
|
|
1906
1933
|
return { rdns: rdns };
|
|
1907
1934
|
}
|
|
1908
|
-
function _keyUsageBitsFromDer(extnValue) {
|
|
1935
|
+
function _keyUsageBitsFromDer(extnValue, ctx) {
|
|
1909
1936
|
var bs;
|
|
1910
1937
|
try {
|
|
1911
|
-
bs = asn1.read.bitString(
|
|
1938
|
+
bs = asn1.read.bitString(pkix.decodeNested(extnValue, ctx, "a keyUsage extension value"));
|
|
1912
1939
|
} catch (_e) {
|
|
1913
1940
|
return null;
|
|
1914
1941
|
}
|
|
@@ -1925,6 +1952,7 @@ function _magBytes(intNode) {
|
|
|
1925
1952
|
}
|
|
1926
1953
|
function _sigMagWidth(derSig) {
|
|
1927
1954
|
var n;
|
|
1955
|
+
// allow:bare-decode on the DER -> C509 conversion, below the X.509 parse door that caps the input.
|
|
1928
1956
|
try { n = asn1.decode(derSig); } catch (e) { throw _err("c509/bad-signature", "the ECDSA issuer signature is not valid DER", e); }
|
|
1929
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");
|
|
1930
1958
|
for (var i = 0; i < 2; i++) {
|
|
@@ -1969,12 +1997,14 @@ _derToType3 = function (input, opts) {
|
|
|
1969
1997
|
var sigAlgName = sigCanon === undefined ? c.signatureAlgorithm.name : sigCanon;
|
|
1970
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"));
|
|
1971
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.
|
|
1972
2001
|
var curveOid = asn1.read.oid(asn1.decode(c.subjectPublicKeyInfo.algorithm.parameters));
|
|
1973
2002
|
var curveCanon = EC_CURVE_BY_OID[curveOid];
|
|
1974
2003
|
var curve = curveCanon === undefined ? oid.name(curveOid) : curveCanon;
|
|
1975
2004
|
var coordLen = EC_FIELD_BYTES[curve];
|
|
1976
2005
|
if (!coordLen) throw _err("c509/non-invertible", "unsupported EC subject curve " + (curve || curveOid));
|
|
1977
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.
|
|
1978
2008
|
var spkiNode = asn1.decode(c.subjectPublicKeyInfo.bytes);
|
|
1979
2009
|
return {
|
|
1980
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
|
});
|
|
@@ -674,7 +675,7 @@ function parsePkiData(input, callerOpts) {
|
|
|
674
675
|
return pkix.runParseWith(input, _BODY_PARSE_OPTS, callerOpts, function (root, ns) {
|
|
675
676
|
var body = schema.walk(PKI_DATA, root, ns).result;
|
|
676
677
|
body.bytes = root.bytes;
|
|
677
|
-
return _finishBody(body, "pkiData");
|
|
678
|
+
return _finishBody(body, "pkiData", ns);
|
|
678
679
|
});
|
|
679
680
|
}
|
|
680
681
|
|
|
@@ -706,7 +707,7 @@ function parsePkiResponse(input, callerOpts) {
|
|
|
706
707
|
return pkix.runParseWith(input, _BODY_PARSE_OPTS, callerOpts, function (root, ns) {
|
|
707
708
|
var body = schema.walk(PKI_RESPONSE, root, ns).result;
|
|
708
709
|
body.bytes = root.bytes;
|
|
709
|
-
return _finishBody(body, "pkiResponse");
|
|
710
|
+
return _finishBody(body, "pkiResponse", ns);
|
|
710
711
|
});
|
|
711
712
|
}
|
|
712
713
|
|
|
@@ -793,7 +794,9 @@ function parse(input, callerOpts) {
|
|
|
793
794
|
throw E("cmc/no-content",
|
|
794
795
|
"a Full PKI Request / Response carries its body as the encapsulated content; this SignedData is detached (RFC 5272 sec. 3.2)");
|
|
795
796
|
}
|
|
796
|
-
var body = eContentType === OID_PKI_DATA
|
|
797
|
+
var body = eContentType === OID_PKI_DATA
|
|
798
|
+
? parsePkiData(encap.eContent, caps)
|
|
799
|
+
: parsePkiResponse(encap.eContent, caps);
|
|
797
800
|
body.cms = parsedCms;
|
|
798
801
|
body.eContentType = eContentType;
|
|
799
802
|
return body;
|
package/lib/schema-cmp.js
CHANGED
|
@@ -283,7 +283,7 @@ var GEN_MSG_CONTENT = schema.seqOf(INFO_TYPE_AND_VALUE, {
|
|
|
283
283
|
build: function (m) { return m.items.map(function (it) { return it.value.result; }); },
|
|
284
284
|
});
|
|
285
285
|
|
|
286
|
-
var CERT_TEMPLATE_LEAF = schema.decode(function (n) { return crmf.walkCertTemplate(n); });
|
|
286
|
+
var CERT_TEMPLATE_LEAF = schema.decode(function (n, ctx) { return crmf.walkCertTemplate(n, ctx); });
|
|
287
287
|
var REV_DETAILS = schema.seq([
|
|
288
288
|
schema.field("certDetails", CERT_TEMPLATE_LEAF),
|
|
289
289
|
schema.optional("crlEntryDetails", EXTENSIONS, { whenUniversal: [TAGS.SEQUENCE] }),
|
|
@@ -348,7 +348,7 @@ var ENCRYPTED_KEY = schema.choice([
|
|
|
348
348
|
{ when: { tagClass: "context", tagNumber: 0 },
|
|
349
349
|
schema: schema.decode(function (n, ctx) {
|
|
350
350
|
var env;
|
|
351
|
-
try { env = cms.walkEnvelopedData(
|
|
351
|
+
try { env = cms.walkEnvelopedData(pkix.decodeNested(asn1.sequenceTlv(n), ctx, "an EncryptedKey envelopedData"), ctx); }
|
|
352
352
|
catch (e) {
|
|
353
353
|
throw ctx.E("cmp/bad-cert-response", "envelopedData [0] must be a well-formed IMPLICIT EnvelopedData (RFC 9810 sec. 5.2.2)", e);
|
|
354
354
|
}
|
|
@@ -521,7 +521,7 @@ var BODY = schema.decode(function (n, ctx) {
|
|
|
521
521
|
var out = { arm: BODY_ARMS[n.tagNumber], tag: n.tagNumber, bytes: inner.bytes };
|
|
522
522
|
if (CRMF_ARMS[n.tagNumber]) {
|
|
523
523
|
var isCcr = n.tagNumber === 13;
|
|
524
|
-
out.decoded = crmf.walkCertReqMessages(inner, { allowSigningAlg: isCcr, allowV1Version: isCcr });
|
|
524
|
+
out.decoded = crmf.walkCertReqMessages(inner, { allowSigningAlg: isCcr, allowV1Version: isCcr }, ctx);
|
|
525
525
|
} else if (DECODED_ARMS[n.tagNumber]) {
|
|
526
526
|
var match = schema.walk(DECODED_ARMS[n.tagNumber], inner, ctx);
|
|
527
527
|
out.decoded = match.result;
|
|
@@ -612,7 +612,7 @@ var PKI_MESSAGE = schema.seq([
|
|
|
612
612
|
for (var cn = 0; cn < controls.length; cn++) {
|
|
613
613
|
if (controls[cn].type !== OID_PKI_ARCHIVE_OPTIONS) continue;
|
|
614
614
|
var arch;
|
|
615
|
-
try { arch =
|
|
615
|
+
try { arch = pkix.decodeNested(controls[cn].value, ctx, "a pkiArchiveOptions control value"); }
|
|
616
616
|
catch (e) { throw ctx.E("cmp/bad-body", "a ccr pkiArchiveOptions control did not decode (RFC 9810 sec. 5.3.11)", e); }
|
|
617
617
|
if (arch.tagClass === "context" && arch.tagNumber === 0) {
|
|
618
618
|
throw ctx.E("cmp/bad-body", "a cross-certification request (ccr) must not send the private key -- a pkiArchiveOptions encryptedPrivKey control is forbidden (RFC 9810 sec. 5.3.11, sec. 5.2.8.3.1)");
|
|
@@ -631,7 +631,7 @@ var PKI_MESSAGE = schema.seq([
|
|
|
631
631
|
if (archCtrls && header.pvno !== 3) {
|
|
632
632
|
for (var ac = 0; ac < archCtrls.length; ac++) {
|
|
633
633
|
if (archCtrls[ac].type !== OID_PKI_ARCHIVE_OPTIONS) continue;
|
|
634
|
-
var opt =
|
|
634
|
+
var opt = pkix.decodeNested(archCtrls[ac].value, ctx, "a pkiArchiveOptions control value");
|
|
635
635
|
if (opt.tagClass === "context" && opt.tagNumber === 0 && opt.children && opt.children.length >= 1 &&
|
|
636
636
|
opt.children[0].tagClass === "context" && opt.children[0].tagNumber === 0) {
|
|
637
637
|
throw ctx.E("cmp/bad-version", "a pkiArchiveOptions encryptedPrivKey using the EnvelopedData form requires CMP version cmp2021(3) (RFC 9810 sec. 5.2.8.3.1, sec. 7)");
|
|
@@ -643,7 +643,7 @@ var PKI_MESSAGE = schema.seq([
|
|
|
643
643
|
if (header.generalInfo) {
|
|
644
644
|
for (var gi = 0; gi < header.generalInfo.length; gi++) {
|
|
645
645
|
if (header.generalInfo[gi].type !== OID_CERT_PROFILE) continue;
|
|
646
|
-
var profileCount =
|
|
646
|
+
var profileCount = pkix.decodeNested(header.generalInfo[gi].value, ctx, "a certProfile infoValue").children.length;
|
|
647
647
|
var bodyCount = null;
|
|
648
648
|
if (body.arm === "p10cr") bodyCount = 1;
|
|
649
649
|
else if (body.decoded && body.decoded.messages) bodyCount = body.decoded.messages.length;
|
|
@@ -654,13 +654,13 @@ var PKI_MESSAGE = schema.seq([
|
|
|
654
654
|
}
|
|
655
655
|
}
|
|
656
656
|
if (body.arm === "ckuann" && header.pvno !== 3) {
|
|
657
|
-
var caKeyUpd =
|
|
657
|
+
var caKeyUpd = pkix.decodeNested(body.bytes, ctx, "a ckuann body");
|
|
658
658
|
if (caKeyUpd.tagClass === "context" && caKeyUpd.tagNumber === 0) {
|
|
659
659
|
throw ctx.E("cmp/bad-version", "a ckuann using the cAKeyUpdAnnV3 [0] RootCaKeyUpdateContent form requires CMP version cmp2021(3) (RFC 9810 sec. 5.3.13, sec. 7)");
|
|
660
660
|
}
|
|
661
661
|
}
|
|
662
662
|
if (body.arm === "popdecc" && header.pvno !== 3) {
|
|
663
|
-
var challenges =
|
|
663
|
+
var challenges = pkix.decodeNested(body.bytes, ctx, "a popdecc body");
|
|
664
664
|
if (challenges.tagClass === "universal" && challenges.tagNumber === TAGS.SEQUENCE && challenges.children) {
|
|
665
665
|
for (var chi = 0; chi < challenges.children.length; chi++) {
|
|
666
666
|
var chKids = challenges.children[chi].children;
|