@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-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;
|
|
@@ -751,7 +751,7 @@ var readName = _valueParser("Name", pkix.name(NS));
|
|
|
751
751
|
|
|
752
752
|
/**
|
|
753
753
|
* @primitive pki.schema.cmp.parse
|
|
754
|
-
* @signature pki.schema.cmp.parse(input) -> pkiMessage
|
|
754
|
+
* @signature pki.schema.cmp.parse(input, caps?) -> pkiMessage
|
|
755
755
|
* @since 0.1.19
|
|
756
756
|
* @status stable
|
|
757
757
|
* @spec RFC 9810, RFC 9483, RFC 9481
|
|
@@ -805,6 +805,11 @@ var readName = _valueParser("Name", pkix.name(NS));
|
|
|
805
805
|
* var m = pki.schema.cmp.parse(der);
|
|
806
806
|
* m.body.arm; // "ir", "ip", "error", ...
|
|
807
807
|
* m.header.transactionID; // raw Buffer or null
|
|
808
|
+
* @opts
|
|
809
|
+
* - `maxBytes` / `maxDepth` / `maxItems` (number) -- decode caps for this parse. Each
|
|
810
|
+
* defaults to the matching `pki.C.LIMITS` figure and may only be set lower; a value
|
|
811
|
+
* above it, or an option outside this set, is refused. A parse that exceeds one is
|
|
812
|
+
* refused with the `/too-large` code of this format.
|
|
808
813
|
*/
|
|
809
814
|
var parse = pkix.makeParser({
|
|
810
815
|
pemLabel: "CMP", PemError: PemError, ErrorClass: CmpError,
|
package/lib/schema-cms.js
CHANGED
|
@@ -127,7 +127,7 @@ function _checkAttrPlacement(attrs, place) {
|
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
-
function _checkContentBindingAttrs(attrs, mode) {
|
|
130
|
+
function _checkContentBindingAttrs(attrs, mode, ctx) {
|
|
131
131
|
var ct = 0, md = 0, seen = Object.create(null);
|
|
132
132
|
for (var i = 0; i < attrs.length; i++) {
|
|
133
133
|
var a = attrs[i];
|
|
@@ -141,16 +141,16 @@ function _checkContentBindingAttrs(attrs, mode) {
|
|
|
141
141
|
ct += 1;
|
|
142
142
|
if (mode === "countersig") throw NS.E("cms/misplaced-attr", "a countersignature's signedAttrs must not carry a content-type attribute (RFC 5652 sec. 11.4)");
|
|
143
143
|
if (a.values.length !== 1) throw NS.E("cms/bad-content-type-attr", "the content-type attribute must be single-valued");
|
|
144
|
-
try { asn1.read.oid(
|
|
144
|
+
try { asn1.read.oid(pkix.decodeNested(a.values[0], ctx, "a content-type attribute value")); }
|
|
145
145
|
catch (e) { throw NS.E("cms/bad-content-type-attr", "the content-type attribute value must be a valid OBJECT IDENTIFIER", e); }
|
|
146
146
|
} else if (a.type === OID_MESSAGE_DIGEST) {
|
|
147
147
|
md += 1;
|
|
148
148
|
if (a.values.length !== 1) throw NS.E("cms/bad-message-digest-attr", "the message-digest attribute must be single-valued");
|
|
149
|
-
try { asn1.read.octetString(
|
|
149
|
+
try { asn1.read.octetString(pkix.decodeNested(a.values[0], ctx, "a message-digest attribute value")); }
|
|
150
150
|
catch (e) { throw NS.E("cms/bad-message-digest-attr", "the message-digest attribute value must be an OCTET STRING", e); }
|
|
151
151
|
} else if (a.type === OID_SIGNING_TIME) {
|
|
152
152
|
if (a.values.length !== 1) throw NS.E("cms/bad-signing-time-attr", "the signing-time attribute must be single-valued (RFC 5652 sec. 11.3)");
|
|
153
|
-
try { asn1.read.time(
|
|
153
|
+
try { asn1.read.time(pkix.decodeNested(a.values[0], ctx, "a signing-time attribute value")); }
|
|
154
154
|
catch (e) { throw NS.E("cms/bad-signing-time-attr", "the signing-time attribute value must be a Time (RFC 5652 sec. 11.3)", e); }
|
|
155
155
|
}
|
|
156
156
|
}
|
|
@@ -187,6 +187,7 @@ function _ascendingDer(nodes) {
|
|
|
187
187
|
function looksLikeSignedAttributes(bytes) {
|
|
188
188
|
if (!bytes || !bytes.length) return false;
|
|
189
189
|
var node;
|
|
190
|
+
// allow:bare-decode a shape probe on bytes a caller hands the signer or verifier, outside any parse.
|
|
190
191
|
try { node = asn1.decode(bytes); }
|
|
191
192
|
catch (_e) { return false; }
|
|
192
193
|
if (!schema.isUniversal(node, asn1.TAGS.SET) || !node.constructed) return false;
|
|
@@ -225,12 +226,12 @@ function looksLikeSignedAttributes(bytes) {
|
|
|
225
226
|
return sawContentType && sawMessageDigest;
|
|
226
227
|
}
|
|
227
228
|
|
|
228
|
-
function _assertContentTypeMatchesAttrs(attrs, eContentType) {
|
|
229
|
+
function _assertContentTypeMatchesAttrs(attrs, eContentType, ctx) {
|
|
229
230
|
for (var i = 0; i < attrs.length; i++) {
|
|
230
231
|
if (attrs[i].type !== OID_CONTENT_TYPE) continue;
|
|
231
232
|
if (attrs[i].values.length !== 1) throw NS.E("cms/bad-content-type-attr", "the content-type attribute must be single-valued (RFC 5652 sec. 11.1)");
|
|
232
233
|
var ctv;
|
|
233
|
-
try { ctv = asn1.read.oid(
|
|
234
|
+
try { ctv = asn1.read.oid(pkix.decodeNested(attrs[i].values[0], ctx, "a content-type attribute value")); }
|
|
234
235
|
catch (e) { throw NS.E("cms/bad-content-type-attr", "the content-type attribute value must be a valid OBJECT IDENTIFIER", e); }
|
|
235
236
|
if (ctv !== eContentType) throw NS.E("cms/content-type-mismatch", "the content-type attribute (" + ctv + ") must equal the eContentType (" + eContentType + ") (RFC 5652 sec. 5.3)");
|
|
236
237
|
}
|
|
@@ -244,13 +245,13 @@ function _checkNoDuplicateAttrs(attrs) {
|
|
|
244
245
|
}
|
|
245
246
|
}
|
|
246
247
|
|
|
247
|
-
function _validateAeadParams(alg, macLen) {
|
|
248
|
+
function _validateAeadParams(alg, macLen, ctx) {
|
|
248
249
|
var kind = AEAD_ALGS[alg.oid];
|
|
249
250
|
if (!kind) return null;
|
|
250
251
|
var K = kind.toUpperCase();
|
|
251
252
|
if (alg.parameters === null) throw NS.E("cms/bad-aead-params", "an AES-" + K + " content-encryption algorithm MUST carry its parameters (RFC 5084 sec. 3." + (kind === "gcm" ? "2" : "1") + ")");
|
|
252
253
|
var node;
|
|
253
|
-
try { node =
|
|
254
|
+
try { node = pkix.decodeNested(alg.parameters, ctx, "AES-" + K + " content-encryption parameters"); }
|
|
254
255
|
catch (e) { throw NS.E("cms/bad-aead-params", "malformed AES-" + K + " parameters", e); }
|
|
255
256
|
if (node.tagClass !== "universal" || node.tagNumber !== T.SEQUENCE || !node.children || node.children.length < 1 || node.children.length > 2) {
|
|
256
257
|
throw NS.E("cms/bad-aead-params", "AES-" + K + " parameters must be a SEQUENCE { aes-nonce, aes-ICVlen DEFAULT 12 } (RFC 5084)");
|
|
@@ -275,8 +276,8 @@ function _validateAeadParams(alg, macLen) {
|
|
|
275
276
|
return { kind: kind, nonce: nonce, icvLen: icvLen };
|
|
276
277
|
}
|
|
277
278
|
|
|
278
|
-
function _assertDerEncodedAttrs(node, code) {
|
|
279
|
-
try {
|
|
279
|
+
function _assertDerEncodedAttrs(node, code, ctx) {
|
|
280
|
+
try { pkix.decodeNested(node.bytes, ctx, "an attribute set"); }
|
|
280
281
|
catch (e) { throw NS.E(code, "the attribute set must be DER encoded even inside a BER envelope (RFC 5652 sec. 5.3)", e); }
|
|
281
282
|
}
|
|
282
283
|
|
|
@@ -373,10 +374,10 @@ function makeSignerInfo(mode) {
|
|
|
373
374
|
}
|
|
374
375
|
var signedAttrs = null, signedAttrsBytes = null;
|
|
375
376
|
if (m.fields.signedAttrs.present) {
|
|
376
|
-
_assertDerEncodedAttrs(m.fields.signedAttrs.node, "cms/bad-signed-attrs");
|
|
377
|
+
_assertDerEncodedAttrs(m.fields.signedAttrs.node, "cms/bad-signed-attrs", ctx);
|
|
377
378
|
signedAttrs = m.fields.signedAttrs.value.items.map(function (it) { return it.value.result; });
|
|
378
379
|
_checkAttrPlacement(signedAttrs, "signed");
|
|
379
|
-
_checkContentBindingAttrs(signedAttrs, mode);
|
|
380
|
+
_checkContentBindingAttrs(signedAttrs, mode, ctx);
|
|
380
381
|
signedAttrsBytes = m.fields.signedAttrs.node.bytes;
|
|
381
382
|
}
|
|
382
383
|
var unsignedAttrs = null;
|
|
@@ -387,7 +388,7 @@ function makeSignerInfo(mode) {
|
|
|
387
388
|
if (unsignedAttrs[u].type !== OID_COUNTERSIGNATURE) continue;
|
|
388
389
|
for (var v = 0; v < unsignedAttrs[u].values.length; v++) {
|
|
389
390
|
var csNode;
|
|
390
|
-
try { csNode =
|
|
391
|
+
try { csNode = pkix.decodeNested(unsignedAttrs[u].values[v], ctx, "a countersignature attribute value"); }
|
|
391
392
|
catch (e) { throw NS.E("cms/bad-countersignature", "a countersignature attribute value must be DER (RFC 5652 sec. 11.4)", e); }
|
|
392
393
|
schema.walk(COUNTERSIGNATURE_SIGNER_INFO, csNode, ctx);
|
|
393
394
|
}
|
|
@@ -418,7 +419,7 @@ var SIGNED_DATA = schema.seq([
|
|
|
418
419
|
schema.field("signerInfos", schema.setOf(SIGNER_INFO, { min: 0, code: "cms/bad-signer-infos", what: "signerInfos" })),
|
|
419
420
|
], {
|
|
420
421
|
assert: "sequence", code: "cms/bad-signed-data", what: "SignedData",
|
|
421
|
-
build: function (m) {
|
|
422
|
+
build: function (m, ctx) {
|
|
422
423
|
var version = m.fields.version.value;
|
|
423
424
|
var encapContentInfo = m.fields.encapContentInfo.value.result;
|
|
424
425
|
var certificates = m.fields.certificates.present ? m.fields.certificates.value.items.map(rawChoiceElement(CERT_CHOICE_TAGS, "cms/bad-certificates", "certificates")) : [];
|
|
@@ -432,7 +433,7 @@ var SIGNED_DATA = schema.seq([
|
|
|
432
433
|
}
|
|
433
434
|
|
|
434
435
|
for (var si = 0; si < signerInfos.length; si++) {
|
|
435
|
-
if (signerInfos[si].signedAttrs) _assertContentTypeMatchesAttrs(signerInfos[si].signedAttrs, encapContentInfo.eContentType);
|
|
436
|
+
if (signerInfos[si].signedAttrs) _assertContentTypeMatchesAttrs(signerInfos[si].signedAttrs, encapContentInfo.eContentType, ctx);
|
|
436
437
|
}
|
|
437
438
|
|
|
438
439
|
var expected = _expectedSignedDataVersion(certificates, crls, signerInfos, encapContentInfo.eContentType);
|
|
@@ -771,7 +772,7 @@ var AUTHENTICATED_DATA = schema.seq([
|
|
|
771
772
|
schema.optional("unauthAttrs", schema.implicitSetOf(3, ATTRIBUTE, { min: 1, code: "cms/bad-unauth-attrs", what: "unauthAttrs" }), { tag: 3 }),
|
|
772
773
|
], {
|
|
773
774
|
assert: "sequence", code: "cms/bad-auth-data", what: "AuthenticatedData",
|
|
774
|
-
build: function (m) {
|
|
775
|
+
build: function (m, ctx) {
|
|
775
776
|
var version = m.fields.version.value;
|
|
776
777
|
var originatorInfo = m.fields.originatorInfo.present ? m.fields.originatorInfo.value.result : null;
|
|
777
778
|
var encapContentInfo = m.fields.encapContentInfo.value.result;
|
|
@@ -789,11 +790,11 @@ var AUTHENTICATED_DATA = schema.seq([
|
|
|
789
790
|
|
|
790
791
|
var authAttrs = null, authAttrsBytes = null;
|
|
791
792
|
if (hasAuthAttrs) {
|
|
792
|
-
_assertDerEncodedAttrs(m.fields.authAttrs.node, "cms/bad-auth-attrs");
|
|
793
|
+
_assertDerEncodedAttrs(m.fields.authAttrs.node, "cms/bad-auth-attrs", ctx);
|
|
793
794
|
authAttrs = m.fields.authAttrs.value.items.map(function (it) { return it.value.result; });
|
|
794
795
|
_checkAttrPlacement(authAttrs, "auth");
|
|
795
|
-
_checkContentBindingAttrs(authAttrs, "content");
|
|
796
|
-
_assertContentTypeMatchesAttrs(authAttrs, encapContentInfo.eContentType);
|
|
796
|
+
_checkContentBindingAttrs(authAttrs, "content", ctx);
|
|
797
|
+
_assertContentTypeMatchesAttrs(authAttrs, encapContentInfo.eContentType, ctx);
|
|
797
798
|
authAttrsBytes = m.fields.authAttrs.node.bytes;
|
|
798
799
|
}
|
|
799
800
|
var unauthAttrs = null;
|
|
@@ -826,10 +827,10 @@ var AUTH_ENVELOPED_DATA = schema.seq([
|
|
|
826
827
|
schema.optional("unauthAttrs", schema.implicitSetOf(2, ATTRIBUTE, { min: 1, code: "cms/bad-unauth-attrs", what: "unauthAttrs" }), { tag: 2 }),
|
|
827
828
|
], {
|
|
828
829
|
assert: "sequence", code: "cms/bad-auth-enveloped-data", what: "AuthEnvelopedData",
|
|
829
|
-
build: function (m) {
|
|
830
|
+
build: function (m, ctx) {
|
|
830
831
|
var encryptedContentInfo = m.fields.authEncryptedContentInfo.value.result;
|
|
831
832
|
var mac = m.fields.mac.value;
|
|
832
|
-
var aead = _validateAeadParams(encryptedContentInfo.contentEncryptionAlgorithm, mac.length);
|
|
833
|
+
var aead = _validateAeadParams(encryptedContentInfo.contentEncryptionAlgorithm, mac.length, ctx);
|
|
833
834
|
|
|
834
835
|
var hasAuthAttrs = m.fields.authAttrs.present;
|
|
835
836
|
if (!hasAuthAttrs && encryptedContentInfo.contentType !== OID_DATA) {
|
|
@@ -837,11 +838,11 @@ var AUTH_ENVELOPED_DATA = schema.seq([
|
|
|
837
838
|
}
|
|
838
839
|
var authAttrs = null, authAttrsBytes = null;
|
|
839
840
|
if (hasAuthAttrs) {
|
|
840
|
-
_assertDerEncodedAttrs(m.fields.authAttrs.node, "cms/bad-auth-attrs");
|
|
841
|
+
_assertDerEncodedAttrs(m.fields.authAttrs.node, "cms/bad-auth-attrs", ctx);
|
|
841
842
|
authAttrs = m.fields.authAttrs.value.items.map(function (it) { return it.value.result; });
|
|
842
843
|
_checkAttrPlacement(authAttrs, "auth");
|
|
843
844
|
_checkNoDuplicateAttrs(authAttrs);
|
|
844
|
-
_assertContentTypeMatchesAttrs(authAttrs, encryptedContentInfo.contentType);
|
|
845
|
+
_assertContentTypeMatchesAttrs(authAttrs, encryptedContentInfo.contentType, ctx);
|
|
845
846
|
authAttrsBytes = m.fields.authAttrs.node.bytes;
|
|
846
847
|
}
|
|
847
848
|
var unauthAttrs = null;
|
|
@@ -934,7 +935,7 @@ var CONTENT_INFO = schema.seq([
|
|
|
934
935
|
|
|
935
936
|
/**
|
|
936
937
|
* @primitive pki.schema.cms.parse
|
|
937
|
-
* @signature pki.schema.cms.parse(input) -> content
|
|
938
|
+
* @signature pki.schema.cms.parse(input, caps?) -> content
|
|
938
939
|
* @since 0.1.10
|
|
939
940
|
* @status stable
|
|
940
941
|
* @spec RFC 5652, RFC 5083, RFC 9629
|
|
@@ -972,6 +973,11 @@ var CONTENT_INFO = schema.seq([
|
|
|
972
973
|
* var cms = pki.schema.cms.parse(der);
|
|
973
974
|
* cms.signerInfos[0].sid.serialNumberHex; // -> "0a1b"
|
|
974
975
|
* cms.encapContentInfo.eContent; // -> Buffer | null (detached)
|
|
976
|
+
* @opts
|
|
977
|
+
* - `maxBytes` / `maxDepth` / `maxItems` (number) -- decode caps for this parse. Each
|
|
978
|
+
* defaults to the matching `pki.C.LIMITS` figure and may only be set lower; a value
|
|
979
|
+
* above it, or an option outside this set, is refused. A parse that exceeds one is
|
|
980
|
+
* refused with the `/too-large` code of this format.
|
|
975
981
|
*/
|
|
976
982
|
var parse = pkix.makeRecordingParser({ pemLabel: "CMS", PemError: PemError, ErrorClass: CmsError, prefix: "cms", what: "CMS ContentInfo", topSchema: CONTENT_INFO, ns: NS }, "cms");
|
|
977
983
|
|
|
@@ -1025,13 +1031,23 @@ function matches(root) {
|
|
|
1025
1031
|
return true;
|
|
1026
1032
|
}
|
|
1027
1033
|
|
|
1028
|
-
|
|
1034
|
+
/** @internal `ctx` is the walk context of the parse that reached this structure, when another
|
|
1035
|
+
* format carries it. Only its caps and budget cross over: a cap the caller named at that
|
|
1036
|
+
* format's door has to reach this walk, and a fault in this structure is still CMS's to report. */
|
|
1037
|
+
function walkEnvelopedData(node, ctx) {
|
|
1038
|
+
return schema.walk(ENVELOPED_DATA, node, schema.withResources(NS, ctx)).result;
|
|
1039
|
+
}
|
|
1029
1040
|
|
|
1030
|
-
var walkSignedData = guard.parsed.recordingWalker("cms", function (node) {
|
|
1031
|
-
return schema.walk(SIGNED_DATA, node, NS).result;
|
|
1041
|
+
var walkSignedData = guard.parsed.recordingWalker("cms", function (node, ctx) {
|
|
1042
|
+
return schema.walk(SIGNED_DATA, node, schema.withResources(NS, ctx)).result;
|
|
1043
|
+
// allow:bare-decode the re-decode a recording walker performs on raw DER a caller hands it, outside any parse.
|
|
1032
1044
|
}, function (der) { return asn1.decode(der, { ber: true }); });
|
|
1033
|
-
function walkEncryptedData(node
|
|
1034
|
-
|
|
1045
|
+
function walkEncryptedData(node, ctx) {
|
|
1046
|
+
return schema.walk(ENCRYPTED_DATA, node, schema.withResources(NS, ctx)).result;
|
|
1047
|
+
}
|
|
1048
|
+
function walkCountersignature(node, ctx) {
|
|
1049
|
+
return schema.walk(COUNTERSIGNATURE_SIGNER_INFO, node, schema.withResources(NS, ctx)).result;
|
|
1050
|
+
}
|
|
1035
1051
|
|
|
1036
1052
|
function assertAttachedCiphertext(eci, E, code, label) {
|
|
1037
1053
|
if (!eci || eci.encryptedContent === null || eci.encryptedContent.length === 0) {
|
package/lib/schema-crl.js
CHANGED
|
@@ -51,20 +51,20 @@ var TIME = pkix.time(NS);
|
|
|
51
51
|
|
|
52
52
|
var CRL_VERSION = pkix.versionReader(NS, { "1": 2 });
|
|
53
53
|
|
|
54
|
-
function decodeExt(ext, scope) {
|
|
54
|
+
function decodeExt(ext, scope, ctx) {
|
|
55
55
|
var value = ext.value;
|
|
56
56
|
var atCrl = scope !== "entry";
|
|
57
57
|
var atEntry = scope !== "crl";
|
|
58
58
|
try {
|
|
59
59
|
if (ext.oid === OID_CRL_NUMBER && atCrl) {
|
|
60
|
-
value = asn1.read.integer(
|
|
60
|
+
value = asn1.read.integer(pkix.decodeNested(ext.value, ctx, "a cRLNumber extension value"));
|
|
61
61
|
if (value < 0n) throw new Error("cRLNumber must be non-negative (INTEGER 0..MAX)");
|
|
62
62
|
} else if (ext.oid === OID_REASON_CODE && atEntry) {
|
|
63
|
-
var reason = asn1.read.enumerated(
|
|
63
|
+
var reason = asn1.read.enumerated(pkix.decodeNested(ext.value, ctx, "a reasonCode extension value"));
|
|
64
64
|
if (!_hasOwn(CRL_REASONS, reason.toString())) throw new Error("undefined CRLReason " + reason.toString());
|
|
65
65
|
value = Number(reason);
|
|
66
66
|
} else if (ext.oid === OID_INVALIDITY_DATE && atEntry) {
|
|
67
|
-
var n =
|
|
67
|
+
var n = pkix.decodeNested(ext.value, ctx, "an invalidityDate extension value");
|
|
68
68
|
if (n.tagClass !== "universal" || n.tagNumber !== TAGS.GENERALIZED_TIME) {
|
|
69
69
|
throw new Error("invalidityDate must be a GeneralizedTime");
|
|
70
70
|
}
|
|
@@ -82,13 +82,13 @@ var REVOKED_ENTRY = schema.seq([
|
|
|
82
82
|
schema.optional("crlEntryExtensions", EXTENSIONS, { whenUniversal: [TAGS.SEQUENCE] }),
|
|
83
83
|
], {
|
|
84
84
|
assert: "sequence", arity: { min: 2 }, code: "crl/bad-revoked-entry", what: "RevokedCertificate",
|
|
85
|
-
build: function (m) {
|
|
85
|
+
build: function (m, ctx) {
|
|
86
86
|
return {
|
|
87
87
|
serialNumber: m.fields.userCertificate.value,
|
|
88
88
|
serialNumberHex: m.fields.userCertificate.node.content.toString("hex"),
|
|
89
89
|
revocationDate: m.fields.revocationDate.value,
|
|
90
90
|
crlEntryExtensions: m.fields.crlEntryExtensions.present
|
|
91
|
-
? m.fields.crlEntryExtensions.value.result.map(function (e) { return decodeExt(e, "entry"); })
|
|
91
|
+
? m.fields.crlEntryExtensions.value.result.map(function (e) { return decodeExt(e, "entry", ctx); })
|
|
92
92
|
: [],
|
|
93
93
|
};
|
|
94
94
|
},
|
|
@@ -111,7 +111,7 @@ var TBS_CERTLIST = schema.seq([
|
|
|
111
111
|
{ minTag: 0, maxTag: 0, unexpectedCode: "crl/bad-tbs", orderCode: "crl/bad-tbs" }),
|
|
112
112
|
], {
|
|
113
113
|
assert: "sequence", code: "crl/bad-tbs", what: "tbsCertList",
|
|
114
|
-
build: function (m) {
|
|
114
|
+
build: function (m, ctx) {
|
|
115
115
|
return {
|
|
116
116
|
version: m.fields.version.present ? m.fields.version.value : 1,
|
|
117
117
|
issuer: m.fields.issuer.value.result,
|
|
@@ -119,7 +119,7 @@ var TBS_CERTLIST = schema.seq([
|
|
|
119
119
|
nextUpdate: m.fields.nextUpdate.present ? m.fields.nextUpdate.value : null,
|
|
120
120
|
revokedCertificates: m.fields.revokedCertificates.present ? m.fields.revokedCertificates.value.result : [],
|
|
121
121
|
crlExtensions: m.fields.crlExtensions.present
|
|
122
|
-
? m.fields.crlExtensions.value.result.map(function (e) { return decodeExt(e, "crl"); })
|
|
122
|
+
? m.fields.crlExtensions.value.result.map(function (e) { return decodeExt(e, "crl", ctx); })
|
|
123
123
|
: [],
|
|
124
124
|
crlExtensionsPresent: m.fields.crlExtensions.present,
|
|
125
125
|
};
|
|
@@ -157,7 +157,7 @@ var CERTIFICATE_LIST = pkix.signedEnvelope(NS, TBS_CERTLIST, {
|
|
|
157
157
|
|
|
158
158
|
/**
|
|
159
159
|
* @primitive pki.schema.crl.parse
|
|
160
|
-
* @signature pki.schema.crl.parse(input) -> crl
|
|
160
|
+
* @signature pki.schema.crl.parse(input, caps?) -> crl
|
|
161
161
|
* @since 0.1.7
|
|
162
162
|
* @status stable
|
|
163
163
|
* @spec RFC 5280
|
|
@@ -180,6 +180,11 @@ var CERTIFICATE_LIST = pkix.signedEnvelope(NS, TBS_CERTLIST, {
|
|
|
180
180
|
* { cert: caCert, key: key });
|
|
181
181
|
* var crl = pki.schema.crl.parse(der);
|
|
182
182
|
* crl.revokedCertificates[0].serialNumberHex; // -> "0a3f"
|
|
183
|
+
* @opts
|
|
184
|
+
* - `maxBytes` / `maxDepth` / `maxItems` (number) -- decode caps for this parse. Each
|
|
185
|
+
* defaults to the matching `pki.C.LIMITS` figure and may only be set lower; a value
|
|
186
|
+
* above it, or an option outside this set, is refused. A parse that exceeds one is
|
|
187
|
+
* refused with the `/too-large` code of this format.
|
|
183
188
|
*/
|
|
184
189
|
var parse = pkix.makeRecordingParser({ pemLabel: "X509 CRL", PemError: PemError, ErrorClass: CrlError, prefix: "crl", what: "CRL", topSchema: CERTIFICATE_LIST, ns: NS }, "crl");
|
|
185
190
|
|
|
@@ -260,10 +265,10 @@ function entryExtensionCriticality(oidRegistry) {
|
|
|
260
265
|
* raw. Returns the record with its decoded `value`; throws `crl/bad-extension-value`. */
|
|
261
266
|
var OID_CERTIFICATE_ISSUER = oid.byName("certificateIssuer");
|
|
262
267
|
var CERTIFICATE_ISSUER_NAMES = pkix.generalNames(NS, { code: "crl/bad-extension-value" });
|
|
263
|
-
function readEntryExtension(ext) {
|
|
264
|
-
if (ext.oid !== OID_CERTIFICATE_ISSUER) return decodeExt(ext);
|
|
268
|
+
function readEntryExtension(ext, ctx) {
|
|
269
|
+
if (ext.oid !== OID_CERTIFICATE_ISSUER) return decodeExt(ext, undefined, ctx);
|
|
265
270
|
var names;
|
|
266
|
-
try { names = schema.walk(CERTIFICATE_ISSUER_NAMES,
|
|
271
|
+
try { names = schema.walk(CERTIFICATE_ISSUER_NAMES, pkix.decodeNested(ext.value, ctx, "a certificateIssuer extension value"), ctx || NS).result; }
|
|
267
272
|
catch (e) {
|
|
268
273
|
throw NS.E("crl/bad-extension-value", "malformed certificateIssuer extension value: " + ((e && e.message) || intrinsic.String(e)), e);
|
|
269
274
|
}
|
package/lib/schema-crmf.js
CHANGED
|
@@ -170,12 +170,12 @@ function popoPrivKey(type) {
|
|
|
170
170
|
}
|
|
171
171
|
}
|
|
172
172
|
if (inner.tagNumber === 3) {
|
|
173
|
-
try { schema.embeddedDer(PKMAC_VALUE, asn1.sequenceTlv(inner), NS, { code: "crmf/bad-popo", what: "agreeMAC [3] PKMACValue" }); }
|
|
173
|
+
try { schema.embeddedDer(PKMAC_VALUE, asn1.sequenceTlv(inner), schema.withResources(NS, ctx), { code: "crmf/bad-popo", what: "agreeMAC [3] PKMACValue" }); }
|
|
174
174
|
catch (e) { throw ctx.E("crmf/bad-popo", "agreeMAC [3] must be a PKMACValue SEQUENCE { algId, BIT STRING } (RFC 4211 sec. 4.2)", e); }
|
|
175
175
|
}
|
|
176
176
|
if (inner.tagNumber === 4) {
|
|
177
177
|
var env;
|
|
178
|
-
try { env = cms.walkEnvelopedData(
|
|
178
|
+
try { env = cms.walkEnvelopedData(pkix.decodeNested(asn1.sequenceTlv(inner), ctx, "an encryptedKey [4] EnvelopedData"), ctx); }
|
|
179
179
|
catch (e) { throw ctx.E("crmf/bad-popo", "encryptedKey [4] must be a well-formed EnvelopedData (RFC 4211 sec. 4.2, RFC 5652 sec. 6.1)", e); }
|
|
180
180
|
if (env.encryptedContentInfo.contentType !== oid.byName("encKeyWithID")) {
|
|
181
181
|
throw ctx.E("crmf/bad-popo", "encryptedKey [4] EnvelopedData content type MUST be id-ct-encKeyWithID (RFC 4211 sec. 4.2)");
|
|
@@ -311,7 +311,7 @@ var CERT_REQ_MESSAGES = schema.seqOf(CERT_REQ_MSG, {
|
|
|
311
311
|
|
|
312
312
|
/**
|
|
313
313
|
* @primitive pki.schema.crmf.parse
|
|
314
|
-
* @signature pki.schema.crmf.parse(input) -> certReqMessages
|
|
314
|
+
* @signature pki.schema.crmf.parse(input, caps?) -> certReqMessages
|
|
315
315
|
* @since 0.1.17
|
|
316
316
|
* @status stable
|
|
317
317
|
* @spec RFC 4211
|
|
@@ -348,6 +348,11 @@ var CERT_REQ_MESSAGES = schema.seqOf(CERT_REQ_MSG, {
|
|
|
348
348
|
* { key: await pki.key.export(pair.privateKey) });
|
|
349
349
|
* var m = pki.schema.crmf.parse(der);
|
|
350
350
|
* m.messages[0].certReq.certTemplate.subject.dn; // "CN=req.example"
|
|
351
|
+
* @opts
|
|
352
|
+
* - `maxBytes` / `maxDepth` / `maxItems` (number) -- decode caps for this parse. Each
|
|
353
|
+
* defaults to the matching `pki.C.LIMITS` figure and may only be set lower; a value
|
|
354
|
+
* above it, or an option outside this set, is refused. A parse that exceeds one is
|
|
355
|
+
* refused with the `/too-large` code of this format.
|
|
351
356
|
*/
|
|
352
357
|
var parse = pkix.makeRecordingParser({
|
|
353
358
|
pemLabel: null, PemError: PemError, ErrorClass: CrmfError, prefix: "crmf",
|
|
@@ -409,16 +414,21 @@ function matches(root) {
|
|
|
409
414
|
schema.isUniversal(tpl, TAGS.SEQUENCE) && !!tpl.children;
|
|
410
415
|
}
|
|
411
416
|
|
|
412
|
-
|
|
413
|
-
|
|
417
|
+
/** @internal `outer` is the walk context of the parse that reached these messages, when another
|
|
418
|
+
* format carries them. Only its caps and budget cross over: the messages are still read in this
|
|
419
|
+
* namespace, so a fault in one keeps the CRMF error class and code. */
|
|
420
|
+
function walkCertReqMessages(node, opts, outer) {
|
|
421
|
+
var ctx = schema.withResources(NS, outer);
|
|
414
422
|
if (opts && (opts.allowSigningAlg || opts.allowV1Version)) {
|
|
415
|
-
ctx = Object.assign({},
|
|
423
|
+
ctx = Object.assign({}, ctx);
|
|
416
424
|
if (opts.allowSigningAlg) ctx.allowSigningAlg = true;
|
|
417
425
|
if (opts.allowV1Version) ctx.allowV1Version = true;
|
|
418
426
|
}
|
|
419
427
|
return schema.walk(CERT_REQ_MESSAGES, node, ctx).result;
|
|
420
428
|
}
|
|
421
|
-
function walkCertTemplate(node
|
|
429
|
+
function walkCertTemplate(node, outer) {
|
|
430
|
+
return schema.walk(CERT_TEMPLATE, node, schema.withResources(NS, outer)).result;
|
|
431
|
+
}
|
|
422
432
|
|
|
423
433
|
module.exports = {
|
|
424
434
|
parse: parse,
|
package/lib/schema-csr.js
CHANGED
|
@@ -121,7 +121,7 @@ var CERTIFICATION_REQUEST = pkix.signedEnvelope(NS, CERTIFICATION_REQUEST_INFO,
|
|
|
121
121
|
|
|
122
122
|
/**
|
|
123
123
|
* @primitive pki.schema.csr.parse
|
|
124
|
-
* @signature pki.schema.csr.parse(input) -> csr
|
|
124
|
+
* @signature pki.schema.csr.parse(input, caps?) -> csr
|
|
125
125
|
* @since 0.1.8
|
|
126
126
|
* @status stable
|
|
127
127
|
* @spec RFC 2986
|
|
@@ -147,6 +147,11 @@ var CERTIFICATION_REQUEST = pkix.signedEnvelope(NS, CERTIFICATION_REQUEST_INFO,
|
|
|
147
147
|
* var csr = pki.schema.csr.parse(der);
|
|
148
148
|
* csr.subject.dn; // -> "CN=req.example"
|
|
149
149
|
* csr.attributes[0].type; // -> "1.2.840.113549.1.9.14"
|
|
150
|
+
* @opts
|
|
151
|
+
* - `maxBytes` / `maxDepth` / `maxItems` (number) -- decode caps for this parse. Each
|
|
152
|
+
* defaults to the matching `pki.C.LIMITS` figure and may only be set lower; a value
|
|
153
|
+
* above it, or an option outside this set, is refused. A parse that exceeds one is
|
|
154
|
+
* refused with the `/too-large` code of this format.
|
|
150
155
|
*/
|
|
151
156
|
var parse = pkix.makeRecordingParser({ pemLabel: "CERTIFICATE REQUEST", PemError: PemError, ErrorClass: CsrError, prefix: "csr", what: "certification request", topSchema: CERTIFICATION_REQUEST, ns: NS }, "csr");
|
|
152
157
|
|
package/lib/schema-csrattrs.js
CHANGED
|
@@ -158,12 +158,13 @@ var CERT_REQ_INFO_TEMPLATE = schema.seq([
|
|
|
158
158
|
hasExtReq = true;
|
|
159
159
|
extReqs += 1;
|
|
160
160
|
if (a.values.length !== 1) throw NS.E("csrattrs/bad-template-attrs", "a template id-ExtensionReq values must be a SET of exactly one Extensions (RFC 9908 sec. 3.2)");
|
|
161
|
-
extensions = schema.walk(EXTENSIONS,
|
|
161
|
+
extensions = schema.walk(EXTENSIONS, pkix.decodeNested(a.values[0], ctx, "a template id-ExtensionReq value"), ctx).result;
|
|
162
162
|
}
|
|
163
163
|
if (a.type === OID_EXT_REQ_TEMPLATE) {
|
|
164
164
|
extReqTemplates += 1;
|
|
165
165
|
if (a.values.length !== 1) throw NS.E("csrattrs/bad-template-attrs", "an id-aa-extensionReqTemplate values must be a SET of exactly one ExtensionReqTemplate (RFC 9908 sec. 3.4)");
|
|
166
|
-
extensionTemplates = extensionTemplates.concat(schema.walk(EXTENSION_REQ_TEMPLATE,
|
|
166
|
+
extensionTemplates = extensionTemplates.concat(schema.walk(EXTENSION_REQ_TEMPLATE,
|
|
167
|
+
pkix.decodeNested(a.values[0], ctx, "an id-aa-extensionReqTemplate value"), ctx).result);
|
|
167
168
|
}
|
|
168
169
|
}
|
|
169
170
|
if (extReqs > 1) throw NS.E("csrattrs/bad-template-attrs", "a template must not carry more than one id-ExtensionReq (RFC 9908 sec. 3.2)");
|
|
@@ -185,25 +186,26 @@ function _enrichAttribute(item, ctx) {
|
|
|
185
186
|
if (KEY_TYPE_OIDS.has(item.oid)) item.isKeyType = true;
|
|
186
187
|
if (item.oid === OID_EXTENSION_REQUEST) {
|
|
187
188
|
if (item.values.length !== 1) throw ctx.E("csrattrs/bad-extension-req", "an id-ExtensionReq attribute values must be a SET of exactly one Extensions (RFC 9908 sec. 3.2)");
|
|
188
|
-
item.extensions = schema.walk(EXTENSIONS,
|
|
189
|
+
item.extensions = schema.walk(EXTENSIONS, pkix.decodeNested(item.values[0], ctx, "an id-ExtensionReq value"), ctx).result;
|
|
189
190
|
} else if (item.oid === OID_EC_PUBLIC_KEY) {
|
|
190
191
|
if (item.values.length > 1) throw ctx.E("csrattrs/bad-key-type-attr", "an ecPublicKey attribute values must be empty or a SET of exactly one named-curve OBJECT IDENTIFIER (RFC 9908 sec. 3.2)");
|
|
191
192
|
if (item.values.length === 1) {
|
|
192
|
-
try { item.curve = asn1.read.oid(
|
|
193
|
+
try { item.curve = asn1.read.oid(pkix.decodeNested(item.values[0], ctx, "an ecPublicKey attribute value")); }
|
|
193
194
|
catch (e) { throw ctx.E("csrattrs/bad-key-type-attr", "an ecPublicKey attribute value must be a named-curve OBJECT IDENTIFIER (RFC 9908 sec. 3.2)", e); }
|
|
194
195
|
}
|
|
195
196
|
} else if (item.oid === OID_RSA_ENCRYPTION) {
|
|
196
197
|
if (item.values.length > 1) throw ctx.E("csrattrs/bad-key-type-attr", "an rsaEncryption attribute values must be empty or a SET of exactly one INTEGER key size (RFC 9908 sec. 3.2)");
|
|
197
198
|
if (item.values.length === 1) {
|
|
198
199
|
var sz;
|
|
199
|
-
try { sz = asn1.read.integer(
|
|
200
|
+
try { sz = asn1.read.integer(pkix.decodeNested(item.values[0], ctx, "an rsaEncryption attribute value")); }
|
|
200
201
|
catch (e) { throw ctx.E("csrattrs/bad-key-type-attr", "an rsaEncryption attribute value must be an INTEGER key size (RFC 9908 sec. 3.2)", e); }
|
|
201
202
|
if (sz < 1n || sz > 65536n) throw ctx.E("csrattrs/bad-key-type-attr", "the rsaEncryption key size " + sz + " is out of range (1..65536 bits)");
|
|
202
203
|
item.keySize = Number(sz);
|
|
203
204
|
}
|
|
204
205
|
} else if (item.oid === OID_TEMPLATE) {
|
|
205
206
|
if (item.values.length !== 1) throw ctx.E("csrattrs/bad-template", "a certificationRequestInfoTemplate attribute values must be a SET of exactly one template (RFC 9908 sec. 3.4)");
|
|
206
|
-
item.template = schema.walk(CERT_REQ_INFO_TEMPLATE,
|
|
207
|
+
item.template = schema.walk(CERT_REQ_INFO_TEMPLATE,
|
|
208
|
+
pkix.decodeNested(item.values[0], ctx, "a certificationRequestInfoTemplate value"), ctx).result;
|
|
207
209
|
}
|
|
208
210
|
}
|
|
209
211
|
|
|
@@ -244,7 +246,7 @@ var CSR_ATTRS = schema.seqOf(ATTR_OR_OID, {
|
|
|
244
246
|
|
|
245
247
|
/**
|
|
246
248
|
* @primitive pki.schema.csrattrs.parse
|
|
247
|
-
* @signature pki.schema.csrattrs.parse(der) -> { items }
|
|
249
|
+
* @signature pki.schema.csrattrs.parse(der, caps?) -> { items }
|
|
248
250
|
* @since 0.1.24
|
|
249
251
|
* @status stable
|
|
250
252
|
* @spec RFC 8951, RFC 9908, RFC 7030
|
|
@@ -269,6 +271,11 @@ var CSR_ATTRS = schema.seqOf(ATTR_OR_OID, {
|
|
|
269
271
|
* ]);
|
|
270
272
|
* var a = pki.schema.csrattrs.parse(der);
|
|
271
273
|
* a.items[0].kind; // -> "oid" | "attribute"
|
|
274
|
+
* @opts
|
|
275
|
+
* - `maxBytes` / `maxDepth` / `maxItems` (number) -- decode caps for this parse. Each
|
|
276
|
+
* defaults to the matching `pki.C.LIMITS` figure and may only be set lower; a value
|
|
277
|
+
* above it, or an option outside this set, is refused. A parse that exceeds one is
|
|
278
|
+
* refused with the `/too-large` code of this format.
|
|
272
279
|
*/
|
|
273
280
|
var parse = pkix.makeParser({ pemLabel: null, PemError: frameworkError.PemError, ErrorClass: CsrattrsError, prefix: "csrattrs", what: "CsrAttrs", topSchema: CSR_ATTRS, ns: NS });
|
|
274
281
|
|
package/lib/schema-engine.js
CHANGED
|
@@ -75,6 +75,66 @@ function _assertArity(schema, kids, ctx) {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
|
|
78
|
+
var RESOURCE_REFUSALS = Object.assign(Object.create(null), {
|
|
79
|
+
"asn1/too-large": 1, "asn1/too-deep": 1, "asn1/too-many-items": 1,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
/** @internal The codec's resource refusals, as distinct from its malformed-input refusals. A
|
|
83
|
+
* caller who tightened a cap has to be able to tell "your bytes are wrong" from "your cap is
|
|
84
|
+
* too small", and only the second is answered by raising the cap. */
|
|
85
|
+
function isResourceRefusal(e) {
|
|
86
|
+
return !!(e && typeof e.code === "string" && RESOURCE_REFUSALS[e.code] === 1);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** @internal Carries a parse's caps and budget onto another namespace. A format whose structure
|
|
90
|
+
* is carried inside another format walks it in its OWN namespace, so a refusal names the domain
|
|
91
|
+
* that owns the fault and keeps its own error class; the caps and the budget are the only things
|
|
92
|
+
* that cross the boundary. Returns `ns` unchanged when there is nothing to carry. */
|
|
93
|
+
function withResources(ns, source) {
|
|
94
|
+
if (!source || (source.caps == null && source.budget == null)) return ns;
|
|
95
|
+
var out = Object.create(null);
|
|
96
|
+
var keys = Object.keys(ns);
|
|
97
|
+
for (var i = 0; i < keys.length; i++) out[keys[i]] = ns[keys[i]];
|
|
98
|
+
out.caps = source.caps;
|
|
99
|
+
out.budget = source.budget;
|
|
100
|
+
return out;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** @internal The decode a parse performs on a value it has already read out of its input: an
|
|
104
|
+
* embedded OCTET STRING payload, an encapsulated protocol body. `ctx` is the walk context the
|
|
105
|
+
* door built, so the caps a caller named at the door hold at every level rather than at the
|
|
106
|
+
* root alone, and a resource refusal down here trips the parse budget before the original
|
|
107
|
+
* error is re-thrown. The error itself is re-thrown unchanged, because a format's existing
|
|
108
|
+
* code for a malformed embedded value is one callers pin.
|
|
109
|
+
*
|
|
110
|
+
* `opts.ber` is read once into a local and carries the relaxed acceptance a format whose door
|
|
111
|
+
* is BER-tolerant already applies at the root. The decode options are built fresh per call. */
|
|
112
|
+
function decodeNested(bytes, ctx, label, opts) {
|
|
113
|
+
var caps = ctx ? ctx.caps : null;
|
|
114
|
+
var ber = !!(opts && opts.ber === true);
|
|
115
|
+
var decodeOpts = Object.create(null);
|
|
116
|
+
if (caps) {
|
|
117
|
+
decodeOpts.maxBytes = caps.maxBytes;
|
|
118
|
+
decodeOpts.maxDepth = caps.maxDepth;
|
|
119
|
+
decodeOpts.maxItems = caps.maxItems;
|
|
120
|
+
}
|
|
121
|
+
if (ber) decodeOpts.ber = true;
|
|
122
|
+
try {
|
|
123
|
+
// allow:bare-decode this IS the primitive every other nested decode routes through.
|
|
124
|
+
return asn1.decode(bytes, decodeOpts);
|
|
125
|
+
} catch (e) {
|
|
126
|
+
if (isResourceRefusal(e) && ctx && ctx.budget) {
|
|
127
|
+
try {
|
|
128
|
+
ctx.budget.trip(label || "an embedded value");
|
|
129
|
+
} catch (_b) {
|
|
130
|
+
/* allow:swallow-unverified the latch is the point; the original error is re-thrown below */
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
throw e;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// allow:bare-decode a round trip through the codec on an integer this module just encoded.
|
|
78
138
|
function _implicitIntContent(v) { return asn1.decode(asn1.build.integer(v)).content; }
|
|
79
139
|
function oidLeaf() { return { kind: "leaf", read: asn1.read.oid, write: function (v) { return asn1.build.oid(v); } }; }
|
|
80
140
|
function integerLeaf() { return { kind: "leaf", read: asn1.read.integer, write: function (v) { return asn1.build.integer(v); } }; }
|
|
@@ -436,6 +496,7 @@ function _encodeRepeat(schema, items, ctx) {
|
|
|
436
496
|
if (schema.unique) {
|
|
437
497
|
var seen = new Set();
|
|
438
498
|
parts.forEach(function (tlv) {
|
|
499
|
+
// allow:bare-decode a round trip through the codec on a TLV this encoder just produced.
|
|
439
500
|
var item = { node: asn1.decode(tlv) };
|
|
440
501
|
item.value = walk(schema.item, item.node, ctx);
|
|
441
502
|
var key = schema.unique(item);
|
|
@@ -500,7 +561,7 @@ function embeddedDer(schema, bytes, ctx, opts) {
|
|
|
500
561
|
}
|
|
501
562
|
var node;
|
|
502
563
|
try {
|
|
503
|
-
node =
|
|
564
|
+
node = decodeNested(bytes, ctx, opts.what || "embedded DER", { ber: !!opts.ber });
|
|
504
565
|
} catch (e) {
|
|
505
566
|
throw ctx.E(opts.code, (opts.what || "embedded DER") + " did not decode: " + e.message, e);
|
|
506
567
|
}
|
|
@@ -540,7 +601,9 @@ module.exports = {
|
|
|
540
601
|
bitString: bitString, implicitBitString: implicitBitString, implicitOctetString: implicitOctetString,
|
|
541
602
|
implicitNull: implicitNull, implicitInteger: implicitInteger, implicitBoolean: implicitBoolean,
|
|
542
603
|
any: any, decode: decode, time: time,
|
|
543
|
-
walk: walk, encode: encode, embeddedDer: embeddedDer,
|
|
604
|
+
walk: walk, encode: encode, embeddedDer: embeddedDer, decodeNested: decodeNested,
|
|
605
|
+
isResourceRefusal: isResourceRefusal, withResources: withResources,
|
|
606
|
+
assertMinimalNamedBits: assertMinimalNamedBits,
|
|
544
607
|
isUniversal: isUniversal, isContext: isContext,
|
|
545
608
|
isUniversalOneOf: isUniversalOneOf, isContextOneOf: isContextOneOf, isContextInRange: isContextInRange,
|
|
546
609
|
};
|