@blamejs/pki 0.8.25 → 0.8.27

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/lib/guard-name.js CHANGED
@@ -290,8 +290,7 @@ function escapeDnValue(v) {
290
290
  return out;
291
291
  }
292
292
 
293
- // @enforced-by guard-shape-reinlined
294
- // @guard-shape lastIndexOf\("@"\)
293
+ // @enforced-by behavioral -- a shape naming the separator cannot fire, since the walk strips string literals before applying one, and the split has no other form that separates it from the authority scans in this tree; the vectors are the guard, where a local part differing in case alone is a different mailbox and a host part that does is the same one
295
294
  function emailEqual(a, b) {
296
295
  if (typeof a !== "string" || typeof b !== "string") return "not-comparable";
297
296
  var ai = _lastIndexOf(a, "@"), bi = _lastIndexOf(b, "@");
@@ -447,7 +446,9 @@ var _SCHEME_TABLE = _charTable("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUV
447
446
  * that needs a host decides what to do with that rather than being handed a guess. `host` is null
448
447
  * when the authority carries none, and `bracketed` says the host arrived inside `[]`, which is the
449
448
  * only place an IPv6 literal may appear. Percent-escapes are left as written, so a caller comparing
450
- * the host decides whether to accept one. */
449
+ * the host decides whether to accept one. `authStart` and `authEnd` bound the authority as written,
450
+ * userinfo and port included, and are equal on a URI that carries none, so a caller reading the rest
451
+ * of the URI reads it from the same walk rather than scanning again. */
451
452
  // @enforced-by behavioral -- a URI walk has no rename-proof shape that separates it from any other character scan; the vectors reading a scheme, refusing two userinfo separators and stripping a port are the guard
452
453
  function uriParts(uri) {
453
454
  if (typeof uri !== "string") return null;
@@ -460,7 +461,8 @@ function uriParts(uri) {
460
461
  if (_charCodeAt(uri, p) !== 0x3a) return null;
461
462
  var scheme = _strSlice(uri, 0, p);
462
463
  if (_charCodeAt(uri, p + 1) !== 0x2f || _charCodeAt(uri, p + 2) !== 0x2f) {
463
- return { scheme: scheme, hasAuthority: false, host: null, bracketed: false };
464
+ return { scheme: scheme, hasAuthority: false, host: null, bracketed: false,
465
+ authStart: p + 1, authEnd: p + 1 };
464
466
  }
465
467
  p += 3;
466
468
  var authStart = p;
@@ -474,12 +476,17 @@ function uriParts(uri) {
474
476
  if (firstAt !== _lastIndexOf(authority, "@")) return null;
475
477
  if (firstAt !== -1) authority = _strSlice(authority, firstAt + 1);
476
478
  var host = _strSlice(authority, 0, _portStripIndex(authority));
477
- if (host === "") return { scheme: scheme, hasAuthority: true, host: null, bracketed: false };
479
+ if (host === "") {
480
+ return { scheme: scheme, hasAuthority: true, host: null, bracketed: false,
481
+ authStart: authStart, authEnd: p };
482
+ }
478
483
  var hn = host.length;
479
484
  if (hn >= 2 && _charCodeAt(host, 0) === 0x5b && _charCodeAt(host, hn - 1) === 0x5d) {
480
- return { scheme: scheme, hasAuthority: true, host: _strSlice(host, 1, hn - 1), bracketed: true };
485
+ return { scheme: scheme, hasAuthority: true, host: _strSlice(host, 1, hn - 1), bracketed: true,
486
+ authStart: authStart, authEnd: p };
481
487
  }
482
- return { scheme: scheme, hasAuthority: true, host: host, bracketed: false };
488
+ return { scheme: scheme, hasAuthority: true, host: host, bracketed: false,
489
+ authStart: authStart, authEnd: p };
483
490
  }
484
491
 
485
492
  /** @internal Where a `:port` suffix starts, or the length when there is none. RFC 3986 sec. 3.2.3
@@ -497,6 +504,138 @@ function _portStripIndex(s) {
497
504
  return (i > 0 && _charCodeAt(s, i - 1) === 0x3a) ? i - 1 : s.length;
498
505
  }
499
506
 
507
+ var _UNRESERVED_TABLE = _charTable("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~");
508
+ var _HEXUP = "0123456789ABCDEF";
509
+
510
+ /** @internal Percent-encoding normalization (RFC 3986 sec. 6.2.2.2, which RFC 3987 sec. 5.3.2.3
511
+ * carries): a triplet encoding an unreserved character is decoded to that character, and the hex
512
+ * digits of every triplet that remains are written in uppercase. A `%` that does not introduce two
513
+ * hex digits is left as written, since a value this cannot read is one the comparison must not
514
+ * rewrite. */
515
+ function _pctNormalize(s) {
516
+ var out = "", i = 0, n = s.length;
517
+ while (i < n) {
518
+ var c = _charCodeAt(s, i);
519
+ if (c !== 0x25 || i + 2 >= n) { out += _charAt(s, i); i += 1; continue; }
520
+ var hi = _hexNibble(_charCodeAt(s, i + 1)), lo = _hexNibble(_charCodeAt(s, i + 2));
521
+ if (hi < 0 || lo < 0) { out += _charAt(s, i); i += 1; continue; }
522
+ var v = hi * 16 + lo;
523
+ out += _UNRESERVED_TABLE[v] ? _fromCharCode(v)
524
+ : "%" + _charAt(_HEXUP, hi) + _charAt(_HEXUP, lo);
525
+ i += 3;
526
+ }
527
+ return out;
528
+ }
529
+
530
+ /** @internal Path segment normalization (RFC 3986 sec. 5.2.4's remove_dot_segments, which RFC 3987
531
+ * sec. 5.3.2.4 carries). The document writes the algorithm as repeated removal from the front of the
532
+ * input and appending to an output buffer; both are done here with an index and a list of the segments
533
+ * kept, because rewriting the remaining input on each step costs a copy of it per segment, and a path
534
+ * of many `a/../` segments then costs time in the square of its length. A certificate can carry a URI
535
+ * of any length the encoding allows, and this runs on one while an operator waits, so the walk reads
536
+ * each character once.
537
+ * A segment is read with the `/` that introduces it, so the kept list holds whole `/segment` units and
538
+ * removing "the last segment" is removing the last of them. A `.` or `..` that opens a relative path
539
+ * takes its following `/` with it, which is what sec. 5.2.4 step 2A does. */
540
+ function _removeDotSegments(path) {
541
+ var kept = [], i = 0, n = path.length;
542
+ while (i < n) {
543
+ var lead = _charCodeAt(path, i) === 0x2f;
544
+ if (lead) i += 1;
545
+ var segStart = i;
546
+ while (i < n && _charCodeAt(path, i) !== 0x2f) i += 1;
547
+ var seg = _strSlice(path, segStart, i);
548
+ if (seg === "." || seg === "..") {
549
+ if (!lead) {
550
+ if (i < n) i += 1;
551
+ continue;
552
+ }
553
+ if (seg === ".." && kept.length) kept.length -= 1;
554
+ /** @internal The last segment of the input being `.` or `..` leaves the path ending in a
555
+ * separator, which steps 2B and 2C write as replacing it with `/`. */
556
+ if (i >= n) _append(kept, "/");
557
+ continue;
558
+ }
559
+ _append(kept, lead ? "/" + seg : seg);
560
+ }
561
+ return intrinsic.join(kept, "");
562
+ }
563
+
564
+ /** @internal The port each scheme RFC 5280 sec. 7.4 names leaves out when it is the default one
565
+ * (RFC 3986 sec. 6.2.3). The clause requires scheme-based normalization for exactly these four and
566
+ * says to omit step 5 for any other scheme, so a scheme absent from this table keeps its port. */
567
+ var _SCHEME_DEFAULT_PORT = intrinsic.assign(_create(null),
568
+ { http: "80", https: "443", ftp: "21", ldap: "389" });
569
+
570
+ /** @internal A digit string with its leading zeros removed, and "0" for one that is all zeros, so a
571
+ * port written with them is read as the number RFC 3986 sec. 3.2.3 says it is. */
572
+ function _dropLeadingZeros(digits) {
573
+ var i = 0;
574
+ while (i < digits.length - 1 && _charCodeAt(digits, i) === 0x30) i += 1;
575
+ return _strSlice(digits, i);
576
+ }
577
+
578
+ /** @internal A URI prepared for comparison as RFC 5280 sec. 7.4 requires, or null for a value that
579
+ * is not a URI. The clause's step 1 converts an internationalized name to ASCII Compatible Encoding;
580
+ * a `uniformResourceIdentifier` is an IA5String, so a value read out of a certificate is already
581
+ * ASCII and that step has nothing to do, and a caller handing this a non-ASCII string gets it back
582
+ * unconverted rather than guessed at. Step 2 lowercases the scheme and the host, step 3 normalizes
583
+ * percent-encoding, step 4 normalizes path segments, and step 5 drops a default port and writes an
584
+ * empty path as `/` for the four schemes the clause names. Everything else keeps its case, which is
585
+ * what makes the exact match the clause then calls for answer correctly. The steps run in the order
586
+ * the clause numbers them, so a host is lowercased before its percent-encoding is read and a triplet
587
+ * standing for an unreserved character contributes that character's own case. */
588
+ function _uriPrepare(uri) {
589
+ var p = uriParts(uri);
590
+ if (p === null) return null;
591
+ var scheme = lowerAscii(p.scheme);
592
+ var rest = _pctNormalize(_strSlice(uri, p.authEnd));
593
+ var authority = "";
594
+ if (p.hasAuthority) {
595
+ var auth = _strSlice(uri, p.authStart, p.authEnd);
596
+ var at = _lastIndexOf(auth, "@");
597
+ var userinfo = at === -1 ? "" : _pctNormalize(_strSlice(auth, 0, at + 1));
598
+ var hostPort = _strSlice(auth, at + 1);
599
+ var cut = _portStripIndex(hostPort);
600
+ var host = _pctNormalize(lowerAscii(_strSlice(hostPort, 0, cut)));
601
+ var port = _strSlice(hostPort, cut);
602
+ /** @internal RFC 3986 sec. 6.2.3, which step 5 runs for the four schemes the clause names: an
603
+ * explicit `:port` whose port is empty, or is that scheme's default, is the same URI as one
604
+ * written without it. sec. 3.2.3 writes `port = *DIGIT`, so the port is a number and `:080` on
605
+ * http is the default one; the digits are read as a number rather than compared as text, which is
606
+ * what the clause's own purpose, reducing false negatives, asks for. A scheme the clause does not
607
+ * name keeps the port as written, since step 5 is omitted for it, and no step removes a trailing
608
+ * dot from a host, so one is kept too. */
609
+ if (_hasOwn(_SCHEME_DEFAULT_PORT, scheme) &&
610
+ (port === ":" || _dropLeadingZeros(_strSlice(port, 1)) === _SCHEME_DEFAULT_PORT[scheme])) {
611
+ port = "";
612
+ }
613
+ authority = "//" + userinfo + host + port;
614
+ }
615
+ var pathEnd = rest.length;
616
+ for (var i = 0; i < rest.length; i++) {
617
+ var c = _charCodeAt(rest, i);
618
+ if (c === 0x3f || c === 0x23) { pathEnd = i; break; }
619
+ }
620
+ var path = _removeDotSegments(_strSlice(rest, 0, pathEnd));
621
+ if (path === "" && p.hasAuthority && _hasOwn(_SCHEME_DEFAULT_PORT, scheme)) path = "/";
622
+ return scheme + ":" + authority + path + _strSlice(rest, pathEnd);
623
+ }
624
+
625
+ /** @internal Whether two URIs name the same resource, under the preparation RFC 5280 sec. 7.4
626
+ * requires before the "case-sensitive exact match" it then calls for. Folding case over the whole
627
+ * value instead reads two URIs differing only in a path or a userinfo as one name, and comparing the
628
+ * values as written reads two differing only in the case of a scheme or a host as two. Returns
629
+ * "not-comparable" for a value that is not a URI, which leaves the caller to say that rather than
630
+ * report a verdict this did not reach. */
631
+ // @enforced-by behavioral -- the preparation is five character scans with no shape that separates it from any other; the vectors driving each step, and the pair that differs only in a path, are the guard
632
+ function uriEqual(a, b) {
633
+ if (typeof a !== "string" || typeof b !== "string") return "not-comparable";
634
+ var pa = _uriPrepare(a), pb = _uriPrepare(b);
635
+ if (pa === null || pb === null) return "not-comparable";
636
+ return pa === pb ? "match" : "no-match";
637
+ }
638
+
500
639
  var _ATEXT_TABLE = _charTable("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%&'*+-/=?^_`{|}~");
501
640
 
502
641
  /** @internal A quoted local part (RFC 5321 sec. 4.1.2 Quoted-string): printable ASCII between
@@ -773,6 +912,7 @@ module.exports = intrinsic.freeze({
773
912
  lowerAscii: lowerAscii, uriSchemeIs: uriSchemeIs,
774
913
  stripTrailingDot: stripTrailingDot, isFqdnHost: isFqdnHost, splitMailbox: splitMailbox,
775
914
  isHostName: isHostName, hostLabels: hostLabels, isXnLabel: isXnLabel, uriParts: uriParts,
915
+ uriEqual: uriEqual,
776
916
  constraintBaseRefusal: constraintBaseRefusal,
777
917
  constraintBaseUnreadable: constraintBaseUnreadable,
778
918
  });
@@ -4,6 +4,7 @@
4
4
  // @internal
5
5
 
6
6
  var constants = require("./constants");
7
+ var guard = require("./guard-all");
7
8
 
8
9
  var MAX_RETRY_AFTER_SECONDS = constants.TIME.days(365) / constants.TIME.seconds(1);
9
10
 
@@ -28,12 +29,27 @@ function _isAllDigits(s) {
28
29
  return true;
29
30
  }
30
31
 
31
- function _rfc850Year(yy, refMs) {
32
- if (typeof refMs !== "number" || !isFinite(refMs)) return yy + (yy < 70 ? 2000 : 1900);
33
- var refYear = new Date(refMs).getUTCFullYear();
34
- var full = Math.floor(refYear / 100) * 100 + yy;
35
- if (full - refYear > 50) full -= 100;
36
- return full;
32
+ /** @internal RFC 7231 sec. 7.1.1.1: a recipient "MUST interpret a timestamp that appears to be more
33
+ * than 50 years in the future as representing the most recent year in the past that had the same last
34
+ * two digits". The reading therefore depends on when the value is received, and a caller that does not
35
+ * say when that was is receiving it now. A fixed cutoff instead is this rule evaluated once, for the
36
+ * year the cutoff was chosen in, and it misreads a widening band of two-digit years for every year
37
+ * that passes after it.
38
+ * THE CLAUSE COMPARES TIMESTAMPS, so this compares timestamps, which is why it runs once the month, day
39
+ * and time are read. A comparison of years alone reads a timestamp in the fiftieth year ahead as inside
40
+ * the window whatever its month: received on 28 September 2026, `31-Dec-76` is fifty years and three
41
+ * months ahead, which is more than fifty, and names 1976. */
42
+ function _rfc850Year(yy, mon, day, hh, mi, ss, refMs) {
43
+ var ref = (typeof refMs === "number" && isFinite(refMs)) ? refMs : Date.now();
44
+ var refDate = new Date(ref);
45
+ var refInstant = guard.time.instantOf(refDate);
46
+ if (isNaN(refInstant)) return yy + (yy < 70 ? 2000 : 1900);
47
+ var full = Math.floor(refDate.getUTCFullYear() / 100) * 100 + yy;
48
+ var horizon = new Date(refInstant);
49
+ horizon.setUTCFullYear(horizon.getUTCFullYear() + 50);
50
+ var horizonInstant = guard.time.instantOf(horizon);
51
+ if (isNaN(horizonInstant)) return full;
52
+ return Date.UTC(full, mon, day, hh, mi, ss) > horizonInstant ? full - 100 : full;
37
53
  }
38
54
 
39
55
  function _scanImf(s) {
@@ -52,7 +68,12 @@ function _scanRfc850(s, refMs) {
52
68
  var mo = _monthAt(s, p); if (!mo || s.charAt(p + 3) !== "-") return null; p += 4;
53
69
  var yy = _digits2(s, p); if (yy < 0 || s.charAt(p + 2) !== " ") return null; p += 3;
54
70
  var t = _timeAt(s, p); if (!t || s.slice(t.next, t.next + 4) !== " GMT" || t.next + 4 !== s.length) return null;
55
- return { day: dd, monName: mo, year: _rfc850Year(yy, refMs), hh: t.hh, mi: t.mi, ss: t.ss };
71
+ /** @internal The century is resolved once the whole timestamp is read, since the clause compares the
72
+ * timestamp rather than its year. A month the calendar does not hold is refused by the round-trip
73
+ * check in `httpDateMs`, which reads the resolved year this returns. */
74
+ var mon = MONTHS.indexOf(mo);
75
+ var year = _rfc850Year(yy, mon, dd, t.hh, t.mi, t.ss, refMs);
76
+ return { day: dd, monName: mo, year: year, hh: t.hh, mi: t.mi, ss: t.ss };
56
77
  }
57
78
  function _scanAsctime(s) {
58
79
  var p = _matchName(s, 0, DOW_SHORT);
package/lib/inspect.js CHANGED
@@ -164,16 +164,17 @@ var KU_LABEL = NAMES.KEY_USAGE;
164
164
  var EKU_LABEL = NAMES.EXT_KEY_USAGE;
165
165
  var GN_KIND = NAMES.GENERAL_NAME;
166
166
 
167
+ /** @internal A 4- or 16-octet buffer is ONE address, rendered by the shared home so that one address
168
+ * reads the same wherever a report carries it: the RFC 3779 blocks reach that home through
169
+ * `_ipAddressText`, and a GeneralName reaching it through a second renderer would print the same
170
+ * octets two ways in one report. An 8- or 32-octet buffer is a name constraint's address AND mask,
171
+ * which is not an address, so each half is rendered and the two are joined. The home is not widened
172
+ * to take those widths, because a mask reaching it as an address would render as one everywhere. */
167
173
  function _ipString(buf) {
168
174
  if (!Buffer.isBuffer(buf)) return "";
169
- if (buf.length === 4) return buf[0] + "." + buf[1] + "." + buf[2] + "." + buf[3];
175
+ if (buf.length === 4 || buf.length === 16) return ipUtils.textFromOctets(buf) || _hexColon(buf, {});
170
176
  if (buf.length === 8) return _ipString(buf.subarray(0, 4)) + "/" + _ipString(buf.subarray(4));
171
- if (buf.length === 16 || buf.length === 32) {
172
- var groups = [];
173
- for (var i = 0; i < 16; i += 2) groups.push((((buf[i] << 8) | buf[i + 1]) >>> 0).toString(16).toUpperCase());
174
- var s = groups.join(":");
175
- return buf.length === 32 ? s + "/" + _ipString(buf.subarray(16)) : s;
176
- }
177
+ if (buf.length === 32) return _ipString(buf.subarray(0, 16)) + "/" + _ipString(buf.subarray(16));
177
178
  return _hexColon(buf, {});
178
179
  }
179
180