
      import { Temporal } from "@js-temporal/polyfill";
      import { URLPattern } from "urlpattern-polyfill";
      globalThis.addEventListener = () => {};
    
import { CryptographicKey, deno_default } from "./lookup-Dvhilldd.js";
import { fetchKey, validateCryptoKey } from "./key-jo5CucF6.js";
import { getLogger } from "@logtape/logtape";
import { SpanStatusCode, trace } from "@opentelemetry/api";
import { ATTR_HTTP_REQUEST_HEADER, ATTR_HTTP_REQUEST_METHOD, ATTR_URL_FULL } from "@opentelemetry/semantic-conventions";
import { decodeBase64, encodeBase64 } from "byte-encodings/base64";
import { encodeHex } from "byte-encodings/hex";
import { Item, decodeDict, encodeItem } from "structured-field-values";

//#region src/sig/http.ts
/**
* Signs a request using the given private key.
* @param request The request to sign.
* @param privateKey The private key to use for signing.
* @param keyId The key ID to use for the signature.  It will be used by the
*              verifier.
* @returns The signed request.
* @throws {TypeError} If the private key is invalid or unsupported.
*/
async function signRequest(request, privateKey, keyId, options = {}) {
	validateCryptoKey(privateKey, "private");
	const tracerProvider = options.tracerProvider ?? trace.getTracerProvider();
	const tracer = tracerProvider.getTracer(deno_default.name, deno_default.version);
	return await tracer.startActiveSpan("http_signatures.sign", async (span) => {
		try {
			const spec = options.spec ?? "draft-cavage-http-signatures-12";
			let signed;
			if (spec === "rfc9421") signed = await signRequestRfc9421(request, privateKey, keyId, span, options.currentTime, options.body);
			else signed = await signRequestDraft(request, privateKey, keyId, span, options.currentTime, options.body);
			if (span.isRecording()) {
				span.setAttribute(ATTR_HTTP_REQUEST_METHOD, signed.method);
				span.setAttribute(ATTR_URL_FULL, signed.url);
				for (const [name, value] of signed.headers) span.setAttribute(ATTR_HTTP_REQUEST_HEADER(name), value);
				span.setAttribute("http_signatures.key_id", keyId.href);
			}
			return signed;
		} catch (error) {
			span.setStatus({
				code: SpanStatusCode.ERROR,
				message: String(error)
			});
			throw error;
		} finally {
			span.end();
		}
	});
}
async function signRequestDraft(request, privateKey, keyId, span, currentTime, bodyBuffer) {
	if (privateKey.algorithm.name !== "RSASSA-PKCS1-v1_5") throw new TypeError("Unsupported algorithm: " + privateKey.algorithm.name);
	const url = new URL(request.url);
	const body = bodyBuffer !== void 0 ? bodyBuffer : request.method !== "GET" && request.method !== "HEAD" ? await request.clone().arrayBuffer() : null;
	const headers = new Headers(request.headers);
	if (!headers.has("Host")) headers.set("Host", url.host);
	if (!headers.has("Digest") && body != null) {
		const digest = await crypto.subtle.digest("SHA-256", body);
		headers.set("Digest", `SHA-256=${encodeBase64(digest)}`);
		if (span.isRecording()) span.setAttribute("http_signatures.digest.sha-256", encodeHex(digest));
	}
	if (!headers.has("Date")) headers.set("Date", currentTime == null ? (/* @__PURE__ */ new Date()).toUTCString() : new Date(currentTime.toString()).toUTCString());
	const serialized = [["(request-target)", `${request.method.toLowerCase()} ${url.pathname}`], ...headers];
	const headerNames = serialized.map(([name]) => name);
	const message = serialized.map(([name, value]) => `${name}: ${value.trim()}`).join("\n");
	const signature = await crypto.subtle.sign("RSASSA-PKCS1-v1_5", privateKey, new TextEncoder().encode(message));
	const sigHeader = `keyId="${keyId.href}",algorithm="rsa-sha256",headers="${headerNames.join(" ")}",signature="${encodeBase64(signature)}"`;
	headers.set("Signature", sigHeader);
	if (span.isRecording()) {
		span.setAttribute("http_signatures.algorithm", "rsa-sha256");
		span.setAttribute("http_signatures.signature", encodeHex(signature));
	}
	return new Request(request, {
		headers,
		body
	});
}
function formatRfc9421SignatureParameters(params) {
	return `alg="${params.algorithm}";keyid="${params.keyId.href}";created=${params.created}`;
}
/**
* Creates a signature base for a request according to RFC 9421.
* @param request The request to create a signature base for.
* @param components The components to include in the signature base.
* @param parameters The signature parameters to include in the signature base.
* @returns The signature base as a string.
*/
function createRfc9421SignatureBase(request, components, parameters) {
	const url = new URL(request.url);
	const baseComponents = [];
	for (const component of components) {
		let value;
		if (component === "@method") value = request.method.toUpperCase();
		else if (component === "@target-uri") value = request.url;
		else if (component === "@authority") value = url.host;
		else if (component === "@scheme") value = url.protocol.slice(0, -1);
		else if (component === "@request-target") value = `${request.method.toLowerCase()} ${url.pathname}${url.search}`;
		else if (component === "@path") value = url.pathname;
		else if (component === "@query") value = url.search.startsWith("?") ? url.search.slice(1) : url.search;
		else if (component === "@query-param") throw new Error("@query-param requires a parameter name");
		else if (component === "@status") throw new Error("@status is only valid for responses");
		else if (component.startsWith("@")) throw new Error(`Unsupported derived component: ${component}`);
		else {
			const header = request.headers.get(component);
			if (header == null) throw new Error(`Missing header: ${component}`);
			value = header;
		}
		baseComponents.push(`"${component}": ${value}`);
	}
	const sigComponents = components.map((c) => `"${c}"`).join(" ");
	baseComponents.push(`"@signature-params": (${sigComponents});${parameters}`);
	return baseComponents.join("\n");
}
/**
* Formats a signature using rfc9421 format.
* @param signature The raw signature bytes.
* @param components The components that were signed.
* @param parameters The signature parameters.
* @returns The formatted signature string.
*/
function formatRfc9421Signature(signature, components, parameters) {
	const signatureInputValue = `sig1=("${components.join("\" \"")}");${parameters}`;
	const signatureValue = `sig1=:${encodeBase64(signature)}:`;
	return [signatureInputValue, signatureValue];
}
/**
* Parse RFC 9421 Signature-Input header.
* @param signatureInput The Signature-Input header value.
* @returns Parsed signature input parameters.
*/
function parseRfc9421SignatureInput(signatureInput) {
	let dict;
	try {
		dict = decodeDict(signatureInput);
	} catch (error) {
		getLogger([
			"fedify",
			"sig",
			"http"
		]).debug("Failed to parse Signature-Input header: {signatureInput}", {
			signatureInput,
			error
		});
		return {};
	}
	const result = {};
	for (const [label, item] of Object.entries(dict)) {
		if (!Array.isArray(item.value) || typeof item.params.keyid !== "string" || typeof item.params.created !== "number") continue;
		const components = item.value.map((subitem) => subitem.value).filter((v) => typeof v === "string");
		const params = encodeItem(new Item(0, item.params));
		result[label] = {
			keyId: item.params.keyid,
			alg: item.params.alg,
			created: item.params.created,
			components,
			parameters: params.slice(params.indexOf(";") + 1)
		};
	}
	return result;
}
/**
* Parse RFC 9421 Signature header.
* @param signature The Signature header value.
* @returns Parsed signature values.
*/
function parseRfc9421Signature(signature) {
	let dict;
	try {
		dict = decodeDict(signature);
	} catch (error) {
		getLogger([
			"fedify",
			"sig",
			"http"
		]).debug("Failed to parse Signature header: {signature}", {
			signature,
			error
		});
		return {};
	}
	const result = {};
	for (const [key, value] of Object.entries(dict)) if (value.value instanceof Uint8Array) result[key] = value.value;
	return result;
}
async function signRequestRfc9421(request, privateKey, keyId, span, currentTime, bodyBuffer) {
	if (privateKey.algorithm.name !== "RSASSA-PKCS1-v1_5") throw new TypeError("Unsupported algorithm: " + privateKey.algorithm.name);
	const url = new URL(request.url);
	const body = bodyBuffer !== void 0 ? bodyBuffer : request.method !== "GET" && request.method !== "HEAD" ? await request.clone().arrayBuffer() : null;
	const headers = new Headers(request.headers);
	if (!headers.has("Host")) headers.set("Host", url.host);
	if (!headers.has("Content-Digest") && body != null) {
		const digest = await crypto.subtle.digest("SHA-256", body);
		headers.set("Content-Digest", `sha-256=:${encodeBase64(digest)}:`);
		if (span.isRecording()) span.setAttribute("http_signatures.digest.sha-256", encodeHex(digest));
	}
	currentTime ??= Temporal.Now.instant();
	const created = currentTime.epochMilliseconds / 1e3 | 0;
	if (!headers.has("Date")) headers.set("Date", new Date(currentTime.toString()).toUTCString());
	const components = [
		"@method",
		"@target-uri",
		"@authority",
		"host",
		"date"
	];
	if (body != null) components.push("content-digest");
	const signatureParams = formatRfc9421SignatureParameters({
		algorithm: "rsa-v1_5-sha256",
		keyId,
		created
	});
	let signatureBase;
	try {
		signatureBase = createRfc9421SignatureBase(new Request(request.url, {
			method: request.method,
			headers
		}), components, signatureParams);
	} catch (error) {
		throw new TypeError(`Failed to create signature base: ${String(error)}; it is probably a bug in the implementation.  Please report it at Fedify's issue tracker.`);
	}
	const signatureBytes = await crypto.subtle.sign("RSASSA-PKCS1-v1_5", privateKey, new TextEncoder().encode(signatureBase));
	const [signatureInput, signature] = formatRfc9421Signature(signatureBytes, components, signatureParams);
	headers.set("Signature-Input", signatureInput);
	headers.set("Signature", signature);
	if (span.isRecording()) {
		span.setAttribute("http_signatures.algorithm", "rsa-v1_5-sha256");
		span.setAttribute("http_signatures.signature", encodeHex(signatureBytes));
		span.setAttribute("http_signatures.created", created.toString());
	}
	return new Request(request, {
		headers,
		body
	});
}
const supportedHashAlgorithms = {
	"sha": "SHA-1",
	"sha-256": "SHA-256",
	"sha-512": "SHA-512"
};
/**
* Verifies the signature of a request.
*
* Note that this function consumes the request body, so it should not be used
* if the request body is already consumed.  Consuming the request body after
* calling this function is okay, since this function clones the request
* under the hood.
*
* @param request The request to verify.
* @param options Options for verifying the request.
* @returns The public key of the verified signature, or `null` if the signature
*          could not be verified.
*/
async function verifyRequest(request, options = {}) {
	const tracerProvider = options.tracerProvider ?? trace.getTracerProvider();
	const tracer = tracerProvider.getTracer(deno_default.name, deno_default.version);
	return await tracer.startActiveSpan("http_signatures.verify", async (span) => {
		if (span.isRecording()) {
			span.setAttribute(ATTR_HTTP_REQUEST_METHOD, request.method);
			span.setAttribute(ATTR_URL_FULL, request.url);
			for (const [name, value] of request.headers) span.setAttribute(ATTR_HTTP_REQUEST_HEADER(name), value);
		}
		try {
			let spec = options.spec;
			if (spec == null) spec = request.headers.has("Signature-Input") ? "rfc9421" : "draft-cavage-http-signatures-12";
			let key;
			if (spec === "rfc9421") key = await verifyRequestRfc9421(request, span, options);
			else key = await verifyRequestDraft(request, span, options);
			if (key == null) span.setStatus({ code: SpanStatusCode.ERROR });
			return key;
		} catch (error) {
			span.setStatus({
				code: SpanStatusCode.ERROR,
				message: String(error)
			});
			throw error;
		} finally {
			span.end();
		}
	});
}
async function verifyRequestDraft(request, span, { documentLoader, contextLoader, timeWindow, currentTime, keyCache, tracerProvider } = {}) {
	const logger = getLogger([
		"fedify",
		"sig",
		"http"
	]);
	if (request.bodyUsed) {
		logger.error("Failed to verify; the request body is already consumed.", { url: request.url });
		return null;
	} else if (request.body?.locked) {
		logger.error("Failed to verify; the request body is locked.", { url: request.url });
		return null;
	}
	const originalRequest = request;
	request = request.clone();
	const dateHeader = request.headers.get("Date");
	if (dateHeader == null) {
		logger.debug("Failed to verify; no Date header found.", { headers: Object.fromEntries(request.headers.entries()) });
		return null;
	}
	const sigHeader = request.headers.get("Signature");
	if (sigHeader == null) {
		logger.debug("Failed to verify; no Signature header found.", { headers: Object.fromEntries(request.headers.entries()) });
		return null;
	}
	const digestHeader = request.headers.get("Digest");
	if (request.method !== "GET" && request.method !== "HEAD" && digestHeader == null) {
		logger.debug("Failed to verify; no Digest header found.", { headers: Object.fromEntries(request.headers.entries()) });
		return null;
	}
	let body = null;
	if (digestHeader != null) {
		body = await request.arrayBuffer();
		const digests = digestHeader.split(",").map((pair) => pair.includes("=") ? pair.split("=", 2) : [pair, ""]);
		let matched = false;
		for (let [algo, digestBase64] of digests) {
			algo = algo.trim().toLowerCase();
			if (!(algo in supportedHashAlgorithms)) continue;
			let digest;
			try {
				digest = decodeBase64(digestBase64);
			} catch (error) {
				logger.debug("Failed to verify; invalid base64 encoding: {digest}.", {
					digest: digestBase64,
					error
				});
				return null;
			}
			if (span.isRecording()) span.setAttribute(`http_signatures.digest.${algo}`, encodeHex(digest));
			const expectedDigest = await crypto.subtle.digest(supportedHashAlgorithms[algo], body);
			if (!timingSafeEqual(digest, new Uint8Array(expectedDigest))) {
				logger.debug("Failed to verify; digest mismatch ({algorithm}): {digest} != {expectedDigest}.", {
					algorithm: algo,
					digest: digestBase64,
					expectedDigest: encodeBase64(expectedDigest)
				});
				return null;
			}
			matched = true;
		}
		if (!matched) {
			logger.debug("Failed to verify; no supported digest algorithm found.  Supported: {supportedAlgorithms}; found: {algorithms}.", {
				supportedAlgorithms: Object.keys(supportedHashAlgorithms),
				algorithms: digests.map(([algo]) => algo)
			});
			return null;
		}
	}
	const date = Temporal.Instant.from(new Date(dateHeader).toISOString());
	const now = currentTime ?? Temporal.Now.instant();
	if (timeWindow !== false) {
		const tw = timeWindow ?? { hours: 1 };
		if (Temporal.Instant.compare(date, now.add(tw)) > 0) {
			logger.debug("Failed to verify; Date is too far in the future.", {
				date: date.toString(),
				now: now.toString()
			});
			return null;
		} else if (Temporal.Instant.compare(date, now.subtract(tw)) < 0) {
			logger.debug("Failed to verify; Date is too far in the past.", {
				date: date.toString(),
				now: now.toString()
			});
			return null;
		}
	}
	const sigValues = Object.fromEntries(sigHeader.split(",").map((pair) => pair.match(/^\s*([A-Za-z]+)=(?:"([^"]*)"|(\d+))\s*$/)).filter((m) => m != null).map((m) => [m[1], m[2] ?? m[3]]));
	if (!("keyId" in sigValues)) {
		logger.debug("Failed to verify; no keyId field found in the Signature header.", { signature: sigHeader });
		return null;
	} else if (!("headers" in sigValues)) {
		logger.debug("Failed to verify; no headers field found in the Signature header.", { signature: sigHeader });
		return null;
	} else if (!("signature" in sigValues)) {
		logger.debug("Failed to verify; no signature field found in the Signature header.", { signature: sigHeader });
		return null;
	}
	if ("expires" in sigValues) {
		const expiresSeconds = parseInt(sigValues.expires);
		if (!Number.isInteger(expiresSeconds)) {
			logger.debug("Failed to verify; invalid expires field in the Signature header: {expires}.", {
				expires: sigValues.expires,
				signature: sigHeader
			});
			return null;
		}
		const expires = Temporal.Instant.fromEpochMilliseconds(expiresSeconds * 1e3);
		if (Temporal.Instant.compare(now, expires) > 0) {
			logger.debug("Failed to verify; signature expired at {expires} (now: {now}).", {
				expires: expires.toString(),
				now: now.toString(),
				signature: sigHeader
			});
			return null;
		}
	}
	if ("created" in sigValues) {
		const createdSeconds = parseInt(sigValues.created);
		if (!Number.isInteger(createdSeconds)) {
			logger.debug("Failed to verify; invalid created field in the Signature header: {created}.", {
				created: sigValues.created,
				signature: sigHeader
			});
			return null;
		}
		if (timeWindow !== false) {
			const created = Temporal.Instant.fromEpochMilliseconds(createdSeconds * 1e3);
			const tw = timeWindow ?? { minutes: 1 };
			if (Temporal.Instant.compare(created, now.add(tw)) > 0) {
				logger.debug("Failed to verify; created is too far in the future.", {
					created: created.toString(),
					now: now.toString()
				});
				return null;
			} else if (Temporal.Instant.compare(created, now.subtract(tw)) < 0) {
				logger.debug("Failed to verify; created is too far in the past.", {
					created: created.toString(),
					now: now.toString()
				});
				return null;
			}
		}
	}
	const { keyId, headers, signature } = sigValues;
	span?.setAttribute("http_signatures.key_id", keyId);
	if ("algorithm" in sigValues) span?.setAttribute("http_signatures.algorithm", sigValues.algorithm);
	const { key, cached } = await fetchKey(new URL(keyId), CryptographicKey, {
		documentLoader,
		contextLoader,
		keyCache,
		tracerProvider
	});
	if (key == null) return null;
	const headerNames = headers.split(/\s+/g);
	if (!headerNames.includes("(request-target)") || !headerNames.includes("date")) {
		logger.debug("Failed to verify; required headers missing in the Signature header: {headers}.", { headers });
		return null;
	}
	if (body != null && !headerNames.includes("digest")) {
		logger.debug("Failed to verify; required headers missing in the Signature header: {headers}.", { headers });
		return null;
	}
	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");
	const sig = decodeBase64(signature);
	span?.setAttribute("http_signatures.signature", encodeHex(sig));
	const verified = await crypto.subtle.verify("RSASSA-PKCS1-v1_5", key.publicKey, sig, new TextEncoder().encode(message));
	if (!verified) {
		if (cached) {
			logger.debug("Failed to verify with the cached key {keyId}; signature {signature} is invalid.  Retrying with the freshly fetched key...", {
				keyId,
				signature,
				message
			});
			return await verifyRequest(originalRequest, {
				documentLoader,
				contextLoader,
				timeWindow,
				currentTime,
				keyCache: {
					get: () => Promise.resolve(void 0),
					set: async (keyId$1, key$1) => await keyCache?.set(keyId$1, key$1)
				}
			});
		}
		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}", {
			keyId,
			signature,
			message
		});
		return null;
	}
	return key;
}
/**
* RFC 9421 map of algorithm identifiers to WebCrypto algorithms
*/
const rfc9421AlgorithmMap = {
	"rsa-v1_5-sha256": {
		name: "RSASSA-PKCS1-v1_5",
		hash: "SHA-256"
	},
	"rsa-v1_5-sha512": {
		name: "RSASSA-PKCS1-v1_5",
		hash: "SHA-512"
	},
	"rsa-pss-sha512": {
		name: "RSA-PSS",
		hash: "SHA-512"
	},
	"ecdsa-p256-sha256": {
		name: "ECDSA",
		hash: "SHA-256"
	},
	"ecdsa-p384-sha384": {
		name: "ECDSA",
		hash: "SHA-384"
	},
	"ed25519": { name: "Ed25519" }
};
/**
* Verifies a Content-Digest header according to RFC 9421.
* @param digestHeader The Content-Digest header value.
* @param body The message body to verify against.
* @returns Whether the digest is valid.
*/
async function verifyRfc9421ContentDigest(digestHeader, body) {
	const digests = digestHeader.split(",").map((pair) => {
		pair = pair.trim();
		const pos = pair.indexOf("=");
		const algo = pos < 0 ? pair : pair.slice(0, pos);
		const value = pos < 0 ? "" : pair.slice(pos + 1);
		return {
			algo: algo.trim().toLowerCase(),
			value: value.trim()
		};
	});
	for (const { algo, value } of digests) {
		let hashAlgo;
		if (algo === "sha-256") hashAlgo = "SHA-256";
		else if (algo === "sha-512") hashAlgo = "SHA-512";
		else continue;
		const base64Match = value.match(/^:([^:]+):$/);
		if (!base64Match) continue;
		let digest;
		try {
			digest = decodeBase64(base64Match[1]);
		} catch {
			continue;
		}
		const calculatedDigest = await crypto.subtle.digest(hashAlgo, body);
		if (timingSafeEqual(digest, new Uint8Array(calculatedDigest))) return true;
	}
	return false;
}
async function verifyRequestRfc9421(request, span, { documentLoader, contextLoader, timeWindow, currentTime, keyCache, tracerProvider } = {}) {
	const logger = getLogger([
		"fedify",
		"sig",
		"http"
	]);
	if (request.bodyUsed) {
		logger.error("Failed to verify; the request body is already consumed.", { url: request.url });
		return null;
	} else if (request.body?.locked) {
		logger.error("Failed to verify; the request body is locked.", { url: request.url });
		return null;
	}
	const originalRequest = request;
	request = request.clone();
	const signatureInputHeader = request.headers.get("Signature-Input");
	if (!signatureInputHeader) {
		logger.debug("Failed to verify; no Signature-Input header found.", { headers: Object.fromEntries(request.headers.entries()) });
		return null;
	}
	const signatureHeader = request.headers.get("Signature");
	if (!signatureHeader) {
		logger.debug("Failed to verify; no Signature header found.", { headers: Object.fromEntries(request.headers.entries()) });
		return null;
	}
	const signatureInputs = parseRfc9421SignatureInput(signatureInputHeader);
	logger.debug("Parsed Signature-Input header: {signatureInputs}", { signatureInputs });
	const signatures = parseRfc9421Signature(signatureHeader);
	const signatureNames = Object.keys(signatureInputs);
	if (signatureNames.length === 0) {
		logger.debug("Failed to verify; no valid signatures found in Signature-Input header.", { header: signatureInputHeader });
		return null;
	}
	let validKey = null;
	for (const sigName of signatureNames) {
		if (!signatures[sigName]) continue;
		const sigInput = signatureInputs[sigName];
		const sigBytes = signatures[sigName];
		if (!sigInput.keyId) {
			logger.debug("Failed to verify; missing keyId in signature {signatureName}.", {
				signatureName: sigName,
				signatureInput: signatureInputHeader
			});
			continue;
		}
		if (!sigInput.created) {
			logger.debug("Failed to verify; missing created timestamp in signature {signatureName}.", {
				signatureName: sigName,
				signatureInput: signatureInputHeader
			});
			continue;
		}
		const signatureCreated = Temporal.Instant.fromEpochMilliseconds(sigInput.created * 1e3);
		const now = currentTime ?? Temporal.Now.instant();
		if (timeWindow !== false) {
			const tw = timeWindow ?? { hours: 1 };
			if (Temporal.Instant.compare(signatureCreated, now.add(tw)) > 0) {
				logger.debug("Failed to verify; signature created time is too far in the future.", {
					created: signatureCreated.toString(),
					now: now.toString()
				});
				continue;
			} else if (Temporal.Instant.compare(signatureCreated, now.subtract(tw)) < 0) {
				logger.debug("Failed to verify; signature created time is too far in the past.", {
					created: signatureCreated.toString(),
					now: now.toString()
				});
				continue;
			}
		}
		if (request.method !== "GET" && request.method !== "HEAD" && sigInput.components.includes("content-digest")) {
			const contentDigestHeader = request.headers.get("Content-Digest");
			if (!contentDigestHeader) {
				logger.debug("Failed to verify; Content-Digest header required but not found.", { components: sigInput.components });
				continue;
			}
			const body = await request.arrayBuffer();
			const digestValid = await verifyRfc9421ContentDigest(contentDigestHeader, body);
			if (!digestValid) {
				logger.debug("Failed to verify; Content-Digest verification failed.", { contentDigest: contentDigestHeader });
				continue;
			}
		}
		span?.setAttribute("http_signatures.key_id", sigInput.keyId);
		span?.setAttribute("http_signatures.created", sigInput.created.toString());
		const { key, cached } = await fetchKey(new URL(sigInput.keyId), CryptographicKey, {
			documentLoader,
			contextLoader,
			keyCache,
			tracerProvider
		});
		if (!key) {
			logger.debug("Failed to fetch key: {keyId}", { keyId: sigInput.keyId });
			continue;
		}
		let alg = sigInput.alg?.toLowerCase();
		if (alg == null) {
			if (key.publicKey.algorithm.name === "RSASSA-PKCS1-v1_5") alg = "hash" in key.publicKey.algorithm ? key.publicKey.algorithm.hash === "SHA-512" ? "rsa-v1_5-sha512" : "rsa-v1_5-sha256" : "rsa-v1_5-sha256";
			else if (key.publicKey.algorithm.name === "RSA-PSS") alg = "rsa-pss-sha512";
			else if (key.publicKey.algorithm.name === "ECDSA") alg = "namedCurve" in key.publicKey.algorithm && key.publicKey.algorithm.namedCurve === "P-256" ? "ecdsa-p256-sha256" : "ecdsa-p384-sha384";
			else if (key.publicKey.algorithm.name === "Ed25519") alg = "ed25519";
		}
		if (alg) span?.setAttribute("http_signatures.algorithm", alg);
		const algorithm = alg && rfc9421AlgorithmMap[alg];
		if (!algorithm) {
			logger.debug("Failed to verify; unsupported algorithm: {algorithm}", {
				algorithm: sigInput.alg,
				supported: Object.keys(rfc9421AlgorithmMap)
			});
			continue;
		}
		let signatureBase;
		try {
			signatureBase = createRfc9421SignatureBase(request, sigInput.components, sigInput.parameters);
		} catch (error) {
			logger.debug("Failed to create signature base for verification: {error}", {
				error,
				signatureInput: sigInput
			});
			continue;
		}
		const signatureBaseBytes = new TextEncoder().encode(signatureBase);
		span?.setAttribute("http_signatures.signature", encodeHex(sigBytes));
		try {
			const verified = await crypto.subtle.verify(algorithm, key.publicKey, sigBytes.slice(), signatureBaseBytes);
			if (verified) {
				validKey = key;
				break;
			} else if (cached) {
				logger.debug("Failed to verify with cached key {keyId}; retrying with fresh key...", { keyId: sigInput.keyId });
				return await verifyRequest(originalRequest, {
					documentLoader,
					contextLoader,
					timeWindow,
					currentTime,
					keyCache: {
						get: () => Promise.resolve(void 0),
						set: async (keyId, key$1) => await keyCache?.set(keyId, key$1)
					},
					spec: "rfc9421"
				});
			} else logger.debug("Failed to verify signature with fetched key {keyId}; signature invalid.", {
				keyId: sigInput.keyId,
				signatureBase
			});
		} catch (error) {
			logger.debug("Error during signature verification: {error}", {
				error,
				keyId: sigInput.keyId,
				algorithm: sigInput.alg
			});
		}
	}
	return validKey;
}
/**
* Helper function to create a new Request for redirect handling.
* @param request The original request.
* @param location The redirect location.
* @param body The request body as ArrayBuffer or null.
* @returns A new Request object for the redirect.
*/
function createRedirectRequest(request, location, body) {
	const url = new URL(location, request.url);
	return new Request(url, {
		method: request.method,
		headers: request.headers,
		body,
		redirect: "manual",
		signal: request.signal,
		mode: request.mode,
		credentials: request.credentials,
		referrer: request.referrer,
		referrerPolicy: request.referrerPolicy,
		integrity: request.integrity,
		keepalive: request.keepalive,
		cache: request.cache
	});
}
/**
* Performs a double-knock request to the given URL.  For the details of
* double-knocking, see
* <https://swicg.github.io/activitypub-http-signature/#how-to-upgrade-supported-versions>.
* @param request The request to send.
* @param identity The identity to use for signing the request.
* @param options The options for double-knock requests.
* @returns The response to the request.
* @since 1.6.0
*/
async function doubleKnock(request, identity, options = {}) {
	const { specDeterminer, log, tracerProvider, signal } = options;
	const origin = new URL(request.url).origin;
	const firstTrySpec = specDeterminer == null ? "rfc9421" : await specDeterminer.determineSpec(origin);
	const body = options.body !== void 0 ? options.body : request.method !== "GET" && request.method !== "HEAD" ? await request.clone().arrayBuffer() : null;
	let signedRequest = await signRequest(request, identity.privateKey, identity.keyId, {
		spec: firstTrySpec,
		tracerProvider,
		body
	});
	log?.(signedRequest);
	let response = await fetch(signedRequest, {
		redirect: "manual",
		signal
	});
	if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) {
		const location = response.headers.get("Location");
		return doubleKnock(createRedirectRequest(request, location, body), identity, {
			...options,
			body
		});
	} else if (response.status === 400 || response.status === 401 || response.status > 401) {
		const spec = firstTrySpec === "draft-cavage-http-signatures-12" ? "rfc9421" : "draft-cavage-http-signatures-12";
		getLogger([
			"fedify",
			"sig",
			"http"
		]).debug("Failed to verify with the spec {spec} ({status} {statusText}); retrying with spec {secondSpec}... (double-knocking)", {
			spec: firstTrySpec,
			secondSpec: spec,
			status: response.status,
			statusText: response.statusText
		});
		signedRequest = await signRequest(request, identity.privateKey, identity.keyId, {
			spec,
			tracerProvider,
			body
		});
		log?.(signedRequest);
		response = await fetch(signedRequest, {
			redirect: "manual",
			signal
		});
		if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) {
			const location = response.headers.get("Location");
			return doubleKnock(createRedirectRequest(request, location, body), identity, {
				...options,
				body
			});
		} else if (response.status !== 400 && response.status !== 401) await specDeterminer?.rememberSpec(origin, spec);
	} else await specDeterminer?.rememberSpec(origin, firstTrySpec);
	return response;
}
/**
* Performs a timing-safe equality comparison between two `Uint8Array` values.
*
* This function is designed to take a constant amount of time to execute,
* dependent only on the length of the longer of the two arrays,
* regardless of where the first difference in bytes occurs. This helps
* prevent timing attacks.
*
* @param a The first bytes.
* @param b The second bytes.
* @returns `true` if the arrays are of the same length and contain the same
*          bytes, `false` otherwise.
* @since 1.6.0
*/
function timingSafeEqual(a, b) {
	const lenA = a.length;
	const lenB = b.length;
	const commonLength = Math.max(lenA, lenB);
	let result = 0;
	for (let i = 0; i < commonLength; i++) {
		const byteA = i < lenA ? a[i] : 0;
		const byteB = i < lenB ? b[i] : 0;
		result |= byteA ^ byteB;
	}
	result |= lenA ^ lenB;
	return result === 0;
}

//#endregion
export { createRfc9421SignatureBase, doubleKnock, formatRfc9421Signature, formatRfc9421SignatureParameters, parseRfc9421Signature, parseRfc9421SignatureInput, signRequest, timingSafeEqual, verifyRequest };