@fedify/fedify 2.4.0-dev.2169 → 2.4.0-dev.2193
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-B2d_Pt0j.mjs → builder-0onlMY4u.mjs} +26 -6
- package/dist/{circuit-breaker-C5NqoLBJ.mjs → circuit-breaker-XJ53md4S.mjs} +16 -11
- 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-D28gMFqP.mjs → compound-proof-B-Fk6Slo.mjs} +3 -3
- package/dist/{context-BykNUf04.d.ts → context-9T--NSo4.d.ts} +55 -1
- package/dist/{context-L0Fnib9j.d.cts → context-CDcAgm4l.d.cts} +55 -1
- package/dist/{deno-DU8dvnWk.mjs → deno-DB_Sh0LK.mjs} +1 -1
- package/dist/{docloader-CghLH7Zi.mjs → docloader-DpWYYY6a.mjs} +25 -17
- package/dist/federation/builder.test.mjs +1 -1
- package/dist/federation/circuit-breaker.test.mjs +50 -1
- package/dist/federation/compound-fanout.test.mjs +3 -3
- package/dist/federation/handler-compound-proof.test.mjs +52 -3
- package/dist/federation/handler.test.mjs +5 -5
- package/dist/federation/hashlink-media.test.mjs +2 -2
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/keycache.test.mjs +1 -1
- package/dist/federation/metrics.test.mjs +1 -1
- package/dist/federation/middleware.test.mjs +182 -13
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.d.cts +2 -2
- package/dist/federation/mod.d.ts +2 -2
- package/dist/federation/mod.js +1 -1
- package/dist/federation/outgoing-proof.test.mjs +5 -5
- 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 +4 -4
- package/dist/federation/portable-gateway-keys.test.mjs +6 -6
- package/dist/federation/portable-inbox.test.mjs +35 -5
- package/dist/federation/portable-parse-uri.test.mjs +1 -1
- package/dist/federation/portable-route-activity.test.d.mts +2 -0
- package/dist/federation/portable-route-activity.test.mjs +259 -0
- package/dist/federation/portable.test.mjs +49 -5
- 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-DrNgkLfJ.cjs → federation-BsGo0EH2.cjs} +76 -35
- package/dist/{http-5CJBdU8n.cjs → http-BEivI0TS.cjs} +279 -87
- package/dist/{http-BGpexPO7.js → http-BGmxtpo2.js} +263 -89
- package/dist/{http-Cok5-vIC.d.ts → http-BrwKINQn.d.cts} +29 -1
- package/dist/{http-DC1cqAQ2.d.cts → http-DGx0KOws.d.ts} +29 -1
- package/dist/{http-Bd6rZlJX.mjs → http-DQf9Mw4V.mjs} +180 -92
- package/dist/{key-gIhVZO3p.mjs → key-DA3Yx8p2.mjs} +4 -4
- package/dist/{key-owner-evidence-Bo3yaeL5.mjs → key-owner-evidence-APB6NpSk.mjs} +1 -1
- package/dist/{keycache-PiOM9pM0.mjs → keycache-Da8A8mZa.mjs} +1 -1
- package/dist/{kv-cache-YRi_g8GB.cjs → kv-cache-1hafSoqH.cjs} +23 -15
- package/dist/{kv-cache-iR55fqlk.js → kv-cache-DduYhpee.js} +24 -16
- package/dist/{kv-cache-D0qjSeWY.mjs → kv-cache-OFBvrKh3.mjs} +1 -1
- package/dist/{ld-BsTkRrwF.mjs → ld-C15yzuY6.mjs} +3 -3
- package/dist/{metrics-jUDFfVgi.mjs → metrics-B1H23mlq.mjs} +1 -1
- package/dist/{middleware-Dq-OCmb6.mjs → middleware-BZQnf0nI.mjs} +1 -1
- package/dist/{middleware-Coqgp-K0.mjs → middleware-BqqycC0P.mjs} +52 -36
- package/dist/{middleware-DlxMxVp5.js → middleware-ClaUFaLb.js} +77 -36
- package/dist/{mod-DK0y9fd-.d.ts → mod-B3OBLj5g.d.ts} +1 -1
- package/dist/{mod-B5z_q9TH.d.ts → mod-C5Mwx5ty.d.ts} +5 -1
- package/dist/{mod-CPPqkyV_.d.cts → mod-Cy9VNHVw.d.ts} +14 -3
- package/dist/{mod-BLOXGs6m.d.cts → mod-D58KCqUg.d.cts} +1 -1
- package/dist/{mod--jv6vyrP.d.ts → mod-DoTWIc7F.d.cts} +14 -3
- package/dist/{mod-BA_F2x53.d.cts → mod-Dp3Ij7sw.d.cts} +5 -1
- package/dist/mod.cjs +4 -4
- package/dist/mod.d.cts +5 -5
- package/dist/mod.d.ts +5 -5
- package/dist/mod.js +4 -4
- package/dist/nodeinfo/handler.test.mjs +1 -1
- package/dist/{owner-D3GoetXS.mjs → owner-BVpZBM-V.mjs} +5 -5
- package/dist/{portable-collection-CnlCamup.cjs → portable-collection-BFSShzwh.cjs} +1 -1
- package/dist/{portable-collection-Bkdh_WpL.js → portable-collection-D535cSz_.js} +1 -1
- package/dist/{portable-collection-C60_acmx.mjs → portable-collection-Dn2GdZdI.mjs} +2 -2
- package/dist/{portable-key-DyF5AnZo.mjs → portable-key-Bmwmq7Y6.mjs} +2 -2
- package/dist/{portable-key-id-Bg259c4Z.mjs → portable-key-id-Ca30-hOa.mjs} +45 -1
- package/dist/{proof-CQAYqWdj.mjs → proof-DrJWKeOl.mjs} +49 -7
- package/dist/{send-DjkQrxt5.mjs → send-CT3a-0Vd.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 +2 -2
- package/dist/sig/compound-proof.test.mjs +2 -2
- package/dist/sig/http.test.mjs +495 -23
- package/dist/sig/key-owner-evidence.test.mjs +2 -2
- package/dist/sig/key.test.mjs +1 -1
- package/dist/sig/ld.test.mjs +2 -2
- 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-key-id.test.d.mts +2 -0
- package/dist/sig/portable-key-id.test.mjs +67 -0
- package/dist/sig/portable-key.test.mjs +7 -7
- package/dist/sig/proof.test.mjs +141 -7
- package/dist/{temporal-oRKP803M.mjs → temporal-DNqluyEc.mjs} +1 -1
- package/dist/testing/kv.test.mjs +1 -1
- package/dist/testing/mod.d.mts +52 -0
- package/dist/utils/docloader.test.mjs +93 -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/package.json +7 -7
|
@@ -3,7 +3,7 @@ import { URLPattern } from "urlpattern-polyfill";
|
|
|
3
3
|
import { getLogger } from "@logtape/logtape";
|
|
4
4
|
import { Activity, CryptographicKey, DataIntegrityProof, Multikey, Object as Object$1, PUBLIC_COLLECTION, getTypeId, isActor } from "@fedify/vocab";
|
|
5
5
|
import { SpanKind, SpanStatusCode, metrics, trace } from "@opentelemetry/api";
|
|
6
|
-
import { FetchError, canonicalizePortableUri, encodeMultibase, formatIri, fromCompatibleEf61Id, getDocumentLoader, getFe34Origin, isGatewayUrl, parseDidKeyVerificationMethod, parseIri, preloadedContexts, toCompatibleEf61Id } from "@fedify/vocab-runtime";
|
|
6
|
+
import { FetchError, canonicalizePortableUri, encodeMultibase, formatIri, fromCompatibleEf61Id, getDocumentLoader, getFe34Origin, isGatewayUrl, parseDidKeyVerificationMethod, parseGatewayUrl, parseIri, preloadedContexts, toCompatibleEf61Id } from "@fedify/vocab-runtime";
|
|
7
7
|
import { getPortableGatewayCandidates, isCompatibleEf61Iri } from "@fedify/vocab-runtime/internal/portable-dereference";
|
|
8
8
|
import { encodeHex } from "byte-encodings/hex";
|
|
9
9
|
import jsonld from "@fedify/vocab-runtime/jsonld";
|
|
@@ -14,7 +14,7 @@ import serialize from "json-canon";
|
|
|
14
14
|
import { ATTR_HTTP_REQUEST_HEADER, ATTR_HTTP_REQUEST_METHOD, ATTR_URL_FULL } from "@opentelemetry/semantic-conventions";
|
|
15
15
|
//#region deno.json
|
|
16
16
|
var name = "@fedify/fedify";
|
|
17
|
-
var version = "2.4.0-dev.
|
|
17
|
+
var version = "2.4.0-dev.2193+402149b5";
|
|
18
18
|
//#endregion
|
|
19
19
|
//#region src/sig/portable-key-id.ts
|
|
20
20
|
const COMPATIBLE_KEY_ID_PATH_PATTERN = /^\/\.well-known\/apgateway\/did(?::|%3A)/i;
|
|
@@ -115,6 +115,50 @@ function getCanonicalPortableId(id) {
|
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
/**
|
|
118
|
+
* Gets the [FEP-fe34] origin of an ID for same-origin checks. The origin of
|
|
119
|
+
* an `ap:` or `ap+ef61:` URI, or of a compatible identifier on any gateway,
|
|
120
|
+
* is the DID of its canonical portable ID, as FEP-ef61 treats a compatible
|
|
121
|
+
* identifier as the portable ID it stands for; unlike `getFe34Origin()`,
|
|
122
|
+
* which gives a compatible identifier the web origin of its gateway. The
|
|
123
|
+
* origin of any other HTTP(S) URL is its web origin, and that of a DID URL
|
|
124
|
+
* is its DID.
|
|
125
|
+
*
|
|
126
|
+
* Only the parsed `URL` is validated, so dot segments that URL parsing has
|
|
127
|
+
* already resolved cannot be detected here.
|
|
128
|
+
*
|
|
129
|
+
* [FEP-fe34]: https://w3id.org/fep/fe34
|
|
130
|
+
* @returns The origin, or `null` if the ID has none that is supported, or is
|
|
131
|
+
* a malformed portable ID or compatible identifier.
|
|
132
|
+
* @internal
|
|
133
|
+
*/
|
|
134
|
+
function getAuthenticationOrigin(id) {
|
|
135
|
+
try {
|
|
136
|
+
if (isPortableId(id)) {
|
|
137
|
+
const canonical = getCanonicalPortableId(id);
|
|
138
|
+
return canonical == null ? null : getFe34Origin(canonical);
|
|
139
|
+
}
|
|
140
|
+
return getFe34Origin(id);
|
|
141
|
+
} catch (error) {
|
|
142
|
+
if (error instanceof TypeError) return null;
|
|
143
|
+
throw error;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Checks whether two IDs identify the same object. Two portable IDs, i.e.,
|
|
148
|
+
* `ap:` or `ap+ef61:` URIs or compatible identifiers, are compared by their
|
|
149
|
+
* canonical portable IDs, so that the same object on different gateways and
|
|
150
|
+
* with different location hints matches. Other IDs have to be equal as
|
|
151
|
+
* they are.
|
|
152
|
+
* @internal
|
|
153
|
+
*/
|
|
154
|
+
function isSameObjectId(a, b) {
|
|
155
|
+
const aPortable = isPortableId(a);
|
|
156
|
+
if (aPortable !== isPortableId(b)) return false;
|
|
157
|
+
if (!aPortable) return a.href === b.href;
|
|
158
|
+
const canonical = getCanonicalPortableId(a);
|
|
159
|
+
return canonical != null && canonical === getCanonicalPortableId(b);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
118
162
|
* Gets the canonical portable ID of an `ap:` or `ap+ef61:` URI or
|
|
119
163
|
* a compatible identifier as it is written, before any URL parsing.
|
|
120
164
|
* Parsing an ID into a `URL` resolves dot segments in its path, which would
|
|
@@ -3860,6 +3904,7 @@ const FEP_2277_COLLECTION_PROPERTIES = [
|
|
|
3860
3904
|
"prev",
|
|
3861
3905
|
"current"
|
|
3862
3906
|
].map((property) => AS_NAMESPACE + property);
|
|
3907
|
+
const FEP_EF61_GATEWAYS = "https://w3id.org/fep/ef61/gateways";
|
|
3863
3908
|
const PORTABLE_OBJECT_ID_PATTERN = /^ap(?:\+ef61)?:\/\//i;
|
|
3864
3909
|
const FUNCTIONAL_PROOF_PROPERTIES = [
|
|
3865
3910
|
`${SECURITY_NAMESPACE}cryptosuite`,
|
|
@@ -3886,6 +3931,35 @@ function hasValidPortableProofShape(proofValue) {
|
|
|
3886
3931
|
});
|
|
3887
3932
|
}
|
|
3888
3933
|
/**
|
|
3934
|
+
* Checks whether an expanded portable actor node has the `gateways` that
|
|
3935
|
+
* FEP-ef61 requires: a non-empty list whose items are all HTTP(S) URIs with
|
|
3936
|
+
* an empty path, query, and fragment. Like the generated vocabulary decoder,
|
|
3937
|
+
* this also tolerates a plain set of values instead of a `@list`, and gateway
|
|
3938
|
+
* strings given as `@value`s instead of `@id`s.
|
|
3939
|
+
*/
|
|
3940
|
+
function hasValidPortableActorGateways(node) {
|
|
3941
|
+
const values = node[FEP_EF61_GATEWAYS];
|
|
3942
|
+
if (!Array.isArray(values)) return false;
|
|
3943
|
+
let items = values;
|
|
3944
|
+
if (values.some((value) => isJsonLdNode(value) && "@list" in value)) {
|
|
3945
|
+
if (values.length !== 1) return false;
|
|
3946
|
+
items = values[0]["@list"];
|
|
3947
|
+
}
|
|
3948
|
+
if (!Array.isArray(items) || items.length < 1) return false;
|
|
3949
|
+
return items.every((item) => {
|
|
3950
|
+
if (!isJsonLdNode(item)) return false;
|
|
3951
|
+
const gateway = typeof item["@id"] === "string" ? item["@id"] : item["@value"];
|
|
3952
|
+
if (typeof gateway !== "string") return false;
|
|
3953
|
+
try {
|
|
3954
|
+
parseGatewayUrl(gateway);
|
|
3955
|
+
} catch (error) {
|
|
3956
|
+
if (error instanceof TypeError) return false;
|
|
3957
|
+
throw error;
|
|
3958
|
+
}
|
|
3959
|
+
return true;
|
|
3960
|
+
});
|
|
3961
|
+
}
|
|
3962
|
+
/**
|
|
3889
3963
|
* Classifies an expanded JSON-LD node into an FEP-2277 core type by the
|
|
3890
3964
|
* properties it has.
|
|
3891
3965
|
* @internal
|
|
@@ -4089,6 +4163,15 @@ async function preparePortableObjectProof(jsonLd, options) {
|
|
|
4089
4163
|
}
|
|
4090
4164
|
};
|
|
4091
4165
|
}
|
|
4166
|
+
if (objectType === "actor" && !hasValidPortableActorGateways(root)) return {
|
|
4167
|
+
prepared: false,
|
|
4168
|
+
root,
|
|
4169
|
+
objectType,
|
|
4170
|
+
result: {
|
|
4171
|
+
verified: false,
|
|
4172
|
+
reason: { type: "invalidGateways" }
|
|
4173
|
+
}
|
|
4174
|
+
};
|
|
4092
4175
|
return {
|
|
4093
4176
|
prepared: true,
|
|
4094
4177
|
root,
|
|
@@ -4142,7 +4225,10 @@ async function verifyPortableObjectProofPolicy(jsonLd, key, options = {}) {
|
|
|
4142
4225
|
*
|
|
4143
4226
|
* Every proof must use a DID URL whose DID matches the portable object's
|
|
4144
4227
|
* authority, i.e., the DID of its canonical portable ID, and every proof must
|
|
4145
|
-
* pass {@link verifyProof}.
|
|
4228
|
+
* pass {@link verifyProof}. A portable actor must also have a non-empty
|
|
4229
|
+
* `gateways` list whose items are all HTTP(S) URIs with an empty path, query,
|
|
4230
|
+
* and fragment; otherwise, it is rejected with the `invalidGateways` reason
|
|
4231
|
+
* before its proofs are verified. The proofs are verified over the document as
|
|
4146
4232
|
* given; a compatible identifier is never rewritten into a portable ID.
|
|
4147
4233
|
*
|
|
4148
4234
|
* [FEP-ef61]: https://w3id.org/fep/ef61
|
|
@@ -5056,16 +5142,19 @@ function parseDraftSignature(signature) {
|
|
|
5056
5142
|
* Lists the HTTP Signatures that a request claims to carry, without verifying
|
|
5057
5143
|
* any of them. Signatures whose key IDs are not URLs are left out.
|
|
5058
5144
|
* @param request The request.
|
|
5145
|
+
* @param maxSignatures The maximum number of RFC 9421 signatures to consider,
|
|
5146
|
+
* counted the same way as by {@link verifyRequest}, so
|
|
5147
|
+
* that the signatures it ignores are not listed either.
|
|
5059
5148
|
* @returns The signatures; RFC 9421 ones if the request has
|
|
5060
5149
|
* a `Signature-Input` header, or a draft-cavage one otherwise.
|
|
5061
5150
|
* @internal
|
|
5062
5151
|
*/
|
|
5063
|
-
function listRequestSignatures(request) {
|
|
5152
|
+
function listRequestSignatures(request, maxSignatures = 3) {
|
|
5064
5153
|
const signatureInput = request.headers.get("Signature-Input");
|
|
5065
5154
|
if (signatureInput != null) {
|
|
5066
5155
|
const signatures = [];
|
|
5067
|
-
const inputs = parseRfc9421SignatureInput(signatureInput);
|
|
5068
|
-
for (const [label, input] of
|
|
5156
|
+
const inputs = Object.entries(parseRfc9421SignatureInput(signatureInput));
|
|
5157
|
+
for (const [label, input] of inputs.slice(0, maxSignatures)) {
|
|
5069
5158
|
const keyId = parseKeyId(input.keyId);
|
|
5070
5159
|
if (keyId == null) continue;
|
|
5071
5160
|
signatures.push({
|
|
@@ -5193,6 +5282,18 @@ async function signRequestRfc9421(request, privateKey, keyId, span, currentTime,
|
|
|
5193
5282
|
body
|
|
5194
5283
|
});
|
|
5195
5284
|
}
|
|
5285
|
+
/**
|
|
5286
|
+
* Checks that a maximum number of signatures to verify is a positive safe
|
|
5287
|
+
* integer or `Infinity`.
|
|
5288
|
+
* @param value The value to check.
|
|
5289
|
+
* @param name The name of the option, used in the error message.
|
|
5290
|
+
* @throws {RangeError} Thrown if the value is not valid.
|
|
5291
|
+
* @internal
|
|
5292
|
+
*/
|
|
5293
|
+
function validateMaxSignatures(value, name) {
|
|
5294
|
+
if (value === Infinity || Number.isSafeInteger(value) && value >= 1) return;
|
|
5295
|
+
throw new RangeError(`${name} must be a positive integer or Infinity.`);
|
|
5296
|
+
}
|
|
5196
5297
|
const supportedHashAlgorithms = {
|
|
5197
5298
|
"sha": "SHA-1",
|
|
5198
5299
|
"sha-256": "SHA-256",
|
|
@@ -5285,6 +5386,8 @@ function recordVerificationResult(span, result) {
|
|
|
5285
5386
|
* @param options Options for verifying the request.
|
|
5286
5387
|
* @returns The public key of the verified signature, or `null` if the signature
|
|
5287
5388
|
* could not be verified.
|
|
5389
|
+
* @throws {RangeError} Thrown if {@link VerifyRequestOptions.maxSignatures}
|
|
5390
|
+
* is not a positive integer or `Infinity`.
|
|
5288
5391
|
*/
|
|
5289
5392
|
async function verifyRequest(request, options = {}) {
|
|
5290
5393
|
const result = await verifyRequestDetailed(request, options);
|
|
@@ -5297,9 +5400,12 @@ async function verifyRequest(request, options = {}) {
|
|
|
5297
5400
|
* @param request The request to verify.
|
|
5298
5401
|
* @param options Options for verifying the request.
|
|
5299
5402
|
* @returns The verified public key, or a structured verification failure.
|
|
5403
|
+
* @throws {RangeError} Thrown if {@link VerifyRequestOptions.maxSignatures}
|
|
5404
|
+
* is not a positive integer or `Infinity`.
|
|
5300
5405
|
* @since 2.1.0
|
|
5301
5406
|
*/
|
|
5302
5407
|
async function verifyRequestDetailed(request, options = {}) {
|
|
5408
|
+
if (options.maxSignatures !== void 0) validateMaxSignatures(options.maxSignatures, "maxSignatures");
|
|
5303
5409
|
return await (options.tracerProvider ?? trace.getTracerProvider()).getTracer(name, version).startActiveSpan("http_signatures.verify", async (span) => {
|
|
5304
5410
|
if (span.isRecording()) {
|
|
5305
5411
|
span.setAttribute(ATTR_HTTP_REQUEST_METHOD, request.method);
|
|
@@ -5608,7 +5714,100 @@ async function verifyRfc9421ContentDigest(digestHeader, body) {
|
|
|
5608
5714
|
}
|
|
5609
5715
|
return false;
|
|
5610
5716
|
}
|
|
5611
|
-
|
|
5717
|
+
/**
|
|
5718
|
+
* Verifies one RFC 9421 signature of a request with a key.
|
|
5719
|
+
* @param request The request, whose body may be consumed already.
|
|
5720
|
+
* @param sigInput The parsed `Signature-Input` member of the signature.
|
|
5721
|
+
* @param sigBytes The bytes of the signature.
|
|
5722
|
+
* @param key The public key named by the signature.
|
|
5723
|
+
* @param span The span to record the algorithm and the signature on.
|
|
5724
|
+
* @returns Whether the signature is verified, and the algorithm of the
|
|
5725
|
+
* signature if it is supported.
|
|
5726
|
+
*/
|
|
5727
|
+
async function verifyRfc9421SignatureWithKey(request, sigInput, sigBytes, key, span) {
|
|
5728
|
+
const logger = getLogger([
|
|
5729
|
+
"fedify",
|
|
5730
|
+
"sig",
|
|
5731
|
+
"http"
|
|
5732
|
+
]);
|
|
5733
|
+
let alg = sigInput.alg?.toLowerCase();
|
|
5734
|
+
if (alg == null) {
|
|
5735
|
+
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";
|
|
5736
|
+
else if (key.publicKey.algorithm.name === "RSA-PSS") alg = "rsa-pss-sha512";
|
|
5737
|
+
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";
|
|
5738
|
+
else if (key.publicKey.algorithm.name === "Ed25519") alg = "ed25519";
|
|
5739
|
+
}
|
|
5740
|
+
if (alg) span?.setAttribute("http_signatures.algorithm", alg);
|
|
5741
|
+
const algorithm = alg && rfc9421AlgorithmMap[alg];
|
|
5742
|
+
if (!algorithm) {
|
|
5743
|
+
logger.debug("Failed to verify; unsupported algorithm: {algorithm}", {
|
|
5744
|
+
algorithm: sigInput.alg,
|
|
5745
|
+
supported: Object.keys(rfc9421AlgorithmMap)
|
|
5746
|
+
});
|
|
5747
|
+
return {
|
|
5748
|
+
verified: false,
|
|
5749
|
+
mismatched: false
|
|
5750
|
+
};
|
|
5751
|
+
}
|
|
5752
|
+
const metricAlgorithm = alg;
|
|
5753
|
+
let signatureBase;
|
|
5754
|
+
try {
|
|
5755
|
+
signatureBase = createRfc9421SignatureBase(request, sigInput.components, sigInput.parameters);
|
|
5756
|
+
} catch (error) {
|
|
5757
|
+
logger.debug("Failed to create signature base for verification: {error}", {
|
|
5758
|
+
error,
|
|
5759
|
+
signatureInput: sigInput
|
|
5760
|
+
});
|
|
5761
|
+
return {
|
|
5762
|
+
verified: false,
|
|
5763
|
+
algorithm: metricAlgorithm,
|
|
5764
|
+
mismatched: false
|
|
5765
|
+
};
|
|
5766
|
+
}
|
|
5767
|
+
const signatureBaseBytes = new TextEncoder().encode(signatureBase);
|
|
5768
|
+
span?.setAttribute("http_signatures.signature", encodeHex(sigBytes));
|
|
5769
|
+
const algorithmName = typeof algorithm === "string" ? algorithm : algorithm.name;
|
|
5770
|
+
if (key.publicKey.algorithm.name !== algorithmName) {
|
|
5771
|
+
logger.debug("Failed to verify; key {keyId} is not a key for algorithm {algorithm}.", {
|
|
5772
|
+
keyId: sigInput.keyId,
|
|
5773
|
+
algorithm: alg
|
|
5774
|
+
});
|
|
5775
|
+
return {
|
|
5776
|
+
verified: false,
|
|
5777
|
+
algorithm: metricAlgorithm,
|
|
5778
|
+
mismatched: true
|
|
5779
|
+
};
|
|
5780
|
+
}
|
|
5781
|
+
let verified;
|
|
5782
|
+
try {
|
|
5783
|
+
verified = await crypto.subtle.verify(algorithm, key.publicKey, sigBytes.slice(), signatureBaseBytes);
|
|
5784
|
+
} catch (error) {
|
|
5785
|
+
logger.debug("Error during signature verification: {error}", {
|
|
5786
|
+
error,
|
|
5787
|
+
keyId: sigInput.keyId,
|
|
5788
|
+
algorithm: sigInput.alg
|
|
5789
|
+
});
|
|
5790
|
+
return {
|
|
5791
|
+
verified: false,
|
|
5792
|
+
algorithm: metricAlgorithm,
|
|
5793
|
+
mismatched: false
|
|
5794
|
+
};
|
|
5795
|
+
}
|
|
5796
|
+
if (verified) return {
|
|
5797
|
+
verified: true,
|
|
5798
|
+
algorithm: metricAlgorithm
|
|
5799
|
+
};
|
|
5800
|
+
logger.debug("Failed to verify signature with key {keyId}; signature invalid.", {
|
|
5801
|
+
keyId: sigInput.keyId,
|
|
5802
|
+
signatureBase
|
|
5803
|
+
});
|
|
5804
|
+
return {
|
|
5805
|
+
verified: false,
|
|
5806
|
+
algorithm: metricAlgorithm,
|
|
5807
|
+
mismatched: true
|
|
5808
|
+
};
|
|
5809
|
+
}
|
|
5810
|
+
async function verifyRequestRfc9421(request, span, metricsContext, { documentLoader, contextLoader, timeWindow, currentTime, keyCache, maxSignatures = 3, meterProvider, tracerProvider } = {}) {
|
|
5612
5811
|
const logger = getLogger([
|
|
5613
5812
|
"fedify",
|
|
5614
5813
|
"sig",
|
|
@@ -5621,7 +5820,6 @@ async function verifyRequestRfc9421(request, span, metricsContext, { documentLoa
|
|
|
5621
5820
|
logger.error("Failed to verify; the request body is locked.", { url: request.url });
|
|
5622
5821
|
return noSignatureResult();
|
|
5623
5822
|
}
|
|
5624
|
-
const originalRequest = request;
|
|
5625
5823
|
request = request.clone();
|
|
5626
5824
|
const signatureInputHeader = request.headers.get("Signature-Input");
|
|
5627
5825
|
if (!signatureInputHeader) {
|
|
@@ -5641,6 +5839,29 @@ async function verifyRequestRfc9421(request, span, metricsContext, { documentLoa
|
|
|
5641
5839
|
logger.debug("Failed to verify; no valid signatures found in Signature-Input header.", { header: signatureInputHeader });
|
|
5642
5840
|
return invalidSignatureResult(null);
|
|
5643
5841
|
}
|
|
5842
|
+
if (signatureNames.length > maxSignatures) {
|
|
5843
|
+
logger.debug("Verifying only the first {maxSignatures} of the {count} signatures in the Signature-Input header; the rest are ignored.", {
|
|
5844
|
+
maxSignatures,
|
|
5845
|
+
count: signatureNames.length
|
|
5846
|
+
});
|
|
5847
|
+
signatureNames.splice(maxSignatures);
|
|
5848
|
+
}
|
|
5849
|
+
const keyLookups = /* @__PURE__ */ new Map();
|
|
5850
|
+
const refreshedKeyIds = /* @__PURE__ */ new Set();
|
|
5851
|
+
const lookUpKey = (keyId, keyCache) => measureSignatureKeyFetch(meterProvider, "http", () => fetchKeyDetailed(keyId, CryptographicKey, {
|
|
5852
|
+
documentLoader,
|
|
5853
|
+
contextLoader,
|
|
5854
|
+
keyCache,
|
|
5855
|
+
tracerProvider,
|
|
5856
|
+
meterProvider,
|
|
5857
|
+
portableKeyResolvers: createPortableKeyResolvers({
|
|
5858
|
+
documentLoader,
|
|
5859
|
+
contextLoader,
|
|
5860
|
+
keyCache,
|
|
5861
|
+
tracerProvider,
|
|
5862
|
+
meterProvider
|
|
5863
|
+
})
|
|
5864
|
+
}));
|
|
5644
5865
|
let failure = noSignatureResult();
|
|
5645
5866
|
let failureAlgorithm;
|
|
5646
5867
|
const setFailure = (result, algorithm) => {
|
|
@@ -5714,94 +5935,40 @@ async function verifyRequestRfc9421(request, span, metricsContext, { documentLoa
|
|
|
5714
5935
|
setFailure(invalidSignatureResult(null));
|
|
5715
5936
|
continue;
|
|
5716
5937
|
}
|
|
5717
|
-
|
|
5718
|
-
|
|
5719
|
-
|
|
5720
|
-
|
|
5721
|
-
tracerProvider,
|
|
5722
|
-
meterProvider,
|
|
5723
|
-
portableKeyResolvers: createPortableKeyResolvers({
|
|
5724
|
-
documentLoader,
|
|
5725
|
-
contextLoader,
|
|
5726
|
-
keyCache,
|
|
5727
|
-
tracerProvider,
|
|
5728
|
-
meterProvider
|
|
5729
|
-
})
|
|
5730
|
-
}));
|
|
5731
|
-
if (fetchError != null) {
|
|
5732
|
-
setFailure(keyFetchErrorResult(keyId, fetchError));
|
|
5733
|
-
continue;
|
|
5734
|
-
}
|
|
5735
|
-
if (!key) {
|
|
5736
|
-
logger.debug("Failed to fetch key: {keyId}", { keyId: sigInput.keyId });
|
|
5737
|
-
setFailure(invalidSignatureResult(keyId));
|
|
5738
|
-
continue;
|
|
5739
|
-
}
|
|
5740
|
-
let alg = sigInput.alg?.toLowerCase();
|
|
5741
|
-
if (alg == null) {
|
|
5742
|
-
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";
|
|
5743
|
-
else if (key.publicKey.algorithm.name === "RSA-PSS") alg = "rsa-pss-sha512";
|
|
5744
|
-
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";
|
|
5745
|
-
else if (key.publicKey.algorithm.name === "Ed25519") alg = "ed25519";
|
|
5746
|
-
}
|
|
5747
|
-
if (alg) span?.setAttribute("http_signatures.algorithm", alg);
|
|
5748
|
-
const algorithm = alg && rfc9421AlgorithmMap[alg];
|
|
5749
|
-
const candidateAlgorithm = algorithm ? alg : void 0;
|
|
5750
|
-
if (!algorithm) {
|
|
5751
|
-
logger.debug("Failed to verify; unsupported algorithm: {algorithm}", {
|
|
5752
|
-
algorithm: sigInput.alg,
|
|
5753
|
-
supported: Object.keys(rfc9421AlgorithmMap)
|
|
5754
|
-
});
|
|
5755
|
-
setFailure(invalidSignatureResult(keyId));
|
|
5756
|
-
continue;
|
|
5938
|
+
let lookup = keyLookups.get(keyId.href);
|
|
5939
|
+
if (lookup == null) {
|
|
5940
|
+
lookup = await lookUpKey(keyId, keyCache);
|
|
5941
|
+
keyLookups.set(keyId.href, lookup);
|
|
5757
5942
|
}
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
if (await crypto.subtle.verify(algorithm, key.publicKey, sigBytes.slice(), signatureBaseBytes)) {
|
|
5773
|
-
metricsContext.algorithm = candidateAlgorithm;
|
|
5943
|
+
while (true) {
|
|
5944
|
+
const { key, cached, fetchError } = lookup;
|
|
5945
|
+
if (fetchError != null) {
|
|
5946
|
+
setFailure(keyFetchErrorResult(keyId, fetchError));
|
|
5947
|
+
break;
|
|
5948
|
+
}
|
|
5949
|
+
if (!key) {
|
|
5950
|
+
logger.debug("Failed to fetch key: {keyId}", { keyId: sigInput.keyId });
|
|
5951
|
+
setFailure(invalidSignatureResult(keyId));
|
|
5952
|
+
break;
|
|
5953
|
+
}
|
|
5954
|
+
const result = await verifyRfc9421SignatureWithKey(request, sigInput, sigBytes, key, span);
|
|
5955
|
+
if (result.verified) {
|
|
5956
|
+
metricsContext.algorithm = result.algorithm;
|
|
5774
5957
|
return {
|
|
5775
5958
|
verified: true,
|
|
5776
5959
|
key,
|
|
5777
5960
|
signatureLabel: sigName
|
|
5778
5961
|
};
|
|
5779
|
-
}
|
|
5962
|
+
}
|
|
5963
|
+
if (result.mismatched && cached && !refreshedKeyIds.has(keyId.href)) {
|
|
5780
5964
|
logger.debug("Failed to verify with cached key {keyId}; retrying with fresh key...", { keyId: sigInput.keyId });
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
currentTime,
|
|
5786
|
-
keyCache: bypassKeyCacheReads(keyCache),
|
|
5787
|
-
spec: "rfc9421",
|
|
5788
|
-
meterProvider,
|
|
5789
|
-
tracerProvider
|
|
5790
|
-
});
|
|
5791
|
-
} else {
|
|
5792
|
-
logger.debug("Failed to verify signature with fetched key {keyId}; signature invalid.", {
|
|
5793
|
-
keyId: sigInput.keyId,
|
|
5794
|
-
signatureBase
|
|
5795
|
-
});
|
|
5796
|
-
setFailure(invalidSignatureResult(keyId), candidateAlgorithm);
|
|
5965
|
+
refreshedKeyIds.add(keyId.href);
|
|
5966
|
+
lookup = await lookUpKey(keyId, bypassKeyCacheReads(keyCache));
|
|
5967
|
+
keyLookups.set(keyId.href, lookup);
|
|
5968
|
+
continue;
|
|
5797
5969
|
}
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
error,
|
|
5801
|
-
keyId: sigInput.keyId,
|
|
5802
|
-
algorithm: sigInput.alg
|
|
5803
|
-
});
|
|
5804
|
-
setFailure(invalidSignatureResult(keyId), candidateAlgorithm);
|
|
5970
|
+
setFailure(invalidSignatureResult(keyId), result.algorithm);
|
|
5971
|
+
break;
|
|
5805
5972
|
}
|
|
5806
5973
|
}
|
|
5807
5974
|
metricsContext.algorithm = failureAlgorithm;
|
|
@@ -5901,16 +6068,19 @@ async function doubleKnock(request, identity, options = {}) {
|
|
|
5901
6068
|
}
|
|
5902
6069
|
async function doubleKnockInternal(request, identity, options, redirected = 0, visited = /* @__PURE__ */ new Set()) {
|
|
5903
6070
|
const { specDeterminer, log, tracerProvider, signal } = options;
|
|
6071
|
+
signal?.throwIfAborted();
|
|
5904
6072
|
const maximumRedirection = options.maxRedirection ?? DEFAULT_MAX_REDIRECTION;
|
|
5905
6073
|
visited.add(request.url);
|
|
5906
6074
|
const origin = new URL(request.url).origin;
|
|
5907
6075
|
const firstTrySpec = specDeterminer == null ? "rfc9421" : await specDeterminer.determineSpec(origin);
|
|
6076
|
+
signal?.throwIfAborted();
|
|
5908
6077
|
const body = options.body !== void 0 ? options.body : request.method !== "GET" && request.method !== "HEAD" ? await request.clone().arrayBuffer() : null;
|
|
5909
6078
|
let signedRequest = await signRequest(request, identity.privateKey, identity.keyId, {
|
|
5910
6079
|
spec: firstTrySpec,
|
|
5911
6080
|
tracerProvider,
|
|
5912
6081
|
body
|
|
5913
6082
|
});
|
|
6083
|
+
signal?.throwIfAborted();
|
|
5914
6084
|
log?.(signedRequest);
|
|
5915
6085
|
let response = await fetchDoubleKnockRequest(request, signedRequest, signal);
|
|
5916
6086
|
if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) {
|
|
@@ -5935,6 +6105,7 @@ async function doubleKnockInternal(request, identity, options, redirected = 0, v
|
|
|
5935
6105
|
let fulfilled = false;
|
|
5936
6106
|
let challengeRequest;
|
|
5937
6107
|
for (const entry of entries) {
|
|
6108
|
+
signal?.throwIfAborted();
|
|
5938
6109
|
const rfc9421 = fulfillAcceptSignature(entry, localKeyId, localAlg);
|
|
5939
6110
|
if (rfc9421 == null) continue;
|
|
5940
6111
|
logger.debug("Received Accept-Signature challenge; accumulating label {label} and components {components}.", {
|
|
@@ -5957,6 +6128,7 @@ async function doubleKnockInternal(request, identity, options, redirected = 0, v
|
|
|
5957
6128
|
}
|
|
5958
6129
|
}
|
|
5959
6130
|
if (fulfilled && challengeRequest != null) {
|
|
6131
|
+
signal?.throwIfAborted();
|
|
5960
6132
|
signedRequest = challengeRequest;
|
|
5961
6133
|
log?.(signedRequest);
|
|
5962
6134
|
response = await fetch(signedRequest, {
|
|
@@ -5981,11 +6153,13 @@ async function doubleKnockInternal(request, identity, options, redirected = 0, v
|
|
|
5981
6153
|
status: response.status,
|
|
5982
6154
|
statusText: response.statusText
|
|
5983
6155
|
});
|
|
6156
|
+
signal?.throwIfAborted();
|
|
5984
6157
|
signedRequest = await signRequest(request, identity.privateKey, identity.keyId, {
|
|
5985
6158
|
spec,
|
|
5986
6159
|
tracerProvider,
|
|
5987
6160
|
body
|
|
5988
6161
|
});
|
|
6162
|
+
signal?.throwIfAborted();
|
|
5989
6163
|
log?.(signedRequest);
|
|
5990
6164
|
response = await fetchDoubleKnockRequest(request, signedRequest, signal);
|
|
5991
6165
|
if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) {
|
|
@@ -6028,4 +6202,4 @@ function timingSafeEqual(a, b) {
|
|
|
6028
6202
|
return result === 0;
|
|
6029
6203
|
}
|
|
6030
6204
|
//#endregion
|
|
6031
|
-
export {
|
|
6205
|
+
export { instrumentDocumentLoader as $, hasSignature as A, name as At, fetchActorDocument as B, InvalidContextReferenceError as C, hasPortableActor as Ct, createSignature as D, isPortableUri as Dt, compactJsonLd as E, isPortableKeyId as Et, verifyCompactJsonLd as F, validateCryptoKey as G, fetchKeyDetailed as H, verifyJsonLd as I, getVerifiedKeyOwnerEvidence as J, verifyKeyOwnership as K, verifySignature as L, isClearlyMalformedContextReference as M, isInvalidUrlTypeError as N, detachSignature as O, isSameObjectId as Ot, signJsonLd as P, getRemoteHost as Q, wrapContextLoaderForJsonLd as R, normalizeOutgoingActivityJsonLd as S, getRawCanonicalPortableId as St, attachSignature as T, isPortableId as Tt, generateCryptoKeyPair as U, fetchKey as V, importJwk as W, getDurationMs as X, parseKeyOwnerEvidence as Y, getFederationMetrics as Z, verifyObject as _, validateAcceptSignature as _t, signRequest as a, recordCollectionTotalItems as at, verifyPortableObjectProofWithRoot as b, getGatewayKeyBase as bt, verifyRequestDetailed as c, recordInboxActivity as ct, InvalidPortableObjectIdError as d, recordWebFingerHandle as dt, isAbortError$1 as et, classifyFep2277CoreType as f, registerQueueDepthGauge as ft, verifyMapLocalProof as g, parseAcceptSignature as gt, signObject as h, fulfillAcceptSignature as ht, selectRequestSignature as i, recordCollectionRequest as it, hasSignatureLike as j, version as jt, getNormalizationContextLoader as k, isSamePublicKey as kt, fetchPortableGatewayKey as l, recordOutboxActivity as lt, hasProofLike as m, formatAcceptSignature as mt, listRequestSignatures as n, recordCollectionDispatchDuration as nt, validateMaxSignatures as o, recordDocumentCache as ot, createProof as p, preloadedOnlyDocumentLoader as pt, MAX_PORTABLE_KEY_TTL as q, parseRfc9421SignatureInput as r, recordCollectionPageItems as rt, verifyRequest as s, recordFanoutRecipients as st, doubleKnock as t, recordCircuitBreakerStateChange as tt, resolvePortableActorKey as u, recordOutboxEnqueue as ut, verifyPortableObjectProof as v, getAuthenticationOrigin as vt, assertSafeJsonLd as w, isCompatibleKeyId as wt, verifyProof as x, getPortableDid as xt, verifyPortableObjectProofPolicy as y, getCanonicalPortableId as yt, exportJwk as z };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/// <reference lib="esnext.temporal" />
|
|
2
2
|
import { Actor, CryptographicKey, Multikey } from "@fedify/vocab";
|
|
3
|
-
import { MeterProvider, TracerProvider } from "@opentelemetry/api";
|
|
4
3
|
import { DocumentLoader } from "@fedify/vocab-runtime";
|
|
4
|
+
import { MeterProvider, TracerProvider } from "@opentelemetry/api";
|
|
5
5
|
|
|
6
6
|
//#region src/sig/portable-key.d.ts
|
|
7
7
|
/**
|
|
@@ -601,6 +601,30 @@ interface VerifyRequestOptions {
|
|
|
601
601
|
*/
|
|
602
602
|
spec?: HttpMessageSignaturesSpec;
|
|
603
603
|
/**
|
|
604
|
+
* The maximum number of [RFC 9421] signatures of a request to verify.
|
|
605
|
+
* A request can carry several signatures, each of which may make Fedify
|
|
606
|
+
* fetch the key that it names, so only the first ones in the order of
|
|
607
|
+
* the `Signature-Input` header are verified, and the rest are ignored as
|
|
608
|
+
* if they were absent. Every signature among the first ones counts, even
|
|
609
|
+
* if it fails before its key is fetched, e.g., because it is too old, or
|
|
610
|
+
* names the same key as another one. The key of a key ID is looked up
|
|
611
|
+
* only once for all the signatures that name it.
|
|
612
|
+
*
|
|
613
|
+
* It has to be a positive integer, or `Infinity` to verify every
|
|
614
|
+
* signature, which lets a single request make Fedify fetch any number of
|
|
615
|
+
* keys. Draft-cavage HTTP Signatures carry a single signature, so this
|
|
616
|
+
* option does not affect them.
|
|
617
|
+
*
|
|
618
|
+
* Three by default.
|
|
619
|
+
*
|
|
620
|
+
* [RFC 9421]: https://www.rfc-editor.org/rfc/rfc9421
|
|
621
|
+
* @throws {RangeError} Thrown by {@link verifyRequest} and
|
|
622
|
+
* {@link verifyRequestDetailed} if the value is not a positive
|
|
623
|
+
* integer or `Infinity`.
|
|
624
|
+
* @since 2.4.0
|
|
625
|
+
*/
|
|
626
|
+
maxSignatures?: number;
|
|
627
|
+
/**
|
|
604
628
|
* The OpenTelemetry tracer provider. If omitted, the global tracer provider
|
|
605
629
|
* is used.
|
|
606
630
|
* @since 1.3.0
|
|
@@ -651,6 +675,8 @@ type VerifyRequestDetailedResult = {
|
|
|
651
675
|
* @param options Options for verifying the request.
|
|
652
676
|
* @returns The public key of the verified signature, or `null` if the signature
|
|
653
677
|
* could not be verified.
|
|
678
|
+
* @throws {RangeError} Thrown if {@link VerifyRequestOptions.maxSignatures}
|
|
679
|
+
* is not a positive integer or `Infinity`.
|
|
654
680
|
*/
|
|
655
681
|
declare function verifyRequest(request: Request, options?: VerifyRequestOptions): Promise<CryptographicKey | null>;
|
|
656
682
|
/**
|
|
@@ -660,6 +686,8 @@ declare function verifyRequest(request: Request, options?: VerifyRequestOptions)
|
|
|
660
686
|
* @param request The request to verify.
|
|
661
687
|
* @param options Options for verifying the request.
|
|
662
688
|
* @returns The verified public key, or a structured verification failure.
|
|
689
|
+
* @throws {RangeError} Thrown if {@link VerifyRequestOptions.maxSignatures}
|
|
690
|
+
* is not a positive integer or `Infinity`.
|
|
663
691
|
* @since 2.1.0
|
|
664
692
|
*/
|
|
665
693
|
declare function verifyRequestDetailed(request: Request, options?: VerifyRequestOptions): Promise<VerifyRequestDetailedResult>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/// <reference lib="esnext.temporal" />
|
|
2
2
|
import { Actor, CryptographicKey, Multikey } from "@fedify/vocab";
|
|
3
|
-
import { DocumentLoader } from "@fedify/vocab-runtime";
|
|
4
3
|
import { MeterProvider, TracerProvider } from "@opentelemetry/api";
|
|
4
|
+
import { DocumentLoader } from "@fedify/vocab-runtime";
|
|
5
5
|
|
|
6
6
|
//#region src/sig/portable-key.d.ts
|
|
7
7
|
/**
|
|
@@ -601,6 +601,30 @@ interface VerifyRequestOptions {
|
|
|
601
601
|
*/
|
|
602
602
|
spec?: HttpMessageSignaturesSpec;
|
|
603
603
|
/**
|
|
604
|
+
* The maximum number of [RFC 9421] signatures of a request to verify.
|
|
605
|
+
* A request can carry several signatures, each of which may make Fedify
|
|
606
|
+
* fetch the key that it names, so only the first ones in the order of
|
|
607
|
+
* the `Signature-Input` header are verified, and the rest are ignored as
|
|
608
|
+
* if they were absent. Every signature among the first ones counts, even
|
|
609
|
+
* if it fails before its key is fetched, e.g., because it is too old, or
|
|
610
|
+
* names the same key as another one. The key of a key ID is looked up
|
|
611
|
+
* only once for all the signatures that name it.
|
|
612
|
+
*
|
|
613
|
+
* It has to be a positive integer, or `Infinity` to verify every
|
|
614
|
+
* signature, which lets a single request make Fedify fetch any number of
|
|
615
|
+
* keys. Draft-cavage HTTP Signatures carry a single signature, so this
|
|
616
|
+
* option does not affect them.
|
|
617
|
+
*
|
|
618
|
+
* Three by default.
|
|
619
|
+
*
|
|
620
|
+
* [RFC 9421]: https://www.rfc-editor.org/rfc/rfc9421
|
|
621
|
+
* @throws {RangeError} Thrown by {@link verifyRequest} and
|
|
622
|
+
* {@link verifyRequestDetailed} if the value is not a positive
|
|
623
|
+
* integer or `Infinity`.
|
|
624
|
+
* @since 2.4.0
|
|
625
|
+
*/
|
|
626
|
+
maxSignatures?: number;
|
|
627
|
+
/**
|
|
604
628
|
* The OpenTelemetry tracer provider. If omitted, the global tracer provider
|
|
605
629
|
* is used.
|
|
606
630
|
* @since 1.3.0
|
|
@@ -651,6 +675,8 @@ type VerifyRequestDetailedResult = {
|
|
|
651
675
|
* @param options Options for verifying the request.
|
|
652
676
|
* @returns The public key of the verified signature, or `null` if the signature
|
|
653
677
|
* could not be verified.
|
|
678
|
+
* @throws {RangeError} Thrown if {@link VerifyRequestOptions.maxSignatures}
|
|
679
|
+
* is not a positive integer or `Infinity`.
|
|
654
680
|
*/
|
|
655
681
|
declare function verifyRequest(request: Request, options?: VerifyRequestOptions): Promise<CryptographicKey | null>;
|
|
656
682
|
/**
|
|
@@ -660,6 +686,8 @@ declare function verifyRequest(request: Request, options?: VerifyRequestOptions)
|
|
|
660
686
|
* @param request The request to verify.
|
|
661
687
|
* @param options Options for verifying the request.
|
|
662
688
|
* @returns The verified public key, or a structured verification failure.
|
|
689
|
+
* @throws {RangeError} Thrown if {@link VerifyRequestOptions.maxSignatures}
|
|
690
|
+
* is not a positive integer or `Infinity`.
|
|
663
691
|
* @since 2.1.0
|
|
664
692
|
*/
|
|
665
693
|
declare function verifyRequestDetailed(request: Request, options?: VerifyRequestOptions): Promise<VerifyRequestDetailedResult>;
|