@blamejs/pki 0.8.3 → 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 +19 -0
- package/lib/guard-parsed.js +2 -2
- 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/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,25 @@ All notable changes to `@blamejs/pki` are documented here. The format
|
|
|
4
4
|
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this
|
|
5
5
|
project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## v0.8.4 — 2026-09-25
|
|
8
|
+
|
|
9
|
+
A decode cap bounds every structure a parse reads, and a refusal caused by a cap is no longer reported as malformed input.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- `pki.schema.engine.decodeNested(bytes, ctx, label, opts?)`, the decode to perform on a value already read out of a parse's input. It applies the caps carried on the walk context and records a resource refusal against the parse before re-throwing, which is what a format parser composed on the engine needs so its embedded payloads answer to the caller's caps.
|
|
14
|
+
- `pki.schema.engine.withResources(ns, source)` carries a parse's caps and budget onto another namespace, for a format whose structure is carried inside another one. The structure is still walked in its own namespace, so a fault in it keeps that format's error class and code. `pki.schema.engine.isResourceRefusal(err)` reports whether a codec error was a limit rather than a claim about the bytes.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- An input refused for exceeding a decode limit reports `<format>/too-large` in cases where it previously reported the format's own malformed-value code, such as `cms/bad-aead-params` or `cmp/bad-body`. A parser that decodes a value out of its input wraps a decode failure in a code of its own, and that code sent a reader to inspect bytes that were fine and a cap that was too small. A caller matching on one of those codes to detect malformed input will stop seeing over-budget inputs under it. The wrapped error is carried on the refusal as its `cause`.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- A decode cap bounds every structure a parse reads, not the input alone. An embedded `OCTET STRING` payload, an encapsulated protocol body, a `SafeContents` inside a PKCS#12 store and an attribute value are all decoded under the caps the caller named. The 0.8.3 notes recorded that a structure decoded from inside the input kept the built-in `pki.C.LIMITS` defaults; it no longer does.
|
|
23
|
+
- `pki.schema.cmc.parse` applies the caller's caps to the `PKIData` or `PKIResponse` body. It read the caps, bounded the CMS carrier with them, and then decoded the body that carries the request or response with the built-in defaults.
|
|
24
|
+
- A parse cannot return a result after a decode limit has refused something inside that parse, whatever the code between the two does with the failure. A parser that catches a resource refusal and re-throws its own typed error is the common shape, and that shape previously carried the parse past the latch the budget sets. The door now reads the latch on the failure path as well as on the success path.
|
|
25
|
+
|
|
7
26
|
## v0.8.3 — 2026-09-25
|
|
8
27
|
|
|
9
28
|
A parser takes decode caps from its caller, and a resource refusal says so instead of reading as malformed input.
|
package/lib/guard-parsed.js
CHANGED
|
@@ -158,8 +158,8 @@ function recordingParser(kind, parse, ErrorClass, code, label) {
|
|
|
158
158
|
|
|
159
159
|
// @enforced-by guard-shape-reinlined -- shares recordingParser's shape below: the PROVENANCE.set
|
|
160
160
|
function recordingWalker(kind, walkNode, decodeBytes) {
|
|
161
|
-
return function (node) {
|
|
162
|
-
var out = walkNode(node);
|
|
161
|
+
return function (node, ctx) {
|
|
162
|
+
var out = walkNode(node, ctx);
|
|
163
163
|
if (out && typeof out === "object" && node && _isBytes(node.bytes)) {
|
|
164
164
|
_weakSet(PROVENANCE, out, {
|
|
165
165
|
kind: kind,
|
package/lib/schema-attrcert.js
CHANGED
|
@@ -147,7 +147,7 @@ var V2_FORM = schema.seq([
|
|
|
147
147
|
if (issuerName.names[0].tagNumber !== 4) {
|
|
148
148
|
throw ctx.E("attrcert/bad-issuer-name", "the issuerName GeneralName must be a directoryName [4] (RFC 5755 sec. 4.2.3)");
|
|
149
149
|
}
|
|
150
|
-
var dirName =
|
|
150
|
+
var dirName = pkix.decodeNested(issuerName.names[0].bytes, ctx, "the issuerName directoryName").children[0];
|
|
151
151
|
if (!dirName.children || dirName.children.length < 1) {
|
|
152
152
|
throw ctx.E("attrcert/bad-issuer-name", "the issuerName directoryName must be a non-empty distinguished name (RFC 5755 sec. 4.2.3)");
|
|
153
153
|
}
|
|
@@ -217,14 +217,14 @@ var ATTRIBUTE_CERTIFICATE = pkix.signedEnvelope(NS, ACINFO, {
|
|
|
217
217
|
var attributes = acinfo.fields.attributes.value.result;
|
|
218
218
|
attributes.forEach(function (a) {
|
|
219
219
|
var dec = _ATTR_VALUE_DECODERS[a.type];
|
|
220
|
-
a.decoded = a.values.map(function (v) { return dec ? dec(v) : { opaque: true, bytes: v }; });
|
|
220
|
+
a.decoded = a.values.map(function (v) { return dec ? dec(v, ctx) : { opaque: true, bytes: v }; });
|
|
221
221
|
});
|
|
222
222
|
|
|
223
223
|
var extField = acinfo.fields.extensions;
|
|
224
224
|
var extensions = (extField && extField.present) ? extField.value.result : [];
|
|
225
225
|
extensions.forEach(function (x) {
|
|
226
226
|
var dec = _AC_EXT_DECODERS[x.oid];
|
|
227
|
-
x.decoded = dec ? dec(x.value) : { opaque: true, bytes: x.value };
|
|
227
|
+
x.decoded = dec ? dec(x.value, ctx) : { opaque: true, bytes: x.value };
|
|
228
228
|
});
|
|
229
229
|
|
|
230
230
|
return {
|
|
@@ -398,12 +398,13 @@ function _oneGN(node, code) {
|
|
|
398
398
|
|
|
399
399
|
// @internal
|
|
400
400
|
function readGeneralName(der, code) {
|
|
401
|
+
// allow:bare-decode reached from the builder, not from a parse, so no parse context carries caps here.
|
|
401
402
|
return _oneGN(asn1.decode(der), code || "attrcert/bad-input");
|
|
402
403
|
}
|
|
403
404
|
|
|
404
405
|
function _svceAuthInfo(authInfoAllowed, code) {
|
|
405
|
-
return function (buf) {
|
|
406
|
-
var kids = _seqChildren(buf, code, "SvceAuthInfo");
|
|
406
|
+
return function (buf, ctx) {
|
|
407
|
+
var kids = _seqChildren(buf, code, "SvceAuthInfo", ctx);
|
|
407
408
|
if (kids.length < 2 || kids.length > 3) throw NS.E(code, "SvceAuthInfo must be a SEQUENCE { service, ident, authInfo? }");
|
|
408
409
|
var service = _oneGN(kids[0], code);
|
|
409
410
|
var ident = _oneGN(kids[1], code);
|
|
@@ -417,12 +418,12 @@ function _svceAuthInfo(authInfoAllowed, code) {
|
|
|
417
418
|
};
|
|
418
419
|
}
|
|
419
420
|
|
|
420
|
-
function _ietfAttrSyntax(buf) {
|
|
421
|
+
function _ietfAttrSyntax(buf, ctx) {
|
|
421
422
|
var C = "attrcert/bad-ietf-attr";
|
|
422
|
-
var kids = _seqChildren(buf, C, "IetfAttrSyntax");
|
|
423
|
+
var kids = _seqChildren(buf, C, "IetfAttrSyntax", ctx);
|
|
423
424
|
if (!kids.length || kids.length > 2) throw NS.E(C, "IetfAttrSyntax must be a SEQUENCE { policyAuthority [0]?, values }");
|
|
424
425
|
var policyAuthority = null, i = 0;
|
|
425
|
-
if (_isCtx(kids[0], 0)) { policyAuthority = schema.walk(_GNS_0, kids[0], NS).result; i = 1; }
|
|
426
|
+
if (_isCtx(kids[0], 0)) { policyAuthority = schema.walk(_GNS_0, kids[0], schema.withResources(NS, ctx)).result; i = 1; }
|
|
426
427
|
var valuesNode = kids[i];
|
|
427
428
|
if (!valuesNode || !_isU(valuesNode, TAGS.SEQUENCE) || !valuesNode.children || !valuesNode.children.length) {
|
|
428
429
|
throw NS.E(C, "IetfAttrSyntax values must be a non-empty SEQUENCE OF value");
|
|
@@ -438,12 +439,12 @@ function _ietfAttrSyntax(buf) {
|
|
|
438
439
|
return { policyAuthority: policyAuthority, values: values, homogeneous: Object.keys(kinds).length === 1 };
|
|
439
440
|
}
|
|
440
441
|
|
|
441
|
-
function _role(buf) {
|
|
442
|
+
function _role(buf, ctx) {
|
|
442
443
|
var C = "attrcert/bad-role";
|
|
443
|
-
var kids = _seqChildren(buf, C, "RoleSyntax");
|
|
444
|
+
var kids = _seqChildren(buf, C, "RoleSyntax", ctx);
|
|
444
445
|
if (!kids.length || kids.length > 2) throw NS.E(C, "RoleSyntax must be a SEQUENCE { roleAuthority [0]?, roleName [1] }");
|
|
445
446
|
var roleAuthority = null, i = 0;
|
|
446
|
-
if (_isCtx(kids[0], 0)) { roleAuthority = schema.walk(_GNS_0, kids[0], NS).result; i = 1; }
|
|
447
|
+
if (_isCtx(kids[0], 0)) { roleAuthority = schema.walk(_GNS_0, kids[0], schema.withResources(NS, ctx)).result; i = 1; }
|
|
447
448
|
var rn = kids[i];
|
|
448
449
|
if (!_isCtx(rn, 1) || !rn.children || rn.children.length !== 1) throw NS.E(C, "RoleSyntax roleName must be an EXPLICIT [1] wrapping exactly one GeneralName");
|
|
449
450
|
if (i + 1 !== kids.length) throw NS.E(C, "RoleSyntax has unexpected trailing fields");
|
|
@@ -473,9 +474,9 @@ function _securityCategory(node, C) {
|
|
|
473
474
|
if (!_isCtx(valueNode, 1) || !valueNode.children || valueNode.children.length !== 1) throw NS.E(C, "SecurityCategory value must be an EXPLICIT [1] wrapping one element");
|
|
474
475
|
return { type: type, valueBytes: Buffer.concat([valueNode.children[0].bytes]) };
|
|
475
476
|
}
|
|
476
|
-
function _clearance(buf) {
|
|
477
|
+
function _clearance(buf, ctx) {
|
|
477
478
|
var C = "attrcert/bad-clearance";
|
|
478
|
-
var kids = _seqChildren(buf, C, "Clearance");
|
|
479
|
+
var kids = _seqChildren(buf, C, "Clearance", ctx);
|
|
479
480
|
if (!kids.length || kids.length > 3) throw NS.E(C, "Clearance must be a SEQUENCE { policyId, classList?, securityCategories? }");
|
|
480
481
|
if (!_isU(kids[0], TAGS.OBJECT_IDENTIFIER)) throw NS.E(C, "Clearance policyId must be an OBJECT IDENTIFIER");
|
|
481
482
|
var policyId; try { policyId = asn1.read.oid(kids[0]); } catch (e) { throw NS.E(C, "Clearance policyId must be an OBJECT IDENTIFIER", e); }
|
|
@@ -508,16 +509,16 @@ _ATTR_VALUE_DECODERS[_Oav("chargingIdentity")] = _ietfAttrSyntax;
|
|
|
508
509
|
_ATTR_VALUE_DECODERS[_Oav("group")] = _ietfAttrSyntax;
|
|
509
510
|
|
|
510
511
|
|
|
511
|
-
function _acAuditIdentity(buf) {
|
|
512
|
+
function _acAuditIdentity(buf, ctx) {
|
|
512
513
|
var C = "attrcert/bad-audit-identity";
|
|
513
|
-
var n = _decodeTop(buf, C, "AuditIdentity");
|
|
514
|
+
var n = _decodeTop(buf, C, "AuditIdentity", ctx);
|
|
514
515
|
if (!_isU(n, TAGS.OCTET_STRING)) throw NS.E(C, "AuditIdentity must be an OCTET STRING");
|
|
515
516
|
return Buffer.concat([asn1.read.octetString(n)]);
|
|
516
517
|
}
|
|
517
518
|
|
|
518
|
-
function _noRevAvail(buf) {
|
|
519
|
+
function _noRevAvail(buf, ctx) {
|
|
519
520
|
var C = "attrcert/bad-no-rev-avail";
|
|
520
|
-
var n = _decodeTop(buf, C, "NoRevAvail");
|
|
521
|
+
var n = _decodeTop(buf, C, "NoRevAvail", ctx);
|
|
521
522
|
try { asn1.read.nullValue(n); } catch (e) { throw NS.E(C, "NoRevAvail must be a DER NULL (RFC 5755 sec. 4.3.6)", e); }
|
|
522
523
|
return { noRevAvail: true };
|
|
523
524
|
}
|
|
@@ -546,13 +547,13 @@ function _acDecodeTargets(node, C, allowTargetCert) {
|
|
|
546
547
|
if (!_isU(node, TAGS.SEQUENCE) || !node.children || !node.children.length) throw NS.E(C, "Targets must be a non-empty SEQUENCE OF Target");
|
|
547
548
|
return node.children.map(function (t) { return _acTarget(t, C, allowTargetCert); });
|
|
548
549
|
}
|
|
549
|
-
function _seqOfTargets(buf, C, allowTargetCert) {
|
|
550
|
-
var kids = _seqChildren(buf, C, "SEQUENCE OF Targets");
|
|
550
|
+
function _seqOfTargets(buf, C, allowTargetCert, ctx) {
|
|
551
|
+
var kids = _seqChildren(buf, C, "SEQUENCE OF Targets", ctx);
|
|
551
552
|
if (!kids.length) throw NS.E(C, "a SEQUENCE OF Targets must be non-empty");
|
|
552
553
|
return kids.map(function (targetsNode) { return _acDecodeTargets(targetsNode, C, allowTargetCert); });
|
|
553
554
|
}
|
|
554
|
-
function _targetInformation(buf) { return _seqOfTargets(buf, "attrcert/bad-targets", true); }
|
|
555
|
-
function _acProxying(buf) { return _seqOfTargets(buf, "attrcert/bad-proxy-info", false); }
|
|
555
|
+
function _targetInformation(buf, ctx) { return _seqOfTargets(buf, "attrcert/bad-targets", true, ctx); }
|
|
556
|
+
function _acProxying(buf, ctx) { return _seqOfTargets(buf, "attrcert/bad-proxy-info", false, ctx); }
|
|
556
557
|
|
|
557
558
|
function _acAttrSpec(node, C) {
|
|
558
559
|
if (!node.children) throw NS.E(C, "an AttrSpec must be a SEQUENCE OF OBJECT IDENTIFIER");
|
|
@@ -561,9 +562,9 @@ function _acAttrSpec(node, C) {
|
|
|
561
562
|
try { return asn1.read.oid(o); } catch (e) { throw NS.E(C, "an AttrSpec element must be an OBJECT IDENTIFIER", e); }
|
|
562
563
|
});
|
|
563
564
|
}
|
|
564
|
-
function _aaControls(buf) {
|
|
565
|
+
function _aaControls(buf, ctx) {
|
|
565
566
|
var C = "attrcert/bad-aa-controls";
|
|
566
|
-
var kids = _seqChildren(buf, C, "AAControls");
|
|
567
|
+
var kids = _seqChildren(buf, C, "AAControls", ctx);
|
|
567
568
|
if (kids.length > 4) throw NS.E(C, "AAControls has too many fields");
|
|
568
569
|
var out = { pathLenConstraint: null, permittedAttrs: null, excludedAttrs: null, permitUnSpecified: true }, i = 0;
|
|
569
570
|
if (kids[i] && _isU(kids[i], TAGS.INTEGER)) { out.pathLenConstraint = guard.range.uint31(_readInt(kids[i], C, "pathLenConstraint"), NS.E, C, "AAControls pathLenConstraint"); i++; }
|
|
@@ -588,9 +589,18 @@ _AC_EXT_DECODERS[_Oav("acProxying")] = _acProxying;
|
|
|
588
589
|
* through the certificate decoders, so a relying party running the sec. 6 "pointer in AC" scheme
|
|
589
590
|
* reads the distribution point and the OCSP location off the parse rather than off raw bytes. */
|
|
590
591
|
var _CERT_EXT_DECODERS = pkix.certExtensionDecoders(NS).byOid;
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
592
|
+
/** @internal The certificate decoders close over the namespace they were built with, so a parse
|
|
593
|
+
* that named caps gets its own table and every decode below these three reads the caller's
|
|
594
|
+
* figure. A caller outside a parse passes no context and reuses the module's table. */
|
|
595
|
+
function _certExtDecoder(name) {
|
|
596
|
+
var key = _Oav(name);
|
|
597
|
+
return function (buf, ctx) {
|
|
598
|
+
return (ctx ? pkix.certExtensionDecoders(schema.withResources(NS, ctx)).byOid : _CERT_EXT_DECODERS)[key](buf);
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
_AC_EXT_DECODERS[_Oav("authorityKeyIdentifier")] = _certExtDecoder("authorityKeyIdentifier");
|
|
602
|
+
_AC_EXT_DECODERS[_Oav("authorityInfoAccess")] = _certExtDecoder("authorityInfoAccess");
|
|
603
|
+
_AC_EXT_DECODERS[_Oav("cRLDistributionPoints")] = _certExtDecoder("cRLDistributionPoints");
|
|
594
604
|
|
|
595
605
|
// @internal
|
|
596
606
|
function validateAttributeValue(type, valueDer) {
|
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;
|