@blamejs/pki 0.5.23 → 0.5.24
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 +17 -1
- package/MIGRATING.md +31 -0
- package/README.md +1 -1
- package/lib/acme.js +269 -37
- package/lib/cmp-verify.js +3 -8
- package/lib/guard-all.js +7 -2
- package/lib/guard-async.js +3 -1
- package/lib/guard-bytes.js +4 -2
- package/lib/guard-compress.js +3 -1
- package/lib/guard-crypto.js +4 -1
- package/lib/guard-der.js +4 -1
- package/lib/guard-encoding.js +3 -1
- package/lib/guard-header.js +4 -1
- package/lib/guard-identifier.js +4 -2
- package/lib/guard-intrinsic.js +7 -2
- package/lib/guard-json.js +4 -1
- package/lib/guard-limits.js +4 -1
- package/lib/guard-list.js +6 -2
- package/lib/guard-name.js +17 -7
- package/lib/guard-parsed.js +5 -2
- package/lib/guard-range.js +5 -2
- package/lib/guard-secret.js +3 -1
- package/lib/guard-text.js +3 -1
- package/lib/guard-time.js +5 -2
- package/lib/schema-pkix.js +136 -84
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/schema-pkix.js
CHANGED
|
@@ -22,6 +22,38 @@ var intrinsic = require("./guard-intrinsic");
|
|
|
22
22
|
// replaceable properties to answer one thing, and here it decides which version numbers a format
|
|
23
23
|
// accepts -- shared by every format module, so one admitted name widens all of them at once.
|
|
24
24
|
var _hasOwn = intrinsic.hasOwn;
|
|
25
|
+
// The rest of the operations this module decides with, taken at load for the same reason. What
|
|
26
|
+
// these factories produce is what a consumer compares an identity against -- a certificate's
|
|
27
|
+
// subject alternative names against an enrollment's own, a distinguished name against an issuer's
|
|
28
|
+
// -- so a name that traversal, folding or a byte copy invents here is a name the encoded structure
|
|
29
|
+
// never carried, and the comparison downstream still answers true.
|
|
30
|
+
var _forEach = intrinsic.forEach;
|
|
31
|
+
var _map = intrinsic.map;
|
|
32
|
+
var _push = intrinsic.push;
|
|
33
|
+
var _join = intrinsic.join;
|
|
34
|
+
var _stringify = intrinsic.stringify;
|
|
35
|
+
var _String = intrinsic.String;
|
|
36
|
+
var _isBuffer = intrinsic.isBuffer;
|
|
37
|
+
var _bufferFrom = intrinsic.bufferFrom;
|
|
38
|
+
var _bufferConcat = intrinsic.bufferConcat;
|
|
39
|
+
var _assign = intrinsic.assign;
|
|
40
|
+
var _subarray = intrinsic.subarray;
|
|
41
|
+
var _strIndexOf = intrinsic.stringIndexOf;
|
|
42
|
+
var _bufToString = intrinsic.uncurry(Buffer.prototype.toString);
|
|
43
|
+
var _strSlice = intrinsic.uncurry(String.prototype.slice);
|
|
44
|
+
var _charAt = intrinsic.uncurry(String.prototype.charAt);
|
|
45
|
+
var _strReplace = intrinsic.uncurry(String.prototype.replace);
|
|
46
|
+
var _split = intrinsic.uncurry(String.prototype.split);
|
|
47
|
+
var _reExec = intrinsic.uncurry(RegExp.prototype.exec);
|
|
48
|
+
var _reTest = intrinsic.uncurry(RegExp.prototype.test);
|
|
49
|
+
var _create = intrinsic.create;
|
|
50
|
+
var _isUint8Array = intrinsic.types.isUint8Array;
|
|
51
|
+
var _bigIntToString = intrinsic.bigIntToString;
|
|
52
|
+
var _weakGet = intrinsic.weakGet;
|
|
53
|
+
var _weakSet = intrinsic.weakSet;
|
|
54
|
+
var _WeakMapI = intrinsic.WeakMap;
|
|
55
|
+
var _arrayFrom = Array.from;
|
|
56
|
+
var _getUTCFullYear = intrinsic.uncurry(Date.prototype.getUTCFullYear);
|
|
25
57
|
// ct is a leaf format module (requires only the codec / oid / constants / error
|
|
26
58
|
// core, never pkix) so the SCT-list extension VALUE decoder registers here as a
|
|
27
59
|
// registry row, not a switch. The one-directional edge keeps the layering acyclic.
|
|
@@ -48,10 +80,10 @@ function pemDecode(text, label, PemError) {
|
|
|
48
80
|
text = guard.text.decode(text, constants.LIMITS.PEM_MAX_BYTES, PemError, {
|
|
49
81
|
charset: "latin1", tooLarge: "pem/too-large", badInput: "pem/bad-input", label: "PEM input",
|
|
50
82
|
});
|
|
51
|
-
var m = PEM_RE
|
|
83
|
+
var m = _reExec(PEM_RE, text);
|
|
52
84
|
if (!m) throw new PemError("pem/no-block", "no PEM block found");
|
|
53
|
-
if (label && m[1] !== label) throw new PemError("pem/label-mismatch", "expected " +
|
|
54
|
-
var b64 = m[2]
|
|
85
|
+
if (label && m[1] !== label) throw new PemError("pem/label-mismatch", "expected " + _stringify(label) + " block, got " + _stringify(m[1]));
|
|
86
|
+
var b64 = _strReplace(m[2], /[\r\n\t ]+/g, "");
|
|
55
87
|
// Strict canonical base64 (RFC 4648 sec. 3.5) via the shared encoding guard.
|
|
56
88
|
return guard.encoding.base64(b64, null, function (c, msg) { return new PemError(c, msg); }, "pem/bad-base64", "PEM base64 body");
|
|
57
89
|
}
|
|
@@ -70,15 +102,15 @@ function pemDecodeAll(text, label, PemError) {
|
|
|
70
102
|
var re = /-----BEGIN ([A-Z0-9 ]+)-----([\s\S]*?)-----END \1-----/g;
|
|
71
103
|
var blocks = [];
|
|
72
104
|
var lastEnd = 0, m;
|
|
73
|
-
while ((m = re
|
|
74
|
-
if (/\S
|
|
75
|
-
if (m[1] !== label) throw new PemError("pem/label-mismatch", "expected " +
|
|
76
|
-
var b64 = m[2]
|
|
77
|
-
blocks
|
|
105
|
+
while ((m = _reExec(re, text)) !== null) {
|
|
106
|
+
if (_reTest(/\S/, _strSlice(text, lastEnd, m.index))) throw new PemError("pem/explanatory-text", "explanatory text is not permitted around PEM blocks (RFC 8555 sec. 9.1)");
|
|
107
|
+
if (m[1] !== label) throw new PemError("pem/label-mismatch", "expected " + _stringify(label) + " block, got " + _stringify(m[1]));
|
|
108
|
+
var b64 = _strReplace(m[2], /[\r\n\t ]+/g, "");
|
|
109
|
+
_push(blocks, guard.encoding.base64(b64, null, function (c, msg) { return new PemError(c, msg); }, "pem/bad-base64", "PEM base64 body"));
|
|
78
110
|
lastEnd = re.lastIndex;
|
|
79
111
|
}
|
|
80
112
|
if (blocks.length === 0) throw new PemError("pem/no-block", "no PEM block found");
|
|
81
|
-
if (/\S
|
|
113
|
+
if (_reTest(/\S/, _strSlice(text, lastEnd))) throw new PemError("pem/explanatory-text", "explanatory text is not permitted after the PEM chain (RFC 8555 sec. 9.1)");
|
|
82
114
|
return blocks;
|
|
83
115
|
}
|
|
84
116
|
|
|
@@ -88,14 +120,14 @@ function pemDecodeAll(text, label, PemError) {
|
|
|
88
120
|
// an embedded "-----" or newline) would emit armor pemDecode itself rejects
|
|
89
121
|
// or corrupt the armor grammar: a config-time authoring fault, thrown here.
|
|
90
122
|
function pemEncode(der, label, PemError) {
|
|
91
|
-
if (typeof label !== "string" ||
|
|
123
|
+
if (typeof label !== "string" || !_reTest(/^[A-Z0-9]+( [A-Z0-9]+)*$/, label)) {
|
|
92
124
|
throw new PemError("pem/bad-label", "pemEncode requires an uppercase A-Z0-9 label with single spaces");
|
|
93
125
|
}
|
|
94
126
|
// Re-view through the byte guard: a string input would silently utf8-armor into
|
|
95
127
|
// a bogus PEM, and a detached-backed Buffer would armor an empty body; both
|
|
96
128
|
// fail closed (a Uint8Array / Buffer of real DER re-views cleanly).
|
|
97
129
|
var buf = guard.bytes.view(der, PemError, "pem/bad-input", "pemEncode DER input");
|
|
98
|
-
var b64 = buf
|
|
130
|
+
var b64 = _strReplace(_strReplace(_bufToString(buf, "base64"), /(.{64})/g, "$1\n"), /\n$/, "");
|
|
99
131
|
return "-----BEGIN " + label + "-----\n" + b64 + "\n-----END " + label + "-----\n";
|
|
100
132
|
}
|
|
101
133
|
|
|
@@ -104,7 +136,7 @@ function pemEncode(der, label, PemError) {
|
|
|
104
136
|
// Uint8Array is taken as bytes. opts: { pemLabel, PemError, ErrorClass, prefix }.
|
|
105
137
|
function coerceToDer(input, opts) {
|
|
106
138
|
if (typeof input === "string") return pemDecode(input, opts.pemLabel, opts.PemError);
|
|
107
|
-
if (
|
|
139
|
+
if (_isBuffer(input) || _isUint8Array(input)) {
|
|
108
140
|
// Route the DER Buffer / Uint8Array through the shared byte guard so a
|
|
109
141
|
// detached backing store fails closed here for every format that composes
|
|
110
142
|
// this boundary (x509/crl/csr/pkcs8/cms/pkcs12), not per-format.
|
|
@@ -120,8 +152,8 @@ function coerceToDer(input, opts) {
|
|
|
120
152
|
// leading tag+length bytes are non-printable, so a non-SEQUENCE DER (a bare
|
|
121
153
|
// SET / INTEGER) is not misrouted here; it decodes and fails closed structurally.
|
|
122
154
|
function _isPemArmor(buf) {
|
|
123
|
-
var head = buf
|
|
124
|
-
var idx = head
|
|
155
|
+
var head = _bufToString(_subarray(buf, 0, 4096), "latin1");
|
|
156
|
+
var idx = _strIndexOf(head, "-----BEGIN");
|
|
125
157
|
if (idx === -1) return false;
|
|
126
158
|
for (var i = 0; i < idx; i++) {
|
|
127
159
|
var c = buf[i];
|
|
@@ -143,7 +175,7 @@ function decodeRoot(der, opts) {
|
|
|
143
175
|
// strictness verdict included.
|
|
144
176
|
try { return asn1.decode(der, opts.ber ? { ber: true } : undefined); }
|
|
145
177
|
catch (e) {
|
|
146
|
-
throw new opts.ErrorClass(opts.prefix + "/bad-der", (opts.what || "input") + " DER did not decode: " + ((e && e.message) ||
|
|
178
|
+
throw new opts.ErrorClass(opts.prefix + "/bad-der", (opts.what || "input") + " DER did not decode: " + ((e && e.message) || _String(e)), e);
|
|
147
179
|
}
|
|
148
180
|
}
|
|
149
181
|
|
|
@@ -178,7 +210,7 @@ function makeNS(prefix, ErrorClass, oidModule) {
|
|
|
178
210
|
// version is rejected at the leaf (asn1/*).
|
|
179
211
|
function versionReader(ns, accept) {
|
|
180
212
|
return schema.decode(function (n) {
|
|
181
|
-
var key = asn1.read.integer(n)
|
|
213
|
+
var key = _bigIntToString(asn1.read.integer(n));
|
|
182
214
|
if (_hasOwn(accept, key)) return accept[key];
|
|
183
215
|
throw ns.E(ns.prefix + "/bad-version", "unsupported version " + key);
|
|
184
216
|
});
|
|
@@ -337,7 +369,7 @@ function attrValueToString(ns) {
|
|
|
337
369
|
try { s = asn1.read.string(node); }
|
|
338
370
|
catch (e) {
|
|
339
371
|
if (!e || (e.code !== "asn1/expected-string" && e.code !== "asn1/expected-primitive")) {
|
|
340
|
-
throw ns.E(ns.prefix + "/bad-atv", "malformed string in attribute value: " + ((e && e.message) ||
|
|
372
|
+
throw ns.E(ns.prefix + "/bad-atv", "malformed string in attribute value: " + ((e && e.message) || _String(e)));
|
|
341
373
|
}
|
|
342
374
|
// asn1.read.string does not read NumericString, and that is deliberate: a value that returns a plain
|
|
343
375
|
// string joins the RFC 5280 sec. 7.1 name-comparison identity class, and NumericString is not a
|
|
@@ -347,11 +379,11 @@ function attrValueToString(ns) {
|
|
|
347
379
|
// surfacing it in the type-distinct RFC 4514 hex form, which keeps it out of that identity class.
|
|
348
380
|
if (node.tagClass === "universal" && node.tagNumber === asn1.TAGS.NUMERIC_STRING) {
|
|
349
381
|
try { asn1.read.numericString(node); }
|
|
350
|
-
catch (e2) { throw ns.E(ns.prefix + "/bad-atv", "malformed NumericString in attribute value: " + ((e2 && e2.message) ||
|
|
382
|
+
catch (e2) { throw ns.E(ns.prefix + "/bad-atv", "malformed NumericString in attribute value: " + ((e2 && e2.message) || _String(e2))); }
|
|
351
383
|
}
|
|
352
|
-
return "#" + node.bytes
|
|
384
|
+
return "#" + _bufToString(node.bytes, "hex");
|
|
353
385
|
}
|
|
354
|
-
if (s
|
|
386
|
+
if (_charAt(s, 0) === "#" || _charAt(s, 0) === "\\") return "\\" + s;
|
|
355
387
|
return s;
|
|
356
388
|
}, function (value) {
|
|
357
389
|
// encode: a leading '\' escapes the next character (the decode's escape of a
|
|
@@ -361,9 +393,9 @@ function attrValueToString(ns) {
|
|
|
361
393
|
// the first invalid character and would emit truncated bytes. Any other
|
|
362
394
|
// string encodes as UTF8String (the decode does not preserve the original
|
|
363
395
|
// string type, so this is the canonical re-encoding, not a byte-exact one).
|
|
364
|
-
if (typeof value === "string" && value
|
|
365
|
-
if (typeof value === "string" && value
|
|
366
|
-
var hex = value
|
|
396
|
+
if (typeof value === "string" && _charAt(value, 0) === "\\") return asn1.build.utf8(_strSlice(value, 1));
|
|
397
|
+
if (typeof value === "string" && _charAt(value, 0) === "#") {
|
|
398
|
+
var hex = _strSlice(value, 1);
|
|
367
399
|
if (hex.length === 0) {
|
|
368
400
|
throw ns.E(ns.prefix + "/bad-atv", "a #hex attribute value must be a non-empty even run of hex digits (RFC 4514 sec. 2.4)");
|
|
369
401
|
}
|
|
@@ -395,8 +427,8 @@ var _escapeDnValue = guard.name.escapeDnValue;
|
|
|
395
427
|
// the semantic string and then rendered with the full escape set.
|
|
396
428
|
function _dnDisplayValue(v) {
|
|
397
429
|
if (typeof v !== "string") return v;
|
|
398
|
-
if (v
|
|
399
|
-
return _escapeDnValue(v
|
|
430
|
+
if (_charAt(v, 0) === "#") return v;
|
|
431
|
+
return _escapeDnValue(_charAt(v, 0) === "\\" ? _strSlice(v, 1) : v);
|
|
400
432
|
}
|
|
401
433
|
|
|
402
434
|
// time(ns): the RFC 5280 Time production under its profile encoding rule
|
|
@@ -415,7 +447,7 @@ function time(ns) {
|
|
|
415
447
|
return schema.decode(function (n, ctx) {
|
|
416
448
|
var d = base.fn(n, ctx);
|
|
417
449
|
if (n.tagNumber === asn1.TAGS.GENERALIZED_TIME) {
|
|
418
|
-
var y = d
|
|
450
|
+
var y = _getUTCFullYear(d);
|
|
419
451
|
if (y >= 1950 && y < 2050) {
|
|
420
452
|
throw ctx.E(ctx.prefix + "/bad-time", "a date through 2049 must be encoded as UTCTime, not GeneralizedTime (RFC 5280 sec. 4.1.2.5)");
|
|
421
453
|
}
|
|
@@ -453,16 +485,19 @@ function name(ns, opts) {
|
|
|
453
485
|
opts = opts || {};
|
|
454
486
|
function nameBuild(m) {
|
|
455
487
|
var rdns = [], parts = [];
|
|
456
|
-
|
|
488
|
+
// Through the captured traversal: `rdns` is the parsed subject a consumer reads a common name
|
|
489
|
+
// out of, so a replaced Array.prototype.forEach or push could drop the name the certificate
|
|
490
|
+
// carries and seat one it does not.
|
|
491
|
+
_forEach(m.items, function (rdnItem) {
|
|
457
492
|
var atvs = [], atvParts = [];
|
|
458
|
-
rdnItem.value.items
|
|
493
|
+
_forEach(rdnItem.value.items, function (atvItem) {
|
|
459
494
|
var a = atvItem.value.result; // atvItem.value = the atv seq-match; .result = its build result
|
|
460
|
-
atvs
|
|
461
|
-
var label = (a.name && DN_SHORT[a.name]) || a.name || a.type;
|
|
462
|
-
atvParts
|
|
495
|
+
_push(atvs, a);
|
|
496
|
+
var label = (a.name && _hasOwn(DN_SHORT, a.name) && DN_SHORT[a.name]) || a.name || a.type;
|
|
497
|
+
_push(atvParts, label + "=" + _dnDisplayValue(a.value));
|
|
463
498
|
});
|
|
464
|
-
rdns
|
|
465
|
-
parts
|
|
499
|
+
_push(rdns, atvs);
|
|
500
|
+
_push(parts, _join(atvParts, "+"));
|
|
466
501
|
});
|
|
467
502
|
// `dn` is a DISPLAY string, and its spelling is `openssl x509 -subject`: the RDNs in the
|
|
468
503
|
// certificate's own order, joined by ", ". That is deliberately not the RFC 4514 / LDAP
|
|
@@ -473,7 +508,7 @@ function name(ns, opts) {
|
|
|
473
508
|
// guard.name.dnEqual compares the DER under RFC 5280 sec. 7.1, and `bytes` below carries the raw
|
|
474
509
|
// Name DER for a consumer that needs the exact octets (the EST re-enroll subject MUST). Every
|
|
475
510
|
// renderer in the toolkit uses this one spelling, so a certificate has one dn whichever parsed it.
|
|
476
|
-
return { rdns: rdns, dn: parts
|
|
511
|
+
return { rdns: rdns, dn: _join(parts, ", "), bytes: opts.implicitTag != null ? asn1.sequenceTlv(m.node) : m.node.bytes };
|
|
477
512
|
}
|
|
478
513
|
if (opts.implicitTag != null) {
|
|
479
514
|
return schema.implicitSeqOf(opts.implicitTag, relativeDistinguishedName(ns), {
|
|
@@ -561,8 +596,13 @@ function rawNonEmptySequence(ns, opts) {
|
|
|
561
596
|
// (shared with the OCSP revoked-status decoder, which surfaces the same numeric code).
|
|
562
597
|
var CRL_REASON_NAMES = constants.NAMES.CRL_REASON;
|
|
563
598
|
|
|
564
|
-
|
|
565
|
-
|
|
599
|
+
// Which GeneralName alternatives are constructed, and which carry an IA5String. Both tables answer
|
|
600
|
+
// off a tag number the encoded value supplies, and both decide which validation an alternative
|
|
601
|
+
// gets, so neither may inherit: with an ordinary prototype a planted numeric property routes a tag
|
|
602
|
+
// down another alternative's arm, and a dNSName then reaches the IA5 arm's control-byte reject as
|
|
603
|
+
// a constructed value, or misses it altogether.
|
|
604
|
+
var GN_CONSTRUCTED = _assign(_create(null), { 0: 1, 3: 1, 4: 1, 5: 1 });
|
|
605
|
+
var GN_IA5 = _assign(_create(null), { 1: 1, 2: 1, 6: 1 });
|
|
566
606
|
function generalName(ns, opts) {
|
|
567
607
|
opts = opts || {};
|
|
568
608
|
var code = opts.code || (ns.prefix + "/bad-general-name");
|
|
@@ -602,7 +642,7 @@ function generalName(ns, opts) {
|
|
|
602
642
|
// 7-bit IA5, no C0/DEL control byte. An embedded NUL/control in a
|
|
603
643
|
// dNSName/rfc822Name/URI enables a name-truncation bypass (CVE-2009-2408).
|
|
604
644
|
guard.name.assertPrintableIa5(n.content, ctx.E, code, "GeneralName [" + t + "]");
|
|
605
|
-
if (decodeValue) value = n.content
|
|
645
|
+
if (decodeValue) value = _bufToString(n.content, "latin1");
|
|
606
646
|
} else if (t === 7) {
|
|
607
647
|
if (subtreeBase) {
|
|
608
648
|
if (n.content.length !== 8 && n.content.length !== 32) throw ctx.E(code, "GeneralName iPAddress [7] subtree base must be an 8- or 32-octet address+mask (RFC 5280 sec. 4.2.1.10)");
|
|
@@ -611,7 +651,7 @@ function generalName(ns, opts) {
|
|
|
611
651
|
}
|
|
612
652
|
// n.content is proven non-null here (the primitive-form guard above
|
|
613
653
|
// threw on a constructed node); copy the octets out.
|
|
614
|
-
if (decodeValue) value =
|
|
654
|
+
if (decodeValue) value = _bufferConcat([n.content]);
|
|
615
655
|
} else if (t === 8) {
|
|
616
656
|
var regId;
|
|
617
657
|
try { regId = asn1.decodeOidContent(n.content); }
|
|
@@ -642,7 +682,10 @@ function generalNames(ns, opts) {
|
|
|
642
682
|
// decodeValue / subtreeBase thread through to each element (the path
|
|
643
683
|
// validator's constraint matcher passes decodeValue:true).
|
|
644
684
|
var gn = generalName(ns, { code: code, decodeValue: opts.decodeValue === true, subtreeBase: opts.subtreeBase === true });
|
|
645
|
-
|
|
685
|
+
// Through the captured traversal: the names this yields are what a consumer compares an identity
|
|
686
|
+
// against -- a certificate's subject alternative names against an enrollment's own -- and a
|
|
687
|
+
// replaced Array.prototype.map could present a name the encoded GeneralNames does not carry.
|
|
688
|
+
function build(m) { return { names: _map(m.items, function (it) { return it.value; }), bytes: m.node.bytes }; }
|
|
646
689
|
if (opts.implicitTag != null) {
|
|
647
690
|
return schema.implicitSeqOf(opts.implicitTag, gn, { min: 1, code: code, what: opts.what || "GeneralNames", build: build });
|
|
648
691
|
}
|
|
@@ -667,7 +710,7 @@ function generalNames(ns, opts) {
|
|
|
667
710
|
function distributionPointName(ns, node, code) {
|
|
668
711
|
if (node && node.tagClass === "context" && node.tagNumber === 0) {
|
|
669
712
|
var gns = schema.walk(generalNames(ns, { implicitTag: 0, code: code, what: "DistributionPointName fullName" }), node, ns).result;
|
|
670
|
-
return { kind: "fullName", names: gns.names
|
|
713
|
+
return { kind: "fullName", names: _map(gns.names, function (n) { return n.bytes; }) };
|
|
671
714
|
}
|
|
672
715
|
if (node && node.tagClass === "context" && node.tagNumber === 1) {
|
|
673
716
|
// In [1] IMPLICIT RelativeDistinguishedName the context tag replaces the
|
|
@@ -692,14 +735,14 @@ function distributionPointName(ns, node, code) {
|
|
|
692
735
|
//
|
|
693
736
|
// `E(code, message, cause)` is the caller's typed error factory, so each boundary keeps its own
|
|
694
737
|
// domain while the RULE stays single-homed.
|
|
695
|
-
var _KU_DECODER = new
|
|
738
|
+
var _KU_DECODER = new _WeakMapI(); // one decoder table per namespace, built on first use
|
|
696
739
|
function keyUsageOf(ns, cert, E, code, label) {
|
|
697
740
|
var exts = (cert && cert.extensions) || [];
|
|
698
741
|
var want = ns.oid.byName("keyUsage");
|
|
699
742
|
for (var i = 0; i < exts.length; i++) {
|
|
700
743
|
if (exts[i].oid !== want || exts[i].value == null) continue;
|
|
701
|
-
var dec = _KU_DECODER
|
|
702
|
-
if (!dec) { dec = certExtensionDecoders(ns).byOid[want]; _KU_DECODER
|
|
744
|
+
var dec = _weakGet(_KU_DECODER, ns);
|
|
745
|
+
if (!dec) { dec = certExtensionDecoders(ns).byOid[want]; _weakSet(_KU_DECODER, ns, dec); }
|
|
703
746
|
try { return dec(exts[i].value); }
|
|
704
747
|
catch (e) { throw E(code, "the " + label + " keyUsage extension is malformed", e); }
|
|
705
748
|
}
|
|
@@ -765,7 +808,7 @@ function assertPolicyQualifiers(qNode, fail) {
|
|
|
765
808
|
if (qNode.tagClass !== "universal" || qNode.tagNumber !== _T.SEQUENCE || !qNode.children || !qNode.children.length) {
|
|
766
809
|
fail("policyQualifiers must be a non-empty SEQUENCE (RFC 5280 sec. 4.2.1.4)");
|
|
767
810
|
}
|
|
768
|
-
qNode.children
|
|
811
|
+
_forEach(qNode.children, function (pq) {
|
|
769
812
|
if (pq.tagClass !== "universal" || pq.tagNumber !== _T.SEQUENCE || !pq.children || pq.children.length !== 2) {
|
|
770
813
|
fail("policyQualifiers element must be a PolicyQualifierInfo SEQUENCE { policyQualifierId, qualifier } (RFC 5280 sec. 4.2.1.4)");
|
|
771
814
|
}
|
|
@@ -786,7 +829,7 @@ function assertPolicyQualifiers(qNode, fail) {
|
|
|
786
829
|
// octets, so `.length` (UTF-16 units) or a byte count would misjudge a valid value. Exported so
|
|
787
830
|
// every layer that reports the bound counts it identically instead of keeping its own copy.
|
|
788
831
|
var DISPLAY_TEXT_MAX = 200;
|
|
789
|
-
function displayTextChars(str) { return
|
|
832
|
+
function displayTextChars(str) { return _arrayFrom(str).length; }
|
|
790
833
|
|
|
791
834
|
// @internal
|
|
792
835
|
// userNoticeTexts(qualifier) -- the DisplayText values of a decoded UserNotice qualifier, as
|
|
@@ -803,7 +846,9 @@ function displayTextChars(str) { return Array.from(str).length; }
|
|
|
803
846
|
var _DISPLAY_TEXT_TAGS = null;
|
|
804
847
|
function _isDisplayTextNode(n) {
|
|
805
848
|
if (!_DISPLAY_TEXT_TAGS) {
|
|
806
|
-
|
|
849
|
+
// Indexed by a tag number the encoded value supplies, so it holds no prototype: an inherited
|
|
850
|
+
// numeric name would make an unregistered universal tag read as a DisplayText alternative.
|
|
851
|
+
_DISPLAY_TEXT_TAGS = _create(null);
|
|
807
852
|
_DISPLAY_TEXT_TAGS[_T.IA5_STRING] = 1; _DISPLAY_TEXT_TAGS[_T.VISIBLE_STRING] = 1;
|
|
808
853
|
_DISPLAY_TEXT_TAGS[_T.BMP_STRING] = 1; _DISPLAY_TEXT_TAGS[_T.UTF8_STRING] = 1;
|
|
809
854
|
}
|
|
@@ -836,9 +881,9 @@ function _dtEntry(field, node) {
|
|
|
836
881
|
function _noticeNumbers(node) {
|
|
837
882
|
if (!node || node.tagClass !== "universal" || node.tagNumber !== _T.SEQUENCE || !node.children) return null;
|
|
838
883
|
var nums = [], ok = true;
|
|
839
|
-
node.children
|
|
884
|
+
_forEach(node.children, function (n) {
|
|
840
885
|
// allow:swallow-unverified a non-INTEGER member makes the whole reference undecodable, and the caller falls back
|
|
841
|
-
try { nums
|
|
886
|
+
try { _push(nums, _String(asn1.read.integer(n))); } catch (_e) { ok = false; }
|
|
842
887
|
});
|
|
843
888
|
return ok ? nums : null;
|
|
844
889
|
}
|
|
@@ -863,13 +908,13 @@ function userNoticeTexts(qualifier) {
|
|
|
863
908
|
if (!nr.children || nr.children.length !== 2 || !_isDisplayTextNode(nr.children[0])) return [];
|
|
864
909
|
var org = _dtEntry("organization", nr.children[0]);
|
|
865
910
|
org.noticeNumbers = _noticeNumbers(nr.children[1]);
|
|
866
|
-
out
|
|
911
|
+
_push(out, org);
|
|
867
912
|
i++;
|
|
868
913
|
}
|
|
869
914
|
// explicitText [absent | last]: a DisplayText. Anything else remaining is not a UserNotice member.
|
|
870
915
|
if (i < kids.length) {
|
|
871
916
|
if (!_isDisplayTextNode(kids[i])) return [];
|
|
872
|
-
out
|
|
917
|
+
_push(out, _dtEntry("explicitText", kids[i]));
|
|
873
918
|
i++;
|
|
874
919
|
}
|
|
875
920
|
return i === kids.length ? out : [];
|
|
@@ -882,7 +927,7 @@ function _decodeHelpers(ns) {
|
|
|
882
927
|
function decodeTop(buf, code, what) {
|
|
883
928
|
var n;
|
|
884
929
|
try { n = asn1.decode(buf); }
|
|
885
|
-
catch (e) { throw ns.E(code, "malformed " + what + " value: " + ((e && e.message) ||
|
|
930
|
+
catch (e) { throw ns.E(code, "malformed " + what + " value: " + ((e && e.message) || _String(e)), e); }
|
|
886
931
|
return n;
|
|
887
932
|
}
|
|
888
933
|
function seqChildren(buf, code, what) {
|
|
@@ -900,7 +945,7 @@ function _decodeHelpers(ns) {
|
|
|
900
945
|
// by an unresolved name would silently never match, treating a real extension/attribute as opaque).
|
|
901
946
|
function O(nm) {
|
|
902
947
|
var d = ns.oid.byName(nm);
|
|
903
|
-
if (typeof d !== "string") throw new TypeError("pki.schema.pkix: " +
|
|
948
|
+
if (typeof d !== "string") throw new TypeError("pki.schema.pkix: " + _stringify(nm) + " is not a registered OID name");
|
|
904
949
|
return d;
|
|
905
950
|
}
|
|
906
951
|
return { decodeTop: decodeTop, seqChildren: seqChildren, readInt: readInt, O: O };
|
|
@@ -950,7 +995,7 @@ function certExtensionDecoders(ns) {
|
|
|
950
995
|
// KeyUsage is a NamedBitList -- enforce the shared X.690 sec. 11.2.2 minimal-DER rule.
|
|
951
996
|
schema.assertMinimalNamedBits(bs.unusedBits, bs.bytes, function (m) { throw ns.E(C, m); });
|
|
952
997
|
var out = {};
|
|
953
|
-
KU_BITS
|
|
998
|
+
_forEach(KU_BITS, function (nm, bit) {
|
|
954
999
|
var byte = bit >> 3, mask = 0x80 >> (bit & 7);
|
|
955
1000
|
out[nm] = byte < bs.bytes.length && (bs.bytes[byte] & mask) !== 0;
|
|
956
1001
|
});
|
|
@@ -971,7 +1016,7 @@ function certExtensionDecoders(ns) {
|
|
|
971
1016
|
// GeneralSubtrees is SIZE(1..MAX) -- an explicitly present but empty subtree
|
|
972
1017
|
// list (a0 00) absorbs no constraint and is malformed, not a no-op.
|
|
973
1018
|
if (!node.children || node.children.length < 1) throw ns.E(C, "NameConstraints permittedSubtrees/excludedSubtrees must be a non-empty GeneralSubtrees (SIZE 1..MAX, RFC 5280 sec. 4.2.1.10)");
|
|
974
|
-
return node.children
|
|
1019
|
+
return _map(node.children, function (st) {
|
|
975
1020
|
if (st.tagClass !== "universal" || st.tagNumber !== _T.SEQUENCE || !st.children || st.children.length < 1) {
|
|
976
1021
|
throw ns.E(C, "GeneralSubtree must be a SEQUENCE { base, minimum?, maximum? }");
|
|
977
1022
|
}
|
|
@@ -989,7 +1034,7 @@ function certExtensionDecoders(ns) {
|
|
|
989
1034
|
var C = ns.prefix + "/bad-name-constraints";
|
|
990
1035
|
var kids = seqChildren(buf, C, "NameConstraints");
|
|
991
1036
|
var permitted = [], excluded = [], sawP = false, sawE = false, ncLastTag = -1;
|
|
992
|
-
kids
|
|
1037
|
+
_forEach(kids, function (k) {
|
|
993
1038
|
if (k.tagClass !== "context" || !k.children) throw ns.E(C, "NameConstraints fields are [0] permittedSubtrees / [1] excludedSubtrees");
|
|
994
1039
|
if (k.tagNumber <= ncLastTag) throw ns.E(C, "NameConstraints fields must be unique and in ascending order (DER)");
|
|
995
1040
|
ncLastTag = k.tagNumber;
|
|
@@ -1006,8 +1051,10 @@ function certExtensionDecoders(ns) {
|
|
|
1006
1051
|
var C = ns.prefix + "/bad-policy";
|
|
1007
1052
|
var kids = seqChildren(buf, C, "CertificatePolicies");
|
|
1008
1053
|
if (kids.length < 1) throw ns.E(C, "CertificatePolicies must contain at least one PolicyInformation (RFC 5280 sec. 4.2.1.4)");
|
|
1009
|
-
|
|
1010
|
-
|
|
1054
|
+
// The duplicate-policy set answers off a dotted OID the encoded value supplies, so it holds no
|
|
1055
|
+
// prototype: an inherited name would report a policy as already seen and reject a valid one.
|
|
1056
|
+
var seen = _create(null);
|
|
1057
|
+
return _map(kids, function (pi) {
|
|
1011
1058
|
// PolicyInformation ::= SEQUENCE { policyIdentifier, policyQualifiers
|
|
1012
1059
|
// SEQUENCE SIZE(1..MAX) OPTIONAL }. Exactly one or two fields; a second
|
|
1013
1060
|
// field, if present, MUST be a SEQUENCE. Extra/mis-typed fields are malformed.
|
|
@@ -1035,7 +1082,7 @@ function certExtensionDecoders(ns) {
|
|
|
1035
1082
|
var C = ns.prefix + "/bad-policy";
|
|
1036
1083
|
var kids = seqChildren(buf, C, "PolicyMappings");
|
|
1037
1084
|
if (kids.length < 1) throw ns.E(C, "PolicyMappings must contain at least one mapping (RFC 5280 sec. 4.2.1.5)");
|
|
1038
|
-
return kids
|
|
1085
|
+
return _map(kids, function (mp) {
|
|
1039
1086
|
if (mp.tagClass !== "universal" || mp.tagNumber !== _T.SEQUENCE || !mp.children || mp.children.length !== 2) {
|
|
1040
1087
|
throw ns.E(C, "policy mapping must be a SEQUENCE { issuerDomainPolicy, subjectDomainPolicy }");
|
|
1041
1088
|
}
|
|
@@ -1052,7 +1099,7 @@ function certExtensionDecoders(ns) {
|
|
|
1052
1099
|
var kids = seqChildren(buf, C, "PolicyConstraints");
|
|
1053
1100
|
if (kids.length < 1) throw ns.E(C, "PolicyConstraints must contain at least one field (RFC 5280 sec. 4.2.1.11)");
|
|
1054
1101
|
var rep = null, ipm = null, pcLastTag = -1;
|
|
1055
|
-
kids
|
|
1102
|
+
_forEach(kids, function (k) {
|
|
1056
1103
|
if (k.tagClass !== "context") throw ns.E(C, "PolicyConstraints fields are context-tagged [0]/[1]");
|
|
1057
1104
|
if (k.tagNumber <= pcLastTag) throw ns.E(C, "PolicyConstraints fields must be unique and in ascending order (DER)");
|
|
1058
1105
|
pcLastTag = k.tagNumber;
|
|
@@ -1098,7 +1145,7 @@ function certExtensionDecoders(ns) {
|
|
|
1098
1145
|
var descs = seqChildren(buf, C, "AuthorityInfoAccessSyntax");
|
|
1099
1146
|
if (descs.length < 1) throw ns.E(C, "AuthorityInfoAccessSyntax must contain at least one AccessDescription (RFC 5280 sec. 4.2.2.1, SIZE(1..MAX))");
|
|
1100
1147
|
var GN = generalName(ns, { decodeValue: true, code: C });
|
|
1101
|
-
return descs
|
|
1148
|
+
return _map(descs, function (d) {
|
|
1102
1149
|
if (d.tagClass !== "universal" || d.tagNumber !== _T.SEQUENCE || !d.children || d.children.length !== 2) {
|
|
1103
1150
|
throw ns.E(C, "AccessDescription must be a SEQUENCE { accessMethod, accessLocation } (RFC 5280 sec. 4.2.2.1)");
|
|
1104
1151
|
}
|
|
@@ -1115,7 +1162,7 @@ function certExtensionDecoders(ns) {
|
|
|
1115
1162
|
var C = ns.prefix + "/bad-extension-value";
|
|
1116
1163
|
var kids = seqChildren(buf, C, "ExtKeyUsage");
|
|
1117
1164
|
if (kids.length < 1) throw ns.E(C, "ExtKeyUsage must contain at least one KeyPurposeId (RFC 5280 sec. 4.2.1.12)");
|
|
1118
|
-
return kids
|
|
1165
|
+
return _map(kids, function (k) {
|
|
1119
1166
|
try { return asn1.read.oid(k); } catch (e) { throw ns.E(C, "ExtKeyUsage KeyPurposeId must be an OBJECT IDENTIFIER", e); }
|
|
1120
1167
|
});
|
|
1121
1168
|
}
|
|
@@ -1126,7 +1173,7 @@ function certExtensionDecoders(ns) {
|
|
|
1126
1173
|
var kids = seqChildren(buf, C, "AuthorityKeyIdentifier");
|
|
1127
1174
|
var out = { keyIdentifier: null, authorityCertIssuer: null, authorityCertSerialNumber: null };
|
|
1128
1175
|
var lastTag = -1;
|
|
1129
|
-
kids
|
|
1176
|
+
_forEach(kids, function (k) {
|
|
1130
1177
|
if (k.tagClass !== "context") throw ns.E(C, "AuthorityKeyIdentifier fields are context-tagged");
|
|
1131
1178
|
// DER: the OPTIONAL fields appear at most once and in ascending tag order.
|
|
1132
1179
|
if (k.tagNumber <= lastTag) throw ns.E(C, "AuthorityKeyIdentifier fields must be unique and in ascending order (DER)");
|
|
@@ -1135,7 +1182,7 @@ function certExtensionDecoders(ns) {
|
|
|
1135
1182
|
// [2] IMPLICIT INTEGER are primitive leaves; the readers enforce the
|
|
1136
1183
|
// primitive form fail-closed (a constructed context node is rejected,
|
|
1137
1184
|
// never dereferenced for its absent content).
|
|
1138
|
-
if (k.tagNumber === 0) { try { out.keyIdentifier =
|
|
1185
|
+
if (k.tagNumber === 0) { try { out.keyIdentifier = _bufferFrom(asn1.read.octetStringImplicit(k, 0)); } catch (e) { throw ns.E(C, "AuthorityKeyIdentifier keyIdentifier [0] must be an IMPLICIT OCTET STRING", e); } }
|
|
1139
1186
|
else if (k.tagNumber === 1) out.authorityCertIssuer = schema.walk(generalNames(ns, { implicitTag: 1, decodeValue: true, code: C }), k, ns).result;
|
|
1140
1187
|
else if (k.tagNumber === 2) { try { out.authorityCertSerialNumber = asn1.read.integerImplicit(k, 2); } catch (e) { throw ns.E(C, "authorityCertSerialNumber must be an INTEGER", e); } }
|
|
1141
1188
|
else throw ns.E(C, "AuthorityKeyIdentifier has an unexpected field [" + k.tagNumber + "]");
|
|
@@ -1150,7 +1197,7 @@ function certExtensionDecoders(ns) {
|
|
|
1150
1197
|
function subjectKeyIdentifier(buf) {
|
|
1151
1198
|
var C = ns.prefix + "/bad-extension-value";
|
|
1152
1199
|
var n = decodeTop(buf, C, "SubjectKeyIdentifier");
|
|
1153
|
-
try { return
|
|
1200
|
+
try { return _bufferConcat([asn1.read.octetString(n)]); }
|
|
1154
1201
|
catch (e) { throw ns.E(C, "SubjectKeyIdentifier must be an OCTET STRING (RFC 5280 sec. 4.2.1.2)", e); }
|
|
1155
1202
|
}
|
|
1156
1203
|
|
|
@@ -1193,13 +1240,13 @@ function certExtensionDecoders(ns) {
|
|
|
1193
1240
|
var C = ns.prefix + "/bad-crl-distribution-points";
|
|
1194
1241
|
var kids = seqChildren(buf, C, "CRLDistributionPoints");
|
|
1195
1242
|
if (kids.length < 1) throw ns.E(C, "CRLDistributionPoints must contain at least one DistributionPoint (RFC 5280 sec. 4.2.1.13)");
|
|
1196
|
-
return kids
|
|
1243
|
+
return _map(kids, function (dp) {
|
|
1197
1244
|
if (dp.tagClass !== "universal" || dp.tagNumber !== _T.SEQUENCE || !dp.children) {
|
|
1198
1245
|
throw ns.E(C, "DistributionPoint must be a SEQUENCE { distributionPoint?, reasons?, cRLIssuer? }");
|
|
1199
1246
|
}
|
|
1200
1247
|
var out = { distributionPoint: null, reasons: null, cRLIssuer: null };
|
|
1201
1248
|
var lastTag = -1;
|
|
1202
|
-
dp.children
|
|
1249
|
+
_forEach(dp.children, function (f) {
|
|
1203
1250
|
if (f.tagClass !== "context") throw ns.E(C, "DistributionPoint fields are context-tagged [0]/[1]/[2]");
|
|
1204
1251
|
if (f.tagNumber <= lastTag) throw ns.E(C, "DistributionPoint fields must be unique and in ascending order (DER)");
|
|
1205
1252
|
lastTag = f.tagNumber;
|
|
@@ -1241,7 +1288,7 @@ function certExtensionDecoders(ns) {
|
|
|
1241
1288
|
}
|
|
1242
1289
|
function _qcOidSeq(node, C, what) { // a non-empty SEQUENCE OF OBJECT IDENTIFIER
|
|
1243
1290
|
if (!node || node.tagClass !== "universal" || node.tagNumber !== _T.SEQUENCE || !node.children || !node.children.length) throw ns.E(C, what + " must be a non-empty SEQUENCE OF OBJECT IDENTIFIER");
|
|
1244
|
-
return node.children
|
|
1291
|
+
return _map(node.children, function (c) {
|
|
1245
1292
|
if (c.tagClass !== "universal" || c.tagNumber !== _T.OBJECT_IDENTIFIER) throw ns.E(C, what + " members must be OBJECT IDENTIFIERs");
|
|
1246
1293
|
try { return asn1.read.oid(c); } catch (e) { throw ns.E(C, what + " member is not a valid OID", e); }
|
|
1247
1294
|
});
|
|
@@ -1266,14 +1313,16 @@ function certExtensionDecoders(ns) {
|
|
|
1266
1313
|
if (i !== node.children.length) throw ns.E(C, "SemanticsInformation has unexpected trailing fields");
|
|
1267
1314
|
return { semanticsIdentifier: sid, nameRegistrationAuthorities: nras };
|
|
1268
1315
|
}
|
|
1269
|
-
|
|
1316
|
+
// The per-statement decoder table answers off a dotted OID the encoded value supplies, so it holds
|
|
1317
|
+
// no prototype: an inherited name would hand a statement a decoder nothing registered.
|
|
1318
|
+
var qcInfoByOid = _create(null);
|
|
1270
1319
|
qcInfoByOid[O("qcCompliance")] = _qcPresenceOnly("QcCompliance");
|
|
1271
1320
|
qcInfoByOid[O("qcSSCD")] = _qcPresenceOnly("QcSSCD");
|
|
1272
1321
|
qcInfoByOid[O("qcsPkixQCSyntaxV1")] = _qcSemantics;
|
|
1273
1322
|
qcInfoByOid[O("qcsPkixQCSyntaxV2")] = _qcSemantics;
|
|
1274
1323
|
qcInfoByOid[O("qcType")] = function (node, C) {
|
|
1275
1324
|
var oids = _qcOidSeq(node, C, "QcType"); // SIZE(1) in EN 319 412-5 V2.5.1; accept >=1 for legacy certs
|
|
1276
|
-
return { types: oids, typeNames: oids
|
|
1325
|
+
return { types: oids, typeNames: _map(oids, function (d) { return ns.oid.name(d) || null; }) };
|
|
1277
1326
|
};
|
|
1278
1327
|
qcInfoByOid[O("qcRetentionPeriod")] = function (node, C) {
|
|
1279
1328
|
if (!node) throw ns.E(C, "QcRetentionPeriod requires an INTEGER statementInfo");
|
|
@@ -1302,11 +1351,11 @@ function certExtensionDecoders(ns) {
|
|
|
1302
1351
|
};
|
|
1303
1352
|
qcInfoByOid[O("qcCClegislation")] = function (node, C) { // SEQUENCE OF CountryName (PrintableString SIZE 2)
|
|
1304
1353
|
if (!node || node.tagClass !== "universal" || node.tagNumber !== _T.SEQUENCE || !node.children || !node.children.length) throw ns.E(C, "QcCClegislation must be a non-empty SEQUENCE OF CountryName");
|
|
1305
|
-
return { countries: node.children
|
|
1354
|
+
return { countries: _map(node.children, function (c) { var v = _qcStr(c, _T.PRINTABLE_STRING, C, "QcCClegislation CountryName must be a PrintableString"); if (v.length !== 2) throw ns.E(C, "QcCClegislation CountryName must be a 2-letter ISO 3166-1 code"); return v; }) };
|
|
1306
1355
|
};
|
|
1307
1356
|
qcInfoByOid[O("qcPDS")] = function (node, C) { // PdsLocations ::= SEQUENCE OF PdsLocation { url IA5, language Printable(2) }
|
|
1308
1357
|
if (!node || node.tagClass !== "universal" || node.tagNumber !== _T.SEQUENCE || !node.children || !node.children.length) throw ns.E(C, "QcPDS must be a non-empty SEQUENCE OF PdsLocation");
|
|
1309
|
-
return { locations: node.children
|
|
1358
|
+
return { locations: _map(node.children, function (loc) {
|
|
1310
1359
|
if (loc.tagClass !== "universal" || loc.tagNumber !== _T.SEQUENCE || !loc.children || loc.children.length !== 2) throw ns.E(C, "a QcPDS PdsLocation must be a SEQUENCE { url, language }");
|
|
1311
1360
|
var lang = _qcStr(loc.children[1], _T.PRINTABLE_STRING, C, "a QcPDS language must be a PrintableString");
|
|
1312
1361
|
if (lang.length !== 2) throw ns.E(C, "a QcPDS language must be a 2-letter ISO 639-1 code");
|
|
@@ -1317,11 +1366,11 @@ function certExtensionDecoders(ns) {
|
|
|
1317
1366
|
// QcQSCDlegislation (SEQUENCE OF CountryName, like QcCClegislation). Both are registered ids, decoded.
|
|
1318
1367
|
qcInfoByOid[O("qcIdentMethod")] = function (node, C) {
|
|
1319
1368
|
var oids = _qcOidSeq(node, C, "QcIdentMethod");
|
|
1320
|
-
return { methods: oids, methodNames: oids
|
|
1369
|
+
return { methods: oids, methodNames: _map(oids, function (d) { return ns.oid.name(d) || null; }) };
|
|
1321
1370
|
};
|
|
1322
1371
|
qcInfoByOid[O("qcQSCDlegislation")] = function (node, C) {
|
|
1323
1372
|
if (!node || node.tagClass !== "universal" || node.tagNumber !== _T.SEQUENCE || !node.children || !node.children.length) throw ns.E(C, "QcQSCDlegislation must be a non-empty SEQUENCE OF CountryName");
|
|
1324
|
-
return { countries: node.children
|
|
1373
|
+
return { countries: _map(node.children, function (c) { var v = _qcStr(c, _T.PRINTABLE_STRING, C, "QcQSCDlegislation CountryName must be a PrintableString"); if (v.length !== 2) throw ns.E(C, "QcQSCDlegislation CountryName must be a 2-letter ISO 3166-1 code"); return v; }) };
|
|
1325
1374
|
};
|
|
1326
1375
|
function qcStatements(buf) {
|
|
1327
1376
|
var C = ns.prefix + "/bad-qc-statements", CS = ns.prefix + "/bad-qc-statement";
|
|
@@ -1338,7 +1387,7 @@ function certExtensionDecoders(ns) {
|
|
|
1338
1387
|
var infoNode = st.children.length === 2 ? st.children[1] : null;
|
|
1339
1388
|
var decoder = qcInfoByOid[id];
|
|
1340
1389
|
var info = decoder ? decoder(infoNode, CS) : { opaque: true, bytes: infoNode ? infoNode.bytes : null };
|
|
1341
|
-
out
|
|
1390
|
+
_push(out, { statementId: id, name: ns.oid.name(id) || null, info: info });
|
|
1342
1391
|
}
|
|
1343
1392
|
return out;
|
|
1344
1393
|
}
|
|
@@ -1388,13 +1437,16 @@ function certExtensionDecoders(ns) {
|
|
|
1388
1437
|
var C = ns.prefix + "/bad-ms-previous-cert-hash";
|
|
1389
1438
|
var n = decodeTop(buf, C, "CAPrevCertHash");
|
|
1390
1439
|
var hash;
|
|
1391
|
-
try { hash =
|
|
1440
|
+
try { hash = _bufferConcat([asn1.read.octetString(n)]); }
|
|
1392
1441
|
catch (e) { throw ns.E(C, "CAPrevCertHash must be an OCTET STRING", e); }
|
|
1393
1442
|
if (hash.length !== 20) throw ns.E(C, "CAPrevCertHash must be a 20-octet SHA-1 certificate thumbprint");
|
|
1394
1443
|
return hash;
|
|
1395
1444
|
}
|
|
1396
1445
|
|
|
1397
|
-
|
|
1446
|
+
// The extension-decoder registry every consumer indexes with an OID read off the wire
|
|
1447
|
+
// (path validation, inspection, the ACME certificate binding), so it holds no prototype: a
|
|
1448
|
+
// planted dotted name would otherwise hand an extension a decoder nothing registered.
|
|
1449
|
+
var byOid = _create(null);
|
|
1398
1450
|
byOid[O("qcStatements")] = qcStatements;
|
|
1399
1451
|
byOid[O("basicConstraints")] = basicConstraints;
|
|
1400
1452
|
byOid[O("keyUsage")] = keyUsage;
|
|
@@ -1460,10 +1512,10 @@ function extensions(ns, opts) {
|
|
|
1460
1512
|
var extOpts = {
|
|
1461
1513
|
min: 1, code: ns.prefix + "/bad-extensions", what: "Extensions",
|
|
1462
1514
|
unique: function (it) { return it.value.oid; }, dupCode: ns.prefix + "/duplicate-extension",
|
|
1463
|
-
build: function (m) { return m.items
|
|
1515
|
+
build: function (m) { return _map(m.items, function (it) { return it.value; }); },
|
|
1464
1516
|
};
|
|
1465
1517
|
if (opts.implicitTag != null) return schema.implicitSeqOf(opts.implicitTag, EXT, extOpts);
|
|
1466
|
-
return schema.seqOf(EXT,
|
|
1518
|
+
return schema.seqOf(EXT, _assign({ assert: "sequence" }, extOpts));
|
|
1467
1519
|
}
|
|
1468
1520
|
|
|
1469
1521
|
// SubjectPublicKeyInfo ::= SEQUENCE { algorithm AlgorithmIdentifier,
|
|
@@ -1517,7 +1569,7 @@ function attribute(ns, opts) {
|
|
|
1517
1569
|
return {
|
|
1518
1570
|
type: t,
|
|
1519
1571
|
name: ctx.oid.name(t) || null,
|
|
1520
|
-
values: m.fields.values.value.items
|
|
1572
|
+
values: _map(m.fields.values.value.items, function (it) { return it.node.bytes; }),
|
|
1521
1573
|
};
|
|
1522
1574
|
},
|
|
1523
1575
|
});
|
|
@@ -1622,10 +1674,10 @@ function signedEnvelope(ns, tbsSchema, opts) {
|
|
|
1622
1674
|
function dnsNameProblem(s) {
|
|
1623
1675
|
if (typeof s !== "string" || !s.length) return "empty";
|
|
1624
1676
|
if (s.length > 253) return "exceeds 253 octets";
|
|
1625
|
-
if (/\s
|
|
1626
|
-
if (s
|
|
1627
|
-
if (s
|
|
1628
|
-
var labels = s
|
|
1677
|
+
if (_reTest(/\s/, s)) return "whitespace";
|
|
1678
|
+
if (_charAt(s, 0) === "." || _charAt(s, s.length - 1) === ".") return "leading/trailing dot";
|
|
1679
|
+
if (_strIndexOf(s, "_") !== -1) return "underscore forbidden in dNSName";
|
|
1680
|
+
var labels = _split(s, ".");
|
|
1629
1681
|
for (var i = 0; i < labels.length; i++) {
|
|
1630
1682
|
var label = labels[i];
|
|
1631
1683
|
if (label.length === 0) return "empty label";
|
|
@@ -1634,7 +1686,7 @@ function dnsNameProblem(s) {
|
|
|
1634
1686
|
if (labels.length < 2) return "bare wildcard";
|
|
1635
1687
|
continue;
|
|
1636
1688
|
}
|
|
1637
|
-
if (
|
|
1689
|
+
if (!_reTest(/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/i, label)) return "invalid label syntax";
|
|
1638
1690
|
}
|
|
1639
1691
|
return null;
|
|
1640
1692
|
}
|
package/package.json
CHANGED