@fedify/fedify 2.4.0-dev.2256 → 2.4.0-dev.2260
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/{builder-DvSQqk2l.mjs → builder-BK8HfSUm.mjs} +8 -2
- package/dist/compat/mod.d.cts +1 -1
- package/dist/compat/mod.d.ts +1 -1
- package/dist/compat/transformers.test.mjs +1 -1
- package/dist/{compound-proof-5bg_j_ye.mjs → compound-proof-D--HeIbV.mjs} +41 -4
- package/dist/{context-CtI307_A.d.ts → context-7NyiqIcg.d.ts} +111 -2
- package/dist/{context-B6X-t8gI.d.cts → context-tsYPJ2Ew.d.cts} +111 -2
- package/dist/{deno-AmcRMq5z.mjs → deno-DVP7DGJC.mjs} +1 -1
- package/dist/{docloader-CIP_SG39.mjs → docloader-Dv1JJl37.mjs} +2 -2
- package/dist/federation/builder.test.mjs +1 -1
- package/dist/federation/compound-fanout.test.mjs +3 -3
- package/dist/federation/handler-compound-proof.test.mjs +3 -3
- package/dist/federation/handler.test.mjs +76 -5
- package/dist/federation/hashlink-media.test.mjs +2 -2
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/inbox-report.test.d.mts +2 -0
- package/dist/federation/inbox-report.test.mjs +475 -0
- package/dist/federation/metrics.test.mjs +1 -1
- package/dist/federation/middleware.test.mjs +7 -7
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.d.cts +4 -3
- package/dist/federation/mod.d.ts +4 -3
- package/dist/federation/mod.js +1 -1
- package/dist/federation/outgoing-proof.test.mjs +54 -10
- package/dist/federation/portable-actor.test.mjs +3 -3
- package/dist/federation/portable-chain.test.mjs +3 -3
- package/dist/federation/portable-collection.test.mjs +66 -4
- package/dist/federation/portable-delivery.test.mjs +2 -2
- package/dist/federation/portable-gateway-keys.test.mjs +4 -4
- package/dist/federation/portable-inbox.test.mjs +67 -6
- package/dist/federation/portable-parse-uri.test.mjs +1 -1
- package/dist/federation/portable-route-activity.test.mjs +2 -2
- package/dist/federation/portable.test.mjs +3 -3
- package/dist/federation/send.test.mjs +3 -3
- package/dist/federation/tasks/enqueue.test.mjs +1 -1
- package/dist/federation/tasks/tasks.test.mjs +2 -2
- package/dist/federation/temporal.test.mjs +1 -1
- package/dist/federation/webfinger.test.mjs +1 -1
- package/dist/{federation-BDzIgqOd.cjs → federation-Bo5z395X.cjs} +552 -122
- package/dist/{http-1fZ83jj7.cjs → http-BIhWdDf5.cjs} +447 -110
- package/dist/{http-BGb3QioZ.js → http-DRMu1C2T.js} +430 -111
- package/dist/{http-DkqWu-sm.mjs → http-DvpuyDJB.mjs} +141 -98
- package/dist/{key-CEsu7oaD.mjs → key-B7N8mNoY.mjs} +30 -3
- package/dist/{http-BrwKINQn.d.cts → key-C1nv7_jD.d.ts} +638 -229
- package/dist/{http-DGx0KOws.d.ts → key-DYxuyd-s.d.cts} +638 -229
- package/dist/{kv-cache-CJS4mImS.mjs → kv-cache-D0kA0rml.mjs} +1 -1
- package/dist/{kv-cache-CGhEgt98.js → kv-cache-DZLpqhW9.js} +1 -1
- package/dist/{kv-cache-BEWuZSB4.cjs → kv-cache-mF8Zmjix.cjs} +1 -1
- package/dist/{ld-Cf4CPWN_.mjs → ld-BDMNyM2M.mjs} +40 -5
- package/dist/{metrics-CocsZP0E.mjs → metrics-BbBj-VZN.mjs} +2 -1
- package/dist/{middleware-Cne_jMZo.mjs → middleware-CE1X7-UM.mjs} +1 -1
- package/dist/{middleware-Dxn0E9ES.mjs → middleware-DAA7wDdS.mjs} +520 -131
- package/dist/{middleware-BfYke3YQ.js → middleware-DfUWPAa-.js} +552 -122
- package/dist/{mod-C7_2j7VT.d.cts → mod-BojP2Bw3.d.cts} +1 -1
- package/dist/{mod-BlLwewYe.d.ts → mod-CavhAgLw.d.cts} +5 -255
- package/dist/{mod-VHYh1lTK.d.cts → mod-CtpF2BU5.d.ts} +5 -255
- package/dist/{mod-B8MI1mpi.d.ts → mod-DEZXwV6J.d.ts} +1 -1
- package/dist/{mod-DFL6IxYQ.d.cts → mod-Dw-nPxQh.d.cts} +1 -1
- package/dist/{mod-BhTaDR5H.d.ts → mod-Xj5P4s4d.d.ts} +1 -1
- package/dist/mod.cjs +4 -4
- package/dist/mod.d.cts +7 -7
- package/dist/mod.d.ts +7 -7
- package/dist/mod.js +4 -4
- package/dist/nodeinfo/handler.test.mjs +1 -1
- package/dist/{owner-DSJ7nFgy.mjs → owner-CzUycuhd.mjs} +3 -3
- package/dist/{portable-collection-BuBV6xTo.js → portable-collection-COcJxZg_.js} +1 -1
- package/dist/{portable-collection-qskqXJrH.mjs → portable-collection-CTcNJiAa.mjs} +2 -2
- package/dist/{portable-collection-r-mHg1PV.cjs → portable-collection-cPMffcHJ.cjs} +1 -1
- package/dist/{portable-key-woGH_1nn.mjs → portable-key-B4QN3OzR.mjs} +1 -1
- package/dist/{proof-8WiH_-Gq.mjs → proof-nce4pf5S.mjs} +133 -16
- package/dist/{send-JKzZBgPU.mjs → send-4Idpe2z0.mjs} +3 -3
- package/dist/sig/compound-proof-discovery.test.mjs +1 -1
- package/dist/sig/compound-proof-producer.test.mjs +1 -1
- package/dist/sig/compound-proof-verification.test.mjs +19 -2
- package/dist/sig/compound-proof.test.mjs +2 -2
- package/dist/sig/http.test.mjs +2 -2
- package/dist/sig/key.test.mjs +1 -1
- package/dist/sig/ld.test.mjs +2 -2
- package/dist/sig/mitra-receive-interop.test.mjs +4 -4
- package/dist/sig/mitra-send-interop.test.mjs +3 -3
- package/dist/sig/mod.cjs +2 -2
- package/dist/sig/mod.d.cts +2 -2
- package/dist/sig/mod.d.ts +2 -2
- package/dist/sig/mod.js +2 -2
- package/dist/sig/owner.test.mjs +1 -1
- package/dist/sig/portable-collection.test.mjs +1 -1
- package/dist/sig/portable-interop.test.mjs +3 -3
- package/dist/sig/portable-key.test.mjs +6 -6
- package/dist/sig/proof.test.mjs +76 -3
- package/dist/sig/tootik-request-interop.test.mjs +4 -4
- package/dist/sig/tootik-send-interop.test.mjs +3 -3
- package/dist/sig/verification.test.d.mts +2 -0
- package/dist/sig/verification.test.mjs +310 -0
- package/dist/{temporal-CT3fRaMH.mjs → temporal-BbjmPHYn.mjs} +1 -1
- package/dist/testing/kv.test.mjs +1 -1
- package/dist/testing/mod.d.mts +315 -28
- package/dist/utils/docloader.test.mjs +2 -2
- package/dist/utils/kv-cache.test.mjs +1 -1
- package/dist/utils/mod.cjs +1 -1
- package/dist/utils/mod.d.cts +1 -1
- package/dist/utils/mod.d.ts +1 -1
- package/dist/utils/mod.js +1 -1
- package/dist/verification-C3954hQh.mjs +5 -0
- package/dist/verification-Dpfcw8ml.mjs +105 -0
- 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
|
|
6
|
-
import { n as
|
|
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-DVP7DGJC.mjs";
|
|
7
|
+
import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-BbBj-VZN.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-
|
|
10
|
-
import { n as resolvePortableActorKey, r as verifyPortableGatewayKeyDocument } from "./portable-key-
|
|
10
|
+
import { a as fetchKeyDetailed, c as validateCryptoKey, t as bypassKeyCacheReads } from "./key-B7N8mNoY.mjs";
|
|
11
|
+
import { n as resolvePortableActorKey, r as verifyPortableGatewayKeyDocument } from "./portable-key-B4QN3OzR.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,
|
|
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
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
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
|
-
|
|
991
|
-
const
|
|
992
|
-
if (!
|
|
993
|
-
logger.debug("Failed to verify;
|
|
994
|
-
|
|
995
|
-
|
|
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
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
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
|
-
|
|
1006
|
+
return null;
|
|
1003
1007
|
}
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
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 (
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
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
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
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
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
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
|
-
|
|
1044
|
-
|
|
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;
|
|
@@ -2,8 +2,9 @@ import { Temporal } from "temporal-polyfill";
|
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
4
|
import { c as isPortableActorDocument, d as isPortableUri, l as isPortableId, m as parseKeyIdString, s as isCompatibleKeyId } from "./portable-key-id-Ca30-hOa.mjs";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { a as verificationObservation } from "./verification-Dpfcw8ml.mjs";
|
|
6
|
+
import { n as version, t as name } from "./deno-DVP7DGJC.mjs";
|
|
7
|
+
import { _ as recordKeyLookup, n as getDurationMs, t as classifyFetchError } from "./metrics-BbBj-VZN.mjs";
|
|
7
8
|
import { n as attachKeyOwnerEvidence, t as MAX_PORTABLE_KEY_TTL } from "./key-owner-evidence-APB6NpSk.mjs";
|
|
8
9
|
import { getLogger } from "@logtape/logtape";
|
|
9
10
|
import { CryptographicKey, Multikey, Object as Object$1, isActor } from "@fedify/vocab";
|
|
@@ -898,12 +899,38 @@ async function fetchPortableActorKey(cacheKey, cls, options, onCachedUnavailable
|
|
|
898
899
|
};
|
|
899
900
|
}
|
|
900
901
|
async function fetchKeyInternal(keyId, cls, options = {}) {
|
|
901
|
-
return await fetchKeyWithResult(typeof keyId === "string" ? parseKeyIdOrThrow(keyId) : keyId, cls, options, (_cacheKey, _keyId, _keyCache, _logger) => {
|
|
902
|
+
return await fetchKeyWithResult(typeof keyId === "string" ? parseKeyIdOrThrow(keyId) : keyId, cls, options, async (_cacheKey, _keyId, _keyCache, _logger) => {
|
|
903
|
+
const check = options[verificationObservation]?.check;
|
|
904
|
+
if (check != null) try {
|
|
905
|
+
const result = await _keyCache?.getFetchError?.(_cacheKey);
|
|
906
|
+
if (result != null) check.reason = {
|
|
907
|
+
type: "keyFetchError",
|
|
908
|
+
keyId: new URL(_cacheKey.href),
|
|
909
|
+
result
|
|
910
|
+
};
|
|
911
|
+
} catch {}
|
|
902
912
|
return {
|
|
903
913
|
key: null,
|
|
904
914
|
cached: true
|
|
905
915
|
};
|
|
906
916
|
}, async (error, cacheKey, keyId, keyCache, logger) => {
|
|
917
|
+
const check = options[verificationObservation]?.check;
|
|
918
|
+
if (check != null) {
|
|
919
|
+
let result;
|
|
920
|
+
try {
|
|
921
|
+
result = error instanceof FetchError && error.response != null ? {
|
|
922
|
+
status: error.response.status,
|
|
923
|
+
response: error.response.clone()
|
|
924
|
+
} : { error: error instanceof Error ? error : new Error(String(error)) };
|
|
925
|
+
} catch {
|
|
926
|
+
result = { error: error instanceof Error ? error : /* @__PURE__ */ new Error() };
|
|
927
|
+
}
|
|
928
|
+
check.reason = {
|
|
929
|
+
type: "keyFetchError",
|
|
930
|
+
keyId: new URL(cacheKey.href),
|
|
931
|
+
result
|
|
932
|
+
};
|
|
933
|
+
}
|
|
907
934
|
logger.debug("Failed to fetch key {keyId}.", {
|
|
908
935
|
keyId,
|
|
909
936
|
error
|