@blamejs/pki 0.5.9 → 0.5.11

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.
Files changed (54) hide show
  1. package/CHANGELOG.md +80 -19
  2. package/README.md +1 -1
  3. package/lib/acme.js +5 -5
  4. package/lib/asn1-der.js +1 -1
  5. package/lib/attrcert-sign.js +1 -1
  6. package/lib/cbor-det.js +4 -4
  7. package/lib/cmc-build.js +5 -5
  8. package/lib/cmc-verify.js +14 -11
  9. package/lib/cms-decrypt.js +2 -2
  10. package/lib/cms-sign.js +3 -5
  11. package/lib/cms-verify.js +9 -6
  12. package/lib/constants.js +1 -1
  13. package/lib/crl-sign.js +1 -1
  14. package/lib/crmf-sign.js +1 -1
  15. package/lib/ct.js +8 -5
  16. package/lib/est.js +20 -14
  17. package/lib/guard-all.js +12 -12
  18. package/lib/guard-bytes.js +568 -82
  19. package/lib/guard-compress.js +2 -2
  20. package/lib/guard-der.js +1 -1
  21. package/lib/guard-identifier.js +694 -6
  22. package/lib/guard-limits.js +2 -2
  23. package/lib/guard-parsed.js +10 -6
  24. package/lib/guard-range.js +1 -1
  25. package/lib/guard-secret.js +1 -1
  26. package/lib/guard-text.js +1 -1
  27. package/lib/guard-time.js +65 -4
  28. package/lib/hpke.js +1 -1
  29. package/lib/http-digest.js +1 -1
  30. package/lib/inspect.js +8 -3
  31. package/lib/jose.js +1 -1
  32. package/lib/key.js +48 -6
  33. package/lib/lint.js +15 -8
  34. package/lib/ocsp-verify.js +10 -4
  35. package/lib/ocsp.js +35 -6
  36. package/lib/oid.js +1 -1
  37. package/lib/path-validate.js +99 -22
  38. package/lib/schema-c509.js +5 -5
  39. package/lib/schema-cmp.js +1 -1
  40. package/lib/schema-crl.js +1 -1
  41. package/lib/schema-engine.js +2 -2
  42. package/lib/schema-pkix.js +1 -1
  43. package/lib/sigstore.js +6 -2
  44. package/lib/smime.js +1 -1
  45. package/lib/trust.js +2 -2
  46. package/lib/tsp-sign.js +2 -2
  47. package/lib/validator-attcert.js +3 -3
  48. package/lib/validator-cose.js +3 -3
  49. package/lib/webauthn-mds.js +17 -17
  50. package/lib/webauthn.js +12 -12
  51. package/lib/webcrypto.js +16 -7
  52. package/lib/x509-sign.js +1 -1
  53. package/package.json +4 -2
  54. package/sbom.cdx.json +6 -6
package/lib/cms-sign.js CHANGED
@@ -264,7 +264,7 @@ function _assertKeyMatchesSpki(keyOnly, soKey, soSpki, scheme, sig, signedBytes,
264
264
  // A signing-time Time value: UTCTime before 2050, GeneralizedTime from 2050 (RFC 5652 sec. 11.3 /
265
265
  // RFC 5280 sec. 4.1.2.5). A caller Date overrides; false omits the attribute (handled above).
266
266
  function _timeValue(when) {
267
- var d = (when instanceof Date) ? when : new Date();
267
+ var d = guard.time.isDate(when) ? when : new Date();
268
268
  return d.getUTCFullYear() < 2050 ? b.utcTime(d) : b.generalizedTime(d);
269
269
  }
270
270
 
@@ -368,8 +368,7 @@ function sign(content, signers, opts) {
368
368
  }
369
369
 
370
370
  function _sign(content, signers, opts) {
371
- opts = opts || {};
372
- if (typeof opts !== "object" || Buffer.isBuffer(opts)) throw _err("cms/bad-input", "pki.cms.sign options must be an object");
371
+ opts = guard.identifier.optionsObject(opts, _err, "cms/bad-input", "pki.cms.sign options");
373
372
  guard.identifier.assertKnownKeys(opts, KNOWN_SIGN_OPTS, _err, "cms/bad-input", "unknown opts field ");
374
373
  // The arguments were copied at entry (see `sign` above), which is what makes the refusal below
375
374
  // hold: flipping signedAttributes from true to false after the call returns would otherwise skip
@@ -590,8 +589,7 @@ function countersign(cmsInput, signers, opts) {
590
589
  }
591
590
 
592
591
  function _countersign(cmsInput, signers, opts) {
593
- opts = opts || {};
594
- if (typeof opts !== "object" || Buffer.isBuffer(opts)) throw _err("cms/bad-input", "pki.cms.countersign options must be an object");
592
+ opts = guard.identifier.optionsObject(opts, _err, "cms/bad-input", "pki.cms.countersign options");
595
593
  guard.identifier.assertKnownKeys(opts, KNOWN_COUNTERSIGN_OPTS, _err, "cms/bad-input", "unknown opts field ");
596
594
  var list = Array.isArray(signers) ? signers : [signers];
597
595
  if (!list.length) throw _err("cms/bad-input", "pki.cms.countersign requires at least one countersigner");
package/lib/cms-verify.js CHANGED
@@ -579,7 +579,7 @@ function _verifyOneCountersig(vDer, targetSig, parsedCerts) {
579
579
  // Coverage residual, and deliberately kept. Every value reaching here was already walked by the
580
580
  // decoder, which validates an id-countersignature by content and not by the attribute type. A
581
581
  // malformed one therefore refused the whole message before verify was entered, so this catch
582
- // cannot be reached through the public path. That is settled behaviour: a message the decoder
582
+ // cannot be reached through the public path. That is settled behavior: a message the decoder
583
583
  // has found to be malformed has no sound remainder to report a verdict over, so it is refused
584
584
  // whole instead of surfaced as one failed countersignature.
585
585
  // cms-verify.test.js pins that through the shipped verbs; this stays because the walk is also
@@ -693,8 +693,7 @@ function verify(input, opts) {
693
693
  }
694
694
 
695
695
  function _verify(input, opts) {
696
- opts = opts || {};
697
- if (typeof opts !== "object" || Buffer.isBuffer(opts)) throw _err("cms/bad-input", "pki.cms.verify options must be an object");
696
+ opts = guard.identifier.optionsObject(opts, _err, "cms/bad-input", "pki.cms.verify options");
698
697
  // An unrecognized option is refused, not swallowed. This is what kept the missing trust seam
699
698
  // silent: a caller writing `trustAnchors` before it existed -- or `trustAnchor` now -- got a
700
699
  // verdict that looked anchored and was not. The guard rejects through a (code, message) FACTORY
@@ -744,7 +743,7 @@ function _verify(input, opts) {
744
743
  // read a promise turn later, and everything in it stays caller-owned across that gap: the array
745
744
  // can be re-pointed, an anchor's DER rewritten, the validation instant moved. Any of those would
746
745
  // have the chain judged against a configuration the caller never asked for while the verdict
747
- // reports the one they did. The same defence the input and the certificates already have.
746
+ // reports the one they did. The same defense the input and the certificates already have.
748
747
  var trustCfg = _snapshotTrust(opts);
749
748
  return Promise.all(parsed.signerInfos.map(function (si) {
750
749
  // res.valid reflects only the PRIMARY signerInfos; a countersignature / unsigned attribute is
@@ -807,7 +806,11 @@ function _cloneAnchorValue(v, depth) {
807
806
  // field by field yields `{}` -- an anchor's `distrustAfter: { emailProtection: <Date> }` would
808
807
  // survive the snapshot as an empty object and be rejected as an invalid date, disabling the very
809
808
  // policy it encodes. Copied by value, like the validation instant.
810
- if (v instanceof Date) return new Date(v.getTime());
809
+ // The instant comes through the guard's intrinsic read, so a caller's `getTime` override can
810
+ // neither throw out of the snapshot nor put a different instant in the copy than the original
811
+ // holds -- which would leave the anchor policy that was checked and the one that is applied two
812
+ // different values.
813
+ if (guard.time.isDate(v)) return new Date(guard.time.instantOf(v));
811
814
  // A structure too deep to copy is REFUSED, not shared. Returning it by reference at the cap
812
815
  // would leave part of the anchor caller-mutable across the chain walk while the rest was
813
816
  // snapshotted -- a guarantee that holds for the shallow fields and quietly lapses for the deep
@@ -835,7 +838,7 @@ function _snapshotTrust(opts) {
835
838
  }
836
839
  return {
837
840
  trustAnchors: anchors,
838
- time: opts.time instanceof Date ? new Date(opts.time.getTime()) : opts.time,
841
+ time: guard.time.isDate(opts.time) ? new Date(guard.time.instantOf(opts.time)) : opts.time,
839
842
  requiredEku: Array.isArray(opts.requiredEku) ? opts.requiredEku.slice() : opts.requiredEku,
840
843
  checkPurpose: opts.checkPurpose,
841
844
  };
package/lib/constants.js CHANGED
@@ -165,7 +165,7 @@ var LIMITS = {
165
165
  // is copied/tokenized so a hostile server cannot drive unbounded work parsing it
166
166
  // (CWE-770). 8 KiB clears any realistic multi-scheme Digest challenge.
167
167
  HTTP_AUTH_HEADER_MAX_BYTES: BYTES.kib(8),
168
- // Deterministic-CBOR codec ceilings (RFC 8949), the DER neighbours' siblings:
168
+ // Deterministic-CBOR codec ceilings (RFC 8949), the DER neighbors' siblings:
169
169
  // a whole-document cap refused before the walk, a nesting cap, and a per-value
170
170
  // bignum ceiling the document cap can't provide. Unlike DER_MAX_INTEGER_BYTES,
171
171
  // the bignum cap carries NO +1 sign octet -- a CBOR tag-2/3 bignum body is pure
package/lib/crl-sign.js CHANGED
@@ -426,7 +426,7 @@ function _sign(spec, issuer, opts) {
426
426
  nextU = _timeDer(spec.nextUpdate, "nextUpdate");
427
427
  // allow:nan-date-comparison-unguarded -- both operands are guard.time.assertValid'd via _timeDer on the
428
428
  // two lines above (thisUpdate + nextUpdate), so an Invalid Date throws before this comparison.
429
- if (spec.nextUpdate.getTime() < spec.thisUpdate.getTime()) throw _err("crl/bad-input", "nextUpdate must not be before thisUpdate (RFC 5280 sec. 5.1.2.5)");
429
+ if (guard.time.instantOf(spec.nextUpdate) < guard.time.instantOf(spec.thisUpdate)) throw _err("crl/bad-input", "nextUpdate must not be before thisUpdate (RFC 5280 sec. 5.1.2.5)");
430
430
  }
431
431
 
432
432
  var extResult = _buildCrlExtensions(spec, { issuerCert: issuerCert, issuerSpki: issuerSpki });
package/lib/crmf-sign.js CHANGED
@@ -74,7 +74,7 @@ function _encodeOptionalValidity(validity) {
74
74
  if (nb != null) parts.push(b.explicit(0, _b.timeDer(nb, "validity notBefore")));
75
75
  if (na != null) parts.push(b.explicit(1, _b.timeDer(na, "validity notAfter")));
76
76
  // allow:nan-date-comparison-unguarded -- both instants passed timeDer's guard.time.assertValid above.
77
- if (nb != null && na != null && nb.getTime() > na.getTime()) throw _err("crmf/bad-validity", "notBefore must not be after notAfter");
77
+ if (nb != null && na != null && guard.time.instantOf(nb) > guard.time.instantOf(na)) throw _err("crmf/bad-validity", "notBefore must not be after notAfter");
78
78
  return b.implicit(4, b.sequence(parts));
79
79
  }
80
80
  // CertTemplate ::= SEQUENCE { [0..9] all IMPLICIT OPTIONAL }. A request omits serialNumber [1] / signingAlg
package/lib/ct.js CHANGED
@@ -666,7 +666,7 @@ function _parseTemporalInterval(ti) {
666
666
  var start = rfc3339.parse(ti.start_inclusive, _ctErr, "ct/bad-date", "temporal_interval.start_inclusive");
667
667
  var end = rfc3339.parse(ti.end_exclusive, _ctErr, "ct/bad-date", "temporal_interval.end_exclusive");
668
668
  // allow:nan-date-comparison-unguarded -- start/end are rfc3339.parse results, guaranteed non-NaN (rfc3339.isValid rejects a NaN date).
669
- if (start.getTime() >= end.getTime()) throw _ctErr("ct/bad-log-list", "a CT log temporal_interval start_inclusive must be strictly before end_exclusive");
669
+ if (guard.time.instantOf(start) >= guard.time.instantOf(end)) throw _ctErr("ct/bad-log-list", "a CT log temporal_interval start_inclusive must be strictly before end_exclusive");
670
670
  return { startInclusive: start, endExclusive: end };
671
671
  }
672
672
 
@@ -700,10 +700,13 @@ function _sameTemporal(a, b) {
700
700
  // inputs; the remaining c8 sub-arm is a short-circuit path (when `a == null` is true, `b == null` is
701
701
  // never evaluated) with no additional reachable input.
702
702
  if (a == null || b == null) return false;
703
- return a.startInclusive.getTime() === b.startInclusive.getTime() && a.endExclusive.getTime() === b.endExclusive.getTime();
703
+ return guard.time.instantOf(a.startInclusive) === guard.time.instantOf(b.startInclusive) &&
704
+ guard.time.instantOf(a.endExclusive) === guard.time.instantOf(b.endExclusive);
704
705
  }
705
706
  function _logsAgree(a, b) {
706
- return a.state.name === b.state.name && a.state.since.getTime() === b.state.since.getTime() && _sameTemporal(a.temporalInterval, b.temporalInterval);
707
+ return a.state.name === b.state.name &&
708
+ guard.time.instantOf(a.state.since) === guard.time.instantOf(b.state.since) &&
709
+ _sameTemporal(a.temporalInterval, b.temporalInterval);
707
710
  }
708
711
 
709
712
  /**
@@ -783,7 +786,7 @@ function parseLogList(json, opts) {
783
786
  // The covered certificate's notAfter for the temporal gate: an explicit opts.certNotAfter, else derived
784
787
  // from a leafCert (entryType 0) via the shipped x509 parser, else null (a precert TBS can't be parsed).
785
788
  function _resolveNotAfter(entry, opts) {
786
- if (opts.certNotAfter instanceof Date) return opts.certNotAfter;
789
+ if (guard.time.isDate(opts.certNotAfter)) return opts.certNotAfter;
787
790
  if (entry && entry.entryType === 0 && entry.leafCert != null) {
788
791
  // Inline require (circular-load): ct.js is loaded before schema-x509 finishes initializing in the
789
792
  // index chain (schema-x509 -> schema-pkix is mid-init when ct.js is first required), so a top-level
@@ -845,7 +848,7 @@ async function verifySctWithLogList(entry, sct, logList, opts) {
845
848
  if (!log.state.trusted) {
846
849
  if (!log.state.conditional) throw _ctErr("ct/log-untrusted", "the CT log state '" + log.state.name + "' is not trusted");
847
850
  var ts = guard.range.uint64(sct.timestamp, _ctErr, "ct/bad-input", "sct.timestamp");
848
- if (ts >= BigInt(log.state.since.getTime())) throw _ctErr("ct/log-untrusted", "the CT log is retired and the SCT is not timestamped before its retirement (" + log.state.since.toISOString() + ")");
851
+ if (ts >= BigInt(guard.time.instantOf(log.state.since))) throw _ctErr("ct/log-untrusted", "the CT log is retired and the SCT is not timestamped before its retirement (" + log.state.since.toISOString() + ")");
849
852
  }
850
853
  // Temporal gate (fail-closed): the covered cert's notAfter must be in [start_inclusive, end_exclusive).
851
854
  if (log.temporalInterval) {
package/lib/est.js CHANGED
@@ -699,8 +699,11 @@ function _attr(typeOid, valueNodes) { return asn1.build.sequence([asn1.build.oid
699
699
  // attribute whose value is their RFC 4648 base64 (SIZE 1..255). The builder
700
700
  // takes caller-supplied binding bytes and never fakes one.
701
701
  function challengePasswordFromTlsUnique(channelBinding) {
702
- if (!Buffer.isBuffer(channelBinding) || channelBinding.length === 0) throw E("est/bad-input", "challengePasswordFromTlsUnique requires the channel-binding bytes");
703
- var b64 = channelBinding.toString("base64");
702
+ // The count comes from the guard, so an own `length` on the caller's Buffer cannot report a
703
+ // full one as empty. `length` is shadowable on any instance, and `Object.defineProperty(buf,
704
+ // "length", { get: function () { return 0; } })` had this refuse bytes that were there.
705
+ if (!Buffer.isBuffer(channelBinding) || guard.bytes.lengthOf(channelBinding) === 0) throw E("est/bad-input", "challengePasswordFromTlsUnique requires the channel-binding bytes");
706
+ var b64 = guard.bytes.view(channelBinding, EstError, "est/bad-input", "the channel-binding bytes").toString("base64");
704
707
  if (b64.length > 255) throw E("est/tls-unique-too-long", "the base64 tls-unique value exceeds 255 octets (RFC 7030 sec. 3.5)");
705
708
  return _attr(OID_CHALLENGE_PASSWORD, [asn1.build.printable(b64)]);
706
709
  }
@@ -954,7 +957,7 @@ function _drive(method, url, body, headers, opts, transport, budgets) {
954
957
  // Measure an injected string body as UTF-8 -- the byte width the body is decoded/transfer-decoded at,
955
958
  // and what the real socket transport counts -- so a non-ASCII body (multi-byte chars are ~half the
956
959
  // count under latin1) cannot undercount past the DoS cap and reach body handling.
957
- var blen = Buffer.isBuffer(res.body) ? res.body.length : Buffer.byteLength(String(res.body == null ? "" : res.body), "utf8");
960
+ var blen = Buffer.isBuffer(res.body) ? guard.bytes.lengthOf(res.body) : Buffer.byteLength(String(res.body == null ? "" : res.body), "utf8");
958
961
  if (blen > budgets.maxResponseBytes) throw E("est/response-too-large", "the response body (" + blen + " bytes) exceeds the " + budgets.maxResponseBytes + "-byte cap (RFC 7030 sec. 6)");
959
962
  // The transport contract returns lowercased headers, but the injectable seam only promises
960
963
  // { status, headers, body }; normalize here so a redirect Location / WWW-Authenticate from an
@@ -1104,7 +1107,7 @@ function _certsResult(op, res, opts, csrSpki) {
1104
1107
  if (verdict.status !== "ok") throw E("est/http-error", "an EST " + op + " response must be HTTP 200 or 202 (RFC 7030 sec. 4.1.3 / 4.2.3), got " + res.status);
1105
1108
  // Measure the body as UTF-8 for parity with the transport cap above (this check only distinguishes empty
1106
1109
  // from non-empty, where the encoding is immaterial, but the shape is kept uniform so no site undercounts).
1107
- var bodyLen = Buffer.isBuffer(res.body) ? res.body.length : Buffer.byteLength(String(res.body == null ? "" : res.body), "utf8");
1110
+ var bodyLen = Buffer.isBuffer(res.body) ? guard.bytes.lengthOf(res.body) : Buffer.byteLength(String(res.body == null ? "" : res.body), "utf8");
1108
1111
  if (bodyLen === 0) throw E("est/empty-body", "a 200 " + op + " response carried an empty body (RFC 7030 sec. 4.1.3 / 4.2.3)");
1109
1112
  var parsed = parseCertsOnly(transferDecode(res.body));
1110
1113
  if (op === "cacerts") return { certificates: parsed.certificates, crls: parsed.crls };
@@ -1231,7 +1234,7 @@ function fullcmc(baseUrl, request, opts) {
1231
1234
  _knownOpts(opts, FULLCMC_OPTS, "fullcmc");
1232
1235
  der = _cmcRequestDer(request);
1233
1236
  // Confirm this IS a Full PKI Request before any of it goes over the wire. The
1234
- // bytes are about to be labelled `smime-type=CMC-request`, so a PKIResponse or
1237
+ // bytes are about to be labeled `smime-type=CMC-request`, so a PKIResponse or
1235
1238
  // an unparseable blob handed in by mistake would be POSTed to a CA under a
1236
1239
  // label that does not describe it -- and, since only an `issued` outcome is
1237
1240
  // correlated, could still come back as a pending or rejected verdict, making
@@ -1449,11 +1452,14 @@ function _cmcSent(opts, der) {
1449
1452
  // Uint8Array would leave those forms aliased across the request's whole flight, which is the same
1450
1453
  // window this exists to close, open for exactly the inputs that came in by the wider door.
1451
1454
  // A DataView is copied over its OWN window, not the whole backing buffer it happens to sit in.
1455
+ // One call, because guard.bytes decides what a byte form is and where its bytes are. The three
1456
+ // hand-rolled branches this replaces read `v.buffer`, `v.byteOffset` and `v.byteLength` off the
1457
+ // value, and all three are accessors a caller's subclass can answer: one that lied copied a
1458
+ // different store than the array held, so the bytes checked here and the bytes sent later were
1459
+ // two different reads.
1452
1460
  function _copyBytes(v) {
1453
- if (Buffer.isBuffer(v) || v instanceof Uint8Array) return guard.bytes.snapshot(v, EstError, "est/bad-input", "a byte field of the request");
1454
- if (ArrayBuffer.isView(v)) return Buffer.from(new Uint8Array(v.buffer, v.byteOffset, v.byteLength));
1455
- if (v instanceof ArrayBuffer) return Buffer.from(new Uint8Array(v));
1456
- return v;
1461
+ if (!guard.bytes.isByteSource(v)) return v;
1462
+ return guard.bytes.snapshotSource(v, EstError, "est/bad-input", "a byte field of the request");
1457
1463
  }
1458
1464
 
1459
1465
  // The exchange-binding controls the request ACTUALLY carries (RFC 5272 sec. 6.6
@@ -1567,7 +1573,7 @@ function _assertAgrees(name, supplied, carried) {
1567
1573
  } else {
1568
1574
  // Through the shared authoring guard, not a bare BigInt(): a `number` above
1569
1575
  // Number.MAX_SAFE_INTEGER has already lost the digits that distinguish it from
1570
- // its neighbour, so converting it here would compare a value the caller never
1576
+ // its neighbor, so converting it here would compare a value the caller never
1571
1577
  // wrote. A transaction identifier is an unbounded INTEGER on the wire; large
1572
1578
  // ones are passed as a bigint, and this says so rather than rounding.
1573
1579
  same = carried != null &&
@@ -1665,13 +1671,13 @@ function _fullcmcResult(res, opts, wanted, sent) {
1665
1671
  "an EST /fullcmc response must be HTTP 200 or 202 (RFC 7030 sec. 4.3.2), got " + res.status);
1666
1672
  }
1667
1673
 
1668
- var bodyLen = Buffer.isBuffer(res.body) ? res.body.length : Buffer.byteLength(String(res.body == null ? "" : res.body), "utf8");
1674
+ var bodyLen = Buffer.isBuffer(res.body) ? guard.bytes.lengthOf(res.body) : Buffer.byteLength(String(res.body == null ? "" : res.body), "utf8");
1669
1675
  if (bodyLen === 0) throw E("est/empty-body", "a 200 /fullcmc response carried an empty body (RFC 7030 sec. 4.3.2)");
1670
1676
  var der = transferDecode(res.body);
1671
1677
 
1672
1678
  // FC5 admits two smime-types, and the LABEL must agree with the BYTES: a
1673
1679
  // certs-only label over a Full PKI Response (or the reverse) is a server
1674
- // mislabelling its own body, and accepting either shape under either label
1680
+ // mislabeling its own body, and accepting either shape under either label
1675
1681
  // would make the content-type check decorative.
1676
1682
  // Through the shared case-insensitive accessor: HTTP header names are
1677
1683
  // case-insensitive (RFC 9110 sec. 5.1), the classifier already reads them that
@@ -2040,7 +2046,7 @@ async function _serverkeygenResult(res, opts, derived) {
2040
2046
  // A 202 is an enrollment posture (RFC 7030 sec. 4.4 treats /serverkeygen as an enroll): surfaced, never slept.
2041
2047
  if (verdict.status === "retry") return { retry: true, retryAfterSeconds: verdict.retryAfterSeconds, retryAfterDate: verdict.retryAfterDate };
2042
2048
  if (verdict.status !== "ok") throw E("est/http-error", "an EST serverkeygen response must be HTTP 200 or 202 (RFC 7030 sec. 4.4.2), got " + res.status);
2043
- var bodyLen = Buffer.isBuffer(res.body) ? res.body.length : Buffer.byteLength(String(res.body == null ? "" : res.body), "utf8");
2049
+ var bodyLen = Buffer.isBuffer(res.body) ? guard.bytes.lengthOf(res.body) : Buffer.byteLength(String(res.body == null ? "" : res.body), "utf8");
2044
2050
  if (bodyLen === 0) throw E("est/empty-body", "a 200 serverkeygen response carried an empty body (RFC 7030 sec. 4.4.2)");
2045
2051
  _assertConfidentialCipher(res);
2046
2052
  // parseServerKeygenResponse does the per-part transfer-decode; the recipient coherence is bound to the CSR.
@@ -2142,7 +2148,7 @@ function _csrattrsResult(res, opts) {
2142
2148
  // A 202 is nonconforming for a policy GET (sec. 4.5.2 lists only 200 / 204 / 404).
2143
2149
  if (verdict.status === "retry") throw E("est/http-error", "a /csrattrs response must be HTTP 200, 204, or 404, not 202 (RFC 7030 sec. 4.5.2)");
2144
2150
  if (verdict.status !== "ok") throw E("est/http-error", "an EST csrattrs response must be HTTP 200 / 204 / 404 (RFC 7030 sec. 4.5.2), got " + res.status);
2145
- var bodyLen = Buffer.isBuffer(res.body) ? res.body.length : Buffer.byteLength(String(res.body == null ? "" : res.body), "utf8");
2151
+ var bodyLen = Buffer.isBuffer(res.body) ? guard.bytes.lengthOf(res.body) : Buffer.byteLength(String(res.body == null ? "" : res.body), "utf8");
2146
2152
  // An empty HTTP body is distinct from a valid EMPTY CsrAttrs (`30 00` / base64 `MAA=`), which parses as {items:[]}.
2147
2153
  if (bodyLen === 0) throw E("est/empty-body", "a 200 csrattrs response carried an empty body (RFC 7030 sec. 4.5.2)");
2148
2154
  var attrs = csrattrsFmt.parse(transferDecode(res.body));
package/lib/guard-all.js CHANGED
@@ -11,35 +11,35 @@
11
11
  // itself, with no guard re-implemented inline:
12
12
  //
13
13
  // guard.bytes.view / .source -- untrusted byte-source -> Buffer re-view
14
- // (detached-buffer fail-open defence)
14
+ // (detached-buffer fail-open defense)
15
15
  // guard.text.decode -- byte-source -> string, cap before copy
16
- // (parser-DoS string-allocation defence)
16
+ // (parser-DoS string-allocation defense)
17
17
  // guard.limits.cap / .depthCap -- config-time resource-cap validation
18
- // (recursion / allocation DoS defence)
18
+ // (recursion / allocation DoS defense)
19
19
  // guard.crypto.constantTimeEqual -- length-checked constant-time compare
20
- // (timing side-channel defence)
20
+ // (timing side-channel defense)
21
21
  // guard.range.int / .uint31 / .positiveInt31
22
22
  // -- bound a decoded integer before narrowing
23
- // to Number (silent-narrowing defence)
23
+ // to Number (silent-narrowing defense)
24
24
  // guard.time.assertValid / .within -- validate a Date-instant before a temporal
25
- // comparison (NaN-Date fail-open defence)
25
+ // comparison (NaN-Date fail-open defense)
26
26
  // guard.name.dnEqual / .rdnEqual / .assertNoControlBytes / .assertPrintableIa5
27
27
  // -- canonical DN identity + name-string integrity
28
28
  // guard.name.escapeControlBytes / .escapeDnValue
29
29
  // -- render-side name safety: control-byte + RFC 4514 escaping
30
- // (CVE-2009-2408 truncation / identity defence)
30
+ // (CVE-2009-2408 truncation / identity defense)
31
31
  // guard.encoding.base64url / .base64 / .hex
32
32
  // -- strict textual-encoding decode, canonical
33
- // + capped (encoding-malleability defence)
33
+ // + capped (encoding-malleability defense)
34
34
  // guard.identifier.assertCanonicalOid
35
35
  // -- canonical dotted-decimal OID string form
36
- // (canonicalization-divergence defence)
36
+ // (canonicalization-divergence defense)
37
37
  // guard.compress.bounded -- decompress an untrusted stream under a hard
38
38
  // output cap, whole-input (decompression-bomb +
39
- // trailing-frame malleability defence)
39
+ // trailing-frame malleability defense)
40
40
  // guard.header.assertField -- emitted MIME/RFC 5322 header field name +
41
41
  // value integrity (CR/LF/NUL header-injection
42
- // defence, CWE-93)
42
+ // defense, CWE-93)
43
43
  // guard.parsed.accept -- a CLAIMED-parsed structure carries every
44
44
  // field the consuming code dereferences
45
45
  // (type confusion / unverified provenance,
@@ -49,7 +49,7 @@
49
49
  // token of a guard (the Buffer.from(x.buffer, byteOffset) re-view, the
50
50
  // timingSafeEqual call, the MAX_DECODE_DEPTH_CEILING check) must appear only in
51
51
  // its guard module, so a new boundary cannot re-inline the shape and forget the
52
- // defence.
52
+ // defense.
53
53
 
54
54
  var bytes = require("./guard-bytes");
55
55
  var text = require("./guard-text");