@blamejs/pki 0.5.22 → 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 +30 -1
- package/MIGRATING.md +31 -0
- package/README.md +1 -1
- package/lib/acme.js +284 -47
- package/lib/attrcert-sign.js +10 -5
- package/lib/cmc-build.js +6 -2
- package/lib/cmc-verify.js +6 -2
- package/lib/cmp-verify.js +6 -11
- package/lib/cms-verify.js +1 -1
- package/lib/crl-sign.js +10 -5
- package/lib/ct.js +6 -1
- package/lib/est.js +7 -3
- 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/hpke.js +5 -1
- package/lib/http-digest.js +13 -3
- package/lib/path-validate.js +1 -1
- package/lib/schema-attrcert.js +5 -1
- package/lib/schema-crl.js +5 -1
- package/lib/schema-ocsp.js +7 -2
- package/lib/schema-pkix.js +142 -85
- package/lib/tls-cert-compress.js +5 -1
- package/lib/trust.js +5 -1
- package/lib/webauthn-mds.js +5 -1
- package/lib/webauthn.js +6 -2
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/schema-pkix.js
CHANGED
|
@@ -17,6 +17,43 @@ var asn1 = require("./asn1-der");
|
|
|
17
17
|
var constants = require("./constants");
|
|
18
18
|
var schema = require("./schema-engine");
|
|
19
19
|
var guard = require("./guard-all");
|
|
20
|
+
var intrinsic = require("./guard-intrinsic");
|
|
21
|
+
// Own-key membership through an operation taken at load. Written out, the question reads two
|
|
22
|
+
// replaceable properties to answer one thing, and here it decides which version numbers a format
|
|
23
|
+
// accepts -- shared by every format module, so one admitted name widens all of them at once.
|
|
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);
|
|
20
57
|
// ct is a leaf format module (requires only the codec / oid / constants / error
|
|
21
58
|
// core, never pkix) so the SCT-list extension VALUE decoder registers here as a
|
|
22
59
|
// registry row, not a switch. The one-directional edge keeps the layering acyclic.
|
|
@@ -43,10 +80,10 @@ function pemDecode(text, label, PemError) {
|
|
|
43
80
|
text = guard.text.decode(text, constants.LIMITS.PEM_MAX_BYTES, PemError, {
|
|
44
81
|
charset: "latin1", tooLarge: "pem/too-large", badInput: "pem/bad-input", label: "PEM input",
|
|
45
82
|
});
|
|
46
|
-
var m = PEM_RE
|
|
83
|
+
var m = _reExec(PEM_RE, text);
|
|
47
84
|
if (!m) throw new PemError("pem/no-block", "no PEM block found");
|
|
48
|
-
if (label && m[1] !== label) throw new PemError("pem/label-mismatch", "expected " +
|
|
49
|
-
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, "");
|
|
50
87
|
// Strict canonical base64 (RFC 4648 sec. 3.5) via the shared encoding guard.
|
|
51
88
|
return guard.encoding.base64(b64, null, function (c, msg) { return new PemError(c, msg); }, "pem/bad-base64", "PEM base64 body");
|
|
52
89
|
}
|
|
@@ -65,15 +102,15 @@ function pemDecodeAll(text, label, PemError) {
|
|
|
65
102
|
var re = /-----BEGIN ([A-Z0-9 ]+)-----([\s\S]*?)-----END \1-----/g;
|
|
66
103
|
var blocks = [];
|
|
67
104
|
var lastEnd = 0, m;
|
|
68
|
-
while ((m = re
|
|
69
|
-
if (/\S
|
|
70
|
-
if (m[1] !== label) throw new PemError("pem/label-mismatch", "expected " +
|
|
71
|
-
var b64 = m[2]
|
|
72
|
-
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"));
|
|
73
110
|
lastEnd = re.lastIndex;
|
|
74
111
|
}
|
|
75
112
|
if (blocks.length === 0) throw new PemError("pem/no-block", "no PEM block found");
|
|
76
|
-
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)");
|
|
77
114
|
return blocks;
|
|
78
115
|
}
|
|
79
116
|
|
|
@@ -83,14 +120,14 @@ function pemDecodeAll(text, label, PemError) {
|
|
|
83
120
|
// an embedded "-----" or newline) would emit armor pemDecode itself rejects
|
|
84
121
|
// or corrupt the armor grammar: a config-time authoring fault, thrown here.
|
|
85
122
|
function pemEncode(der, label, PemError) {
|
|
86
|
-
if (typeof label !== "string" ||
|
|
123
|
+
if (typeof label !== "string" || !_reTest(/^[A-Z0-9]+( [A-Z0-9]+)*$/, label)) {
|
|
87
124
|
throw new PemError("pem/bad-label", "pemEncode requires an uppercase A-Z0-9 label with single spaces");
|
|
88
125
|
}
|
|
89
126
|
// Re-view through the byte guard: a string input would silently utf8-armor into
|
|
90
127
|
// a bogus PEM, and a detached-backed Buffer would armor an empty body; both
|
|
91
128
|
// fail closed (a Uint8Array / Buffer of real DER re-views cleanly).
|
|
92
129
|
var buf = guard.bytes.view(der, PemError, "pem/bad-input", "pemEncode DER input");
|
|
93
|
-
var b64 = buf
|
|
130
|
+
var b64 = _strReplace(_strReplace(_bufToString(buf, "base64"), /(.{64})/g, "$1\n"), /\n$/, "");
|
|
94
131
|
return "-----BEGIN " + label + "-----\n" + b64 + "\n-----END " + label + "-----\n";
|
|
95
132
|
}
|
|
96
133
|
|
|
@@ -99,7 +136,7 @@ function pemEncode(der, label, PemError) {
|
|
|
99
136
|
// Uint8Array is taken as bytes. opts: { pemLabel, PemError, ErrorClass, prefix }.
|
|
100
137
|
function coerceToDer(input, opts) {
|
|
101
138
|
if (typeof input === "string") return pemDecode(input, opts.pemLabel, opts.PemError);
|
|
102
|
-
if (
|
|
139
|
+
if (_isBuffer(input) || _isUint8Array(input)) {
|
|
103
140
|
// Route the DER Buffer / Uint8Array through the shared byte guard so a
|
|
104
141
|
// detached backing store fails closed here for every format that composes
|
|
105
142
|
// this boundary (x509/crl/csr/pkcs8/cms/pkcs12), not per-format.
|
|
@@ -115,8 +152,8 @@ function coerceToDer(input, opts) {
|
|
|
115
152
|
// leading tag+length bytes are non-printable, so a non-SEQUENCE DER (a bare
|
|
116
153
|
// SET / INTEGER) is not misrouted here; it decodes and fails closed structurally.
|
|
117
154
|
function _isPemArmor(buf) {
|
|
118
|
-
var head = buf
|
|
119
|
-
var idx = head
|
|
155
|
+
var head = _bufToString(_subarray(buf, 0, 4096), "latin1");
|
|
156
|
+
var idx = _strIndexOf(head, "-----BEGIN");
|
|
120
157
|
if (idx === -1) return false;
|
|
121
158
|
for (var i = 0; i < idx; i++) {
|
|
122
159
|
var c = buf[i];
|
|
@@ -138,7 +175,7 @@ function decodeRoot(der, opts) {
|
|
|
138
175
|
// strictness verdict included.
|
|
139
176
|
try { return asn1.decode(der, opts.ber ? { ber: true } : undefined); }
|
|
140
177
|
catch (e) {
|
|
141
|
-
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);
|
|
142
179
|
}
|
|
143
180
|
}
|
|
144
181
|
|
|
@@ -173,8 +210,8 @@ function makeNS(prefix, ErrorClass, oidModule) {
|
|
|
173
210
|
// version is rejected at the leaf (asn1/*).
|
|
174
211
|
function versionReader(ns, accept) {
|
|
175
212
|
return schema.decode(function (n) {
|
|
176
|
-
var key = asn1.read.integer(n)
|
|
177
|
-
if (
|
|
213
|
+
var key = _bigIntToString(asn1.read.integer(n));
|
|
214
|
+
if (_hasOwn(accept, key)) return accept[key];
|
|
178
215
|
throw ns.E(ns.prefix + "/bad-version", "unsupported version " + key);
|
|
179
216
|
});
|
|
180
217
|
}
|
|
@@ -332,7 +369,7 @@ function attrValueToString(ns) {
|
|
|
332
369
|
try { s = asn1.read.string(node); }
|
|
333
370
|
catch (e) {
|
|
334
371
|
if (!e || (e.code !== "asn1/expected-string" && e.code !== "asn1/expected-primitive")) {
|
|
335
|
-
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)));
|
|
336
373
|
}
|
|
337
374
|
// asn1.read.string does not read NumericString, and that is deliberate: a value that returns a plain
|
|
338
375
|
// string joins the RFC 5280 sec. 7.1 name-comparison identity class, and NumericString is not a
|
|
@@ -342,11 +379,11 @@ function attrValueToString(ns) {
|
|
|
342
379
|
// surfacing it in the type-distinct RFC 4514 hex form, which keeps it out of that identity class.
|
|
343
380
|
if (node.tagClass === "universal" && node.tagNumber === asn1.TAGS.NUMERIC_STRING) {
|
|
344
381
|
try { asn1.read.numericString(node); }
|
|
345
|
-
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))); }
|
|
346
383
|
}
|
|
347
|
-
return "#" + node.bytes
|
|
384
|
+
return "#" + _bufToString(node.bytes, "hex");
|
|
348
385
|
}
|
|
349
|
-
if (s
|
|
386
|
+
if (_charAt(s, 0) === "#" || _charAt(s, 0) === "\\") return "\\" + s;
|
|
350
387
|
return s;
|
|
351
388
|
}, function (value) {
|
|
352
389
|
// encode: a leading '\' escapes the next character (the decode's escape of a
|
|
@@ -356,9 +393,9 @@ function attrValueToString(ns) {
|
|
|
356
393
|
// the first invalid character and would emit truncated bytes. Any other
|
|
357
394
|
// string encodes as UTF8String (the decode does not preserve the original
|
|
358
395
|
// string type, so this is the canonical re-encoding, not a byte-exact one).
|
|
359
|
-
if (typeof value === "string" && value
|
|
360
|
-
if (typeof value === "string" && value
|
|
361
|
-
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);
|
|
362
399
|
if (hex.length === 0) {
|
|
363
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)");
|
|
364
401
|
}
|
|
@@ -390,8 +427,8 @@ var _escapeDnValue = guard.name.escapeDnValue;
|
|
|
390
427
|
// the semantic string and then rendered with the full escape set.
|
|
391
428
|
function _dnDisplayValue(v) {
|
|
392
429
|
if (typeof v !== "string") return v;
|
|
393
|
-
if (v
|
|
394
|
-
return _escapeDnValue(v
|
|
430
|
+
if (_charAt(v, 0) === "#") return v;
|
|
431
|
+
return _escapeDnValue(_charAt(v, 0) === "\\" ? _strSlice(v, 1) : v);
|
|
395
432
|
}
|
|
396
433
|
|
|
397
434
|
// time(ns): the RFC 5280 Time production under its profile encoding rule
|
|
@@ -410,7 +447,7 @@ function time(ns) {
|
|
|
410
447
|
return schema.decode(function (n, ctx) {
|
|
411
448
|
var d = base.fn(n, ctx);
|
|
412
449
|
if (n.tagNumber === asn1.TAGS.GENERALIZED_TIME) {
|
|
413
|
-
var y = d
|
|
450
|
+
var y = _getUTCFullYear(d);
|
|
414
451
|
if (y >= 1950 && y < 2050) {
|
|
415
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)");
|
|
416
453
|
}
|
|
@@ -448,16 +485,19 @@ function name(ns, opts) {
|
|
|
448
485
|
opts = opts || {};
|
|
449
486
|
function nameBuild(m) {
|
|
450
487
|
var rdns = [], parts = [];
|
|
451
|
-
|
|
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) {
|
|
452
492
|
var atvs = [], atvParts = [];
|
|
453
|
-
rdnItem.value.items
|
|
493
|
+
_forEach(rdnItem.value.items, function (atvItem) {
|
|
454
494
|
var a = atvItem.value.result; // atvItem.value = the atv seq-match; .result = its build result
|
|
455
|
-
atvs
|
|
456
|
-
var label = (a.name && DN_SHORT[a.name]) || a.name || a.type;
|
|
457
|
-
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));
|
|
458
498
|
});
|
|
459
|
-
rdns
|
|
460
|
-
parts
|
|
499
|
+
_push(rdns, atvs);
|
|
500
|
+
_push(parts, _join(atvParts, "+"));
|
|
461
501
|
});
|
|
462
502
|
// `dn` is a DISPLAY string, and its spelling is `openssl x509 -subject`: the RDNs in the
|
|
463
503
|
// certificate's own order, joined by ", ". That is deliberately not the RFC 4514 / LDAP
|
|
@@ -468,7 +508,7 @@ function name(ns, opts) {
|
|
|
468
508
|
// guard.name.dnEqual compares the DER under RFC 5280 sec. 7.1, and `bytes` below carries the raw
|
|
469
509
|
// Name DER for a consumer that needs the exact octets (the EST re-enroll subject MUST). Every
|
|
470
510
|
// renderer in the toolkit uses this one spelling, so a certificate has one dn whichever parsed it.
|
|
471
|
-
return { rdns: rdns, dn: parts
|
|
511
|
+
return { rdns: rdns, dn: _join(parts, ", "), bytes: opts.implicitTag != null ? asn1.sequenceTlv(m.node) : m.node.bytes };
|
|
472
512
|
}
|
|
473
513
|
if (opts.implicitTag != null) {
|
|
474
514
|
return schema.implicitSeqOf(opts.implicitTag, relativeDistinguishedName(ns), {
|
|
@@ -556,8 +596,13 @@ function rawNonEmptySequence(ns, opts) {
|
|
|
556
596
|
// (shared with the OCSP revoked-status decoder, which surfaces the same numeric code).
|
|
557
597
|
var CRL_REASON_NAMES = constants.NAMES.CRL_REASON;
|
|
558
598
|
|
|
559
|
-
|
|
560
|
-
|
|
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 });
|
|
561
606
|
function generalName(ns, opts) {
|
|
562
607
|
opts = opts || {};
|
|
563
608
|
var code = opts.code || (ns.prefix + "/bad-general-name");
|
|
@@ -597,7 +642,7 @@ function generalName(ns, opts) {
|
|
|
597
642
|
// 7-bit IA5, no C0/DEL control byte. An embedded NUL/control in a
|
|
598
643
|
// dNSName/rfc822Name/URI enables a name-truncation bypass (CVE-2009-2408).
|
|
599
644
|
guard.name.assertPrintableIa5(n.content, ctx.E, code, "GeneralName [" + t + "]");
|
|
600
|
-
if (decodeValue) value = n.content
|
|
645
|
+
if (decodeValue) value = _bufToString(n.content, "latin1");
|
|
601
646
|
} else if (t === 7) {
|
|
602
647
|
if (subtreeBase) {
|
|
603
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)");
|
|
@@ -606,7 +651,7 @@ function generalName(ns, opts) {
|
|
|
606
651
|
}
|
|
607
652
|
// n.content is proven non-null here (the primitive-form guard above
|
|
608
653
|
// threw on a constructed node); copy the octets out.
|
|
609
|
-
if (decodeValue) value =
|
|
654
|
+
if (decodeValue) value = _bufferConcat([n.content]);
|
|
610
655
|
} else if (t === 8) {
|
|
611
656
|
var regId;
|
|
612
657
|
try { regId = asn1.decodeOidContent(n.content); }
|
|
@@ -637,7 +682,10 @@ function generalNames(ns, opts) {
|
|
|
637
682
|
// decodeValue / subtreeBase thread through to each element (the path
|
|
638
683
|
// validator's constraint matcher passes decodeValue:true).
|
|
639
684
|
var gn = generalName(ns, { code: code, decodeValue: opts.decodeValue === true, subtreeBase: opts.subtreeBase === true });
|
|
640
|
-
|
|
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 }; }
|
|
641
689
|
if (opts.implicitTag != null) {
|
|
642
690
|
return schema.implicitSeqOf(opts.implicitTag, gn, { min: 1, code: code, what: opts.what || "GeneralNames", build: build });
|
|
643
691
|
}
|
|
@@ -662,7 +710,7 @@ function generalNames(ns, opts) {
|
|
|
662
710
|
function distributionPointName(ns, node, code) {
|
|
663
711
|
if (node && node.tagClass === "context" && node.tagNumber === 0) {
|
|
664
712
|
var gns = schema.walk(generalNames(ns, { implicitTag: 0, code: code, what: "DistributionPointName fullName" }), node, ns).result;
|
|
665
|
-
return { kind: "fullName", names: gns.names
|
|
713
|
+
return { kind: "fullName", names: _map(gns.names, function (n) { return n.bytes; }) };
|
|
666
714
|
}
|
|
667
715
|
if (node && node.tagClass === "context" && node.tagNumber === 1) {
|
|
668
716
|
// In [1] IMPLICIT RelativeDistinguishedName the context tag replaces the
|
|
@@ -687,14 +735,14 @@ function distributionPointName(ns, node, code) {
|
|
|
687
735
|
//
|
|
688
736
|
// `E(code, message, cause)` is the caller's typed error factory, so each boundary keeps its own
|
|
689
737
|
// domain while the RULE stays single-homed.
|
|
690
|
-
var _KU_DECODER = new
|
|
738
|
+
var _KU_DECODER = new _WeakMapI(); // one decoder table per namespace, built on first use
|
|
691
739
|
function keyUsageOf(ns, cert, E, code, label) {
|
|
692
740
|
var exts = (cert && cert.extensions) || [];
|
|
693
741
|
var want = ns.oid.byName("keyUsage");
|
|
694
742
|
for (var i = 0; i < exts.length; i++) {
|
|
695
743
|
if (exts[i].oid !== want || exts[i].value == null) continue;
|
|
696
|
-
var dec = _KU_DECODER
|
|
697
|
-
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); }
|
|
698
746
|
try { return dec(exts[i].value); }
|
|
699
747
|
catch (e) { throw E(code, "the " + label + " keyUsage extension is malformed", e); }
|
|
700
748
|
}
|
|
@@ -760,7 +808,7 @@ function assertPolicyQualifiers(qNode, fail) {
|
|
|
760
808
|
if (qNode.tagClass !== "universal" || qNode.tagNumber !== _T.SEQUENCE || !qNode.children || !qNode.children.length) {
|
|
761
809
|
fail("policyQualifiers must be a non-empty SEQUENCE (RFC 5280 sec. 4.2.1.4)");
|
|
762
810
|
}
|
|
763
|
-
qNode.children
|
|
811
|
+
_forEach(qNode.children, function (pq) {
|
|
764
812
|
if (pq.tagClass !== "universal" || pq.tagNumber !== _T.SEQUENCE || !pq.children || pq.children.length !== 2) {
|
|
765
813
|
fail("policyQualifiers element must be a PolicyQualifierInfo SEQUENCE { policyQualifierId, qualifier } (RFC 5280 sec. 4.2.1.4)");
|
|
766
814
|
}
|
|
@@ -781,7 +829,7 @@ function assertPolicyQualifiers(qNode, fail) {
|
|
|
781
829
|
// octets, so `.length` (UTF-16 units) or a byte count would misjudge a valid value. Exported so
|
|
782
830
|
// every layer that reports the bound counts it identically instead of keeping its own copy.
|
|
783
831
|
var DISPLAY_TEXT_MAX = 200;
|
|
784
|
-
function displayTextChars(str) { return
|
|
832
|
+
function displayTextChars(str) { return _arrayFrom(str).length; }
|
|
785
833
|
|
|
786
834
|
// @internal
|
|
787
835
|
// userNoticeTexts(qualifier) -- the DisplayText values of a decoded UserNotice qualifier, as
|
|
@@ -798,7 +846,9 @@ function displayTextChars(str) { return Array.from(str).length; }
|
|
|
798
846
|
var _DISPLAY_TEXT_TAGS = null;
|
|
799
847
|
function _isDisplayTextNode(n) {
|
|
800
848
|
if (!_DISPLAY_TEXT_TAGS) {
|
|
801
|
-
|
|
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);
|
|
802
852
|
_DISPLAY_TEXT_TAGS[_T.IA5_STRING] = 1; _DISPLAY_TEXT_TAGS[_T.VISIBLE_STRING] = 1;
|
|
803
853
|
_DISPLAY_TEXT_TAGS[_T.BMP_STRING] = 1; _DISPLAY_TEXT_TAGS[_T.UTF8_STRING] = 1;
|
|
804
854
|
}
|
|
@@ -831,9 +881,9 @@ function _dtEntry(field, node) {
|
|
|
831
881
|
function _noticeNumbers(node) {
|
|
832
882
|
if (!node || node.tagClass !== "universal" || node.tagNumber !== _T.SEQUENCE || !node.children) return null;
|
|
833
883
|
var nums = [], ok = true;
|
|
834
|
-
node.children
|
|
884
|
+
_forEach(node.children, function (n) {
|
|
835
885
|
// allow:swallow-unverified a non-INTEGER member makes the whole reference undecodable, and the caller falls back
|
|
836
|
-
try { nums
|
|
886
|
+
try { _push(nums, _String(asn1.read.integer(n))); } catch (_e) { ok = false; }
|
|
837
887
|
});
|
|
838
888
|
return ok ? nums : null;
|
|
839
889
|
}
|
|
@@ -858,13 +908,13 @@ function userNoticeTexts(qualifier) {
|
|
|
858
908
|
if (!nr.children || nr.children.length !== 2 || !_isDisplayTextNode(nr.children[0])) return [];
|
|
859
909
|
var org = _dtEntry("organization", nr.children[0]);
|
|
860
910
|
org.noticeNumbers = _noticeNumbers(nr.children[1]);
|
|
861
|
-
out
|
|
911
|
+
_push(out, org);
|
|
862
912
|
i++;
|
|
863
913
|
}
|
|
864
914
|
// explicitText [absent | last]: a DisplayText. Anything else remaining is not a UserNotice member.
|
|
865
915
|
if (i < kids.length) {
|
|
866
916
|
if (!_isDisplayTextNode(kids[i])) return [];
|
|
867
|
-
out
|
|
917
|
+
_push(out, _dtEntry("explicitText", kids[i]));
|
|
868
918
|
i++;
|
|
869
919
|
}
|
|
870
920
|
return i === kids.length ? out : [];
|
|
@@ -877,7 +927,7 @@ function _decodeHelpers(ns) {
|
|
|
877
927
|
function decodeTop(buf, code, what) {
|
|
878
928
|
var n;
|
|
879
929
|
try { n = asn1.decode(buf); }
|
|
880
|
-
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); }
|
|
881
931
|
return n;
|
|
882
932
|
}
|
|
883
933
|
function seqChildren(buf, code, what) {
|
|
@@ -895,7 +945,7 @@ function _decodeHelpers(ns) {
|
|
|
895
945
|
// by an unresolved name would silently never match, treating a real extension/attribute as opaque).
|
|
896
946
|
function O(nm) {
|
|
897
947
|
var d = ns.oid.byName(nm);
|
|
898
|
-
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");
|
|
899
949
|
return d;
|
|
900
950
|
}
|
|
901
951
|
return { decodeTop: decodeTop, seqChildren: seqChildren, readInt: readInt, O: O };
|
|
@@ -945,7 +995,7 @@ function certExtensionDecoders(ns) {
|
|
|
945
995
|
// KeyUsage is a NamedBitList -- enforce the shared X.690 sec. 11.2.2 minimal-DER rule.
|
|
946
996
|
schema.assertMinimalNamedBits(bs.unusedBits, bs.bytes, function (m) { throw ns.E(C, m); });
|
|
947
997
|
var out = {};
|
|
948
|
-
KU_BITS
|
|
998
|
+
_forEach(KU_BITS, function (nm, bit) {
|
|
949
999
|
var byte = bit >> 3, mask = 0x80 >> (bit & 7);
|
|
950
1000
|
out[nm] = byte < bs.bytes.length && (bs.bytes[byte] & mask) !== 0;
|
|
951
1001
|
});
|
|
@@ -966,7 +1016,7 @@ function certExtensionDecoders(ns) {
|
|
|
966
1016
|
// GeneralSubtrees is SIZE(1..MAX) -- an explicitly present but empty subtree
|
|
967
1017
|
// list (a0 00) absorbs no constraint and is malformed, not a no-op.
|
|
968
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)");
|
|
969
|
-
return node.children
|
|
1019
|
+
return _map(node.children, function (st) {
|
|
970
1020
|
if (st.tagClass !== "universal" || st.tagNumber !== _T.SEQUENCE || !st.children || st.children.length < 1) {
|
|
971
1021
|
throw ns.E(C, "GeneralSubtree must be a SEQUENCE { base, minimum?, maximum? }");
|
|
972
1022
|
}
|
|
@@ -984,7 +1034,7 @@ function certExtensionDecoders(ns) {
|
|
|
984
1034
|
var C = ns.prefix + "/bad-name-constraints";
|
|
985
1035
|
var kids = seqChildren(buf, C, "NameConstraints");
|
|
986
1036
|
var permitted = [], excluded = [], sawP = false, sawE = false, ncLastTag = -1;
|
|
987
|
-
kids
|
|
1037
|
+
_forEach(kids, function (k) {
|
|
988
1038
|
if (k.tagClass !== "context" || !k.children) throw ns.E(C, "NameConstraints fields are [0] permittedSubtrees / [1] excludedSubtrees");
|
|
989
1039
|
if (k.tagNumber <= ncLastTag) throw ns.E(C, "NameConstraints fields must be unique and in ascending order (DER)");
|
|
990
1040
|
ncLastTag = k.tagNumber;
|
|
@@ -1001,8 +1051,10 @@ function certExtensionDecoders(ns) {
|
|
|
1001
1051
|
var C = ns.prefix + "/bad-policy";
|
|
1002
1052
|
var kids = seqChildren(buf, C, "CertificatePolicies");
|
|
1003
1053
|
if (kids.length < 1) throw ns.E(C, "CertificatePolicies must contain at least one PolicyInformation (RFC 5280 sec. 4.2.1.4)");
|
|
1004
|
-
|
|
1005
|
-
|
|
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) {
|
|
1006
1058
|
// PolicyInformation ::= SEQUENCE { policyIdentifier, policyQualifiers
|
|
1007
1059
|
// SEQUENCE SIZE(1..MAX) OPTIONAL }. Exactly one or two fields; a second
|
|
1008
1060
|
// field, if present, MUST be a SEQUENCE. Extra/mis-typed fields are malformed.
|
|
@@ -1030,7 +1082,7 @@ function certExtensionDecoders(ns) {
|
|
|
1030
1082
|
var C = ns.prefix + "/bad-policy";
|
|
1031
1083
|
var kids = seqChildren(buf, C, "PolicyMappings");
|
|
1032
1084
|
if (kids.length < 1) throw ns.E(C, "PolicyMappings must contain at least one mapping (RFC 5280 sec. 4.2.1.5)");
|
|
1033
|
-
return kids
|
|
1085
|
+
return _map(kids, function (mp) {
|
|
1034
1086
|
if (mp.tagClass !== "universal" || mp.tagNumber !== _T.SEQUENCE || !mp.children || mp.children.length !== 2) {
|
|
1035
1087
|
throw ns.E(C, "policy mapping must be a SEQUENCE { issuerDomainPolicy, subjectDomainPolicy }");
|
|
1036
1088
|
}
|
|
@@ -1047,7 +1099,7 @@ function certExtensionDecoders(ns) {
|
|
|
1047
1099
|
var kids = seqChildren(buf, C, "PolicyConstraints");
|
|
1048
1100
|
if (kids.length < 1) throw ns.E(C, "PolicyConstraints must contain at least one field (RFC 5280 sec. 4.2.1.11)");
|
|
1049
1101
|
var rep = null, ipm = null, pcLastTag = -1;
|
|
1050
|
-
kids
|
|
1102
|
+
_forEach(kids, function (k) {
|
|
1051
1103
|
if (k.tagClass !== "context") throw ns.E(C, "PolicyConstraints fields are context-tagged [0]/[1]");
|
|
1052
1104
|
if (k.tagNumber <= pcLastTag) throw ns.E(C, "PolicyConstraints fields must be unique and in ascending order (DER)");
|
|
1053
1105
|
pcLastTag = k.tagNumber;
|
|
@@ -1093,7 +1145,7 @@ function certExtensionDecoders(ns) {
|
|
|
1093
1145
|
var descs = seqChildren(buf, C, "AuthorityInfoAccessSyntax");
|
|
1094
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))");
|
|
1095
1147
|
var GN = generalName(ns, { decodeValue: true, code: C });
|
|
1096
|
-
return descs
|
|
1148
|
+
return _map(descs, function (d) {
|
|
1097
1149
|
if (d.tagClass !== "universal" || d.tagNumber !== _T.SEQUENCE || !d.children || d.children.length !== 2) {
|
|
1098
1150
|
throw ns.E(C, "AccessDescription must be a SEQUENCE { accessMethod, accessLocation } (RFC 5280 sec. 4.2.2.1)");
|
|
1099
1151
|
}
|
|
@@ -1110,7 +1162,7 @@ function certExtensionDecoders(ns) {
|
|
|
1110
1162
|
var C = ns.prefix + "/bad-extension-value";
|
|
1111
1163
|
var kids = seqChildren(buf, C, "ExtKeyUsage");
|
|
1112
1164
|
if (kids.length < 1) throw ns.E(C, "ExtKeyUsage must contain at least one KeyPurposeId (RFC 5280 sec. 4.2.1.12)");
|
|
1113
|
-
return kids
|
|
1165
|
+
return _map(kids, function (k) {
|
|
1114
1166
|
try { return asn1.read.oid(k); } catch (e) { throw ns.E(C, "ExtKeyUsage KeyPurposeId must be an OBJECT IDENTIFIER", e); }
|
|
1115
1167
|
});
|
|
1116
1168
|
}
|
|
@@ -1121,7 +1173,7 @@ function certExtensionDecoders(ns) {
|
|
|
1121
1173
|
var kids = seqChildren(buf, C, "AuthorityKeyIdentifier");
|
|
1122
1174
|
var out = { keyIdentifier: null, authorityCertIssuer: null, authorityCertSerialNumber: null };
|
|
1123
1175
|
var lastTag = -1;
|
|
1124
|
-
kids
|
|
1176
|
+
_forEach(kids, function (k) {
|
|
1125
1177
|
if (k.tagClass !== "context") throw ns.E(C, "AuthorityKeyIdentifier fields are context-tagged");
|
|
1126
1178
|
// DER: the OPTIONAL fields appear at most once and in ascending tag order.
|
|
1127
1179
|
if (k.tagNumber <= lastTag) throw ns.E(C, "AuthorityKeyIdentifier fields must be unique and in ascending order (DER)");
|
|
@@ -1130,7 +1182,7 @@ function certExtensionDecoders(ns) {
|
|
|
1130
1182
|
// [2] IMPLICIT INTEGER are primitive leaves; the readers enforce the
|
|
1131
1183
|
// primitive form fail-closed (a constructed context node is rejected,
|
|
1132
1184
|
// never dereferenced for its absent content).
|
|
1133
|
-
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); } }
|
|
1134
1186
|
else if (k.tagNumber === 1) out.authorityCertIssuer = schema.walk(generalNames(ns, { implicitTag: 1, decodeValue: true, code: C }), k, ns).result;
|
|
1135
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); } }
|
|
1136
1188
|
else throw ns.E(C, "AuthorityKeyIdentifier has an unexpected field [" + k.tagNumber + "]");
|
|
@@ -1145,7 +1197,7 @@ function certExtensionDecoders(ns) {
|
|
|
1145
1197
|
function subjectKeyIdentifier(buf) {
|
|
1146
1198
|
var C = ns.prefix + "/bad-extension-value";
|
|
1147
1199
|
var n = decodeTop(buf, C, "SubjectKeyIdentifier");
|
|
1148
|
-
try { return
|
|
1200
|
+
try { return _bufferConcat([asn1.read.octetString(n)]); }
|
|
1149
1201
|
catch (e) { throw ns.E(C, "SubjectKeyIdentifier must be an OCTET STRING (RFC 5280 sec. 4.2.1.2)", e); }
|
|
1150
1202
|
}
|
|
1151
1203
|
|
|
@@ -1188,13 +1240,13 @@ function certExtensionDecoders(ns) {
|
|
|
1188
1240
|
var C = ns.prefix + "/bad-crl-distribution-points";
|
|
1189
1241
|
var kids = seqChildren(buf, C, "CRLDistributionPoints");
|
|
1190
1242
|
if (kids.length < 1) throw ns.E(C, "CRLDistributionPoints must contain at least one DistributionPoint (RFC 5280 sec. 4.2.1.13)");
|
|
1191
|
-
return kids
|
|
1243
|
+
return _map(kids, function (dp) {
|
|
1192
1244
|
if (dp.tagClass !== "universal" || dp.tagNumber !== _T.SEQUENCE || !dp.children) {
|
|
1193
1245
|
throw ns.E(C, "DistributionPoint must be a SEQUENCE { distributionPoint?, reasons?, cRLIssuer? }");
|
|
1194
1246
|
}
|
|
1195
1247
|
var out = { distributionPoint: null, reasons: null, cRLIssuer: null };
|
|
1196
1248
|
var lastTag = -1;
|
|
1197
|
-
dp.children
|
|
1249
|
+
_forEach(dp.children, function (f) {
|
|
1198
1250
|
if (f.tagClass !== "context") throw ns.E(C, "DistributionPoint fields are context-tagged [0]/[1]/[2]");
|
|
1199
1251
|
if (f.tagNumber <= lastTag) throw ns.E(C, "DistributionPoint fields must be unique and in ascending order (DER)");
|
|
1200
1252
|
lastTag = f.tagNumber;
|
|
@@ -1236,7 +1288,7 @@ function certExtensionDecoders(ns) {
|
|
|
1236
1288
|
}
|
|
1237
1289
|
function _qcOidSeq(node, C, what) { // a non-empty SEQUENCE OF OBJECT IDENTIFIER
|
|
1238
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");
|
|
1239
|
-
return node.children
|
|
1291
|
+
return _map(node.children, function (c) {
|
|
1240
1292
|
if (c.tagClass !== "universal" || c.tagNumber !== _T.OBJECT_IDENTIFIER) throw ns.E(C, what + " members must be OBJECT IDENTIFIERs");
|
|
1241
1293
|
try { return asn1.read.oid(c); } catch (e) { throw ns.E(C, what + " member is not a valid OID", e); }
|
|
1242
1294
|
});
|
|
@@ -1261,14 +1313,16 @@ function certExtensionDecoders(ns) {
|
|
|
1261
1313
|
if (i !== node.children.length) throw ns.E(C, "SemanticsInformation has unexpected trailing fields");
|
|
1262
1314
|
return { semanticsIdentifier: sid, nameRegistrationAuthorities: nras };
|
|
1263
1315
|
}
|
|
1264
|
-
|
|
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);
|
|
1265
1319
|
qcInfoByOid[O("qcCompliance")] = _qcPresenceOnly("QcCompliance");
|
|
1266
1320
|
qcInfoByOid[O("qcSSCD")] = _qcPresenceOnly("QcSSCD");
|
|
1267
1321
|
qcInfoByOid[O("qcsPkixQCSyntaxV1")] = _qcSemantics;
|
|
1268
1322
|
qcInfoByOid[O("qcsPkixQCSyntaxV2")] = _qcSemantics;
|
|
1269
1323
|
qcInfoByOid[O("qcType")] = function (node, C) {
|
|
1270
1324
|
var oids = _qcOidSeq(node, C, "QcType"); // SIZE(1) in EN 319 412-5 V2.5.1; accept >=1 for legacy certs
|
|
1271
|
-
return { types: oids, typeNames: oids
|
|
1325
|
+
return { types: oids, typeNames: _map(oids, function (d) { return ns.oid.name(d) || null; }) };
|
|
1272
1326
|
};
|
|
1273
1327
|
qcInfoByOid[O("qcRetentionPeriod")] = function (node, C) {
|
|
1274
1328
|
if (!node) throw ns.E(C, "QcRetentionPeriod requires an INTEGER statementInfo");
|
|
@@ -1297,11 +1351,11 @@ function certExtensionDecoders(ns) {
|
|
|
1297
1351
|
};
|
|
1298
1352
|
qcInfoByOid[O("qcCClegislation")] = function (node, C) { // SEQUENCE OF CountryName (PrintableString SIZE 2)
|
|
1299
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");
|
|
1300
|
-
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; }) };
|
|
1301
1355
|
};
|
|
1302
1356
|
qcInfoByOid[O("qcPDS")] = function (node, C) { // PdsLocations ::= SEQUENCE OF PdsLocation { url IA5, language Printable(2) }
|
|
1303
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");
|
|
1304
|
-
return { locations: node.children
|
|
1358
|
+
return { locations: _map(node.children, function (loc) {
|
|
1305
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 }");
|
|
1306
1360
|
var lang = _qcStr(loc.children[1], _T.PRINTABLE_STRING, C, "a QcPDS language must be a PrintableString");
|
|
1307
1361
|
if (lang.length !== 2) throw ns.E(C, "a QcPDS language must be a 2-letter ISO 639-1 code");
|
|
@@ -1312,11 +1366,11 @@ function certExtensionDecoders(ns) {
|
|
|
1312
1366
|
// QcQSCDlegislation (SEQUENCE OF CountryName, like QcCClegislation). Both are registered ids, decoded.
|
|
1313
1367
|
qcInfoByOid[O("qcIdentMethod")] = function (node, C) {
|
|
1314
1368
|
var oids = _qcOidSeq(node, C, "QcIdentMethod");
|
|
1315
|
-
return { methods: oids, methodNames: oids
|
|
1369
|
+
return { methods: oids, methodNames: _map(oids, function (d) { return ns.oid.name(d) || null; }) };
|
|
1316
1370
|
};
|
|
1317
1371
|
qcInfoByOid[O("qcQSCDlegislation")] = function (node, C) {
|
|
1318
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");
|
|
1319
|
-
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; }) };
|
|
1320
1374
|
};
|
|
1321
1375
|
function qcStatements(buf) {
|
|
1322
1376
|
var C = ns.prefix + "/bad-qc-statements", CS = ns.prefix + "/bad-qc-statement";
|
|
@@ -1333,7 +1387,7 @@ function certExtensionDecoders(ns) {
|
|
|
1333
1387
|
var infoNode = st.children.length === 2 ? st.children[1] : null;
|
|
1334
1388
|
var decoder = qcInfoByOid[id];
|
|
1335
1389
|
var info = decoder ? decoder(infoNode, CS) : { opaque: true, bytes: infoNode ? infoNode.bytes : null };
|
|
1336
|
-
out
|
|
1390
|
+
_push(out, { statementId: id, name: ns.oid.name(id) || null, info: info });
|
|
1337
1391
|
}
|
|
1338
1392
|
return out;
|
|
1339
1393
|
}
|
|
@@ -1383,13 +1437,16 @@ function certExtensionDecoders(ns) {
|
|
|
1383
1437
|
var C = ns.prefix + "/bad-ms-previous-cert-hash";
|
|
1384
1438
|
var n = decodeTop(buf, C, "CAPrevCertHash");
|
|
1385
1439
|
var hash;
|
|
1386
|
-
try { hash =
|
|
1440
|
+
try { hash = _bufferConcat([asn1.read.octetString(n)]); }
|
|
1387
1441
|
catch (e) { throw ns.E(C, "CAPrevCertHash must be an OCTET STRING", e); }
|
|
1388
1442
|
if (hash.length !== 20) throw ns.E(C, "CAPrevCertHash must be a 20-octet SHA-1 certificate thumbprint");
|
|
1389
1443
|
return hash;
|
|
1390
1444
|
}
|
|
1391
1445
|
|
|
1392
|
-
|
|
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);
|
|
1393
1450
|
byOid[O("qcStatements")] = qcStatements;
|
|
1394
1451
|
byOid[O("basicConstraints")] = basicConstraints;
|
|
1395
1452
|
byOid[O("keyUsage")] = keyUsage;
|
|
@@ -1455,10 +1512,10 @@ function extensions(ns, opts) {
|
|
|
1455
1512
|
var extOpts = {
|
|
1456
1513
|
min: 1, code: ns.prefix + "/bad-extensions", what: "Extensions",
|
|
1457
1514
|
unique: function (it) { return it.value.oid; }, dupCode: ns.prefix + "/duplicate-extension",
|
|
1458
|
-
build: function (m) { return m.items
|
|
1515
|
+
build: function (m) { return _map(m.items, function (it) { return it.value; }); },
|
|
1459
1516
|
};
|
|
1460
1517
|
if (opts.implicitTag != null) return schema.implicitSeqOf(opts.implicitTag, EXT, extOpts);
|
|
1461
|
-
return schema.seqOf(EXT,
|
|
1518
|
+
return schema.seqOf(EXT, _assign({ assert: "sequence" }, extOpts));
|
|
1462
1519
|
}
|
|
1463
1520
|
|
|
1464
1521
|
// SubjectPublicKeyInfo ::= SEQUENCE { algorithm AlgorithmIdentifier,
|
|
@@ -1512,7 +1569,7 @@ function attribute(ns, opts) {
|
|
|
1512
1569
|
return {
|
|
1513
1570
|
type: t,
|
|
1514
1571
|
name: ctx.oid.name(t) || null,
|
|
1515
|
-
values: m.fields.values.value.items
|
|
1572
|
+
values: _map(m.fields.values.value.items, function (it) { return it.node.bytes; }),
|
|
1516
1573
|
};
|
|
1517
1574
|
},
|
|
1518
1575
|
});
|
|
@@ -1617,10 +1674,10 @@ function signedEnvelope(ns, tbsSchema, opts) {
|
|
|
1617
1674
|
function dnsNameProblem(s) {
|
|
1618
1675
|
if (typeof s !== "string" || !s.length) return "empty";
|
|
1619
1676
|
if (s.length > 253) return "exceeds 253 octets";
|
|
1620
|
-
if (/\s
|
|
1621
|
-
if (s
|
|
1622
|
-
if (s
|
|
1623
|
-
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, ".");
|
|
1624
1681
|
for (var i = 0; i < labels.length; i++) {
|
|
1625
1682
|
var label = labels[i];
|
|
1626
1683
|
if (label.length === 0) return "empty label";
|
|
@@ -1629,7 +1686,7 @@ function dnsNameProblem(s) {
|
|
|
1629
1686
|
if (labels.length < 2) return "bare wildcard";
|
|
1630
1687
|
continue;
|
|
1631
1688
|
}
|
|
1632
|
-
if (
|
|
1689
|
+
if (!_reTest(/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/i, label)) return "invalid label syntax";
|
|
1633
1690
|
}
|
|
1634
1691
|
return null;
|
|
1635
1692
|
}
|
package/lib/tls-cert-compress.js
CHANGED
|
@@ -52,6 +52,10 @@
|
|
|
52
52
|
|
|
53
53
|
var C = require("./constants");
|
|
54
54
|
var guard = require("./guard-all");
|
|
55
|
+
var intrinsic = require("./guard-intrinsic");
|
|
56
|
+
// Own-key membership through an operation taken at load. Written out, the question reads two
|
|
57
|
+
// replaceable properties to answer one thing, and here it decides which algorithms are supported.
|
|
58
|
+
var _hasOwn = intrinsic.hasOwn;
|
|
55
59
|
var frameworkError = require("./framework-error");
|
|
56
60
|
var validator = require("./validator-all");
|
|
57
61
|
var ByteWriter = require("./byte-writer");
|
|
@@ -88,7 +92,7 @@ var LEVEL_OPT = {
|
|
|
88
92
|
// guard on this runtime. An algorithm the guard cannot decompress must never be offered
|
|
89
93
|
// or accepted: advertising one would mean accepting a message we then cannot open.
|
|
90
94
|
var SUPPORTED = guard.compress.algorithms().filter(function (n) {
|
|
91
|
-
return
|
|
95
|
+
return _hasOwn(ALG_BY_NAME, n) && typeof COMPRESS[n] === "function";
|
|
92
96
|
});
|
|
93
97
|
|
|
94
98
|
// The minimum Certificate message size and the widest opaque<..2^24-1> vector, both owned
|