@blamejs/pki 0.6.40 → 0.6.42

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 (75) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +1 -1
  3. package/lib/acme.js +24 -13
  4. package/lib/asn1-der.js +5 -5
  5. package/lib/attrcert-sign.js +14 -14
  6. package/lib/cbor-det.js +2 -2
  7. package/lib/cmc-build.js +16 -16
  8. package/lib/cmc-verify.js +5 -5
  9. package/lib/cmp-build.js +29 -19
  10. package/lib/cmp-session.js +21 -16
  11. package/lib/cmp-verify.js +4 -4
  12. package/lib/cms-decrypt.js +8 -8
  13. package/lib/cms-encrypt.js +22 -22
  14. package/lib/cms-sign.js +17 -17
  15. package/lib/cms-verify.js +6 -6
  16. package/lib/composite-kem.js +1 -1
  17. package/lib/composite-sig.js +4 -4
  18. package/lib/constants.js +13 -9
  19. package/lib/crl-sign.js +17 -17
  20. package/lib/crmf-sign.js +11 -11
  21. package/lib/csr-sign.js +2 -2
  22. package/lib/ct.js +19 -11
  23. package/lib/edwards-point.js +2 -2
  24. package/lib/est.js +74 -43
  25. package/lib/guard-async.js +17 -1
  26. package/lib/guard-bytes.js +2 -2
  27. package/lib/guard-compress.js +5 -5
  28. package/lib/guard-identifier.js +45 -1
  29. package/lib/guard-name.js +272 -1
  30. package/lib/guard-time.js +3 -1
  31. package/lib/hpke.js +10 -10
  32. package/lib/http-digest.js +2 -2
  33. package/lib/http-transport.js +3 -3
  34. package/lib/inspect.js +9 -9
  35. package/lib/jose.js +14 -14
  36. package/lib/key.js +7 -7
  37. package/lib/lint.js +6 -4
  38. package/lib/merkle.js +2 -2
  39. package/lib/mime.js +2 -2
  40. package/lib/ocsp-verify.js +1 -1
  41. package/lib/ocsp.js +5 -5
  42. package/lib/oid.js +3 -3
  43. package/lib/path-validate.js +50 -120
  44. package/lib/pbes2.js +2 -2
  45. package/lib/pkcs12-build.js +15 -15
  46. package/lib/pki-build.js +2 -2
  47. package/lib/scep.js +47 -16
  48. package/lib/schema-all.js +1 -1
  49. package/lib/schema-attrcert.js +4 -3
  50. package/lib/schema-c509.js +23 -23
  51. package/lib/schema-cmc.js +6 -6
  52. package/lib/schema-cmp.js +3 -3
  53. package/lib/schema-cms.js +15 -8
  54. package/lib/schema-crmf.js +1 -1
  55. package/lib/schema-csr.js +1 -1
  56. package/lib/schema-engine.js +2 -1
  57. package/lib/schema-pkcs12.js +1 -1
  58. package/lib/schema-pkix.js +3 -0
  59. package/lib/schema-tsp.js +2 -2
  60. package/lib/shbs.js +2 -2
  61. package/lib/sign-scheme.js +9 -9
  62. package/lib/sigstore.js +9 -8
  63. package/lib/smime.js +12 -12
  64. package/lib/tls-cert-compress.js +6 -6
  65. package/lib/trust.js +179 -10
  66. package/lib/tsp-sign.js +11 -11
  67. package/lib/validator-attcert.js +2 -2
  68. package/lib/validator-cose.js +5 -5
  69. package/lib/validator-sig.js +3 -3
  70. package/lib/validator-tpm.js +5 -4
  71. package/lib/webauthn.js +12 -12
  72. package/lib/webcrypto.js +22 -14
  73. package/lib/x509-sign.js +8 -8
  74. package/package.json +1 -1
  75. package/sbom.cdx.json +6 -6
package/CHANGELOG.md CHANGED
@@ -4,6 +4,31 @@ All notable changes to `@blamejs/pki` are documented here. The format
4
4
  follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this
5
5
  project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## v0.6.42 — 2026-09-06
8
+
9
+ A trust anchor carries the namespace a root program trusts a root for, so a certificate outside it is refused even where the root certificate states no such limit.
10
+
11
+ ### Added
12
+
13
+ - pki.trust.anchor(entry, opts) accepts opts.nameConstraints, the namespace a root program applies to a root outside any extension the root certificate carries. It is a { permitted, excluded } pair of { tag, base } subtrees, the shape pki.path.validate takes for opts.initialPermittedSubtrees, and validate seeds it as the RFC 5280 section 6.1.1(h)(i) initial value: it intersects with each certificate's own nameConstraints so a leaf must satisfy both, and an excluded subtree rejects a name whatever the permitted set says. A verdict reports it at anchorConstraints.nameConstraintsApplied. The subtrees are copied out of the caller's object, so changing that object afterwards leaves the namespace an anchor already vouches for unchanged. An overlay naming no subtree, one carrying a malformed subtree, and one supplying its subtrees through an accessor are each refused with trust/bad-input, since carrying any of them would widen the namespace the operator asked to restrict.
14
+
15
+ ### Changed
16
+
17
+ - pki.path.validate refuses a subtree in opts.initialPermittedSubtrees, opts.initialExcludedSubtrees, or a trust anchor's nameConstraints whose base is outside the form its tag names, with path/bad-input. Tag 1 is a mailbox, a host name, or a host name written as a subtree with a leading dot, and a mailbox may name its domain as a bracketed IPv4 or IPv6 address literal; tags 2 and 6 are host names, and a tag-6 base is the dotted host a certificate's URI is compared by rather than a URI; tag 4 names at least one relative name, with dotted-decimal object identifier types and string values; tag 7 is 8 bytes for IPv4 or 32 for IPv6. A host name is labels of letters, digits and hyphens, with a hyphen at neither edge of a label, 63 characters to a label and 253 to the name. An anchor whose nameConstraints names no subtree at all is refused the same way. Such a base previously reached the comparison, where it either matched every name, so a permitted subtree restricted nothing, or matched none, so an excluded subtree kept nothing out. A certificate's own nameConstraints extension is unaffected and is compared as the certificate wrote it.
18
+
19
+ ## v0.6.41 — 2026-09-05
20
+
21
+ A lookup table answers from its own entries, so a name taken off the wire cannot resolve to something the table never registered.
22
+
23
+ ### Changed
24
+
25
+ - Every object the toolkit publishes as a table carries no prototype: pki.C.TIME, pki.C.BYTES, pki.C.LIMITS, pki.C.NAMES and each name table under it, pki.asn1.TAGS, pki.asn1.build, pki.cbor.build, pki.ct.HASH_ALGORITHMS, pki.ct.SIGNATURE_ALGORITHMS, pki.hpke.suites, pki.path.PROCESSED_EXTENSIONS and pki.path.TARGET_UNPROCESSED_IF_CRITICAL. The same holds for a map a verb returns that is keyed by names it read, rather than by a fixed set of fields. Among them: the results of pki.oid.all, pki.est.paths and pki.scep.parseCapabilities, the headers on a pki.transport response and on each part pki.est.splitMultipartMixed returns, the counts map on a pki.lint.certificate result, the extensions map on a pki.sigstore.verifyBundle identity, and the purposes map on each anchor from pki.trust.parseCertdata, pki.trust.parseCcadbCsv and pki.trust.anchor. A result read by a fixed set of field names keeps the prototype it had, which covers parsed certificates, decoded WebAuthn authenticator data, CMS recipient identifiers and the other parse records. Reading an entry, calling a member, listing the keys, spreading, destructuring and serializing to JSON are unchanged; calling an Object.prototype method on the object itself, such as table.hasOwnProperty(name), no longer works, so use Object.hasOwn(table, name).
26
+
27
+ ### Fixed
28
+
29
+ - A lookup table carries no prototype, so a name the wire supplies resolves to an entry the table registered or to nothing. Two verbs were reachable: pki.webauthn.verify selected a verifier the table never held for an attestation naming constructor, toString or another Object.prototype member as its format, and pki.schema.c509.parse accepted a certificate whose subject-public-key algorithm named such a member instead of throwing c509/unknown-algorithm. The same change applies to the signature-scheme, content-encryption, key-agreement, extension-decoder and status-name registries.
30
+ - Every verb that takes an injected opts.transport names the option when the value is not callable, refusing it with the module's own bad-input code at the door, before the message or the certificate is read. This covers all eighteen: the six pki.est verbs, the seven pki.scep verbs, pki.acme.client, pki.cmp.transfer, pki.cmp.session, pki.ct.fetchLogList and pki.path.build. Previously most of them let the value reach the call site, where the operator got a bare TypeError naming no option. A falsy transport and an explicit null are refused the same way. Either had been read as no transport supplied and replaced with the default network client, so a caller that believed it had injected a transport reached the CA instead, which is the case worth upgrading for. Only an option no lookup finds falls back, so a defaults object carrying the transport on its prototype still works. A value that passes for callable and then throws when it is called, which is what a class constructor does, is reported as that verb's typed error too, rather than escaping as a TypeError.
31
+
7
32
  ## v0.6.40 — 2026-09-05
8
33
 
9
34
  Every verify verdict now reaches its caller as the object the verb built, and a list the toolkit assembles carries the entries the toolkit put in it.
package/README.md CHANGED
@@ -245,7 +245,7 @@ comment blocks, is at [pkijs.com](https://pkijs.com).
245
245
  | `pki.ocsp` | Online Certificate Status Protocol (RFC 6960), both the responder and relying-party surface. `buildRequest(query, opts)` builds an OCSPRequest for one or more `{ cert, issuer }` pairs, with the CertID hashed under SHA-1 by default per the RFC 5019 lightweight profile or under SHA-2, plus an optional RFC 9654 nonce and an optional requestor signature. `sign(responseData, responder, opts)` produces a signed BasicOCSPResponse over the exact `ResponseData` DER, from the issuing CA directly or a delegated responder, under any `pki.cms.sign` key including the post-quantum ML-DSA and SLH-DSA sets, with `good`, `revoked` (reason and time), or `unknown` per-certificate status. `buildErrorResponse(status)` produces the unsigned §2.3 error (`tryLater`, `unauthorized`, and the rest). `verify(response, opts)` verifies a response fail-closed against the same hardened gates `pki.path.ocspChecker` runs: the CertID binding, responder authorization (the issuing CA, or a CA-issued delegate bearing id-kp-OCSPSigning and id-pkix-ocsp-nocheck and passing the full out-of-path certificate gates), the signature over `tbsResponseDataBytes`, currency against `thisUpdate` and `nextUpdate`, and the request-nonce echo. It returns `{ status: "good" / "revoked" / "unknown", ... }` and never silently accepts. `verifyRequest(request, opts)` is the responder-side inverse of `buildRequest`: it verifies a client's signed request (RFC 6960 §4.1.1) under the requestor certificate the request carries, trying each embedded certificate since the field is unordered, and reports `signed`, `signatureValid`, every certificate whose key verified (`signerCerts`, so a key carried under an expired certificate beside its renewal is not hidden behind ordering) with the first one's decoded subject, the full embedded certificate bag (`certs`, including intermediates for path building), and the request's CertIDs; an unsigned request is reported with `signed: false` rather than refused. Transport-free — `buildRequest`, `sign`, `buildErrorResponse`, `verify`, `verifyRequest` |
246
246
  | `pki.ct` | Certificate Transparency (RFC 6962). `parseSctList` decodes the `SignedCertificateTimestampList` a certificate or OCSP response carries, a TLS-presentation-language payload inside the §3.3 double DER wrap, into per-SCT log id, exact `timestamp` (BigInt), named signature algorithm, and raw signature. `reconstructSignedData` rebuilds the exact `digitally-signed` preimage, and `verifySct` verifies an SCT signature against a log's public key, routing an ECDSA signature through the strict DER-conformance gate and verifying through the crypto engine, resolving true or false and throwing a typed error on a structural fault. On the producing side, `encodeSctList` builds the extension value byte for byte as the exact inverse of `parseSctList`, and `signSct` performs a log's signing step. For trust, `parseLogList` ingests the CT log-list JSON into constraint-carrying trusted logs, recomputing each log's id as SHA-256 of its key and refusing a disagreeing id (a swapped key, §3.2) and decoding the state and temporal-interval constraints; `verifySctWithLogList` resolves the log key from an SCT's log id, enforces the state (usable, qualified, and readonly trusted; retired only before retirement; pending and rejected refused) and the temporal-interval window, then delegates the signature check to `verifySct`. `verifyLogListSignature(json, signature, publicKey)` verifies the detached `log_list.sig` over the raw log-list bytes against a caller-pinned signer key (RSASSA-PKCS1-v1.5/SHA-256 and an EC P-256 arm, with forgeable-key defenses failing closed), cross-checked against `openssl dgst`. `fetchLogList(opts)` turns that chain into a live client: it GETs the `log_list.json` and its detached `log_list.sig` over `pki.transport`, verifies the detached signature over the raw fetched bytes against a caller-pinned distributor key before parsing, so an unverified document is never parsed, read, cached, or surfaced, then ingests the same bytes through `parseLogList` and returns the trusted-log set plus the surfaced `version` and `timestamp`. Above the per-SCT layer, `verifySctList` renders a certificate-level verdict over every SCT a certificate carries (§3.3), reporting how many distinct trusted logs verified an SCT (so a duplicated SCT cannot inflate the count) and from how many distinct trusted operators against a caller's CT policy (`minScts` and `minOperators`, both defaulting to the RFC floor of one), where a policy shortfall is a verdict and a per-SCT failure is a recorded row rather than a thrown call; `x509CertEntry` reconstructs the precertificate log entry from a final certificate by removing the SCT-list extension from its TBSCertificate as byte surgery, so its embedded SCTs verify end to end (§3.2). The toolkit bakes in no vendor URL or key, TLS trust is explicit with `rejectUnauthorized` always on, each response is size-capped before the trust chain, and the transport is injectable so the whole path is testable offline — `parseSctList`, `reconstructSignedData`, `verifySct`, `encodeSctList`, `signSct`, `parseLogList`, `verifySctWithLogList`, `verifySctList`, `x509CertEntry`, `verifyLogListSignature`, `fetchLogList` |
247
247
  | `pki.merkle` | Merkle-tree proof verification (RFC 6962 / RFC 9162). `leafHash`, `nodeHash`, and `emptyRootHash` build the domain-separated (0x00 leaf, 0x01 node) SHA-256 tree hashes. `verifyInclusion` folds an audit proof back to a root, and `verifyConsistency` reconstructs both the old and new root, which is the append-only guarantee; each is constant-time-compared to a trusted checkpoint root. Fail-closed on bad geometry, sync hashing, transport-free — `leafHash`, `nodeHash`, `emptyRootHash`, `verifyInclusion`, `verifyConsistency` |
248
- | `pki.trust` | Mozilla and CCADB trust-store ingestion. `parseCertdata` reads the NSS `certdata.txt` object stream and `parseCcadbCsv` the CCADB CSV export, both into one constraint-carrying anchor shape: the per-purpose trust bits, where only `CKT_NSS_TRUSTED_DELEGATOR` grants, and the per-purpose distrust-after dates the bare root list omits. Certificate and trust objects pair by byte-exact issuer and serial rather than adjacency and are cross-checked against the parsed DER, so metadata cannot attach to the wrong root. `anchor()` hands an entry to `pki.path.validate({ trustAnchors, checkPurpose })`. Offline, fail-closed, bounded — `parseCertdata`, `parseCcadbCsv`, `anchor` |
248
+ | `pki.trust` | Mozilla and CCADB trust-store ingestion. `parseCertdata` reads the NSS `certdata.txt` object stream and `parseCcadbCsv` the CCADB CSV export, both into one constraint-carrying anchor shape: the per-purpose trust bits, where only `CKT_NSS_TRUSTED_DELEGATOR` grants, and the per-purpose distrust-after dates the bare root list omits. Certificate and trust objects pair by byte-exact issuer and serial rather than adjacency and are cross-checked against the parsed DER, so metadata cannot attach to the wrong root. `anchor()` hands an entry to `pki.path.validate({ trustAnchors, checkPurpose })`, and `anchor(entry, { nameConstraints })` attaches the namespace a root program trusts a root for when that is narrower than the root certificate states, a `{ permitted, excluded }` pair of `{ tag, base }` subtrees that `validate` seeds as the RFC 5280 §6.1.1(h)(i) initial value, intersecting with each certificate's own constraints. Offline, fail-closed, bounded — `parseCertdata`, `parseCcadbCsv`, `anchor` |
249
249
  | `pki.shbs` | Stateful hash-based signature verification: HSS/LMS (RFC 8554), carried in X.509 by RFC 9802 and in CMS by RFC 9708, profiled by NIST SP 800-208 for CNSA 2.0 firmware signing. `verify` checks an HSS signature, where every level must pass, and `verifyLms` a single-tree LMS, over the raw public-key and signature blobs the parsers already surface. Pure public-input SHA-256 and SHAKE256 hashing, a data-driven typecode registry, and bounds-before-slice reads; a malformed blob throws a typed `ShbsError` while a well-formed but wrong signature returns `false`. Verification only by design, since stateful signing needs atomic one-time-key state that belongs in an HSM — `verify`, `verifyLms` |
250
250
  | `pki.hpke` | Hybrid Public Key Encryption (RFC 9180), the encrypt-to-a-public-key primitive behind TLS ECH, MLS, and OHTTP. `setupS` and `setupR` establish a sender or recipient context (KEM encapsulation plus the HKDF key schedule); the context's `seal` and `open` AEAD-encrypt with a sequence-counter nonce, and `export` derives further secrets; the module-level `seal` and `open` are single-shot wrappers. DHKEM (P-256, P-521, X25519, X448) by HKDF-SHA256/SHA512 by AES-GCM / ChaCha20Poly1305 / export-only, across all four modes, proven against the RFC 9180 Appendix A vectors. DHKEM(P-384) and HKDF-SHA384 are RFC-registered but Appendix A ships no vector for them, so they fail closed until an authoritative KAT exists. Pure composition over `node:crypto`; ML-KEM and X-Wing are a registry data-row extension pending stable drafts — `suites`, `setupS`, `setupR`, `seal`, `open` |
251
251
  | `pki.kem` | Composite ML-KEM key establishment (draft-ietf-lamps-pq-composite-kem), a post-quantum ML-KEM hybridized with a traditional RSA-OAEP, ECDH, X25519, or X448 so the established secret holds if either component is later broken. `encapsulate` turns a recipient's composite `SubjectPublicKeyInfo` into a 256-bit shared secret and a ciphertext; `decapsulate` recovers the same secret from the ciphertext and the composite PKCS#8 private key. Each component KEM runs independently and the two secrets are mixed through a SHA3-256 combiner that binds the traditional ciphertext, the traditional public key, and a per-algorithm label. The twelve algorithms pair ML-KEM-768 and ML-KEM-1024 with RSA-OAEP 2048/3072/4096, ECDH over P-256/P-384/P-521 and brainpoolP256r1/P384r1, X25519, and X448, each verified against the draft Appendix G known-answer vectors; a malformed key or ciphertext, an unsupported algorithm, or a component decapsulation failure throws a typed `KemError` — `encapsulate`, `decapsulate` |
package/lib/acme.js CHANGED
@@ -215,14 +215,14 @@ function _isUriString(v) { return _isString(v) && _isUriShape(v); }
215
215
 
216
216
  var CRL_REASONS = [0, 1, 2, 3, 4, 5, 6, 8, 9, 10];
217
217
 
218
- var KNOWN_CHALLENGE_TYPES = { "http-01": 1, "dns-01": 1, "tls-alpn-01": 1 };
218
+ var KNOWN_CHALLENGE_TYPES = intrinsic.assign(intrinsic.create(null), { "http-01": 1, "dns-01": 1, "tls-alpn-01": 1 });
219
219
 
220
- var STATUS = {
220
+ var STATUS = intrinsic.assign(intrinsic.create(null), {
221
221
  account: ["valid", "deactivated", "revoked"],
222
222
  order: ["pending", "ready", "processing", "valid", "invalid"],
223
223
  authorization: ["pending", "valid", "invalid", "deactivated", "expired", "revoked"],
224
224
  challenge: ["pending", "processing", "valid", "invalid"],
225
- };
225
+ });
226
226
 
227
227
  var SPECS = intrinsic.assign(intrinsic.create(null), {
228
228
  directory: [
@@ -627,7 +627,7 @@ var OID_AKI = oid.byName("authorityKeyIdentifier");
627
627
  var OID_CN = oid.byName("commonName");
628
628
  var _extNs = pkix.makeNS("acme", AcmeError, oid);
629
629
  var _extDecoders = pkix.certExtensionDecoders(_extNs);
630
- var _extCtx = { E: function (c, m, cause) { return new AcmeError(c, m, cause); }, oid: oid };
630
+ var _extCtx = intrinsic.assign(intrinsic.create(null), { E: function (c, m, cause) { return new AcmeError(c, m, cause); }, oid: oid });
631
631
 
632
632
  /**
633
633
  * @primitive pki.acme.tlsAlpn01Extension
@@ -726,7 +726,7 @@ async function verifyTlsAlpn01(certDer, token, accountJwk, identifier) {
726
726
 
727
727
 
728
728
  function _outerHeader(o) {
729
- var h = { alg: o.alg, nonce: o.nonce, url: o.url };
729
+ var h = intrinsic.assign(intrinsic.create(null), { alg: o.alg, nonce: o.nonce, url: o.url });
730
730
  if (_hasOwn(o, "kid")) h.kid = o.kid;
731
731
  if (_hasOwn(o, "jwk")) h.jwk = o.jwk;
732
732
  return h;
@@ -1778,11 +1778,11 @@ function _parseLinkNext(headers, base) { var m = _parseLinkRelation(headers, bas
1778
1778
  * var acct = await acme.newAccount({ termsOfServiceAgreed: true });
1779
1779
  * var ord = await acme.newOrder({ identifiers: [{ type: "dns", value: "example.org" }] });
1780
1780
  */
1781
- var _CLIENT_OPTS = {
1781
+ var _CLIENT_OPTS = intrinsic.assign(intrinsic.create(null), {
1782
1782
  accountKey: 1, accountJwk: 1, alg: 1, resignKeys: 1, transport: 1, tls: 1, timeout: 1, maxResponseBytes: 1,
1783
1783
  maxRedirects: 1, maxNonceRetries: 1, maxPolls: 1, maxTotalWait: 1, sleep: 1, clock: 1, proxy: 1,
1784
- };
1785
- var _METHOD_OPTS = {
1784
+ });
1785
+ var _METHOD_OPTS = intrinsic.assign(intrinsic.create(null), {
1786
1786
  finalize: { csr: 1, identifiers: 1 },
1787
1787
  poll: { maxPolls: 1, maxTotalWait: 1, onRetryAfter: 1 },
1788
1788
  download: { expectedSpki: 1, identifiers: 1, requireBinding: 1, selectChain: 1, maxAlternates: 1 },
@@ -1792,12 +1792,20 @@ var _METHOD_OPTS = {
1792
1792
  scheduleRenewal: { random: 1, shouldStop: 1, renew: 1, maxChecks: 1, maxWait: 1, longTermRetrySeconds: 1, temporaryBaseSeconds: 1 },
1793
1793
  updateAccount: { contact: 1 },
1794
1794
  listOrders: { maxPages: 1 },
1795
- };
1795
+ });
1796
1796
  function _gateOpts(bag, whitelist, name) {
1797
1797
  bag = guard.identifier.optionsObject(bag, E, "acme/bad-input", "pki.acme " + name + " options");
1798
1798
  guard.identifier.assertKnownKeys(bag, whitelist, E, "acme/bad-input", "unknown " + name + " option ");
1799
1799
  return bag;
1800
1800
  }
1801
+ /** @internal Refused at the door and again where the value is used. The checked value is RETURNED
1802
+ * and it is that one the caller uses: reading the option again would ask a second time, and an
1803
+ * accessor can answer differently on each read. */
1804
+ function _assertTransport(opts) {
1805
+ return guard.identifier.assertCallableOption(opts, "transport", E, "acme/bad-input", "opts.transport",
1806
+ "(request) => Promise<{ status, headers, body }>");
1807
+ }
1808
+
1801
1809
  function _resignKeysOf(raw) {
1802
1810
  if (raw == null) return null;
1803
1811
  if (!_isArrayI(raw) || raw.length === 0) throw E("acme/bad-input", "resignKeys, when set, must be a non-empty array of { alg, key } (RFC 8555 sec. 6.2)");
@@ -1821,7 +1829,7 @@ function _chooseResign(resignKeys, serverAlgs) {
1821
1829
  return null;
1822
1830
  }
1823
1831
  function _jwkForAlg(jwk, alg) {
1824
- var out = {};
1832
+ var out = intrinsic.create(null);
1825
1833
  var keys = intrinsic.ownKeys(jwk);
1826
1834
  for (var i = 0; i < keys.length; i++) { out[keys[i]] = jwk[keys[i]]; }
1827
1835
  out.alg = alg;
@@ -1830,6 +1838,9 @@ function _jwkForAlg(jwk, alg) {
1830
1838
  function client(directoryUrl, opts) {
1831
1839
  opts = guard.identifier.optionsObject(opts, E, "acme/bad-input", "pki.acme.client options");
1832
1840
  guard.identifier.assertKnownKeys(opts, _CLIENT_OPTS, E, "acme/bad-input", "unknown pki.acme.client option ");
1841
+ /** @internal Read once, at the door, and carried from here. Asking again would let an option that
1842
+ * answers differently on each read hand back something other than the value that was checked. */
1843
+ var checkedTransport = _assertTransport(opts);
1833
1844
  if (!opts.accountKey) throw E("acme/bad-input", "client requires opts.accountKey (the account private key)");
1834
1845
  if (!_isObject(opts.accountJwk)) throw E("acme/bad-input", "client requires opts.accountJwk (the account public JWK)");
1835
1846
  if (!_isString(opts.alg)) throw E("acme/bad-input", "client requires opts.alg (the account-key JWS alg)");
@@ -1838,7 +1849,7 @@ function client(directoryUrl, opts) {
1838
1849
  var accountKey = opts.accountKey, accountJwk = opts.accountJwk, alg = opts.alg;
1839
1850
  var resignKeys = _resignKeysOf(opts.resignKeys);
1840
1851
 
1841
- var transport = opts.transport;
1852
+ var transport = checkedTransport;
1842
1853
  if (!transport) {
1843
1854
  var t0 = opts.tls || {};
1844
1855
  var hasAnchors = t0.anchors !== undefined && t0.anchors !== null && !(Array.isArray(t0.anchors) && t0.anchors.length === 0);
@@ -1912,10 +1923,10 @@ function client(directoryUrl, opts) {
1912
1923
  function _sendFollowing(method, url, headers, body, redirects) {
1913
1924
  var reqHeaders = Object.assign({ "user-agent": CLIENT_USER_AGENT }, headers || {});
1914
1925
  return guard.async.deferred(function () {
1915
- return guard.async.awaited(transport({ method: method, url: url, headers: reqHeaders, body: body, tls: _tlsFor(url), proxy: budgets.proxy, timeout: budgets.timeout, maxResponseBytes: budgets.maxResponseBytes }), E, "acme/bad-input", "opts.transport");
1926
+ return guard.async.invoked(transport, [{ method: method, url: url, headers: reqHeaders, body: body, tls: _tlsFor(url), proxy: budgets.proxy, timeout: budgets.timeout, maxResponseBytes: budgets.maxResponseBytes }], E, "acme/bad-input", "opts.transport");
1916
1927
  }).then(function (res) {
1917
1928
  res = res || {};
1918
- var h = {};
1929
+ var h = intrinsic.create(null);
1919
1930
  Object.keys(res.headers || {}).forEach(function (k) { h[k.toLowerCase()] = res.headers[k]; });
1920
1931
  var blen = res.body == null ? 0 : (typeof res.body === "string" ? Buffer.byteLength(res.body, "utf8")
1921
1932
  : (Buffer.isBuffer(res.body) ? guard.bytes.lengthOf(res.body) : guard.bytes.lengthOf(guard.bytes.source(res.body, E, "acme/bad-response", "the response body"))));
package/lib/asn1-der.js CHANGED
@@ -46,7 +46,7 @@ function _asn1Error(c, m) { return new Asn1Error(c, m); }
46
46
  function _oidErr(c, m) { return new OidError(c, m); }
47
47
 
48
48
 
49
- var UNIVERSAL_TYPES = {
49
+ var UNIVERSAL_TYPES = intrinsic.assign(intrinsic.create(null), {
50
50
  BOOLEAN: { tag: 0x01, form: "primitive" },
51
51
  INTEGER: { tag: 0x02, form: "primitive" },
52
52
  BIT_STRING: { tag: 0x03, form: "primitive" },
@@ -69,9 +69,9 @@ var UNIVERSAL_TYPES = {
69
69
  UNIVERSAL_STRING: { tag: 0x1c, form: "primitive" },
70
70
  CHARACTER_STRING: { tag: 0x1d, form: "constructed" },
71
71
  BMP_STRING: { tag: 0x1e, form: "primitive" },
72
- };
72
+ });
73
73
 
74
- var TAGS = {};
74
+ var TAGS = intrinsic.create(null);
75
75
  Object.keys(UNIVERSAL_TYPES).forEach(function (k) { TAGS[k] = UNIVERSAL_TYPES[k].tag; });
76
76
 
77
77
  var CLASS_UNIVERSAL = 0x00;
@@ -797,7 +797,7 @@ function _utcTimeString(date) {
797
797
  * pki.asn1.build.utf8("example.com"),
798
798
  * ]);
799
799
  */
800
- var build = {
800
+ var build = intrinsic.assign(intrinsic.create(null), {
801
801
  boolean: function (v) { return _universal(TAGS.BOOLEAN, false, Buffer.from([v ? 0xff : 0x00])); },
802
802
  integer: function (v) { return _universal(TAGS.INTEGER, false, _intContent(v, "build.integer")); },
803
803
  enumerated: function (v) { return _universal(TAGS.ENUMERATED, false, _intContent(v, "build.enumerated")); },
@@ -873,7 +873,7 @@ var build = {
873
873
  return encodeTLV(CLASS_CONTEXT, node.constructed, tagNumber, buf.slice(buf.length - node.length));
874
874
  },
875
875
  raw: function (buf) { return _asBuffer(buf, "build.raw"); },
876
- };
876
+ });
877
877
  intrinsic.freeze(build);
878
878
 
879
879
  function _intContent(v, who) {
@@ -45,21 +45,21 @@ var _b = pkiBuild.makeBuilder({
45
45
  NAME_SCHEMA: pkix.name(NS), SPKI_SCHEMA: pkix.spki(NS), EXT_DECODERS: EXT_DECODERS,
46
46
  });
47
47
 
48
- var KNOWN_SPEC_KEYS = { holder: 1, notBeforeTime: 1, notAfterTime: 1, serialNumber: 1, attributes: 1, extensions: 1 };
49
- var KNOWN_HOLDER_KEYS = { entityName: 1, baseCertificateID: 1, fromCertificate: 1, objectDigestInfo: 1 };
50
- var ATTR_OID_NAME = {
48
+ var KNOWN_SPEC_KEYS = intrinsic.assign(intrinsic.create(null), { holder: 1, notBeforeTime: 1, notAfterTime: 1, serialNumber: 1, attributes: 1, extensions: 1 });
49
+ var KNOWN_HOLDER_KEYS = intrinsic.assign(intrinsic.create(null), { entityName: 1, baseCertificateID: 1, fromCertificate: 1, objectDigestInfo: 1 });
50
+ var ATTR_OID_NAME = intrinsic.assign(intrinsic.create(null), {
51
51
  role: "role", clearance: "clearance", group: "group", chargingIdentity: "chargingIdentity",
52
52
  accessIdentity: "accessIdentity", authenticationInfo: "authenticationInfo",
53
- };
54
- var EXT_META = {
53
+ });
54
+ var EXT_META = intrinsic.assign(intrinsic.create(null), {
55
55
  auditIdentity: { name: "acAuditIdentity", critical: true },
56
56
  targetInformation: { name: "targetInformation", critical: true },
57
57
  noRevAvail: { name: "noRevAvail", critical: false },
58
58
  aaControls: { name: "aaControls", critical: true },
59
59
  acProxying: { name: "acProxying", critical: true },
60
60
  authorityKeyIdentifier: { name: "authorityKeyIdentifier", critical: false },
61
- };
62
- var REQUIRED_CRITICALITY = {};
61
+ });
62
+ var REQUIRED_CRITICALITY = intrinsic.create(null);
63
63
  Object.keys(EXT_META).forEach(function (k) { REQUIRED_CRITICALITY[O(EXT_META[k].name)] = EXT_META[k].critical; });
64
64
  var CLASSLIST_BIT = intrinsic.assign(intrinsic.create(null), { unmarked: 0, unclassified: 1, restricted: 2, confidential: 3, secret: 4, topSecret: 5 });
65
65
 
@@ -196,14 +196,14 @@ function _encodeSvceAuthInfo(sai, authInfoAllowed, label) {
196
196
  return b.sequence(parts);
197
197
  }
198
198
 
199
- var ATTR_VALUE_ENCODER = {
199
+ var ATTR_VALUE_ENCODER = intrinsic.assign(intrinsic.create(null), {
200
200
  role: function (v) { return _encodeRole(v); },
201
201
  clearance: function (v) { return _encodeClearance(v); },
202
202
  group: function (v) { return _encodeIetfAttrSyntax(v); },
203
203
  chargingIdentity: function (v) { return _encodeIetfAttrSyntax(v); },
204
204
  authenticationInfo: function (v) { return _encodeSvceAuthInfo(v, true, "authenticationInfo"); },
205
205
  accessIdentity: function (v) { return _encodeSvceAuthInfo(v, false, "accessIdentity"); },
206
- };
206
+ });
207
207
 
208
208
 
209
209
  function _encodeTarget(t) {
@@ -252,7 +252,7 @@ function _extensionValue(key, val, aaSpki) {
252
252
 
253
253
 
254
254
  function _buildAttributes(attrSpec) {
255
- var attrs = [], seen = {};
255
+ var attrs = [], seen = intrinsic.create(null);
256
256
  function add(type, valueTlv) {
257
257
  if (seen[type]) throw _err("attrcert/duplicate-attribute", "duplicate " + (oid.name(type) || type) + " attribute (RFC 5755 sec. 4.2.7)");
258
258
  seen[type] = true;
@@ -304,7 +304,7 @@ function _buildExtensions(extSpec, aaSpki) {
304
304
  if (extSpec == null) return null;
305
305
  if (Array.isArray(extSpec)) {
306
306
  if (!extSpec.length) return null;
307
- var seenA = {};
307
+ var seenA = intrinsic.create(null);
308
308
  var exts = extSpec.map(function (e, i) {
309
309
  var der = _b.reqDer(e, "extension");
310
310
  _b.assertValidExtension(der, i);
@@ -330,7 +330,7 @@ function _buildExtensions(extSpec, aaSpki) {
330
330
  guard.identifier.assertKnownKeys(extSpec, EXT_META, _err, "attrcert/bad-input", function (k) {
331
331
  return "unknown extension " + JSON.stringify(k) + "; pass a pre-encoded Extension DER via the array form for a custom extension";
332
332
  });
333
- var out = [], seen = {};
333
+ var out = [], seen = intrinsic.create(null);
334
334
  Object.keys(extSpec).forEach(function (k) {
335
335
  if (extSpec[k] == null) return;
336
336
  var meta = EXT_META[k], eOid = O(meta.name);
@@ -443,7 +443,7 @@ function _sign(spec, issuer, opts) {
443
443
  });
444
444
  }
445
445
 
446
- var KNOWN_VERIFY_OPTS = { time: 1, target: 1, revocationStatus: 1 };
446
+ var KNOWN_VERIFY_OPTS = intrinsic.assign(intrinsic.create(null), { time: 1, target: 1, revocationStatus: 1 });
447
447
  var REVOCATION_STATUS = Object.create(null);
448
448
  REVOCATION_STATUS.notRevoked = 1;
449
449
  REVOCATION_STATUS.revoked = 1;
@@ -560,7 +560,7 @@ function _checkTargeting(ac, want) {
560
560
  reason: "this attribute certificate does not name this verifier among its targets (RFC 5755 sec. 4.3.2)" };
561
561
  }
562
562
 
563
- var SUPPORTED_CRITICAL = {};
563
+ var SUPPORTED_CRITICAL = intrinsic.create(null);
564
564
  SUPPORTED_CRITICAL[O("targetInformation")] = 1;
565
565
  SUPPORTED_CRITICAL[O("acAuditIdentity")] = 1;
566
566
 
package/lib/cbor-det.js CHANGED
@@ -645,7 +645,7 @@ function _head(mt, argument, who) {
645
645
  if (a < 4294967296n) { b = Buffer.alloc(5); b[0] = mtb | 26; b.writeUInt32BE(Number(a), 1); return b; }
646
646
  b = Buffer.alloc(9); b[0] = mtb | 27; b.writeBigUInt64BE(a, 1); return b;
647
647
  }
648
- var build = {
648
+ var build = Object.assign(Object.create(null), {
649
649
  uint: function (n) { var a = typeof n === "bigint" ? n : BigInt(n); if (a < 0n) throw new CborError("cbor/bad-argument", "build.uint: value must be non-negative"); return _head(0, a, "build.uint"); },
650
650
  nint: function (n) { var a = typeof n === "bigint" ? n : BigInt(n); if (a >= 0n) throw new CborError("cbor/bad-argument", "build.nint: value must be negative"); return _head(1, -1n - a, "build.nint"); },
651
651
  int: function (n) { var a = typeof n === "bigint" ? n : BigInt(n); return a >= 0n ? _head(0, a, "build.int") : _head(1, -1n - a, "build.int"); },
@@ -690,7 +690,7 @@ var build = {
690
690
  return build.tag(1, build.int(secs));
691
691
  },
692
692
  oid: function (dotted) { return build.tag(111, build.byteString(asn1.encodeOidContent(dotted))); },
693
- };
693
+ });
694
694
 
695
695
  module.exports = {
696
696
  decode: decode,
package/lib/cmc-build.js CHANGED
@@ -31,7 +31,7 @@ var OID_IDENTIFICATION = O("id-cmc-identification");
31
31
  var OID_SHA256 = O("sha256");
32
32
  var OID_HMAC_SHA256 = O("hmacWithSHA256");
33
33
 
34
- var RENEWAL_FORBIDDEN = {};
34
+ var RENEWAL_FORBIDDEN = intrinsic.create(null);
35
35
  RENEWAL_FORBIDDEN[OID_IDENTIFICATION] = "Identification";
36
36
  RENEWAL_FORBIDDEN[O("id-cmc-identityProof")] = "Identity Proof";
37
37
  RENEWAL_FORBIDDEN[OID_IDENTITY_PROOF_V2] = "Identity Proof V2";
@@ -40,11 +40,11 @@ var OID_RESPONSE_BODY = O("id-cmc-responseBody");
40
40
 
41
41
  var POP_LINK_RANDOM_BYTES = 64;
42
42
 
43
- var KNOWN_SPEC_KEYS = {
43
+ var KNOWN_SPEC_KEYS = intrinsic.assign(intrinsic.create(null), {
44
44
  requests: 1, controls: 1, cmsSequence: 1, otherMsgSequence: 1,
45
45
  identityProof: 1, popLink: 1, renewal: 1,
46
46
  transactionId: 1, senderNonce: 1, dataReturn: 1,
47
- };
47
+ });
48
48
 
49
49
  var OID_TRANSACTION_ID = O("id-cmc-transactionId");
50
50
  var OID_SENDER_NONCE = O("id-cmc-senderNonce");
@@ -96,10 +96,10 @@ function makeIdAllocator() {
96
96
  }
97
97
 
98
98
 
99
- var KNOWN_REQUEST_KEYS = { tcr: 1, crm: 1, orm: 1, bodyPartID: 1 };
100
- var KNOWN_CONTROL_KEYS = { type: 1, value: 1 };
101
- var KNOWN_IDENTITY_PROOF_KEYS = { identity: 1, secret: 1 };
102
- var KNOWN_POP_LINK_KEYS = { secret: 1 };
99
+ var KNOWN_REQUEST_KEYS = intrinsic.assign(intrinsic.create(null), { tcr: 1, crm: 1, orm: 1, bodyPartID: 1 });
100
+ var KNOWN_CONTROL_KEYS = intrinsic.assign(intrinsic.create(null), { type: 1, value: 1 });
101
+ var KNOWN_IDENTITY_PROOF_KEYS = intrinsic.assign(intrinsic.create(null), { identity: 1, secret: 1 });
102
+ var KNOWN_POP_LINK_KEYS = intrinsic.assign(intrinsic.create(null), { secret: 1 });
103
103
 
104
104
  function fixedRequestId(req, index) {
105
105
  if (!req || typeof req !== "object") throw E("cmc/bad-input", "each request must be an object");
@@ -209,24 +209,24 @@ function _claimRawElement(el, ids, what) {
209
209
  return der;
210
210
  }
211
211
 
212
- var BINDING_BY_OID = {};
212
+ var BINDING_BY_OID = intrinsic.create(null);
213
213
  BINDING_BY_OID[O("id-cmc-transactionId")] = "transactionId";
214
214
  BINDING_BY_OID[O("id-cmc-senderNonce")] = "senderNonce";
215
215
  BINDING_BY_OID[O("id-cmc-dataReturn")] = "dataReturn";
216
216
 
217
- var BINDING_READER = {
217
+ var BINDING_READER = intrinsic.assign(intrinsic.create(null), {
218
218
  transactionId: function (node) { return asn1.read.integer(node); },
219
219
  senderNonce: function (node) { return asn1.read.octetString(node); },
220
220
  dataReturn: function (node) { return asn1.read.octetString(node); },
221
- };
222
- var BINDING_TYPE_NAME = {
221
+ });
222
+ var BINDING_TYPE_NAME = intrinsic.assign(intrinsic.create(null), {
223
223
  transactionId: "an INTEGER (RFC 5272 sec. 6.6)",
224
224
  senderNonce: "an OCTET STRING (RFC 5272 sec. 6.6)",
225
225
  dataReturn: "an OCTET STRING (RFC 5272 sec. 6.4)",
226
- };
226
+ });
227
227
 
228
228
  function _assertNoDuplicateBinding(callerControls, spec) {
229
- var seen = {};
229
+ var seen = intrinsic.create(null);
230
230
  callerControls.forEach(function (c) {
231
231
  var field = BINDING_BY_OID[_oidOf(c.type)];
232
232
  if (!field) return;
@@ -259,7 +259,7 @@ function _oidOf(v) {
259
259
  }
260
260
 
261
261
 
262
- var REQUEST_FORBIDDEN_CONTROL = {};
262
+ var REQUEST_FORBIDDEN_CONTROL = intrinsic.create(null);
263
263
  REQUEST_FORBIDDEN_CONTROL[OID_RESPONSE_BODY] =
264
264
  "id-cmc-responseBody may appear only in the control sequence of a PKIResponse (RFC 6402 sec. 2.6)";
265
265
  REQUEST_FORBIDDEN_CONTROL[O("id-cmc-statusInfo")] =
@@ -469,7 +469,7 @@ async function _build(spec, signer, opts) {
469
469
  function _copySigners(signer, owned) {
470
470
  function one(s) {
471
471
  if (!s || typeof s !== "object") return s;
472
- var out = {}, k;
472
+ var out = intrinsic.create(null), k;
473
473
  for (k in s) {
474
474
  if (!_hasOwn(s, k)) continue;
475
475
  out[k] = copyValue(s[k], k === "key");
@@ -483,7 +483,7 @@ function _copySigners(signer, owned) {
483
483
  }
484
484
  if (Buffer.isBuffer(v) || v instanceof Uint8Array) return _own(Buffer.from(v), true); // allow:byte-source-narrow -- private key bytes (Buffer borrowed / Uint8Array copied+wiped)
485
485
  if (!v || typeof v !== "object" || typeof v.type === "string") return v;
486
- var c = {}, ck;
486
+ var c = intrinsic.create(null), ck;
487
487
  for (ck in v) {
488
488
  if (!_hasOwn(v, ck)) continue;
489
489
  var cv = v[ck];
package/lib/cmc-verify.js CHANGED
@@ -40,7 +40,7 @@ var OID_DATA_RETURN = O("id-cmc-dataReturn");
40
40
  var OID_TRUSTED_ANCHORS = O("id-cmc-trustedAnchors");
41
41
  var OID_CONFIRM_CERT_ACCEPTANCE = O("id-cmc-confirmCertAcceptance");
42
42
 
43
- var OUTCOME_BY_STATUS = {
43
+ var OUTCOME_BY_STATUS = intrinsic.assign(intrinsic.create(null), {
44
44
  success: "issued",
45
45
  pending: "pending",
46
46
  partial: "pending",
@@ -48,9 +48,9 @@ var OUTCOME_BY_STATUS = {
48
48
  popRequired: "pop-required",
49
49
  failed: "rejected",
50
50
  noSupport: "rejected",
51
- };
51
+ });
52
52
 
53
- var OUTCOME_SEVERITY = { rejected: 4, "pop-required": 3, "confirm-required": 2, pending: 1, issued: 0 };
53
+ var OUTCOME_SEVERITY = intrinsic.assign(intrinsic.create(null), { rejected: 4, "pop-required": 3, "confirm-required": 2, pending: 1, issued: 0 });
54
54
 
55
55
  function _singleValue(control, what) {
56
56
  if (control.values.length !== 1) {
@@ -319,7 +319,7 @@ function _copyAnyBytes(v) {
319
319
  }
320
320
 
321
321
  function _snapshotSent(sent) {
322
- var out = {}, k;
322
+ var out = intrinsic.create(null), k;
323
323
  for (k in sent) { if (_hasOwn(sent, k)) out[k] = sent[k]; }
324
324
  if (out.transactionId != null) {
325
325
  out.transactionIdValue = guard.range.authoredInteger(out.transactionId, E, "cmc/bad-input", "sent.transactionId");
@@ -331,7 +331,7 @@ function _snapshotSent(sent) {
331
331
  out.bodyPartPaths = out.bodyPartPaths.map(function (p) { return Array.isArray(p) ? p.slice() : p; });
332
332
  }
333
333
  if (out.recipient && typeof out.recipient === "object") {
334
- var r = {}, rk;
334
+ var r = intrinsic.create(null), rk;
335
335
  for (rk in out.recipient) {
336
336
  if (!_hasOwn(out.recipient, rk)) continue;
337
337
  r[rk] = _copyAnyBytes(out.recipient[rk]);
package/lib/cmp-build.js CHANGED
@@ -71,10 +71,10 @@ var _b = pkiBuild.makeBuilder({
71
71
  NAME_SCHEMA: pkix.name(NS), SPKI_SCHEMA: pkix.spki(NS), EXT_DECODERS: EXT_DECODERS,
72
72
  });
73
73
 
74
- var KNOWN_HEADER_KEYS = {
74
+ var KNOWN_HEADER_KEYS = intrinsic.assign(intrinsic.create(null), {
75
75
  sender: 1, recipient: 1, pvno: 1, messageTime: 1, senderKID: 1, recipKID: 1,
76
76
  transactionID: 1, senderNonce: 1, recipNonce: 1, freeText: 1, generalInfo: 1,
77
- };
77
+ });
78
78
  var KNOWN_BODY_KEYS = intrinsic.assign(intrinsic.create(null), {
79
79
  ir: 1, cr: 1, kur: 1, krr: 1, ccr: 1, p10cr: 1, certConf: 1, pollReq: 1, genm: 1, rr: 1,
80
80
  ip: 1, cp: 1, kup: 1, ccp: 1, krp: 1, rp: 1, genp: 1, error: 1, pollRep: 1, pkiconf: 1,
@@ -87,11 +87,11 @@ var BODY_TAG = intrinsic.assign(intrinsic.create(null), {
87
87
  });
88
88
  var CRMF_BODY = intrinsic.assign(intrinsic.create(null), { ir: 1, cr: 1, kur: 1, krr: 1, ccr: 1 });
89
89
  var CERT_REP_ARM = intrinsic.assign(intrinsic.create(null), { ip: 1, cp: 1, kup: 1, ccp: 1 });
90
- var KNOWN_OPTS_KEYS = { key: 1, cert: 1, mac: 1, extraCerts: 1, pem: 1, pss: 1, digestAlgorithm: 1 };
91
- var KNOWN_OPTS_SIG_KEYS = { key: 1, cert: 1, extraCerts: 1, pem: 1, pss: 1, digestAlgorithm: 1 };
92
- var KNOWN_OPTS_MAC_KEYS = { mac: 1, extraCerts: 1, pem: 1 };
93
- var KNOWN_MESSAGE_KEYS = { header: 1, body: 1 };
94
- var KNOWN_MAC_KEYS = { secret: 1, salt: 1, iterationCount: 1, prf: 1, keyLength: 1, algorithm: 1 };
90
+ var KNOWN_OPTS_KEYS = intrinsic.assign(intrinsic.create(null), { key: 1, cert: 1, mac: 1, extraCerts: 1, pem: 1, pss: 1, digestAlgorithm: 1 });
91
+ var KNOWN_OPTS_SIG_KEYS = intrinsic.assign(intrinsic.create(null), { key: 1, cert: 1, extraCerts: 1, pem: 1, pss: 1, digestAlgorithm: 1 });
92
+ var KNOWN_OPTS_MAC_KEYS = intrinsic.assign(intrinsic.create(null), { mac: 1, extraCerts: 1, pem: 1 });
93
+ var KNOWN_MESSAGE_KEYS = intrinsic.assign(intrinsic.create(null), { header: 1, body: 1 });
94
+ var KNOWN_MAC_KEYS = intrinsic.assign(intrinsic.create(null), { secret: 1, salt: 1, iterationCount: 1, prf: 1, keyLength: 1, algorithm: 1 });
95
95
 
96
96
  var PBMAC1_DEFAULT_ITER = 100000;
97
97
  var PBMAC1_DEFAULT_SALT_BYTES = 16;
@@ -102,7 +102,7 @@ var PBMAC1_MAX_KEYLEN = 1024;
102
102
  var PBMAC1_MIN_ITER = 1000;
103
103
  var PBMAC1_PRF = intrinsic.assign(intrinsic.create(null), { "SHA-256": "hmacWithSHA256", "SHA-384": "hmacWithSHA384", "SHA-512": "hmacWithSHA512" });
104
104
  var PBMAC1_MAC_OID = intrinsic.assign(intrinsic.create(null), { "SHA-256": "hmacWithSHA256", "SHA-384": "hmacWithSHA384", "SHA-512": "hmacWithSHA512" });
105
- var PBMAC1_PRF_HLEN = { "SHA-256": 32, "SHA-384": 48, "SHA-512": 64 };
105
+ var PBMAC1_PRF_HLEN = intrinsic.assign(intrinsic.create(null), { "SHA-256": 32, "SHA-384": 48, "SHA-512": 64 });
106
106
  var FAIL_INFO_NAMES = ["badAlg", "badMessageCheck", "badRequest", "badTime", "badCertId", "badDataFormat",
107
107
  "wrongAuthority", "incorrectData", "missingTimeStamp", "badPOP", "certRevoked", "certConfirmed",
108
108
  "wrongIntegrity", "badRecipientNonce", "timeNotAvailable", "unacceptedPolicy", "unacceptedExtension",
@@ -256,8 +256,8 @@ function _encodeRevDetails(rd) {
256
256
  return b.sequence(children);
257
257
  }
258
258
 
259
- var KNOWN_REV_DETAILS_KEYS = { certDetails: 1, crlEntryDetails: 1 };
260
- var KNOWN_CRL_ENTRY_DETAIL_KEYS = { reason: 1 };
259
+ var KNOWN_REV_DETAILS_KEYS = intrinsic.assign(intrinsic.create(null), { certDetails: 1, crlEntryDetails: 1 });
260
+ var KNOWN_CRL_ENTRY_DETAIL_KEYS = intrinsic.assign(intrinsic.create(null), { reason: 1 });
261
261
  var CRL_REASON_BY_NAME = intrinsic.create(null);
262
262
  _forEach(_keys(constants.NAMES.CRL_REASON), function (v) { CRL_REASON_BY_NAME[constants.NAMES.CRL_REASON[v]] = intrinsic.Number(v); });
263
263
  function _encodeCrlEntryDetails(spec) {
@@ -272,8 +272,8 @@ function _encodeCrlEntryDetails(spec) {
272
272
  return b.sequence([_b.ext(O("reasonCode"), false, b.enumerated(_bigInt(code)))]);
273
273
  }
274
274
 
275
- var KNOWN_CRL_STATUS_KEYS = { dpn: 1, issuer: 1, thisUpdate: 1 };
276
- var KNOWN_DPN_KEYS = { fullName: 1 };
275
+ var KNOWN_CRL_STATUS_KEYS = intrinsic.assign(intrinsic.create(null), { dpn: 1, issuer: 1, thisUpdate: 1 });
276
+ var KNOWN_DPN_KEYS = intrinsic.assign(intrinsic.create(null), { fullName: 1 });
277
277
  function buildCrlStatusList(spec) {
278
278
  if (!spec || typeof spec !== "object" || _isBuffer(spec)) throw _err("cmp/bad-input", "a crlUpdate request must be an object { dpn | issuer, thisUpdate? }");
279
279
  guard.identifier.assertKnownKeys(spec, KNOWN_CRL_STATUS_KEYS, _err, "cmp/bad-input", "unknown crlUpdate field ");
@@ -453,7 +453,7 @@ function _encodeKeyRecRepContent(spec, state) {
453
453
  return b.sequence(children);
454
454
  }
455
455
 
456
- var CMP2021_POP_METHODS = { agreeMAC: 1, encryptedKey: 1 };
456
+ var CMP2021_POP_METHODS = intrinsic.assign(intrinsic.create(null), { agreeMAC: 1, encryptedKey: 1 });
457
457
  function _popNeedsCmp2021(crmfDer) {
458
458
  var messages;
459
459
  try { messages = schemaCrmf.parse(crmfDer).messages; }
@@ -735,7 +735,7 @@ var DEFAULT_TRANSFER_TIMEOUT = constants.TIME.seconds(30);
735
735
  var MAX_TRANSFER_TIMEOUT = constants.TIME.seconds(600);
736
736
  var PKIXCMP = "application/pkixcmp";
737
737
  var PKIXCMP_POLL = "application/pkixcmp-poll";
738
- var KNOWN_TRANSFER_OPTS = { transport: 1, tls: 1, headers: 1, timeout: 1, maxResponseBytes: 1, proxy: 1 };
738
+ var KNOWN_TRANSFER_OPTS = intrinsic.assign(intrinsic.create(null), { transport: 1, tls: 1, headers: 1, timeout: 1, maxResponseBytes: 1, proxy: 1 });
739
739
 
740
740
  function _cmpMessageDer(input) {
741
741
  var der;
@@ -783,14 +783,24 @@ function transfer(url, message, opts) {
783
783
  return guard.async.deferred(function () { return _transfer(url, message, opts); });
784
784
  }
785
785
 
786
+ /** @internal Refused at the door and again where the value is used. The checked value is RETURNED
787
+ * and it is that one the caller uses: reading the option again would ask a second time, and an
788
+ * accessor can answer differently on each read. */
789
+ function _assertTransport(opts) {
790
+ return guard.identifier.assertCallableOption(opts, "transport", _err, "cmp/bad-input", "opts.transport",
791
+ "(request) => Promise<{ status, headers, body }>");
792
+ }
793
+
786
794
  function _transfer(url, message, opts) {
787
795
  opts = opts || {};
788
796
  guard.identifier.assertKnownKeys(opts, KNOWN_TRANSFER_OPTS, _err, "cmp/bad-input", "unknown opts field ");
797
+ /** @internal Read once, at the door, and carried from here. Asking again would let an option that
798
+ * answers differently on each read hand back something other than the value that was checked. */
799
+ var transport = _assertTransport(opts);
789
800
  var der = _cmpMessageDer(message);
790
801
  var parsedUrl;
791
802
  try { parsedUrl = new URL(String(url)); }
792
803
  catch (e) { throw _err("cmp/bad-url", "the CMP URL did not parse: " + guard.text.showValue(url), e); }
793
- var transport = opts.transport;
794
804
  if (!transport) {
795
805
  var t = opts.tls || {};
796
806
  var hasAnchors = t.anchors !== undefined && t.anchors !== null && !(Array.isArray(t.anchors) && t.anchors.length === 0);
@@ -802,16 +812,16 @@ function _transfer(url, message, opts) {
802
812
  var proxy;
803
813
  try { proxy = httpTransport.snapshotProxy(opts.proxy); }
804
814
  catch (e) { throw _err("cmp/bad-input", "the options object could not be read", e); }
805
- var headers = {};
815
+ var headers = intrinsic.create(null);
806
816
  Object.keys(opts.headers || {}).forEach(function (k) {
807
817
  var lk = k.toLowerCase();
808
818
  if (lk !== "content-length" && lk !== "transfer-encoding" && lk !== "content-type") headers[k] = opts.headers[k];
809
819
  });
810
820
  headers["content-type"] = PKIXCMP;
811
821
  var tls = _tlsForTransfer(opts);
812
- return guard.async.awaited(transport({ method: "POST", url: parsedUrl.href, headers: headers, body: der, tls: tls, proxy: proxy, timeout: timeout, maxResponseBytes: maxResponseBytes }), _err, "cmp/bad-input", "opts.transport").then(function (res) {
822
+ return guard.async.invoked(transport, [{ method: "POST", url: parsedUrl.href, headers: headers, body: der, tls: tls, proxy: proxy, timeout: timeout, maxResponseBytes: maxResponseBytes }], _err, "cmp/bad-input", "opts.transport").then(function (res) {
813
823
  res = res || {};
814
- var h = {};
824
+ var h = intrinsic.create(null);
815
825
  Object.keys(res.headers || {}).forEach(function (k) { h[k.toLowerCase()] = res.headers[k]; });
816
826
  var body = guard.bytes.isByteSource(res.body)
817
827
  ? guard.bytes.source(res.body, CmpError, "cmp/bad-response", "the CMP response body")
@@ -835,7 +845,7 @@ function _wellKnownSeg(v, name) {
835
845
  catch (e) { throw _err("cmp/bad-url", "the CMP " + name + " is not encodable (e.g. an unpaired surrogate): " + JSON.stringify(s), e); }
836
846
  }
837
847
 
838
- var KNOWN_WELLKNOWN_OPTS = { label: 1, operation: 1 };
848
+ var KNOWN_WELLKNOWN_OPTS = intrinsic.assign(intrinsic.create(null), { label: 1, operation: 1 });
839
849
 
840
850
  function _isAsciiLetter(ch) { return (ch >= "A" && ch <= "Z") || (ch >= "a" && ch <= "z"); }
841
851
  function _stripSchemeAuthority(s) {