@openeudi/openid4vp 0.12.0 → 0.13.0

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/dist/index.cjs CHANGED
@@ -45,7 +45,7 @@ var init_reflect_polyfill = __esm({
45
45
  });
46
46
 
47
47
  // src/errors.ts
48
- exports.InvalidSignatureError = void 0; exports.ExpiredCredentialError = void 0; exports.UnsupportedFormatError = void 0; exports.MalformedCredentialError = void 0; exports.NonceValidationError = void 0; exports.HaipValidationError = void 0; exports.OpenID4VPError = void 0; exports.TrustAnchorNotFoundError = void 0; exports.CertificateChainError = void 0; exports.RevokedCertificateError = void 0; exports.RevocationCheckFailedError = void 0; exports.LotlFetchError = void 0; exports.LotlSignatureError = void 0; exports.LotlConfigurationError = void 0; exports.SignedRequestBuildError = void 0; exports.UnsupportedJweError = void 0; exports.DecryptionFailedError = void 0; exports.MissingDecryptionKeyError = void 0; exports.MissingVerifierEncryptionKeyError = void 0; exports.MultipleCredentialsNotSupportedError = void 0;
48
+ exports.InvalidSignatureError = void 0; exports.ExpiredCredentialError = void 0; exports.UnsupportedFormatError = void 0; exports.MalformedCredentialError = void 0; exports.NonceValidationError = void 0; exports.HaipValidationError = void 0; exports.OpenID4VPError = void 0; exports.TrustAnchorNotFoundError = void 0; exports.CertificateChainError = void 0; exports.RevokedCertificateError = void 0; exports.RevocationCheckFailedError = void 0; exports.LotlFetchError = void 0; exports.LotlSignatureError = void 0; exports.LotlConfigurationError = void 0; exports.SignedRequestBuildError = void 0; exports.UnsupportedJweError = void 0; exports.DecryptionFailedError = void 0; exports.MissingDecryptionKeyError = void 0; exports.MissingVerifierEncryptionKeyError = void 0; exports.MultipleCredentialsNotSupportedError = void 0; exports.GuardedFetchError = void 0;
49
49
  var init_errors = __esm({
50
50
  "src/errors.ts"() {
51
51
  init_reflect_polyfill();
@@ -192,6 +192,16 @@ var init_errors = __esm({
192
192
  this.presentationCount = presentationCount;
193
193
  }
194
194
  };
195
+ exports.GuardedFetchError = class extends exports.OpenID4VPError {
196
+ code = "guarded_fetch_rejected";
197
+ reason;
198
+ url;
199
+ constructor(reason, message, options) {
200
+ super(message, { cause: options.cause });
201
+ this.reason = reason;
202
+ this.url = options.url;
203
+ }
204
+ };
195
205
  }
196
206
  });
197
207
 
@@ -6588,7 +6598,7 @@ function dnIsUnder(childDn, subtreeDn) {
6588
6598
  }
6589
6599
  return true;
6590
6600
  }
6591
- var NAME_CONSTRAINTS_OID, DEFAULT_ALGORITHMS, ChainBuilder;
6601
+ var NAME_CONSTRAINTS_OID, ISO_18013_5_DS_EKU, DEFAULT_MAX_CHAIN_LENGTH, DEFAULT_MAX_SIGNATURE_CHECKS, searchAborts, DEFAULT_ALGORITHMS, ChainBuilder;
6592
6602
  var init_ChainBuilder = __esm({
6593
6603
  "src/trust/ChainBuilder.ts"() {
6594
6604
  init_reflect_polyfill();
@@ -6597,6 +6607,10 @@ var init_ChainBuilder = __esm({
6597
6607
  init_errors();
6598
6608
  init_x509_utils();
6599
6609
  NAME_CONSTRAINTS_OID = "2.5.29.30";
6610
+ ISO_18013_5_DS_EKU = "1.0.18013.5.1.2";
6611
+ DEFAULT_MAX_CHAIN_LENGTH = 5;
6612
+ DEFAULT_MAX_SIGNATURE_CHECKS = 32;
6613
+ searchAborts = /* @__PURE__ */ new WeakSet();
6600
6614
  DEFAULT_ALGORITHMS = [
6601
6615
  "ES256",
6602
6616
  "ES384",
@@ -6619,62 +6633,130 @@ var init_ChainBuilder = __esm({
6619
6633
  * optionally using additional intermediates. Returns the ordered chain
6620
6634
  * (leaf → anchor). Throws `CertificateChainError` on any validation
6621
6635
  * failure.
6636
+ *
6637
+ * Trust is by presence in the anchor set and is established ONLY by (a) the
6638
+ * leaf being byte-identical to an anchor, or (b) a verified signature by an
6639
+ * anchor's key on the last certificate of the path. Subject/Issuer DN
6640
+ * equality is used to select candidates, never to conclude trust
6641
+ * (GHSA-4c2f-96cf-f5fc).
6642
+ *
6643
+ * The search is depth-first and backtracks: when several intermediates share
6644
+ * a Subject DN — a re-issued certificate next to its expired predecessor, or
6645
+ * cross-signed copies under different roots — a candidate that fails a check
6646
+ * or leads nowhere does not end the search. The result therefore does not
6647
+ * depend on the order of `intermediates`. Work is bounded by
6648
+ * `maxChainLength` and `maxSignatureChecks`.
6622
6649
  */
6623
- async build(leaf, anchors, intermediates = []) {
6624
- const errors = [];
6650
+ async build(leaf, anchors, intermediates = [], context = {}) {
6651
+ const search = {
6652
+ remaining: this.opts.maxSignatureChecks ?? DEFAULT_MAX_SIGNATURE_CHECKS
6653
+ };
6654
+ let lastFailure;
6625
6655
  for (const anchor of anchors) {
6656
+ search.specific = void 0;
6657
+ search.fallback = void 0;
6626
6658
  try {
6627
- return await this.tryBuild(leaf, anchor, intermediates);
6659
+ const chain = await this.tryBuild(leaf, anchor, intermediates, context, search);
6660
+ if (chain) return chain;
6661
+ lastFailure = search.specific ?? search.fallback;
6628
6662
  } catch (err) {
6629
- errors.push(err);
6663
+ if (searchAborts.has(err)) throw err;
6664
+ lastFailure = err;
6630
6665
  }
6631
6666
  }
6632
- const last = errors[errors.length - 1];
6633
- if (last instanceof exports.CertificateChainError) throw last;
6667
+ if (lastFailure instanceof exports.CertificateChainError) throw lastFailure;
6634
6668
  throw new exports.CertificateChainError("no valid chain to any anchor", {
6635
6669
  reason: "signature",
6636
- cause: last
6670
+ cause: lastFailure
6637
6671
  });
6638
6672
  }
6639
- async tryBuild(leaf, anchor, intermediates) {
6640
- this.checkPerCert(leaf);
6641
- this.checkLeafKeyUsage(leaf);
6642
- const chain = [leaf];
6643
- let current = leaf;
6644
- const pool = new x509.X509Certificates(intermediates);
6645
- let nonLeafDepth = 0;
6646
- while (current.subject !== anchor.subject) {
6647
- const issuer = pool.find((c) => c.subject === current.issuer);
6648
- if (!issuer) {
6649
- if (current.issuer !== anchor.subject) {
6650
- throw new exports.CertificateChainError(`no issuer certificate found for ${current.subject}`, {
6651
- reason: "signature"
6652
- });
6653
- }
6654
- this.checkPerCert(anchor);
6655
- this.checkCaAndPathLen(anchor, nonLeafDepth);
6656
- this.checkAkiSkiMatch(current, anchor);
6657
- await this.verifySignature(current, anchor);
6658
- chain.push(anchor);
6659
- this.checkNameConstraints(leaf, chain.slice(1));
6660
- return chain;
6673
+ /** Search for a path from `leaf` that closes at `anchor`. */
6674
+ async tryBuild(leaf, anchor, intermediates, context, search) {
6675
+ this.checkPerCert(leaf, context);
6676
+ if (certificatesEqual(leaf, anchor)) {
6677
+ this.checkLeafKeyUsage(leaf, "generic");
6678
+ return [leaf];
6679
+ }
6680
+ const profile = this.opts.profile ?? "generic";
6681
+ this.checkLeafKeyUsage(leaf, profile);
6682
+ this.checkLeafExtendedKeyUsage(leaf, profile);
6683
+ const pool = intermediates.filter(
6684
+ (c) => !certificatesEqual(c, anchor) && !certificatesEqual(c, leaf)
6685
+ );
6686
+ return this.extendPath([leaf], anchor, pool, context, search);
6687
+ }
6688
+ /**
6689
+ * Try to complete `chain` (leaf first) to `anchor`. Returns the completed
6690
+ * chain, or `undefined` when no extension of this prefix works — failures
6691
+ * are recorded on `search` so the caller can report the most specific one.
6692
+ * Throws only to abandon the whole search (signature budget exhausted).
6693
+ */
6694
+ async extendPath(chain, anchor, pool, context, search) {
6695
+ const leaf = chain[0];
6696
+ const current = chain[chain.length - 1];
6697
+ const maxLength = this.opts.maxChainLength ?? DEFAULT_MAX_CHAIN_LENGTH;
6698
+ if (chain.length + 1 > maxLength) {
6699
+ search.specific = new exports.CertificateChainError(
6700
+ `chain from ${leaf.subject} exceeds the maximum length of ${maxLength} certificates`,
6701
+ { reason: "path_length" }
6702
+ );
6703
+ return void 0;
6704
+ }
6705
+ const nonLeafDepth = chain.length - 1;
6706
+ if (current.issuer === anchor.subject) {
6707
+ try {
6708
+ await this.closeAtAnchor(current, anchor, nonLeafDepth, context, search);
6709
+ const closed = [...chain, anchor];
6710
+ this.checkNameConstraints(leaf, closed.slice(1));
6711
+ return closed;
6712
+ } catch (err) {
6713
+ if (searchAborts.has(err)) throw err;
6714
+ search.specific = err;
6661
6715
  }
6662
- this.checkPerCert(issuer);
6663
- this.checkCaAndPathLen(issuer, nonLeafDepth);
6664
- this.checkAkiSkiMatch(current, issuer);
6665
- await this.verifySignature(current, issuer);
6666
- chain.push(issuer);
6667
- current = issuer;
6668
- nonLeafDepth += 1;
6669
- }
6670
- if (!certificatesEqual(current, anchor)) {
6671
- throw new exports.CertificateChainError(
6672
- `certificate ${current.subject} shares the trust anchor's Subject DN but is not the anchor (no signature closure)`,
6673
- { reason: "signature" }
6716
+ }
6717
+ const candidates = pool.filter(
6718
+ (c) => c.subject === current.issuer && !chain.some((x) => certificatesEqual(x, c))
6719
+ );
6720
+ let verified = 0;
6721
+ for (const candidate of candidates) {
6722
+ if (!await this.signatureVerifies(current, candidate, search)) continue;
6723
+ verified++;
6724
+ try {
6725
+ this.checkPerCert(candidate, context);
6726
+ this.checkCaAndPathLen(candidate, nonLeafDepth);
6727
+ this.checkAkiSkiMatch(current, candidate);
6728
+ } catch (err) {
6729
+ search.specific = err;
6730
+ continue;
6731
+ }
6732
+ const found = await this.extendPath([...chain, candidate], anchor, pool, context, search);
6733
+ if (found) return found;
6734
+ }
6735
+ search.fallback = new exports.CertificateChainError(
6736
+ candidates.length > 0 && verified === 0 ? `signature verification failed for ${current.subject}` : `no issuer certificate found for ${current.subject}`,
6737
+ { reason: "signature" }
6738
+ );
6739
+ return void 0;
6740
+ }
6741
+ async closeAtAnchor(current, anchor, nonLeafDepth, context, search) {
6742
+ this.checkPerCert(anchor, context);
6743
+ this.checkCaAndPathLen(anchor, nonLeafDepth);
6744
+ this.checkAkiSkiMatch(current, anchor);
6745
+ this.spendSignatureCheck(search);
6746
+ await this.verifySignature(current, anchor);
6747
+ }
6748
+ /** Charge one signature verification to the search, or abandon it. */
6749
+ spendSignatureCheck(search) {
6750
+ if (search.remaining <= 0) {
6751
+ const limit = this.opts.maxSignatureChecks ?? DEFAULT_MAX_SIGNATURE_CHECKS;
6752
+ const err = new exports.CertificateChainError(
6753
+ `certificate path search exceeded ${limit} signature checks`,
6754
+ { reason: "path_length" }
6674
6755
  );
6756
+ searchAborts.add(err);
6757
+ throw err;
6675
6758
  }
6676
- this.checkNameConstraints(leaf, chain.slice(1));
6677
- return chain;
6759
+ search.remaining--;
6678
6760
  }
6679
6761
  /**
6680
6762
  * Enforce nameConstraints asserted by any CA above the leaf against the
@@ -6723,7 +6805,7 @@ var init_ChainBuilder = __esm({
6723
6805
  }
6724
6806
  }
6725
6807
  checkCaAndPathLen(cert, nonLeafDepth) {
6726
- this.checkCaKeyUsage(cert);
6808
+ this.checkCaKeyUsage(cert, this.opts.profile ?? "generic");
6727
6809
  const bc = cert.getExtension(x509.BasicConstraintsExtension);
6728
6810
  if (!bc || !bc.ca) {
6729
6811
  throw new exports.CertificateChainError(
@@ -6738,9 +6820,17 @@ var init_ChainBuilder = __esm({
6738
6820
  );
6739
6821
  }
6740
6822
  }
6741
- checkLeafKeyUsage(leaf) {
6823
+ checkLeafKeyUsage(leaf, profile) {
6742
6824
  const ext = leaf.getExtension(x509.KeyUsagesExtension);
6743
- if (!ext) return;
6825
+ if (!ext) {
6826
+ if (profile === "iso18013-5") {
6827
+ throw new exports.CertificateChainError(
6828
+ `document signer ${leaf.subject} has no keyUsage extension (ISO 18013-5 requires digitalSignature)`,
6829
+ { reason: "key_usage" }
6830
+ );
6831
+ }
6832
+ return;
6833
+ }
6744
6834
  if ((ext.usages & x509.KeyUsageFlags.digitalSignature) === 0) {
6745
6835
  throw new exports.CertificateChainError(
6746
6836
  `leaf ${leaf.subject} does not assert digitalSignature key usage`,
@@ -6748,9 +6838,28 @@ var init_ChainBuilder = __esm({
6748
6838
  );
6749
6839
  }
6750
6840
  }
6751
- checkCaKeyUsage(cert) {
6841
+ checkLeafExtendedKeyUsage(leaf, profile) {
6842
+ if (profile !== "iso18013-5") return;
6843
+ const ext = leaf.getExtension(x509.ExtendedKeyUsageExtension);
6844
+ const usages = (ext?.usages ?? []).map(String);
6845
+ if (!usages.includes(ISO_18013_5_DS_EKU)) {
6846
+ throw new exports.CertificateChainError(
6847
+ `document signer ${leaf.subject} lacks extended key usage ${ISO_18013_5_DS_EKU} (id-mdl-kp-mdlDS)`,
6848
+ { reason: "extended_key_usage" }
6849
+ );
6850
+ }
6851
+ }
6852
+ checkCaKeyUsage(cert, profile) {
6752
6853
  const ext = cert.getExtension(x509.KeyUsagesExtension);
6753
- if (!ext) return;
6854
+ if (!ext) {
6855
+ if (profile === "iso18013-5") {
6856
+ throw new exports.CertificateChainError(
6857
+ `CA ${cert.subject} has no keyUsage extension (ISO 18013-5 requires keyCertSign)`,
6858
+ { reason: "key_usage" }
6859
+ );
6860
+ }
6861
+ return;
6862
+ }
6754
6863
  if ((ext.usages & x509.KeyUsageFlags.keyCertSign) === 0) {
6755
6864
  throw new exports.CertificateChainError(
6756
6865
  `CA ${cert.subject} does not assert keyCertSign key usage`,
@@ -6771,30 +6880,46 @@ var init_ChainBuilder = __esm({
6771
6880
  );
6772
6881
  }
6773
6882
  }
6883
+ /**
6884
+ * Signature check used to select among DN-matching candidates. Never throws
6885
+ * on a bad signature; throws only when the search budget is exhausted.
6886
+ */
6887
+ async signatureVerifies(child, issuer, search) {
6888
+ this.spendSignatureCheck(search);
6889
+ try {
6890
+ return await child.verify({ publicKey: issuer.publicKey, signatureOnly: true });
6891
+ } catch {
6892
+ return false;
6893
+ }
6894
+ }
6774
6895
  async verifySignature(child, issuer) {
6775
6896
  const ok = await child.verify({ publicKey: issuer.publicKey, signatureOnly: true });
6776
6897
  if (!ok) {
6777
6898
  throw new exports.CertificateChainError(`signature verification failed for ${child.subject}`, { reason: "signature" });
6778
6899
  }
6779
6900
  }
6780
- checkPerCert(cert) {
6781
- this.checkValidity(cert);
6901
+ checkPerCert(cert, context = {}) {
6902
+ const now = (this.opts.now ?? (() => /* @__PURE__ */ new Date()))();
6903
+ this.checkValidity(cert, now, "");
6904
+ if (context.issuedAt) {
6905
+ this.checkValidity(cert, context.issuedAt, ` at issuance time ${context.issuedAt.toISOString()}`);
6906
+ }
6782
6907
  this.checkAlgorithm(cert);
6783
6908
  }
6784
- checkValidity(cert) {
6785
- const now = (this.opts.now ?? (() => /* @__PURE__ */ new Date()))().getTime();
6909
+ checkValidity(cert, at, label) {
6910
+ const time = at.getTime();
6786
6911
  const skewMs = (this.opts.clockSkewTolerance ?? 60) * 1e3;
6787
6912
  const notBefore = cert.notBefore.getTime();
6788
6913
  const notAfter = cert.notAfter.getTime();
6789
- if (now + skewMs < notBefore) {
6914
+ if (time + skewMs < notBefore) {
6790
6915
  throw new exports.CertificateChainError(
6791
- `certificate ${cert.subject} not yet valid (notBefore=${cert.notBefore.toISOString()})`,
6916
+ `certificate ${cert.subject} not yet valid${label} (notBefore=${cert.notBefore.toISOString()})`,
6792
6917
  { reason: "validity" }
6793
6918
  );
6794
6919
  }
6795
- if (now - skewMs > notAfter) {
6920
+ if (time - skewMs > notAfter) {
6796
6921
  throw new exports.CertificateChainError(
6797
- `certificate ${cert.subject} expired (notAfter=${cert.notAfter.toISOString()})`,
6922
+ `certificate ${cert.subject} expired${label} (notAfter=${cert.notAfter.toISOString()})`,
6798
6923
  { reason: "validity" }
6799
6924
  );
6800
6925
  }
@@ -6841,6 +6966,349 @@ var init_ecdsa_util = __esm({
6841
6966
  }
6842
6967
  });
6843
6968
 
6969
+ // src/http/ip-address.ts
6970
+ function parseIPv4(input) {
6971
+ const parts = input.split(".");
6972
+ if (parts.length !== 4) return void 0;
6973
+ const bytes = [];
6974
+ for (const part of parts) {
6975
+ if (!/^\d{1,3}$/.test(part)) return void 0;
6976
+ const value = Number(part);
6977
+ if (value > 255) return void 0;
6978
+ bytes.push(value);
6979
+ }
6980
+ return bytes;
6981
+ }
6982
+ function parseIPv6(input) {
6983
+ const address = input.split("%")[0];
6984
+ if (!address.includes(":")) return void 0;
6985
+ let head = address;
6986
+ let embeddedV4;
6987
+ const lastColon = address.lastIndexOf(":");
6988
+ const tail = address.slice(lastColon + 1);
6989
+ if (tail.includes(".")) {
6990
+ embeddedV4 = parseIPv4(tail);
6991
+ if (!embeddedV4) return void 0;
6992
+ head = address.slice(0, lastColon + 1) + "0:0";
6993
+ }
6994
+ const doubleColon = head.split("::");
6995
+ if (doubleColon.length > 2) return void 0;
6996
+ const toGroups = (s) => s === "" ? [] : s.split(":");
6997
+ const left = toGroups(doubleColon[0]);
6998
+ const right = doubleColon.length === 2 ? toGroups(doubleColon[1]) : [];
6999
+ const missing = 8 - left.length - right.length;
7000
+ if (doubleColon.length === 2 ? missing < 1 : missing !== 0) return void 0;
7001
+ const groups = [...left, ...Array(doubleColon.length === 2 ? missing : 0).fill("0"), ...right];
7002
+ const bytes = [];
7003
+ for (const group of groups) {
7004
+ if (!/^[0-9a-fA-F]{1,4}$/.test(group)) return void 0;
7005
+ const value = parseInt(group, 16);
7006
+ bytes.push(value >> 8, value & 255);
7007
+ }
7008
+ if (embeddedV4) bytes.splice(12, 4, ...embeddedV4);
7009
+ return bytes;
7010
+ }
7011
+ function inPrefix(bytes, prefix, bits) {
7012
+ for (let i = 0; i < bits; i++) {
7013
+ const byte = i >> 3;
7014
+ const mask = 128 >> (i & 7);
7015
+ if ((bytes[byte] & mask) !== ((prefix[byte] ?? 0) & mask)) return false;
7016
+ }
7017
+ return true;
7018
+ }
7019
+ function isPublicV4(bytes) {
7020
+ return !BLOCKED_V4.some(([prefix, bits]) => inPrefix(bytes, prefix, bits));
7021
+ }
7022
+ function isPublicV6(bytes) {
7023
+ const first10Zero = bytes.slice(0, 10).every((b) => b === 0);
7024
+ if (first10Zero && bytes[10] === 0 && bytes[11] === 0) {
7025
+ const v4 = bytes.slice(12);
7026
+ if (v4.slice(0, 3).every((b) => b === 0)) return false;
7027
+ return isPublicV4(v4);
7028
+ }
7029
+ if (first10Zero && bytes[10] === 255 && bytes[11] === 255) return isPublicV4(bytes.slice(12));
7030
+ if (inPrefix(bytes, [0, 100, 255, 155], 96)) return isPublicV4(bytes.slice(12));
7031
+ if (inPrefix(bytes, [32, 2], 16)) return isPublicV4(bytes.slice(2, 6));
7032
+ return !BLOCKED_V6.some(([prefix, bits]) => inPrefix(bytes, prefix, bits));
7033
+ }
7034
+ function isPublicIpAddress(ip) {
7035
+ const v4 = parseIPv4(ip);
7036
+ if (v4) return isPublicV4(v4);
7037
+ const v6 = parseIPv6(ip);
7038
+ if (v6) return isPublicV6(v6);
7039
+ return false;
7040
+ }
7041
+ function isIpLiteral(host) {
7042
+ return parseIPv4(host) !== void 0 || parseIPv6(host) !== void 0;
7043
+ }
7044
+ var BLOCKED_V4, BLOCKED_V6;
7045
+ var init_ip_address = __esm({
7046
+ "src/http/ip-address.ts"() {
7047
+ init_reflect_polyfill();
7048
+ BLOCKED_V4 = [
7049
+ [[0, 0, 0, 0], 8],
7050
+ [[10, 0, 0, 0], 8],
7051
+ [[100, 64, 0, 0], 10],
7052
+ [[127, 0, 0, 0], 8],
7053
+ [[169, 254, 0, 0], 16],
7054
+ [[172, 16, 0, 0], 12],
7055
+ [[192, 0, 0, 0], 24],
7056
+ [[192, 0, 2, 0], 24],
7057
+ [[192, 88, 99, 0], 24],
7058
+ [[192, 168, 0, 0], 16],
7059
+ [[198, 18, 0, 0], 15],
7060
+ [[198, 51, 100, 0], 24],
7061
+ [[203, 0, 113, 0], 24],
7062
+ [[224, 0, 0, 0], 4],
7063
+ [[240, 0, 0, 0], 4]
7064
+ ];
7065
+ BLOCKED_V6 = [
7066
+ [[1, 0], 64],
7067
+ // 100::/64 discard-only
7068
+ [[32, 1, 0, 0], 32],
7069
+ // 2001::/32 Teredo (embeds an obfuscated IPv4)
7070
+ [[32, 1, 13, 184], 32],
7071
+ // 2001:db8::/32 documentation
7072
+ [[252], 7],
7073
+ // fc00::/7 unique local (incl. fd00:ec2::254 metadata)
7074
+ [[254, 128], 10],
7075
+ // fe80::/10 link-local
7076
+ [[254, 192], 10],
7077
+ // fec0::/10 deprecated site-local
7078
+ [[255], 8]
7079
+ // ff00::/8 multicast
7080
+ ];
7081
+ }
7082
+ });
7083
+
7084
+ // src/http/guarded-fetch.ts
7085
+ function loadDns() {
7086
+ dnsModule ??= (async () => {
7087
+ try {
7088
+ const specifier = "node:dns";
7089
+ const mod = await import(
7090
+ /* webpackIgnore: true */
7091
+ /* @vite-ignore */
7092
+ specifier
7093
+ );
7094
+ return mod.promises ?? mod.default?.promises;
7095
+ } catch {
7096
+ return void 0;
7097
+ }
7098
+ })();
7099
+ return dnsModule;
7100
+ }
7101
+ function toError(err) {
7102
+ return err instanceof Error ? err : new Error(String(err));
7103
+ }
7104
+ function createGuardedFetcher(options = {}) {
7105
+ const allowHttp = options.allowHttp ?? false;
7106
+ const allowPrivate = options.allowPrivateNetworks ?? false;
7107
+ const maxRedirects = options.maxRedirects ?? exports.GUARDED_FETCH_DEFAULTS.maxRedirects;
7108
+ const timeoutMs = options.timeoutMs ?? exports.GUARDED_FETCH_DEFAULTS.timeoutMs;
7109
+ const maxBytes = options.maxResponseBytes ?? exports.GUARDED_FETCH_DEFAULTS.maxResponseBytes;
7110
+ const lookup = options.lookup ?? defaultLookup;
7111
+ const transport = options.fetch ?? ((url, init) => globalThis.fetch(url, init));
7112
+ return async (input, init = {}) => {
7113
+ const controller = new AbortController();
7114
+ const timer = setTimeout(() => {
7115
+ controller.abort(
7116
+ new exports.GuardedFetchError("timeout", `request to ${input} exceeded ${timeoutMs} ms`, { url: input })
7117
+ );
7118
+ }, timeoutMs);
7119
+ const callerSignal = init.signal ?? void 0;
7120
+ const onCallerAbort = () => controller.abort(callerSignal?.reason);
7121
+ if (callerSignal?.aborted) onCallerAbort();
7122
+ else callerSignal?.addEventListener("abort", onCallerAbort, { once: true });
7123
+ const aborted = new Promise((_, reject) => {
7124
+ const fail = () => reject(toError(controller.signal.reason));
7125
+ if (controller.signal.aborted) fail();
7126
+ else controller.signal.addEventListener("abort", fail, { once: true });
7127
+ });
7128
+ aborted.catch(() => {
7129
+ });
7130
+ const race = (p) => Promise.race([p, aborted]);
7131
+ try {
7132
+ let url = await race(validateTarget(input, void 0));
7133
+ let method = (init.method ?? "GET").toUpperCase();
7134
+ let body = init.body;
7135
+ const headers = new Headers(init.headers);
7136
+ for (let hop = 0; ; hop++) {
7137
+ const response = await race(
7138
+ transport(url.href, { ...init, method, body, headers, redirect: "manual", signal: controller.signal })
7139
+ );
7140
+ if (response.type === "opaqueredirect") {
7141
+ throw new exports.GuardedFetchError(
7142
+ "invalid_redirect",
7143
+ `redirect from ${url.href} cannot be inspected by this transport`,
7144
+ { url: url.href }
7145
+ );
7146
+ }
7147
+ if (!REDIRECT_STATUSES.has(response.status)) {
7148
+ const bytes = await readBounded(response, url.href, maxBytes, race);
7149
+ return new Response(NULL_BODY_STATUSES.has(response.status) ? null : bytes, {
7150
+ status: response.status,
7151
+ statusText: response.statusText,
7152
+ headers: response.headers
7153
+ });
7154
+ }
7155
+ await response.body?.cancel().catch(() => {
7156
+ });
7157
+ if (hop >= maxRedirects) {
7158
+ throw new exports.GuardedFetchError(
7159
+ "too_many_redirects",
7160
+ `more than ${maxRedirects} redirects starting at ${input}`,
7161
+ { url: url.href }
7162
+ );
7163
+ }
7164
+ const location = response.headers.get("location");
7165
+ if (!location) {
7166
+ throw new exports.GuardedFetchError(
7167
+ "invalid_redirect",
7168
+ `HTTP ${response.status} from ${url.href} has no Location header`,
7169
+ { url: url.href }
7170
+ );
7171
+ }
7172
+ let next;
7173
+ try {
7174
+ next = new URL(location, url);
7175
+ } catch (err) {
7176
+ throw new exports.GuardedFetchError("invalid_redirect", `unparseable Location from ${url.href}`, {
7177
+ url: url.href,
7178
+ cause: toError(err)
7179
+ });
7180
+ }
7181
+ const nextUrl = await race(validateTarget(next.href, url));
7182
+ if (response.status === 303 || (response.status === 301 || response.status === 302) && method === "POST") {
7183
+ if (method !== "HEAD") method = "GET";
7184
+ body = void 0;
7185
+ for (const h of BODY_HEADERS) headers.delete(h);
7186
+ }
7187
+ if (nextUrl.origin !== url.origin) {
7188
+ for (const h of CROSS_ORIGIN_STRIPPED_HEADERS) headers.delete(h);
7189
+ }
7190
+ url = nextUrl;
7191
+ }
7192
+ } catch (err) {
7193
+ if (controller.signal.aborted) throw toError(controller.signal.reason);
7194
+ throw err;
7195
+ } finally {
7196
+ clearTimeout(timer);
7197
+ callerSignal?.removeEventListener("abort", onCallerAbort);
7198
+ }
7199
+ };
7200
+ async function validateTarget(raw, previous) {
7201
+ let url;
7202
+ try {
7203
+ url = new URL(raw);
7204
+ } catch (err) {
7205
+ throw new exports.GuardedFetchError("invalid_url", `not an absolute URL: ${raw}`, { url: raw, cause: toError(err) });
7206
+ }
7207
+ if (url.protocol !== "https:" && !(allowHttp && url.protocol === "http:")) {
7208
+ throw new exports.GuardedFetchError("insecure_scheme", `refusing ${url.protocol} URL ${url.href}: HTTPS is required`, {
7209
+ url: url.href
7210
+ });
7211
+ }
7212
+ if (previous?.protocol === "https:" && url.protocol !== "https:") {
7213
+ throw new exports.GuardedFetchError("insecure_scheme", `refusing https -> http redirect to ${url.href}`, {
7214
+ url: url.href
7215
+ });
7216
+ }
7217
+ if (allowPrivate) return url;
7218
+ const host = url.hostname.replace(/^\[|\]$/g, "").toLowerCase();
7219
+ if (host === "localhost" || host.endsWith(".localhost")) {
7220
+ throw new exports.GuardedFetchError("private_address", `refusing loopback host ${host}`, { url: url.href });
7221
+ }
7222
+ if (isIpLiteral(host)) {
7223
+ if (!isPublicIpAddress(host)) {
7224
+ throw new exports.GuardedFetchError("private_address", `refusing non-public address ${host}`, { url: url.href });
7225
+ }
7226
+ return url;
7227
+ }
7228
+ let addresses;
7229
+ try {
7230
+ addresses = await lookup(host);
7231
+ } catch (err) {
7232
+ throw new exports.GuardedFetchError("dns_resolution_failed", `could not resolve ${host}`, {
7233
+ url: url.href,
7234
+ cause: toError(err)
7235
+ });
7236
+ }
7237
+ if (addresses === void 0) return url;
7238
+ if (addresses.length === 0) {
7239
+ throw new exports.GuardedFetchError("dns_resolution_failed", `${host} resolved to no addresses`, {
7240
+ url: url.href
7241
+ });
7242
+ }
7243
+ const blocked = addresses.find((a) => !isPublicIpAddress(a));
7244
+ if (blocked !== void 0) {
7245
+ throw new exports.GuardedFetchError("private_address", `${host} resolves to non-public address ${blocked}`, {
7246
+ url: url.href
7247
+ });
7248
+ }
7249
+ return url;
7250
+ }
7251
+ }
7252
+ async function readBounded(response, url, maxBytes, race) {
7253
+ const tooLarge = () => new exports.GuardedFetchError("response_too_large", `response from ${url} exceeds ${maxBytes} bytes`, { url });
7254
+ const declared = response.headers.get("content-length");
7255
+ if (declared !== null && Number(declared) > maxBytes) {
7256
+ await response.body?.cancel().catch(() => {
7257
+ });
7258
+ throw tooLarge();
7259
+ }
7260
+ if (!response.body) return null;
7261
+ const reader = response.body.getReader();
7262
+ const chunks = [];
7263
+ let total = 0;
7264
+ try {
7265
+ for (; ; ) {
7266
+ const { done, value } = await race(reader.read());
7267
+ if (done) break;
7268
+ total += value.byteLength;
7269
+ if (total > maxBytes) throw tooLarge();
7270
+ chunks.push(value);
7271
+ }
7272
+ } catch (err) {
7273
+ reader.cancel().catch(() => {
7274
+ });
7275
+ throw err;
7276
+ }
7277
+ const out = new Uint8Array(new ArrayBuffer(total));
7278
+ let offset = 0;
7279
+ for (const chunk of chunks) {
7280
+ out.set(chunk, offset);
7281
+ offset += chunk.byteLength;
7282
+ }
7283
+ return out;
7284
+ }
7285
+ function createDefaultTrustFetcher() {
7286
+ return createGuardedFetcher({ allowHttp: true });
7287
+ }
7288
+ exports.GUARDED_FETCH_DEFAULTS = void 0; var REDIRECT_STATUSES, NULL_BODY_STATUSES, CROSS_ORIGIN_STRIPPED_HEADERS, BODY_HEADERS, dnsModule, defaultLookup;
7289
+ var init_guarded_fetch = __esm({
7290
+ "src/http/guarded-fetch.ts"() {
7291
+ init_reflect_polyfill();
7292
+ init_errors();
7293
+ init_ip_address();
7294
+ exports.GUARDED_FETCH_DEFAULTS = {
7295
+ maxRedirects: 3,
7296
+ timeoutMs: 15e3,
7297
+ maxResponseBytes: 16 * 1024 * 1024
7298
+ };
7299
+ REDIRECT_STATUSES = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
7300
+ NULL_BODY_STATUSES = /* @__PURE__ */ new Set([101, 103, 204, 205, 304]);
7301
+ CROSS_ORIGIN_STRIPPED_HEADERS = ["authorization", "cookie", "proxy-authorization"];
7302
+ BODY_HEADERS = ["content-type", "content-length", "content-encoding", "content-language", "content-location"];
7303
+ defaultLookup = async (hostname) => {
7304
+ const dns = await loadDns();
7305
+ if (!dns) return void 0;
7306
+ const records = await dns.lookup(hostname, { all: true, verbatim: true });
7307
+ return records.map((r) => r.address);
7308
+ };
7309
+ }
7310
+ });
7311
+
6844
7312
  // src/trust/CrlFetcher.ts
6845
7313
  function normalizeCrl(parsed, der) {
6846
7314
  const thisUpdate = parsed.tbsCertList.thisUpdate.getTime();
@@ -6876,11 +7344,12 @@ var init_CrlFetcher = __esm({
6876
7344
  init_es2015();
6877
7345
  init_es20152();
6878
7346
  init_ecdsa_util();
7347
+ init_guarded_fetch();
6879
7348
  CrlFetcher = class {
6880
7349
  fetcher;
6881
7350
  cache;
6882
7351
  constructor(opts = {}) {
6883
- this.fetcher = opts.fetcher ?? globalThis.fetch.bind(globalThis);
7352
+ this.fetcher = opts.fetcher ?? createDefaultTrustFetcher();
6884
7353
  this.cache = opts.cache;
6885
7354
  }
6886
7355
  async fetchAndParse(url) {
@@ -7612,12 +8081,13 @@ var init_OcspClient = __esm({
7612
8081
  init_es20152();
7613
8082
  init_ChainBuilder();
7614
8083
  init_ecdsa_util();
8084
+ init_guarded_fetch();
7615
8085
  DEFAULT_MAX_RESPONSE_AGE_MS = 7 * 24 * 3600 * 1e3;
7616
8086
  OcspClient = class {
7617
8087
  fetcher;
7618
8088
  cache;
7619
8089
  constructor(opts = {}) {
7620
- this.fetcher = opts.fetcher ?? globalThis.fetch.bind(globalThis);
8090
+ this.fetcher = opts.fetcher ?? createDefaultTrustFetcher();
7621
8091
  this.cache = opts.cache;
7622
8092
  }
7623
8093
  /**
@@ -8083,7 +8553,9 @@ var init_ProvenanceResolver = __esm({
8083
8553
  // src/trust/TrustEvaluator.ts
8084
8554
  var TrustEvaluator_exports = {};
8085
8555
  __export(TrustEvaluator_exports, {
8086
- TrustEvaluator: () => TrustEvaluator
8556
+ MAX_CREDENTIAL_CERTIFICATES: () => MAX_CREDENTIAL_CERTIFICATES,
8557
+ TrustEvaluator: () => TrustEvaluator,
8558
+ evaluateCredentialIssuer: () => evaluateCredentialIssuer
8087
8559
  });
8088
8560
  function deriveHint(leaf) {
8089
8561
  const aki = leaf.getExtension(x509.AuthorityKeyIdentifierExtension);
@@ -8094,6 +8566,13 @@ function deriveHint(leaf) {
8094
8566
  aki: akiBytes
8095
8567
  };
8096
8568
  }
8569
+ function deriveSelfHint(cert) {
8570
+ const skiHex = getSkiHex(cert);
8571
+ return {
8572
+ issuer: cert.subject,
8573
+ aki: skiHex ? hexToBytes(skiHex) : void 0
8574
+ };
8575
+ }
8097
8576
  function hexToBytes(s) {
8098
8577
  const clean = s.replace(/[^0-9a-f]/gi, "");
8099
8578
  const out = new Uint8Array(clean.length / 2);
@@ -8125,7 +8604,31 @@ async function resolveProvenance(store, anchor) {
8125
8604
  return void 0;
8126
8605
  }
8127
8606
  }
8128
- var TrustEvaluator;
8607
+ async function evaluateCredentialIssuer(input) {
8608
+ const [signerDer, ...rest] = input.chain;
8609
+ if (!signerDer) {
8610
+ throw new exports.MalformedCredentialError("Missing issuer certificate");
8611
+ }
8612
+ if (input.chain.length > MAX_CREDENTIAL_CERTIFICATES) {
8613
+ throw new exports.MalformedCredentialError(
8614
+ `x5c/x5chain carries ${input.chain.length} certificates; at most ${MAX_CREDENTIAL_CERTIFICATES} are accepted`
8615
+ );
8616
+ }
8617
+ const parse = (bytes, position) => {
8618
+ try {
8619
+ return new x509.X509Certificate(bytes);
8620
+ } catch (err) {
8621
+ throw new exports.MalformedCredentialError(
8622
+ `Invalid certificate at x5c/x5chain position ${position}: ${err.message}`
8623
+ );
8624
+ }
8625
+ };
8626
+ const leaf = parse(signerDer, 0);
8627
+ const intermediates = rest.map((bytes, i) => parse(bytes, i + 1));
8628
+ const { chain, issuedAt, ...evaluatorOptions } = input;
8629
+ return new TrustEvaluator(evaluatorOptions).evaluate(leaf, { intermediates, issuedAt });
8630
+ }
8631
+ var TrustEvaluator, MAX_CREDENTIAL_CERTIFICATES;
8129
8632
  var init_TrustEvaluator = __esm({
8130
8633
  "src/trust/TrustEvaluator.ts"() {
8131
8634
  init_reflect_polyfill();
@@ -8151,13 +8654,10 @@ var init_TrustEvaluator = __esm({
8151
8654
  revocationChecker;
8152
8655
  async evaluate(leaf, context = {}) {
8153
8656
  const hint = deriveHint(leaf);
8154
- const seen = /* @__PURE__ */ new Set();
8155
8657
  const anchors = [];
8156
8658
  const pushUnique = (batch) => {
8157
8659
  for (const a of batch) {
8158
- const key = a.certificate.serialNumber;
8159
- if (seen.has(key)) continue;
8160
- seen.add(key);
8660
+ if (anchors.some((x) => certificatesEqual(x.certificate, a.certificate))) continue;
8161
8661
  anchors.push(a);
8162
8662
  }
8163
8663
  };
@@ -8165,6 +8665,7 @@ var init_TrustEvaluator = __esm({
8165
8665
  for (const inter of context.intermediates ?? []) {
8166
8666
  pushUnique(await this.trustStore.getAnchors(deriveHint(inter)));
8167
8667
  }
8668
+ pushUnique(await this.trustStore.getAnchors(deriveSelfHint(leaf)));
8168
8669
  if (anchors.length === 0) {
8169
8670
  throw new exports.TrustAnchorNotFoundError(
8170
8671
  `no trust anchor for issuer=${hint.issuer ?? "(none)"}`
@@ -8174,7 +8675,8 @@ var init_TrustEvaluator = __esm({
8174
8675
  const chain = await this.chainBuilder.build(
8175
8676
  leaf,
8176
8677
  anchorCerts,
8177
- context.intermediates ?? []
8678
+ context.intermediates ?? [],
8679
+ { issuedAt: context.issuedAt }
8178
8680
  );
8179
8681
  const terminusCert = chain[chain.length - 1];
8180
8682
  const anchor = anchors.find(
@@ -8214,6 +8716,7 @@ var init_TrustEvaluator = __esm({
8214
8716
  };
8215
8717
  }
8216
8718
  };
8719
+ MAX_CREDENTIAL_CERTIFICATES = 10;
8217
8720
  }
8218
8721
  });
8219
8722
 
@@ -9005,6 +9508,14 @@ function bytesEqual3(a, b) {
9005
9508
  }
9006
9509
  return true;
9007
9510
  }
9511
+ function peekMsoSigned(payload) {
9512
+ try {
9513
+ const signed = decodeMso(payload).validityInfo.signed;
9514
+ return Number.isFinite(signed.getTime()) ? signed : void 0;
9515
+ } catch {
9516
+ return void 0;
9517
+ }
9518
+ }
9008
9519
  function derToPem(der) {
9009
9520
  let binary = "";
9010
9521
  for (const b of der) binary += String.fromCharCode(b);
@@ -9165,17 +9676,17 @@ var MdocParser = class {
9165
9676
  }
9166
9677
  let trustResult;
9167
9678
  if (options.trustStore) {
9168
- const { TrustEvaluator: TrustEvaluator2 } = await Promise.resolve().then(() => (init_TrustEvaluator(), TrustEvaluator_exports));
9169
- const evaluator = new TrustEvaluator2({
9679
+ const { evaluateCredentialIssuer: evaluateCredentialIssuer2 } = await Promise.resolve().then(() => (init_TrustEvaluator(), TrustEvaluator_exports));
9680
+ trustResult = await evaluateCredentialIssuer2({
9681
+ chain: cose.x5chain,
9682
+ profile: "iso18013-5",
9683
+ issuedAt: peekMsoSigned(cose.payload),
9170
9684
  trustStore: options.trustStore,
9171
9685
  revocationPolicy: options.revocationPolicy ?? "skip",
9172
9686
  fetcher: options.fetcher,
9173
9687
  cache: options.cache,
9174
9688
  clockSkewTolerance: options.clockSkewTolerance
9175
9689
  });
9176
- const { X509Certificate: X509Certificate9 } = await import('@peculiar/x509');
9177
- const leaf = new X509Certificate9(issuerCertBytes);
9178
- trustResult = await evaluator.evaluate(leaf);
9179
9690
  } else {
9180
9691
  if (options.trustedCertificates.length === 0) {
9181
9692
  if (options.skipTrustCheck !== true) {
@@ -9324,6 +9835,13 @@ var MdocParser = class {
9324
9835
  init_reflect_polyfill();
9325
9836
  init_errors();
9326
9837
  var DEFAULT_ALLOWED_ALGORITHMS2 = ["ES256", "ES384", "ES512"];
9838
+ var SD_JWT_VC_TYP = "dc+sd-jwt";
9839
+ var LEGACY_SD_JWT_VC_TYP = "vc+sd-jwt";
9840
+ function normalizeTyp(typ) {
9841
+ if (typeof typ !== "string") return void 0;
9842
+ const lower = typ.toLowerCase();
9843
+ return lower.startsWith("application/") ? lower.slice("application/".length) : lower;
9844
+ }
9327
9845
  var sha256Hasher = async (data, _alg) => {
9328
9846
  const encoder2 = new TextEncoder();
9329
9847
  const input = typeof data === "string" ? encoder2.encode(data) : data;
@@ -9436,6 +9954,18 @@ var SdJwtParser = class {
9436
9954
  if (typeof alg !== "string" || !allowedAlgorithms.includes(alg)) {
9437
9955
  return invalidResult(`Unsupported algorithm: ${alg}`);
9438
9956
  }
9957
+ const typ = normalizeTyp(header.typ);
9958
+ if (typ === LEGACY_SD_JWT_VC_TYP) {
9959
+ if (options.allowLegacyVcSdJwtTyp !== true) {
9960
+ return invalidResult(
9961
+ `Legacy SD-JWT VC typ '${LEGACY_SD_JWT_VC_TYP}' is not accepted (removed in draft-ietf-oauth-sd-jwt-vc-19; expected '${SD_JWT_VC_TYP}'). Set allowLegacyVcSdJwtTyp to accept it during migration.`
9962
+ );
9963
+ }
9964
+ } else if (typ !== SD_JWT_VC_TYP) {
9965
+ return invalidResult(
9966
+ `Unsupported SD-JWT VC typ: ${header.typ === void 0 ? "(missing)" : String(header.typ)} (expected '${SD_JWT_VC_TYP}')`
9967
+ );
9968
+ }
9439
9969
  const x5cArray = header.x5c;
9440
9970
  const hasX5c = Array.isArray(x5cArray) && x5cArray.length > 0 && typeof x5cArray[0] === "string";
9441
9971
  let issuerKey;
@@ -9505,17 +10035,22 @@ var SdJwtParser = class {
9505
10035
  };
9506
10036
  let trustResult;
9507
10037
  if (issuerCertBytes === void 0) ; else if (options.trustStore) {
9508
- const { TrustEvaluator: TrustEvaluator2 } = await Promise.resolve().then(() => (init_TrustEvaluator(), TrustEvaluator_exports));
9509
- const evaluator = new TrustEvaluator2({
10038
+ const { evaluateCredentialIssuer: evaluateCredentialIssuer2 } = await Promise.resolve().then(() => (init_TrustEvaluator(), TrustEvaluator_exports));
10039
+ const x5cRest = x5cArray.slice(1);
10040
+ if (!x5cRest.every((entry) => typeof entry === "string")) {
10041
+ throw new exports.MalformedCredentialError("x5c entries must be base64 strings");
10042
+ }
10043
+ const iat = payload["iat"];
10044
+ trustResult = await evaluateCredentialIssuer2({
10045
+ chain: [issuerCertBytes, ...x5cRest.map((entry) => base64ToBytes(entry))],
10046
+ profile: "generic",
10047
+ issuedAt: typeof iat === "number" && Number.isFinite(iat) ? new Date(iat * 1e3) : void 0,
9510
10048
  trustStore: options.trustStore,
9511
10049
  revocationPolicy: options.revocationPolicy ?? "skip",
9512
10050
  fetcher: options.fetcher,
9513
10051
  cache: options.cache,
9514
10052
  clockSkewTolerance: options.clockSkewTolerance
9515
10053
  });
9516
- const { X509Certificate: X509Certificate9 } = await import('@peculiar/x509');
9517
- const leaf = new X509Certificate9(issuerCertBytes);
9518
- trustResult = await evaluator.evaluate(leaf);
9519
10054
  } else {
9520
10055
  if (options.trustedCertificates.length === 0) {
9521
10056
  if (options.skipTrustCheck !== true) {
@@ -9951,6 +10486,9 @@ var InMemoryCache = class {
9951
10486
  }
9952
10487
  };
9953
10488
 
10489
+ // src/index.ts
10490
+ init_guarded_fetch();
10491
+
9954
10492
  // src/trust/index.ts
9955
10493
  init_reflect_polyfill();
9956
10494
 
@@ -9998,14 +10536,10 @@ var CompositeTrustStore = class {
9998
10536
  const results = await Promise.all(
9999
10537
  this.stores.map((store) => store.getAnchors(hint))
10000
10538
  );
10001
- const seen = /* @__PURE__ */ new Set();
10002
10539
  const out = [];
10003
10540
  for (const batch of results) {
10004
10541
  for (const anchor of batch) {
10005
- const ski = getSkiHex(anchor.certificate);
10006
- const key = ski ?? anchor.certificate.serialNumber;
10007
- if (seen.has(key)) continue;
10008
- seen.add(key);
10542
+ if (out.some((a) => certificatesEqual(a.certificate, anchor.certificate))) continue;
10009
10543
  out.push(anchor);
10010
10544
  }
10011
10545
  }
@@ -10099,11 +10633,12 @@ var EU_LOTL_SIGNING_ANCHORS = Object.freeze(
10099
10633
  // src/trust/LotlFetcher.ts
10100
10634
  init_reflect_polyfill();
10101
10635
  init_errors();
10636
+ init_guarded_fetch();
10102
10637
  var XMLDSIG_NS = "http://www.w3.org/2000/09/xmldsig#";
10103
10638
  var LotlFetcher = class {
10104
10639
  fetcher;
10105
10640
  constructor(opts = {}) {
10106
- this.fetcher = opts.fetcher ?? globalThis.fetch.bind(globalThis);
10641
+ this.fetcher = opts.fetcher ?? createDefaultTrustFetcher();
10107
10642
  }
10108
10643
  async fetchSigned(url, signingAnchors) {
10109
10644
  assertCryptoEngineRegistered();
@@ -10565,7 +11100,7 @@ function equalBytes(a, b) {
10565
11100
 
10566
11101
  // src/index.ts
10567
11102
  init_errors();
10568
- var VERSION = "0.12.0";
11103
+ var VERSION = "0.13.0";
10569
11104
  /*! Bundled license information:
10570
11105
 
10571
11106
  pvtsutils/build/index.es.js:
@@ -10656,6 +11191,7 @@ exports.buildHaipQuery = buildHaipQuery;
10656
11191
  exports.buildOid4vpSessionTranscript = buildOid4vpSessionTranscript;
10657
11192
  exports.buildOpenID4VPHandoverSessionTranscript = buildOpenID4VPHandoverSessionTranscript;
10658
11193
  exports.createAuthorizationRequest = createAuthorizationRequest;
11194
+ exports.createGuardedFetcher = createGuardedFetcher;
10659
11195
  exports.createSignedAuthorizationRequest = createSignedAuthorizationRequest;
10660
11196
  exports.decryptAuthorizationResponse = decryptAuthorizationResponse;
10661
11197
  exports.isHaipQuery = isHaipQuery;