@fedify/fedify 2.4.0-dev.2256 → 2.4.0-dev.2259

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 (105) hide show
  1. package/dist/{builder-DvSQqk2l.mjs → builder-CBNa1Uw2.mjs} +8 -2
  2. package/dist/compat/mod.d.cts +1 -1
  3. package/dist/compat/mod.d.ts +1 -1
  4. package/dist/compat/transformers.test.mjs +1 -1
  5. package/dist/{compound-proof-5bg_j_ye.mjs → compound-proof-CMPv4UK0.mjs} +20 -3
  6. package/dist/{context-CtI307_A.d.ts → context-7NyiqIcg.d.ts} +111 -2
  7. package/dist/{context-B6X-t8gI.d.cts → context-tsYPJ2Ew.d.cts} +111 -2
  8. package/dist/{deno-AmcRMq5z.mjs → deno-C8XaXmPB.mjs} +1 -1
  9. package/dist/{docloader-CIP_SG39.mjs → docloader-CpdFLwAg.mjs} +2 -2
  10. package/dist/federation/builder.test.mjs +1 -1
  11. package/dist/federation/compound-fanout.test.mjs +3 -3
  12. package/dist/federation/handler-compound-proof.test.mjs +3 -3
  13. package/dist/federation/handler.test.mjs +76 -5
  14. package/dist/federation/hashlink-media.test.mjs +2 -2
  15. package/dist/federation/idempotency.test.mjs +2 -2
  16. package/dist/federation/inbox-report.test.d.mts +2 -0
  17. package/dist/federation/inbox-report.test.mjs +475 -0
  18. package/dist/federation/metrics.test.mjs +1 -1
  19. package/dist/federation/middleware.test.mjs +7 -7
  20. package/dist/federation/mod.cjs +1 -1
  21. package/dist/federation/mod.d.cts +4 -3
  22. package/dist/federation/mod.d.ts +4 -3
  23. package/dist/federation/mod.js +1 -1
  24. package/dist/federation/outgoing-proof.test.mjs +5 -5
  25. package/dist/federation/portable-actor.test.mjs +3 -3
  26. package/dist/federation/portable-chain.test.mjs +3 -3
  27. package/dist/federation/portable-collection.test.mjs +4 -4
  28. package/dist/federation/portable-delivery.test.mjs +2 -2
  29. package/dist/federation/portable-gateway-keys.test.mjs +4 -4
  30. package/dist/federation/portable-inbox.test.mjs +67 -6
  31. package/dist/federation/portable-parse-uri.test.mjs +1 -1
  32. package/dist/federation/portable-route-activity.test.mjs +2 -2
  33. package/dist/federation/portable.test.mjs +3 -3
  34. package/dist/federation/send.test.mjs +3 -3
  35. package/dist/federation/tasks/enqueue.test.mjs +1 -1
  36. package/dist/federation/tasks/tasks.test.mjs +2 -2
  37. package/dist/federation/temporal.test.mjs +1 -1
  38. package/dist/federation/webfinger.test.mjs +1 -1
  39. package/dist/{federation-BDzIgqOd.cjs → federation-D6giSGUT.cjs} +524 -118
  40. package/dist/{http-DkqWu-sm.mjs → http-BqSbUEiQ.mjs} +141 -98
  41. package/dist/{http-1fZ83jj7.cjs → http-Cki-npuA.cjs} +447 -110
  42. package/dist/{http-BGb3QioZ.js → http-OMtKlbx1.js} +430 -111
  43. package/dist/{http-BrwKINQn.d.cts → key-C1nv7_jD.d.ts} +638 -229
  44. package/dist/{http-DGx0KOws.d.ts → key-DYxuyd-s.d.cts} +638 -229
  45. package/dist/{key-CEsu7oaD.mjs → key-DecAtABm.mjs} +30 -3
  46. package/dist/{kv-cache-CJS4mImS.mjs → kv-cache-CWWlMv-s.mjs} +1 -1
  47. package/dist/{kv-cache-BEWuZSB4.cjs → kv-cache-D73yVcE5.cjs} +1 -1
  48. package/dist/{kv-cache-CGhEgt98.js → kv-cache-J0v3UbUn.js} +1 -1
  49. package/dist/{ld-Cf4CPWN_.mjs → ld-UsN-TIPh.mjs} +40 -5
  50. package/dist/{metrics-CocsZP0E.mjs → metrics-DWCn4VXX.mjs} +2 -1
  51. package/dist/{middleware-BfYke3YQ.js → middleware-BqzfIlUO.js} +524 -118
  52. package/dist/{middleware-Dxn0E9ES.mjs → middleware-CkjwiEBd.mjs} +512 -127
  53. package/dist/{middleware-Cne_jMZo.mjs → middleware-DHn2q0L1.mjs} +1 -1
  54. package/dist/{mod-C7_2j7VT.d.cts → mod-BojP2Bw3.d.cts} +1 -1
  55. package/dist/{mod-BlLwewYe.d.ts → mod-CavhAgLw.d.cts} +5 -255
  56. package/dist/{mod-VHYh1lTK.d.cts → mod-CtpF2BU5.d.ts} +5 -255
  57. package/dist/{mod-B8MI1mpi.d.ts → mod-DEZXwV6J.d.ts} +1 -1
  58. package/dist/{mod-DFL6IxYQ.d.cts → mod-Dw-nPxQh.d.cts} +1 -1
  59. package/dist/{mod-BhTaDR5H.d.ts → mod-Xj5P4s4d.d.ts} +1 -1
  60. package/dist/mod.cjs +4 -4
  61. package/dist/mod.d.cts +7 -7
  62. package/dist/mod.d.ts +7 -7
  63. package/dist/mod.js +4 -4
  64. package/dist/nodeinfo/handler.test.mjs +1 -1
  65. package/dist/{owner-DSJ7nFgy.mjs → owner-CEBn81LD.mjs} +3 -3
  66. package/dist/{portable-collection-r-mHg1PV.cjs → portable-collection-DqTUBKY7.cjs} +1 -1
  67. package/dist/{portable-collection-BuBV6xTo.js → portable-collection-ZkUNjtb7.js} +1 -1
  68. package/dist/{portable-collection-qskqXJrH.mjs → portable-collection-qFSYw4TG.mjs} +2 -2
  69. package/dist/{portable-key-woGH_1nn.mjs → portable-key-LlMkP-vz.mjs} +1 -1
  70. package/dist/{proof-8WiH_-Gq.mjs → proof-BHRf_jMG.mjs} +133 -16
  71. package/dist/{send-JKzZBgPU.mjs → send-DjG8DDU_.mjs} +3 -3
  72. package/dist/sig/compound-proof-discovery.test.mjs +1 -1
  73. package/dist/sig/compound-proof-producer.test.mjs +1 -1
  74. package/dist/sig/compound-proof-verification.test.mjs +19 -2
  75. package/dist/sig/compound-proof.test.mjs +2 -2
  76. package/dist/sig/http.test.mjs +2 -2
  77. package/dist/sig/key.test.mjs +1 -1
  78. package/dist/sig/ld.test.mjs +2 -2
  79. package/dist/sig/mitra-receive-interop.test.mjs +4 -4
  80. package/dist/sig/mitra-send-interop.test.mjs +3 -3
  81. package/dist/sig/mod.cjs +2 -2
  82. package/dist/sig/mod.d.cts +2 -2
  83. package/dist/sig/mod.d.ts +2 -2
  84. package/dist/sig/mod.js +2 -2
  85. package/dist/sig/owner.test.mjs +1 -1
  86. package/dist/sig/portable-collection.test.mjs +1 -1
  87. package/dist/sig/portable-interop.test.mjs +3 -3
  88. package/dist/sig/portable-key.test.mjs +6 -6
  89. package/dist/sig/proof.test.mjs +76 -3
  90. package/dist/sig/tootik-request-interop.test.mjs +4 -4
  91. package/dist/sig/tootik-send-interop.test.mjs +3 -3
  92. package/dist/sig/verification.test.d.mts +2 -0
  93. package/dist/sig/verification.test.mjs +310 -0
  94. package/dist/{temporal-CT3fRaMH.mjs → temporal-BRFH_dte.mjs} +1 -1
  95. package/dist/testing/kv.test.mjs +1 -1
  96. package/dist/testing/mod.d.mts +315 -28
  97. package/dist/utils/docloader.test.mjs +2 -2
  98. package/dist/utils/kv-cache.test.mjs +1 -1
  99. package/dist/utils/mod.cjs +1 -1
  100. package/dist/utils/mod.d.cts +1 -1
  101. package/dist/utils/mod.d.ts +1 -1
  102. package/dist/utils/mod.js +1 -1
  103. package/dist/verification-C3954hQh.mjs +5 -0
  104. package/dist/verification-Dpfcw8ml.mjs +105 -0
  105. package/package.json +7 -7
@@ -2,12 +2,13 @@ import { Temporal } from "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { m as parseKeyIdString } from "./portable-key-id-Ca30-hOa.mjs";
5
- import { n as version, t as name } from "./deno-AmcRMq5z.mjs";
6
- import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-CocsZP0E.mjs";
5
+ import { a as verificationObservation, i as triedKey, n as observeCheck, t as observeAttempt } from "./verification-Dpfcw8ml.mjs";
6
+ import { n as version, t as name } from "./deno-C8XaXmPB.mjs";
7
+ import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-DWCn4VXX.mjs";
7
8
  import { i as validateAcceptSignature, n as fulfillAcceptSignature, r as parseAcceptSignature } from "./accept-CPVqki78.mjs";
8
9
  import { a as promoteKeyOwnerEvidence } from "./key-owner-evidence-APB6NpSk.mjs";
9
- import { a as fetchKeyDetailed, c as validateCryptoKey, t as bypassKeyCacheReads } from "./key-CEsu7oaD.mjs";
10
- import { n as resolvePortableActorKey, r as verifyPortableGatewayKeyDocument } from "./portable-key-woGH_1nn.mjs";
10
+ import { a as fetchKeyDetailed, c as validateCryptoKey, t as bypassKeyCacheReads } from "./key-DecAtABm.mjs";
11
+ import { n as resolvePortableActorKey, r as verifyPortableGatewayKeyDocument } from "./portable-key-LlMkP-vz.mjs";
11
12
  import { getLogger } from "@logtape/logtape";
12
13
  import { CryptographicKey } from "@fedify/vocab";
13
14
  import { SpanStatusCode, trace } from "@opentelemetry/api";
@@ -478,6 +479,14 @@ async function verifyRequest(request, options = {}) {
478
479
  * @since 2.1.0
479
480
  */
480
481
  async function verifyRequestDetailed(request, options = {}) {
482
+ if (options[verificationObservation] != null && options[verificationObservation]?.attempt == null) return await observeAttempt(options, "http", async (observation) => {
483
+ const result = await verifyRequestDetailed(request, {
484
+ ...options,
485
+ [verificationObservation]: observation
486
+ });
487
+ if (!result.verified) observation.attempt.reason = result.reason.type === "noSignature" ? { type: "noSignature" } : { type: "signatureVerificationFailed" };
488
+ return result;
489
+ }, (result) => result.verified);
481
490
  if (options.maxSignatures !== void 0) validateMaxSignatures(options.maxSignatures, "maxSignatures");
482
491
  return await (options.tracerProvider ?? trace.getTracerProvider()).getTracer(name, version).startActiveSpan("http_signatures.verify", async (span) => {
483
492
  if (span.isRecording()) {
@@ -516,7 +525,27 @@ async function verifyRequestDetailed(request, options = {}) {
516
525
  }
517
526
  });
518
527
  }
519
- async function verifyRequestDraft(request, span, metricsContext, { documentLoader, contextLoader, timeWindow, currentTime, keyCache, meterProvider, tracerProvider } = {}) {
528
+ async function verifyRequestDraft(request, span, metricsContext, options = {}) {
529
+ if (options[verificationObservation]?.check != null) return await verifyRequestDraftInternal(request, span, metricsContext, options);
530
+ let result = noSignatureResult();
531
+ const raw = request.headers.get("Signature");
532
+ const declared = raw == null ? null : parseDraftSignature(raw)?.keyId ?? null;
533
+ if (raw == null) return result;
534
+ await observeCheck(options, {
535
+ mechanism: "http",
536
+ spec: "draft-cavage-http-signatures-12",
537
+ label: null
538
+ }, declared, async (observation) => {
539
+ result = await verifyRequestDraftInternal(request, span, metricsContext, {
540
+ ...options,
541
+ [verificationObservation]: observation
542
+ });
543
+ if (!result.verified && result.reason.type === "keyFetchError" && observation.check != null) observation.check.reason = result.reason;
544
+ return result.verified ? result.key : null;
545
+ });
546
+ return result;
547
+ }
548
+ async function verifyRequestDraftInternal(request, span, metricsContext, { documentLoader, contextLoader, timeWindow, currentTime, keyCache, meterProvider, tracerProvider, [verificationObservation]: observation } = {}) {
520
549
  const logger = getLogger([
521
550
  "fedify",
522
551
  "sig",
@@ -697,6 +726,7 @@ async function verifyRequestDraft(request, span, metricsContext, { documentLoade
697
726
  const message = headerNames.map((name) => `${name}: ` + (name === "(request-target)" ? `${request.method.toLowerCase()} ${new URL(request.url).pathname}` : name === "(created)" ? sigValues.created ?? "" : name === "(expires)" ? sigValues.expires ?? "" : name === "host" ? request.headers.get("host") ?? new URL(request.url).host : request.headers.get(name))).join("\n");
698
727
  const sig = decodeBase64(signature);
699
728
  span?.setAttribute("http_signatures.signature", encodeHex(sig));
729
+ triedKey({ [verificationObservation]: observation }, key);
700
730
  if (!await crypto.subtle.verify("RSASSA-PKCS1-v1_5", key.publicKey, sig, new TextEncoder().encode(message))) {
701
731
  if (cached) {
702
732
  logger.debug("Failed to verify with the cached key {keyId}; signature {signature} is invalid. Retrying with the freshly fetched key...", {
@@ -711,7 +741,8 @@ async function verifyRequestDraft(request, span, metricsContext, { documentLoade
711
741
  currentTime,
712
742
  keyCache: bypassKeyCacheReads(keyCache),
713
743
  meterProvider,
714
- tracerProvider
744
+ tracerProvider,
745
+ [verificationObservation]: observation
715
746
  });
716
747
  }
717
748
  logger.debug("Failed to verify with the fetched key {keyId}; signature {signature} is invalid. Check if the key is correct or if the signed message is correct. The message to sign is:\n{message}", {
@@ -797,7 +828,7 @@ async function verifyRfc9421ContentDigest(digestHeader, body) {
797
828
  * @returns Whether the signature is verified, and the algorithm of the
798
829
  * signature if it is supported.
799
830
  */
800
- async function verifyRfc9421SignatureWithKey(request, sigInput, sigBytes, key, span) {
831
+ async function verifyRfc9421SignatureWithKey(request, sigInput, sigBytes, key, span, observationOptions = {}) {
801
832
  const logger = getLogger([
802
833
  "fedify",
803
834
  "sig",
@@ -853,6 +884,7 @@ async function verifyRfc9421SignatureWithKey(request, sigInput, sigBytes, key, s
853
884
  }
854
885
  let verified;
855
886
  try {
887
+ triedKey(observationOptions, key);
856
888
  verified = await crypto.subtle.verify(algorithm, key.publicKey, sigBytes.slice(), signatureBaseBytes);
857
889
  } catch (error) {
858
890
  logger.debug("Error during signature verification: {error}", {
@@ -880,7 +912,7 @@ async function verifyRfc9421SignatureWithKey(request, sigInput, sigBytes, key, s
880
912
  mismatched: true
881
913
  };
882
914
  }
883
- async function verifyRequestRfc9421(request, span, metricsContext, { documentLoader, contextLoader, timeWindow, currentTime, keyCache, maxSignatures = 3, meterProvider, tracerProvider } = {}) {
915
+ async function verifyRequestRfc9421(request, span, metricsContext, { documentLoader, contextLoader, timeWindow, currentTime, keyCache, maxSignatures = 3, [verificationObservation]: observation, meterProvider, tracerProvider } = {}) {
884
916
  const logger = getLogger([
885
917
  "fedify",
886
918
  "sig",
@@ -944,105 +976,116 @@ async function verifyRequestRfc9421(request, span, metricsContext, { documentLoa
944
976
  let body = null;
945
977
  let digestValid = null;
946
978
  for (const sigName of signatureNames) {
947
- if (!signatures[sigName]) {
948
- setFailure(invalidSignatureResult(parseKeyId(signatureInputs[sigName]?.keyId)));
949
- continue;
950
- }
951
- const sigInput = signatureInputs[sigName];
952
- const sigBytes = signatures[sigName];
953
- const keyId = parseKeyId(sigInput.keyId);
954
- if (!sigInput.keyId) {
955
- logger.debug("Failed to verify; missing keyId in signature {signatureName}.", {
956
- signatureName: sigName,
957
- signatureInput: signatureInputHeader
958
- });
959
- setFailure(invalidSignatureResult(null));
960
- continue;
961
- }
962
- if (!sigInput.created) {
963
- logger.debug("Failed to verify; missing created timestamp in signature {signatureName}.", {
964
- signatureName: sigName,
965
- signatureInput: signatureInputHeader
966
- });
967
- setFailure(invalidSignatureResult(keyId));
968
- continue;
969
- }
970
- const signatureCreated = Temporal.Instant.fromEpochMilliseconds(sigInput.created * 1e3);
971
- const now = currentTime ?? Temporal.Now.instant();
972
- if (timeWindow !== false) {
973
- const tw = timeWindow ?? { hours: 1 };
974
- if (Temporal.Instant.compare(signatureCreated, now.add(tw)) > 0) {
975
- logger.debug("Failed to verify; signature created time is too far in the future.", {
976
- created: signatureCreated.toString(),
977
- now: now.toString()
978
- });
979
- setFailure(invalidSignatureResult(keyId));
980
- continue;
981
- } else if (Temporal.Instant.compare(signatureCreated, now.subtract(tw)) < 0) {
982
- logger.debug("Failed to verify; signature created time is too far in the past.", {
983
- created: signatureCreated.toString(),
984
- now: now.toString()
985
- });
986
- setFailure(invalidSignatureResult(keyId));
987
- continue;
979
+ let winning;
980
+ await observeCheck({ [verificationObservation]: observation }, {
981
+ mechanism: "http",
982
+ spec: "rfc9421",
983
+ label: sigName
984
+ }, signatureInputs[sigName]?.keyId ?? null, async (candidateObservation) => {
985
+ if (!signatures[sigName]) {
986
+ setFailure(invalidSignatureResult(parseKeyId(signatureInputs[sigName]?.keyId)));
987
+ return null;
988
988
  }
989
- }
990
- if (request.method !== "GET" && request.method !== "HEAD" && sigInput.components.some((c) => c.value === "content-digest")) {
991
- const contentDigestHeader = request.headers.get("Content-Digest");
992
- if (!contentDigestHeader) {
993
- logger.debug("Failed to verify; Content-Digest header required but not found.", { components: sigInput.components });
994
- setFailure(invalidSignatureResult(keyId));
995
- continue;
989
+ const sigInput = signatureInputs[sigName];
990
+ const sigBytes = signatures[sigName];
991
+ const keyId = parseKeyId(sigInput.keyId);
992
+ if (!sigInput.keyId) {
993
+ logger.debug("Failed to verify; missing keyId in signature {signatureName}.", {
994
+ signatureName: sigName,
995
+ signatureInput: signatureInputHeader
996
+ });
997
+ setFailure(invalidSignatureResult(null));
998
+ return null;
996
999
  }
997
- body ??= await request.arrayBuffer();
998
- digestValid ??= await verifyRfc9421ContentDigest(contentDigestHeader, body);
999
- if (!digestValid) {
1000
- logger.debug("Failed to verify; Content-Digest verification failed.", { contentDigest: contentDigestHeader });
1000
+ if (!sigInput.created) {
1001
+ logger.debug("Failed to verify; missing created timestamp in signature {signatureName}.", {
1002
+ signatureName: sigName,
1003
+ signatureInput: signatureInputHeader
1004
+ });
1001
1005
  setFailure(invalidSignatureResult(keyId));
1002
- continue;
1006
+ return null;
1003
1007
  }
1004
- }
1005
- span?.setAttribute("http_signatures.key_id", sigInput.keyId);
1006
- span?.setAttribute("http_signatures.created", sigInput.created.toString());
1007
- if (keyId == null) {
1008
- setFailure(invalidSignatureResult(null));
1009
- continue;
1010
- }
1011
- let lookup = keyLookups.get(keyId.href);
1012
- if (lookup == null) {
1013
- lookup = await lookUpKey(keyId, keyCache);
1014
- keyLookups.set(keyId.href, lookup);
1015
- }
1016
- while (true) {
1017
- const { key, cached, fetchError } = lookup;
1018
- if (fetchError != null) {
1019
- setFailure(keyFetchErrorResult(keyId, fetchError));
1020
- break;
1008
+ const signatureCreated = Temporal.Instant.fromEpochMilliseconds(sigInput.created * 1e3);
1009
+ const now = currentTime ?? Temporal.Now.instant();
1010
+ if (timeWindow !== false) {
1011
+ const tw = timeWindow ?? { hours: 1 };
1012
+ if (Temporal.Instant.compare(signatureCreated, now.add(tw)) > 0) {
1013
+ logger.debug("Failed to verify; signature created time is too far in the future.", {
1014
+ created: signatureCreated.toString(),
1015
+ now: now.toString()
1016
+ });
1017
+ setFailure(invalidSignatureResult(keyId));
1018
+ return null;
1019
+ } else if (Temporal.Instant.compare(signatureCreated, now.subtract(tw)) < 0) {
1020
+ logger.debug("Failed to verify; signature created time is too far in the past.", {
1021
+ created: signatureCreated.toString(),
1022
+ now: now.toString()
1023
+ });
1024
+ setFailure(invalidSignatureResult(keyId));
1025
+ return null;
1026
+ }
1021
1027
  }
1022
- if (!key) {
1023
- logger.debug("Failed to fetch key: {keyId}", { keyId: sigInput.keyId });
1024
- setFailure(invalidSignatureResult(keyId));
1025
- break;
1028
+ if (request.method !== "GET" && request.method !== "HEAD" && sigInput.components.some((c) => c.value === "content-digest")) {
1029
+ const contentDigestHeader = request.headers.get("Content-Digest");
1030
+ if (!contentDigestHeader) {
1031
+ logger.debug("Failed to verify; Content-Digest header required but not found.", { components: sigInput.components });
1032
+ setFailure(invalidSignatureResult(keyId));
1033
+ return null;
1034
+ }
1035
+ body ??= await request.arrayBuffer();
1036
+ digestValid ??= await verifyRfc9421ContentDigest(contentDigestHeader, body);
1037
+ if (!digestValid) {
1038
+ logger.debug("Failed to verify; Content-Digest verification failed.", { contentDigest: contentDigestHeader });
1039
+ setFailure(invalidSignatureResult(keyId));
1040
+ return null;
1041
+ }
1026
1042
  }
1027
- const result = await verifyRfc9421SignatureWithKey(request, sigInput, sigBytes, key, span);
1028
- if (result.verified) {
1029
- metricsContext.algorithm = result.algorithm;
1030
- return {
1031
- verified: true,
1032
- key,
1033
- signatureLabel: sigName
1034
- };
1043
+ span?.setAttribute("http_signatures.key_id", sigInput.keyId);
1044
+ span?.setAttribute("http_signatures.created", sigInput.created.toString());
1045
+ if (keyId == null) {
1046
+ setFailure(invalidSignatureResult(null));
1047
+ return null;
1035
1048
  }
1036
- if (result.mismatched && cached && !refreshedKeyIds.has(keyId.href)) {
1037
- logger.debug("Failed to verify with cached key {keyId}; retrying with fresh key...", { keyId: sigInput.keyId });
1038
- refreshedKeyIds.add(keyId.href);
1039
- lookup = await lookUpKey(keyId, bypassKeyCacheReads(keyCache));
1049
+ let lookup = keyLookups.get(keyId.href);
1050
+ if (lookup == null) {
1051
+ lookup = await lookUpKey(keyId, keyCache);
1040
1052
  keyLookups.set(keyId.href, lookup);
1041
- continue;
1042
1053
  }
1043
- setFailure(invalidSignatureResult(keyId), result.algorithm);
1044
- break;
1045
- }
1054
+ while (true) {
1055
+ const { key, cached, fetchError } = lookup;
1056
+ if (fetchError != null) {
1057
+ setFailure(keyFetchErrorResult(keyId, fetchError));
1058
+ break;
1059
+ }
1060
+ if (!key) {
1061
+ logger.debug("Failed to fetch key: {keyId}", { keyId: sigInput.keyId });
1062
+ setFailure(invalidSignatureResult(keyId));
1063
+ break;
1064
+ }
1065
+ const result = await verifyRfc9421SignatureWithKey(request, sigInput, sigBytes, key, span, { [verificationObservation]: candidateObservation });
1066
+ if (result.verified) {
1067
+ metricsContext.algorithm = result.algorithm;
1068
+ winning = {
1069
+ verified: true,
1070
+ key,
1071
+ signatureLabel: sigName
1072
+ };
1073
+ return key;
1074
+ }
1075
+ if (result.mismatched && cached && !refreshedKeyIds.has(keyId.href)) {
1076
+ logger.debug("Failed to verify with cached key {keyId}; retrying with fresh key...", { keyId: sigInput.keyId });
1077
+ refreshedKeyIds.add(keyId.href);
1078
+ lookup = await lookUpKey(keyId, bypassKeyCacheReads(keyCache));
1079
+ keyLookups.set(keyId.href, lookup);
1080
+ continue;
1081
+ }
1082
+ setFailure(invalidSignatureResult(keyId), result.algorithm);
1083
+ break;
1084
+ }
1085
+ if (!failure.verified && failure.reason.type === "keyFetchError" && candidateObservation.check != null) candidateObservation.check.reason = failure.reason;
1086
+ return null;
1087
+ });
1088
+ if (winning != null) return winning;
1046
1089
  }
1047
1090
  metricsContext.algorithm = failureAlgorithm;
1048
1091
  return failure;