@fedify/fedify 2.4.0-dev.2127 → 2.4.0-dev.2129
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-B2bgGYbP.mjs → builder-C0bxZDL0.mjs} +3 -3
- 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-SVlHSzV7.mjs → compound-proof-D9Kljk_7.mjs} +3 -3
- package/dist/{context-Ci5vuUr_.d.ts → context-Cz1w5Krc.d.ts} +1 -1
- package/dist/{context-Bm6ldGvx.d.cts → context-Dvec_D_h.d.cts} +1 -1
- package/dist/{deno-Be9xoxps.mjs → deno-_o3lkDlG.mjs} +1 -1
- package/dist/{docloader-Dtuyb0ps.mjs → docloader-BbS0rO-Y.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 +5 -5
- package/dist/federation/hashlink-media.test.mjs +2 -2
- package/dist/federation/idempotency.test.mjs +2 -2
- 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 +2 -2
- package/dist/federation/mod.d.ts +2 -2
- package/dist/federation/mod.js +1 -1
- package/dist/federation/outgoing-proof.test.mjs +6 -6
- package/dist/federation/portable-actor.test.mjs +3 -3
- package/dist/federation/portable-chain.test.mjs +3 -3
- package/dist/federation/portable-gateway-keys.test.mjs +76 -5
- package/dist/federation/portable-inbox.test.mjs +9 -4
- 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-B6IHuS6n.cjs → federation-DRY8KrkB.cjs} +12 -12
- package/dist/{http-o8l4QeUY.cjs → http-B1J6E-bT.cjs} +384 -33
- package/dist/{http-BFeK9sBg.mjs → http-BpOiZE09.mjs} +18 -12
- package/dist/{http-D76Py0M4.d.cts → http-C-x_OLSp.d.ts} +57 -14
- package/dist/{http-Rmbiu-5S.js → http-C2M6wcs8.js} +374 -35
- package/dist/{http-BrQgkJay.d.ts → http-CjezPjFX.d.cts} +57 -14
- package/dist/{key-Cc1b6pW9.mjs → key-DolJjJab.mjs} +103 -16
- package/dist/{kv-cache-tLKBfxHc.mjs → kv-cache-Bq2cah0c.mjs} +1 -1
- package/dist/{kv-cache-BweDce2I.js → kv-cache-D7TiyMxv.js} +1 -1
- package/dist/{kv-cache-BUkgZAcX.cjs → kv-cache-D9nRbG2Q.cjs} +1 -1
- package/dist/{ld-DQNTIrAe.mjs → ld-Dwc176V4.mjs} +3 -3
- package/dist/{metrics-DDE9S63Q.mjs → metrics-CDaHfBXY.mjs} +1 -1
- package/dist/{middleware-CsQorLC_.mjs → middleware-CmB1FD8A.mjs} +1 -1
- package/dist/{middleware-K9aLnf57.js → middleware-CylX-pvI.js} +12 -12
- package/dist/{middleware-BdhvduR9.mjs → middleware-DN3MdTDg.mjs} +24 -24
- package/dist/{mod-Co53r-VP.d.ts → mod-Bz9tFXI2.d.ts} +1 -1
- package/dist/{mod-Cwdt_CWU.d.ts → mod-CZetqT5h.d.ts} +1 -1
- package/dist/{mod-Cr79cQlc.d.cts → mod-D4g9ffEn.d.cts} +1 -1
- package/dist/{mod-AVir5nVI.d.cts → mod-DGOIxEfb.d.cts} +1 -1
- package/dist/{mod-Byt5tvAS.d.cts → mod-S--JxYjo.d.cts} +1 -1
- package/dist/{mod-eVOUPbQE.d.ts → mod-owwElvpw.d.ts} +1 -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-CCfdL83M.mjs → owner-BNHhwjNu.mjs} +37 -4
- package/dist/{portable-collection-WXaHkNqC.mjs → portable-collection-CRpTUHCY.mjs} +2 -2
- package/dist/{portable-collection-DdXsKBg4.js → portable-collection-CgglAI9B.js} +34 -1
- package/dist/{portable-collection-DXogVs__.cjs → portable-collection-PTGD0q70.cjs} +34 -1
- package/dist/portable-key-DqX2emHQ.mjs +395 -0
- package/dist/{portable-key-id-T7lecIbt.mjs → portable-key-id-Bg259c4Z.mjs} +69 -2
- package/dist/{proof-CW8k0R1G.mjs → proof-B4_0E-KU.mjs} +5 -5
- package/dist/{send--FORaIc4.mjs → send-Dpu5qbP5.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 +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.test.mjs +479 -5
- package/dist/sig/proof.test.mjs +3 -3
- package/dist/{temporal-CffY_XOJ.mjs → temporal-78JVR3GK.mjs} +1 -1
- package/dist/testing/kv.test.mjs +1 -1
- 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/package.json +6 -6
- package/dist/portable-key-C-fPq_Yf.mjs +0 -214
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
const { URLPattern } = require("urlpattern-polyfill");
|
|
2
2
|
const require_rolldown_runtime = require("./rolldown-runtime-Dmm3t92W.cjs");
|
|
3
3
|
const require_transformers = require("./transformers-BalCrSsr.cjs");
|
|
4
|
-
const require_http = require("./http-
|
|
4
|
+
const require_http = require("./http-B1J6E-bT.cjs");
|
|
5
5
|
const require_types = require("./types-Be2zbyeG.cjs");
|
|
6
|
-
const require_portable_collection = require("./portable-collection-
|
|
7
|
-
const require_kv_cache = require("./kv-cache-
|
|
6
|
+
const require_portable_collection = require("./portable-collection-PTGD0q70.cjs");
|
|
7
|
+
const require_kv_cache = require("./kv-cache-D9nRbG2Q.cjs");
|
|
8
8
|
let _logtape_logtape = require("@logtape/logtape");
|
|
9
9
|
let _fedify_uri_template = require("@fedify/uri-template");
|
|
10
10
|
let _fedify_vocab = require("@fedify/vocab");
|
|
@@ -4671,7 +4671,7 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
4671
4671
|
"http_signatures.verified": httpSigKey != null,
|
|
4672
4672
|
"http_signatures.key_id": httpSigKey?.id?.href ?? ""
|
|
4673
4673
|
});
|
|
4674
|
-
if (!skipSignatureVerification && !proofVerified && (isPortableActivity(activity) ||
|
|
4674
|
+
if (!skipSignatureVerification && !proofVerified && (isPortableActivity(activity) || isPortableActorKey(httpSigKey))) {
|
|
4675
4675
|
if (deferredLdSignatureError != null) throw deferredLdSignatureError;
|
|
4676
4676
|
logger.error("The activity {activityId} of the portable actor {actorId} is not authenticated by a valid Object Integrity Proof.", {
|
|
4677
4677
|
activity: json,
|
|
@@ -5452,13 +5452,13 @@ const MIN_COMPONENTS = [
|
|
|
5452
5452
|
"@authority"
|
|
5453
5453
|
];
|
|
5454
5454
|
/**
|
|
5455
|
-
* Checks whether an HTTP Signature key is a
|
|
5456
|
-
*
|
|
5457
|
-
* a portable actor, which only the portable actor's
|
|
5458
|
-
* establish.
|
|
5455
|
+
* Checks whether an HTTP Signature key is a key of an FEP-ef61 portable
|
|
5456
|
+
* actor, i.e., its ID is a compatible identifier or an `ap:` or `ap+ef61:`
|
|
5457
|
+
* URI, and it is owned by a portable actor, which only the portable actor's
|
|
5458
|
+
* signed document can establish.
|
|
5459
5459
|
*/
|
|
5460
|
-
function
|
|
5461
|
-
return key?.id != null && require_http.
|
|
5460
|
+
function isPortableActorKey(key) {
|
|
5461
|
+
return key?.id != null && require_http.isPortableKeyId(key.id) && key.ownerId != null && require_http.isPortableId(key.ownerId);
|
|
5462
5462
|
}
|
|
5463
5463
|
/**
|
|
5464
5464
|
* Checks whether an activity needs an Object Integrity Proof by its DID:
|
|
@@ -5895,7 +5895,7 @@ async function signOutgoingActivity(activity, candidates, options) {
|
|
|
5895
5895
|
hasProof = true;
|
|
5896
5896
|
break;
|
|
5897
5897
|
}
|
|
5898
|
-
const keys = candidates.filter((c) => c.privateKey.algorithm.name === "Ed25519" && !require_http.
|
|
5898
|
+
const keys = candidates.filter((c) => c.privateKey.algorithm.name === "Ed25519" && !require_http.isPortableKeyId(c.verificationMethod));
|
|
5899
5899
|
if (!hasProof && activity.id != null && require_http.isPortableId(activity.id)) return await sign(selectPortableActivityKey(activity, activity.id, keys));
|
|
5900
5900
|
if (keys.length < 1 || hasProof && !options.appendToExistingProofs) return {
|
|
5901
5901
|
activity,
|
|
@@ -7863,7 +7863,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
7863
7863
|
const ldSignable = !require_http.hasPortableActor(activity);
|
|
7864
7864
|
for (const { keyId, privateKey } of keys) {
|
|
7865
7865
|
require_http.validateCryptoKey(privateKey, "private");
|
|
7866
|
-
if (ldSignable && rsaKey == null && privateKey.algorithm.name === "RSASSA-PKCS1-v1_5" && !require_http.
|
|
7866
|
+
if (ldSignable && rsaKey == null && privateKey.algorithm.name === "RSASSA-PKCS1-v1_5" && !require_http.isPortableKeyId(keyId)) rsaKey = {
|
|
7867
7867
|
keyId,
|
|
7868
7868
|
privateKey
|
|
7869
7869
|
};
|
|
@@ -16,10 +16,12 @@ json_canon = require_rolldown_runtime.__toESM(json_canon, 1);
|
|
|
16
16
|
let _opentelemetry_semantic_conventions = require("@opentelemetry/semantic-conventions");
|
|
17
17
|
//#region deno.json
|
|
18
18
|
var name = "@fedify/fedify";
|
|
19
|
-
var version = "2.4.0-dev.
|
|
19
|
+
var version = "2.4.0-dev.2129+c0ddb433";
|
|
20
20
|
//#endregion
|
|
21
21
|
//#region src/sig/portable-key-id.ts
|
|
22
22
|
const COMPATIBLE_KEY_ID_PATH_PATTERN = /^\/\.well-known\/apgateway\/did(?::|%3A)/i;
|
|
23
|
+
const RAW_PORTABLE_URI_PATTERN = /^ap(?:\+ef61)?:\/\//i;
|
|
24
|
+
const RAW_COMPATIBLE_ID_PREFIX_PATTERN = /^https?:\/\/[^/?#]*\/\.well-known\/apgateway\/(?=did(?::|%3A))/i;
|
|
23
25
|
/**
|
|
24
26
|
* Checks whether the URL is an `ap:` or `ap+ef61:` URI.
|
|
25
27
|
* @internal
|
|
@@ -83,6 +85,20 @@ function isCompatibleKeyId(keyId) {
|
|
|
83
85
|
return (keyId.protocol === "http:" || keyId.protocol === "https:") && COMPATIBLE_KEY_ID_PATH_PATTERN.test(keyId.pathname);
|
|
84
86
|
}
|
|
85
87
|
/**
|
|
88
|
+
* Checks whether a key ID may name a key that is valid only for HTTP
|
|
89
|
+
* Signatures: a gateway key at an [FEP-ef61] compatible identifier, or a key
|
|
90
|
+
* of a portable actor at an `ap:` or `ap+ef61:` URI. Neither ever makes or
|
|
91
|
+
* verifies an Object Integrity Proof or a Linked Data Signature: a portable
|
|
92
|
+
* object's proof is made by its DID, and a proof made with a gateway key
|
|
93
|
+
* would claim that the gateway authored the object.
|
|
94
|
+
*
|
|
95
|
+
* [FEP-ef61]: https://w3id.org/fep/ef61
|
|
96
|
+
* @internal
|
|
97
|
+
*/
|
|
98
|
+
function isPortableKeyId(keyId) {
|
|
99
|
+
return isPortableUri(keyId) || isCompatibleKeyId(keyId);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
86
102
|
* Gets the canonical portable ID of an actor ID that is either an `ap:` or
|
|
87
103
|
* `ap+ef61:` URI or a compatible identifier.
|
|
88
104
|
* @returns The canonical portable ID, or `null` if the ID is neither, or is
|
|
@@ -101,6 +117,57 @@ function getCanonicalPortableId(id) {
|
|
|
101
117
|
}
|
|
102
118
|
}
|
|
103
119
|
/**
|
|
120
|
+
* Gets the canonical portable ID of an `ap:` or `ap+ef61:` URI or
|
|
121
|
+
* a compatible identifier as it is written, before any URL parsing.
|
|
122
|
+
* Parsing an ID into a `URL` resolves dot segments in its path, which would
|
|
123
|
+
* make it identify another portable object, e.g.,
|
|
124
|
+
* `ap://did:key:z6Mk…/x/../actor` would become `ap://did:key:z6Mk…/actor`.
|
|
125
|
+
* Such an ID has no canonical portable ID here.
|
|
126
|
+
* @param raw The ID as it is written.
|
|
127
|
+
* @returns The canonical portable ID, fragment included, or `null` if the ID
|
|
128
|
+
* is neither an `ap:` or `ap+ef61:` URI nor a compatible identifier,
|
|
129
|
+
* is malformed, or would identify another object once parsed.
|
|
130
|
+
* @internal
|
|
131
|
+
*/
|
|
132
|
+
function getRawCanonicalPortableId(raw) {
|
|
133
|
+
let canonical;
|
|
134
|
+
let parsed;
|
|
135
|
+
try {
|
|
136
|
+
if (RAW_PORTABLE_URI_PATTERN.test(raw)) {
|
|
137
|
+
canonical = (0, _fedify_vocab_runtime.canonicalizePortableUri)(raw);
|
|
138
|
+
parsed = (0, _fedify_vocab_runtime.canonicalizePortableUri)((0, _fedify_vocab_runtime.formatIri)((0, _fedify_vocab_runtime.parseIri)(raw)));
|
|
139
|
+
} else {
|
|
140
|
+
const prefix = raw.match(RAW_COMPATIBLE_ID_PREFIX_PATTERN);
|
|
141
|
+
if (prefix == null || !URL.canParse(raw)) return null;
|
|
142
|
+
canonical = (0, _fedify_vocab_runtime.canonicalizePortableUri)("ap://" + raw.slice(prefix[0].length));
|
|
143
|
+
parsed = getCanonicalPortableId(new URL(raw));
|
|
144
|
+
}
|
|
145
|
+
} catch (error) {
|
|
146
|
+
if (error instanceof TypeError) return null;
|
|
147
|
+
throw error;
|
|
148
|
+
}
|
|
149
|
+
return canonical === parsed ? canonical : null;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Parses a key ID, e.g., the `keyId` of an HTTP Signature, into a `URL`.
|
|
153
|
+
* An `ap:` or `ap+ef61:` key ID is parsed into the internal `URL` form of
|
|
154
|
+
* portable URIs. A portable or compatible key ID that is malformed, or
|
|
155
|
+
* whose identity URL parsing would change, e.g., by resolving dot segments in
|
|
156
|
+
* its path, is refused, so that it can never be taken for, or looked up in
|
|
157
|
+
* the key cache as, the key of another object.
|
|
158
|
+
* @param raw The key ID as it is written.
|
|
159
|
+
* @returns The parsed key ID, or `null` if it is invalid.
|
|
160
|
+
* @internal
|
|
161
|
+
*/
|
|
162
|
+
function parseKeyIdString(raw) {
|
|
163
|
+
if (RAW_PORTABLE_URI_PATTERN.test(raw)) return getRawCanonicalPortableId(raw) == null ? null : (0, _fedify_vocab_runtime.parseIri)(raw);
|
|
164
|
+
if (!URL.canParse(raw)) return null;
|
|
165
|
+
const url = new URL(raw);
|
|
166
|
+
if (isPortableUri(url)) return null;
|
|
167
|
+
if (!RAW_COMPATIBLE_ID_PREFIX_PATTERN.test(raw)) return isCompatibleKeyId(url) ? null : url;
|
|
168
|
+
return getRawCanonicalPortableId(raw) == null ? null : url;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
104
171
|
* Builds the base of the key IDs of a gateway's keys for a portable actor:
|
|
105
172
|
* the actor's compatible identifier on the gateway.
|
|
106
173
|
* @param actorId The portable actor's ID, either an `ap:` or `ap+ef61:` URI
|
|
@@ -4908,6 +4975,17 @@ function bypassKeyCacheReads(keyCache) {
|
|
|
4908
4975
|
};
|
|
4909
4976
|
return bypassed;
|
|
4910
4977
|
}
|
|
4978
|
+
/**
|
|
4979
|
+
* Parses a key ID given as a string. Unlike `new URL()`, it accepts `ap:`
|
|
4980
|
+
* and `ap+ef61:` URIs, and refuses a portable or compatible key ID whose
|
|
4981
|
+
* identity URL parsing would change, e.g., by resolving dot segments.
|
|
4982
|
+
* @throws {TypeError} If the key ID is invalid.
|
|
4983
|
+
*/
|
|
4984
|
+
function parseKeyIdOrThrow(keyId) {
|
|
4985
|
+
const parsed = parseKeyIdString(keyId);
|
|
4986
|
+
if (parsed == null) throw new TypeError(`Invalid key ID: ${keyId}`);
|
|
4987
|
+
return parsed;
|
|
4988
|
+
}
|
|
4911
4989
|
async function withFetchKeySpan(keyId, tracerProvider, fetcher) {
|
|
4912
4990
|
tracerProvider ??= _opentelemetry_api.trace.getTracerProvider();
|
|
4913
4991
|
return await tracerProvider.getTracer(name, version).startActiveSpan("activitypub.fetch_key", {
|
|
@@ -4944,9 +5022,11 @@ async function withFetchKeySpan(keyId, tracerProvider, fetcher) {
|
|
|
4944
5022
|
* Gateway keys of [FEP-ef61] portable actors, i.e., keys whose IDs are
|
|
4945
5023
|
* compatible identifiers that dereference to portable actor documents, are
|
|
4946
5024
|
* not resolved by this function, as they only authenticate HTTP requests;
|
|
4947
|
-
* use `verifyRequest()` or `getKeyOwner()` for them instead.
|
|
4948
|
-
*
|
|
4949
|
-
*
|
|
5025
|
+
* use `verifyRequest()` or `getKeyOwner()` for them instead. Nor are keys
|
|
5026
|
+
* of portable actors whose IDs are `ap:` or `ap+ef61:` URIs, for the same
|
|
5027
|
+
* reason. Keys whose IDs are compatible identifiers or `ap:` or `ap+ef61:`
|
|
5028
|
+
* URIs are never read from or written to the key cache under their IDs,
|
|
5029
|
+
* except for failures to fetch them.
|
|
4950
5030
|
*
|
|
4951
5031
|
* [FEP-ef61]: https://w3id.org/fep/ef61
|
|
4952
5032
|
* @template T The type of the key to fetch. Either {@link CryptographicKey}
|
|
@@ -4959,7 +5039,7 @@ async function withFetchKeySpan(keyId, tracerProvider, fetcher) {
|
|
|
4959
5039
|
* @since 1.3.0
|
|
4960
5040
|
*/
|
|
4961
5041
|
function fetchKey(keyId, cls, options = {}) {
|
|
4962
|
-
keyId = typeof keyId === "string" ?
|
|
5042
|
+
keyId = typeof keyId === "string" ? parseKeyIdOrThrow(keyId) : keyId;
|
|
4963
5043
|
return withFetchKeySpan(keyId, options.tracerProvider, () => fetchKeyInternal(keyId, cls, options));
|
|
4964
5044
|
}
|
|
4965
5045
|
/**
|
|
@@ -4969,9 +5049,11 @@ function fetchKey(keyId, cls, options = {}) {
|
|
|
4969
5049
|
* Gateway keys of [FEP-ef61] portable actors, i.e., keys whose IDs are
|
|
4970
5050
|
* compatible identifiers that dereference to portable actor documents, are
|
|
4971
5051
|
* not resolved by this function, as they only authenticate HTTP requests;
|
|
4972
|
-
* use `verifyRequest()` or `getKeyOwner()` for them instead.
|
|
4973
|
-
*
|
|
4974
|
-
*
|
|
5052
|
+
* use `verifyRequest()` or `getKeyOwner()` for them instead. Nor are keys
|
|
5053
|
+
* of portable actors whose IDs are `ap:` or `ap+ef61:` URIs, for the same
|
|
5054
|
+
* reason. Keys whose IDs are compatible identifiers or `ap:` or `ap+ef61:`
|
|
5055
|
+
* URIs are never read from or written to the key cache under their IDs,
|
|
5056
|
+
* except for failures to fetch them.
|
|
4975
5057
|
*
|
|
4976
5058
|
* [FEP-ef61]: https://w3id.org/fep/ef61
|
|
4977
5059
|
*
|
|
@@ -4985,7 +5067,7 @@ function fetchKey(keyId, cls, options = {}) {
|
|
|
4985
5067
|
* @since 2.1.0
|
|
4986
5068
|
*/
|
|
4987
5069
|
async function fetchKeyDetailed(keyId, cls, options = {}) {
|
|
4988
|
-
const cacheKey = typeof keyId === "string" ?
|
|
5070
|
+
const cacheKey = typeof keyId === "string" ? parseKeyIdOrThrow(keyId) : keyId;
|
|
4989
5071
|
return await withFetchKeySpan(cacheKey, options.tracerProvider, async () => {
|
|
4990
5072
|
return await fetchKeyWithResult(cacheKey, cls, options, async (cacheKey, keyId, keyCache, logger) => {
|
|
4991
5073
|
const fetchError = await keyCache?.getFetchError?.(cacheKey);
|
|
@@ -5124,7 +5206,7 @@ async function resolveDidKey(cacheKey, cls, keyCache, logger) {
|
|
|
5124
5206
|
cached: false
|
|
5125
5207
|
};
|
|
5126
5208
|
}
|
|
5127
|
-
async function resolveFetchedKey(document, cacheKey, documentUrl, keyId, cls, { documentLoader, contextLoader, keyCache, tracerProvider, keyIdBoundByCaller,
|
|
5209
|
+
async function resolveFetchedKey(document, cacheKey, documentUrl, keyId, cls, { documentLoader, contextLoader, keyCache, tracerProvider, keyIdBoundByCaller, portableKeyResolvers }, logger) {
|
|
5128
5210
|
let object;
|
|
5129
5211
|
try {
|
|
5130
5212
|
object = await _fedify_vocab.Object.fromJsonLd(document, {
|
|
@@ -5155,7 +5237,7 @@ async function resolveFetchedKey(document, cacheKey, documentUrl, keyId, cls, {
|
|
|
5155
5237
|
}
|
|
5156
5238
|
if (isCompatibleKeyId(cacheKey) && isPortableActorDocument(object)) {
|
|
5157
5239
|
const scopedCache = keyCache;
|
|
5158
|
-
if (
|
|
5240
|
+
if (portableKeyResolvers == null || cls !== _fedify_vocab.CryptographicKey) {
|
|
5159
5241
|
logger.debug("Failed to verify; key {keyId} is a gateway key of the portable actor {actorId}, which is only accepted for HTTP Signatures.", {
|
|
5160
5242
|
keyId,
|
|
5161
5243
|
actorId: object.id?.href
|
|
@@ -5166,7 +5248,7 @@ async function resolveFetchedKey(document, cacheKey, documentUrl, keyId, cls, {
|
|
|
5166
5248
|
cached: false
|
|
5167
5249
|
};
|
|
5168
5250
|
}
|
|
5169
|
-
const resolution = await
|
|
5251
|
+
const resolution = await portableKeyResolvers.gatewayKey(document, object, cacheKey);
|
|
5170
5252
|
if (resolution.type !== "verified") {
|
|
5171
5253
|
logger.debug("Failed to verify; the portable actor {actorId} does not vouch for its gateway key {keyId}.", {
|
|
5172
5254
|
keyId,
|
|
@@ -5326,6 +5408,11 @@ async function fetchKeyWithResult(cacheKey, cls, options, onCachedUnavailable, o
|
|
|
5326
5408
|
]);
|
|
5327
5409
|
const keyId = cacheKey.href;
|
|
5328
5410
|
const keyCache = options.keyCache;
|
|
5411
|
+
if (isPortableUri(cacheKey)) {
|
|
5412
|
+
const result = await fetchPortableActorKey(cacheKey, cls, options, onCachedUnavailable, onFetchError, logger);
|
|
5413
|
+
outcome = result.outcome;
|
|
5414
|
+
return result.result;
|
|
5415
|
+
}
|
|
5329
5416
|
if (isCompatibleKeyId(cacheKey)) {
|
|
5330
5417
|
const result = await fetchCompatibleKey(cacheKey, cls, options, onCachedUnavailable, onFetchError, logger);
|
|
5331
5418
|
outcome = result.outcome;
|
|
@@ -5377,7 +5464,7 @@ async function fetchKeyWithResult(cacheKey, cls, options, onCachedUnavailable, o
|
|
|
5377
5464
|
*/
|
|
5378
5465
|
function getCompatibleKeyScope(cls, options) {
|
|
5379
5466
|
if (cls !== _fedify_vocab.CryptographicKey) return "multikey";
|
|
5380
|
-
return options.
|
|
5467
|
+
return options.portableKeyResolvers == null ? "cryptographicKey" : "httpSignature";
|
|
5381
5468
|
}
|
|
5382
5469
|
/**
|
|
5383
5470
|
* Looks up a key at an [FEP-ef61] compatible identifier. Such a key may be
|
|
@@ -5431,8 +5518,75 @@ async function fetchCompatibleKey(cacheKey, cls, options, onCachedUnavailable, o
|
|
|
5431
5518
|
outcome: { result: resolved.key != null ? "fetched" : "invalid" }
|
|
5432
5519
|
};
|
|
5433
5520
|
}
|
|
5521
|
+
/**
|
|
5522
|
+
* Looks up a key of an [FEP-ef61] portable actor at an `ap:` or `ap+ef61:`
|
|
5523
|
+
* key ID. Such a key is accepted only for HTTP Signatures, and is rejected
|
|
5524
|
+
* for any other purpose without being fetched. Like keys at compatible
|
|
5525
|
+
* identifiers, what it resolves to is cached in the namespace of the lookup's
|
|
5526
|
+
* purpose, and a failure to fetch the actor's document from every gateway is
|
|
5527
|
+
* cached in the shared namespace. A failure that is neither, e.g., one
|
|
5528
|
+
* gateway being unreachable while another served an invalid document, is not
|
|
5529
|
+
* cached at all.
|
|
5530
|
+
*
|
|
5531
|
+
* [FEP-ef61]: https://w3id.org/fep/ef61
|
|
5532
|
+
*/
|
|
5533
|
+
async function fetchPortableActorKey(cacheKey, cls, options, onCachedUnavailable, onFetchError, logger) {
|
|
5534
|
+
const keyId = cacheKey.href;
|
|
5535
|
+
const resolvers = options.portableKeyResolvers;
|
|
5536
|
+
if (resolvers == null || cls !== _fedify_vocab.CryptographicKey) {
|
|
5537
|
+
logger.debug("Failed to verify; key {keyId} is a key of a portable actor, which is only accepted for HTTP Signatures.", { keyId });
|
|
5538
|
+
return {
|
|
5539
|
+
result: {
|
|
5540
|
+
key: null,
|
|
5541
|
+
cached: false
|
|
5542
|
+
},
|
|
5543
|
+
outcome: { result: "invalid" }
|
|
5544
|
+
};
|
|
5545
|
+
}
|
|
5546
|
+
const sharedCache = options.keyCache;
|
|
5547
|
+
const scopedCache = sharedCache?.compatibleKeyScope?.("httpSignature");
|
|
5548
|
+
const cached = await getCachedFetchKey(cacheKey, keyId, cls, scopedCache, logger);
|
|
5549
|
+
if (cached != null) return {
|
|
5550
|
+
result: cached,
|
|
5551
|
+
outcome: { result: "hit" }
|
|
5552
|
+
};
|
|
5553
|
+
if (await sharedCache?.get(cacheKey) === null) {
|
|
5554
|
+
logger.debug("Entry {keyId} found in cache, but it could not be fetched.", { keyId });
|
|
5555
|
+
return {
|
|
5556
|
+
result: await onCachedUnavailable(cacheKey, keyId, sharedCache, logger),
|
|
5557
|
+
outcome: { result: "hit" }
|
|
5558
|
+
};
|
|
5559
|
+
}
|
|
5560
|
+
logger.debug("Resolving key {keyId} to verify signature...", { keyId });
|
|
5561
|
+
const resolution = await resolvers.actorKey(cacheKey);
|
|
5562
|
+
if (resolution.type === "verified") {
|
|
5563
|
+
await scopedCache?.set(cacheKey, resolution.key, { expires: resolution.expires });
|
|
5564
|
+
return {
|
|
5565
|
+
result: {
|
|
5566
|
+
key: resolution.key,
|
|
5567
|
+
cached: false
|
|
5568
|
+
},
|
|
5569
|
+
outcome: { result: "fetched" }
|
|
5570
|
+
};
|
|
5571
|
+
}
|
|
5572
|
+
if (resolution.type === "rejected") {
|
|
5573
|
+
await scopedCache?.set(cacheKey, null);
|
|
5574
|
+
return {
|
|
5575
|
+
result: {
|
|
5576
|
+
key: null,
|
|
5577
|
+
cached: false
|
|
5578
|
+
},
|
|
5579
|
+
outcome: { result: "invalid" }
|
|
5580
|
+
};
|
|
5581
|
+
}
|
|
5582
|
+
await scopedCache?.delete(cacheKey);
|
|
5583
|
+
return {
|
|
5584
|
+
result: await onFetchError(resolution.error, cacheKey, keyId, resolution.cacheable ? sharedCache : void 0, logger),
|
|
5585
|
+
outcome: resolution.cacheable ? classifyFetchError(resolution.error) : { result: "error" }
|
|
5586
|
+
};
|
|
5587
|
+
}
|
|
5434
5588
|
async function fetchKeyInternal(keyId, cls, options = {}) {
|
|
5435
|
-
return await fetchKeyWithResult(typeof keyId === "string" ?
|
|
5589
|
+
return await fetchKeyWithResult(typeof keyId === "string" ? parseKeyIdOrThrow(keyId) : keyId, cls, options, (_cacheKey, _keyId, _keyCache, _logger) => {
|
|
5436
5590
|
return {
|
|
5437
5591
|
key: null,
|
|
5438
5592
|
cached: true
|
|
@@ -7601,6 +7755,14 @@ const SEC = "https://w3id.org/security#";
|
|
|
7601
7755
|
* - no more than one entry of either property has that ID; and
|
|
7602
7756
|
* - the key ID's origin is one of the actor's `gateways`.
|
|
7603
7757
|
*
|
|
7758
|
+
* An entry has the key's ID if its ID is the key ID, or any compatible
|
|
7759
|
+
* identifier on the same gateway, or the `ap:` or `ap+ef61:` URI, with the
|
|
7760
|
+
* same canonical portable ID. Some publishers, e.g., Mitra, list the keys
|
|
7761
|
+
* of portable actors under `ap:` URIs, but sign requests with compatible key
|
|
7762
|
+
* IDs on their own gateways. IDs are compared as they are written in
|
|
7763
|
+
* the signed document, so an ID that URL parsing would turn into another,
|
|
7764
|
+
* e.g., by resolving dot segments, has no match.
|
|
7765
|
+
*
|
|
7604
7766
|
* [FEP-8b32]: https://w3id.org/fep/8b32
|
|
7605
7767
|
* [FEP-ef61]: https://w3id.org/fep/ef61
|
|
7606
7768
|
* [FEP-521a]: https://w3id.org/fep/521a
|
|
@@ -7612,10 +7774,22 @@ const SEC = "https://w3id.org/security#";
|
|
|
7612
7774
|
* @returns The verification result, which is never `legacy`.
|
|
7613
7775
|
* @internal
|
|
7614
7776
|
*/
|
|
7615
|
-
|
|
7777
|
+
function verifyPortableGatewayKeyDocument(document, actor, keyId, options = {}) {
|
|
7778
|
+
return verifyPortableKeyDocument(document, actor, keyId, true, options);
|
|
7779
|
+
}
|
|
7780
|
+
/**
|
|
7781
|
+
* Verifies that the signed document of a portable actor vouches for the key
|
|
7782
|
+
* with the given ID, either a gateway key at a compatible key ID, or a key of
|
|
7783
|
+
* the actor itself at an `ap:` or `ap+ef61:` key ID. See
|
|
7784
|
+
* {@link verifyPortableGatewayKeyDocument} for the rules; a key of the actor
|
|
7785
|
+
* itself names no gateway, so instead of the key ID's origin being one of
|
|
7786
|
+
* the actor's `gateways`, the actor only has to have a valid gateway, as
|
|
7787
|
+
* FEP-ef61 requires of every portable actor.
|
|
7788
|
+
*/
|
|
7789
|
+
async function verifyPortableKeyDocument(document, actor, keyId, isGatewayKey, options) {
|
|
7616
7790
|
const reject = (reason) => {
|
|
7617
|
-
logger.debug("Failed to verify
|
|
7618
|
-
keyId: keyId
|
|
7791
|
+
logger.debug("Failed to verify key {keyId} of the portable actor {actorId}: {reason}", {
|
|
7792
|
+
keyId: (0, _fedify_vocab_runtime.formatIri)(keyId),
|
|
7619
7793
|
actorId: actor.id?.href,
|
|
7620
7794
|
reason
|
|
7621
7795
|
});
|
|
@@ -7627,8 +7801,9 @@ async function verifyPortableGatewayKeyDocument(document, actor, keyId, options
|
|
|
7627
7801
|
const keyBase = new URL(keyId.href);
|
|
7628
7802
|
keyBase.hash = "";
|
|
7629
7803
|
const claimedActorId = getCanonicalPortableId(keyBase);
|
|
7804
|
+
const claimedKeyId = getCanonicalPortableId(keyId);
|
|
7630
7805
|
const actorId = actor.id == null ? null : getCanonicalPortableId(actor.id);
|
|
7631
|
-
if (claimedActorId == null || actorId == null) return reject("The key ID or the actor ID is not a valid portable ID.");
|
|
7806
|
+
if (claimedActorId == null || claimedKeyId == null || actorId == null) return reject("The key ID or the actor ID is not a valid portable ID.");
|
|
7632
7807
|
if (claimedActorId !== actorId) return reject("The key ID is not a compatible identifier of the actor it dereferences to.");
|
|
7633
7808
|
let verification;
|
|
7634
7809
|
try {
|
|
@@ -7673,10 +7848,12 @@ async function verifyPortableGatewayKeyDocument(document, actor, keyId, options
|
|
|
7673
7848
|
if (!(error instanceof TypeError)) throw error;
|
|
7674
7849
|
return reject("The verified actor document is malformed.");
|
|
7675
7850
|
}
|
|
7676
|
-
|
|
7851
|
+
const rootId = root["@id"];
|
|
7852
|
+
if (!isPortableActorDocument(verifiedActor) || getCanonicalPortableId(verifiedActor.id) !== claimedActorId || typeof rootId !== "string" || getRawCanonicalPortableId(rootId) !== claimedActorId) return reject("The key ID is not a compatible identifier of the actor whose document is signed.");
|
|
7677
7853
|
actor = verifiedActor;
|
|
7678
|
-
const
|
|
7679
|
-
const
|
|
7854
|
+
const findKeyNodes = (values) => findNodes(values, claimedKeyId, isGatewayKey ? keyId.origin : null);
|
|
7855
|
+
const assertionNodes = findKeyNodes(root[`${SEC}assertionMethod`]);
|
|
7856
|
+
const publicKeyNodes = findKeyNodes(root[`${SEC}publicKey`]);
|
|
7680
7857
|
if (assertionNodes.length > 1 || publicKeyNodes.length > 1) return reject("The actor document has more than one key with the ID.");
|
|
7681
7858
|
let publicKey;
|
|
7682
7859
|
if (assertionNodes.length > 0) {
|
|
@@ -7706,7 +7883,9 @@ async function verifyPortableGatewayKeyDocument(document, actor, keyId, options
|
|
|
7706
7883
|
if (embedded.ownerId == null || getCanonicalPortableId(embedded.ownerId) !== actorId) return reject("The owner of the key is not the actor.");
|
|
7707
7884
|
publicKey = embedded.publicKey;
|
|
7708
7885
|
}
|
|
7709
|
-
if (
|
|
7886
|
+
if (isGatewayKey) {
|
|
7887
|
+
if (!actor.gateways.some((gateway) => (0, _fedify_vocab_runtime.isGatewayUrl)(gateway) && gateway.origin === keyId.origin)) return reject(`The gateway ${keyId.origin} is not listed in the actor's gateways.`);
|
|
7888
|
+
} else if (!actor.gateways.some(_fedify_vocab_runtime.isGatewayUrl)) return reject("The actor has no valid gateways.");
|
|
7710
7889
|
const key = new _fedify_vocab.CryptographicKey({
|
|
7711
7890
|
id: keyId,
|
|
7712
7891
|
owner: actor.id,
|
|
@@ -7761,15 +7940,170 @@ async function fetchPortableGatewayKey(keyId, options = {}) {
|
|
|
7761
7940
|
contextLoader
|
|
7762
7941
|
});
|
|
7763
7942
|
}
|
|
7764
|
-
|
|
7943
|
+
/**
|
|
7944
|
+
* Resolves a key of a portable actor at an `ap:` or `ap+ef61:` key ID, e.g.,
|
|
7945
|
+
* `ap://did:key:z6Mk…/actor#main-key`. Such a key ID names no gateway, so
|
|
7946
|
+
* the actor's document, i.e., the key ID without its fragment, is fetched
|
|
7947
|
+
* through the gateways in its `@gateway` location hints, one after another,
|
|
7948
|
+
* until one of them serves a document that vouches for the key. Without
|
|
7949
|
+
* location hints, the document loader is asked for the `ap:` URI itself,
|
|
7950
|
+
* which only a custom document loader can resolve. The hints are chosen by
|
|
7951
|
+
* whoever made the signature, but that does not matter: only a document with
|
|
7952
|
+
* a valid proof by the DID of the key ID vouches for the key.
|
|
7953
|
+
*
|
|
7954
|
+
* The key is taken as a key of the actor itself, not of a gateway, so it is
|
|
7955
|
+
* accepted by the rules of {@link verifyPortableGatewayKeyDocument}, except
|
|
7956
|
+
* that it is listed under the `ap:` or `ap+ef61:` URI with the same canonical
|
|
7957
|
+
* ID as the key ID, and that the actor only has to have a valid gateway.
|
|
7958
|
+
* The key ID must have a fragment; a key served as a standalone document is
|
|
7959
|
+
* not supported.
|
|
7960
|
+
*
|
|
7961
|
+
* If no gateway serves a document that vouches for the key, the result is
|
|
7962
|
+
* `rejected` only if every gateway served a document that definitely does not
|
|
7963
|
+
* vouch for it. If every gateway failed to serve a document at all, the
|
|
7964
|
+
* result is a cacheable `unavailable`, with the error of the first gateway if
|
|
7965
|
+
* every gateway responded with the same HTTP status, so that the status
|
|
7966
|
+
* describes the whole lookup. Otherwise, e.g., if one gateway was
|
|
7967
|
+
* unreachable and another served an invalid document, nothing tells what the
|
|
7968
|
+
* unreachable gateway would have served, so the result is an `unavailable`
|
|
7969
|
+
* that is not cacheable.
|
|
7970
|
+
*
|
|
7971
|
+
* @param keyId The key ID, which must be an `ap:` or `ap+ef61:` URI.
|
|
7972
|
+
* @param options Loaders and telemetry providers.
|
|
7973
|
+
* @returns The resolution, which is never `legacy`.
|
|
7974
|
+
* @internal
|
|
7975
|
+
*/
|
|
7976
|
+
async function resolvePortableActorKey(keyId, options = {}) {
|
|
7977
|
+
const documentLoader = options.documentLoader ?? (0, _fedify_vocab_runtime.getDocumentLoader)();
|
|
7978
|
+
const contextLoader = options.contextLoader ?? (0, _fedify_vocab_runtime.getDocumentLoader)();
|
|
7979
|
+
let formattedKeyId = keyId.href;
|
|
7980
|
+
const reject = (reason) => {
|
|
7981
|
+
logger.debug("Failed to verify key {keyId} of a portable actor: {reason}", {
|
|
7982
|
+
keyId: formattedKeyId,
|
|
7983
|
+
reason
|
|
7984
|
+
});
|
|
7985
|
+
return {
|
|
7986
|
+
type: "rejected",
|
|
7987
|
+
reason
|
|
7988
|
+
};
|
|
7989
|
+
};
|
|
7990
|
+
if (!isPortableUri(keyId) || getCanonicalPortableId(keyId) == null) return reject("The key ID is not a valid ap: or ap+ef61: URI.");
|
|
7991
|
+
formattedKeyId = (0, _fedify_vocab_runtime.formatIri)(keyId);
|
|
7992
|
+
if (keyId.hash === "" || keyId.hash === "#") return reject("The key ID has no fragment; only keys embedded in actor documents are supported.");
|
|
7993
|
+
const actorUrl = new URL(keyId.href);
|
|
7994
|
+
actorUrl.hash = "";
|
|
7995
|
+
const requestUrls = [];
|
|
7996
|
+
for (const gateway of (0, _fedify_vocab_runtime_internal_portable_dereference.getPortableGatewayCandidates)(actorUrl)) try {
|
|
7997
|
+
requestUrls.push((0, _fedify_vocab_runtime.toCompatibleEf61Id)(actorUrl, gateway).href);
|
|
7998
|
+
} catch (error) {
|
|
7999
|
+
if (!(error instanceof TypeError)) throw error;
|
|
8000
|
+
return reject(`The key ID cannot be fetched from gateways: ${error}`);
|
|
8001
|
+
}
|
|
8002
|
+
if (requestUrls.length < 1) requestUrls.push((0, _fedify_vocab_runtime.formatIri)(actorUrl));
|
|
8003
|
+
const attempts = [];
|
|
8004
|
+
for (const url of requestUrls) {
|
|
8005
|
+
let remoteDocument;
|
|
8006
|
+
try {
|
|
8007
|
+
remoteDocument = await documentLoader(url);
|
|
8008
|
+
} catch (error) {
|
|
8009
|
+
logger.debug("Failed to fetch the actor of key {keyId} from {url}: {error}", {
|
|
8010
|
+
keyId: formattedKeyId,
|
|
8011
|
+
url,
|
|
8012
|
+
error
|
|
8013
|
+
});
|
|
8014
|
+
attempts.push({
|
|
8015
|
+
type: "retrievalFailed",
|
|
8016
|
+
error
|
|
8017
|
+
});
|
|
8018
|
+
continue;
|
|
8019
|
+
}
|
|
8020
|
+
let resolution;
|
|
8021
|
+
try {
|
|
8022
|
+
const object = await _fedify_vocab.Object.fromJsonLd(remoteDocument.document, {
|
|
8023
|
+
documentLoader,
|
|
8024
|
+
contextLoader,
|
|
8025
|
+
tracerProvider: options.tracerProvider
|
|
8026
|
+
});
|
|
8027
|
+
resolution = isPortableActorDocument(object) ? await verifyPortableKeyDocument(remoteDocument.document, object, keyId, false, {
|
|
8028
|
+
...options,
|
|
8029
|
+
documentLoader,
|
|
8030
|
+
contextLoader
|
|
8031
|
+
}) : {
|
|
8032
|
+
type: "rejected",
|
|
8033
|
+
reason: "The document is not a portable actor."
|
|
8034
|
+
};
|
|
8035
|
+
} catch (error) {
|
|
8036
|
+
if (error instanceof TypeError) resolution = {
|
|
8037
|
+
type: "rejected",
|
|
8038
|
+
reason: `The document is malformed: ${error.message}`
|
|
8039
|
+
};
|
|
8040
|
+
else {
|
|
8041
|
+
logger.debug("Failed to process the actor of key {keyId} served from {url}: {error}", {
|
|
8042
|
+
keyId: formattedKeyId,
|
|
8043
|
+
url,
|
|
8044
|
+
error
|
|
8045
|
+
});
|
|
8046
|
+
attempts.push({
|
|
8047
|
+
type: "indeterminate",
|
|
8048
|
+
error
|
|
8049
|
+
});
|
|
8050
|
+
continue;
|
|
8051
|
+
}
|
|
8052
|
+
}
|
|
8053
|
+
if (resolution.type === "verified") return resolution;
|
|
8054
|
+
const reason = resolution.type === "rejected" ? resolution.reason : "The document is not a portable actor.";
|
|
8055
|
+
logger.debug("The actor document served from {url} does not vouch for key {keyId}: {reason}", {
|
|
8056
|
+
keyId: formattedKeyId,
|
|
8057
|
+
url,
|
|
8058
|
+
reason
|
|
8059
|
+
});
|
|
8060
|
+
attempts.push({
|
|
8061
|
+
type: "rejected",
|
|
8062
|
+
reason
|
|
8063
|
+
});
|
|
8064
|
+
}
|
|
8065
|
+
const rejections = attempts.flatMap((a) => a.type === "rejected" ? [a.reason] : []);
|
|
8066
|
+
if (rejections.length === attempts.length) return reject(rejections.join(" "));
|
|
8067
|
+
const errors = attempts.flatMap((a) => a.type === "rejected" ? [] : [a.error]);
|
|
8068
|
+
const aggregate = errors.length === 1 ? errors[0] : new AggregateError(errors, `Failed to fetch the actor of key ${formattedKeyId} from any of its gateways: ${requestUrls.join(", ")}`);
|
|
8069
|
+
if (attempts.every((a) => a.type === "retrievalFailed")) {
|
|
8070
|
+
const statuses = errors.map((error) => error instanceof _fedify_vocab_runtime.FetchError ? error.response?.status : void 0);
|
|
8071
|
+
return {
|
|
8072
|
+
type: "unavailable",
|
|
8073
|
+
error: statuses[0] != null && statuses.every((status) => status === statuses[0]) ? errors[0] : aggregate,
|
|
8074
|
+
cacheable: true
|
|
8075
|
+
};
|
|
8076
|
+
}
|
|
8077
|
+
return {
|
|
8078
|
+
type: "unavailable",
|
|
8079
|
+
error: new Error(`Could not determine whether the actor of key ${formattedKeyId} vouches for it.`, { cause: aggregate }),
|
|
8080
|
+
cacheable: false
|
|
8081
|
+
};
|
|
8082
|
+
}
|
|
8083
|
+
/**
|
|
8084
|
+
* Finds the nodes that stand for the key with the given canonical portable
|
|
8085
|
+
* ID. A node whose ID is an `ap:` or `ap+ef61:` URI stands for the key
|
|
8086
|
+
* itself, e.g., Mitra lists the keys of portable actors that way while
|
|
8087
|
+
* signing requests with compatible key IDs. A node whose ID is a compatible
|
|
8088
|
+
* identifier stands for a gateway's key, so it matches only a compatible key
|
|
8089
|
+
* ID on the same gateway; a key ID that names no gateway never selects
|
|
8090
|
+
* a gateway's key, even with the same fragment.
|
|
8091
|
+
* @param values The expanded values of a key property.
|
|
8092
|
+
* @param canonicalKeyId The canonical portable ID of the key ID.
|
|
8093
|
+
* @param gateway The origin of the gateway whose key the key ID names, or
|
|
8094
|
+
* `null` if the key ID names no gateway.
|
|
8095
|
+
*/
|
|
8096
|
+
function findNodes(values, canonicalKeyId, gateway) {
|
|
7765
8097
|
if (!Array.isArray(values)) return [];
|
|
7766
8098
|
const nodes = [];
|
|
7767
8099
|
for (const value of values) {
|
|
7768
8100
|
if (value == null || typeof value !== "object" || Array.isArray(value)) continue;
|
|
7769
8101
|
const node = value;
|
|
7770
8102
|
const nodeId = node["@id"];
|
|
7771
|
-
if (typeof nodeId !== "string"
|
|
7772
|
-
if (
|
|
8103
|
+
if (typeof nodeId !== "string") continue;
|
|
8104
|
+
if (getRawCanonicalPortableId(nodeId) !== canonicalKeyId) continue;
|
|
8105
|
+
const parsed = (0, _fedify_vocab_runtime.parseIri)(nodeId);
|
|
8106
|
+
if (isPortableUri(parsed) || parsed.origin === gateway) nodes.push(node);
|
|
7773
8107
|
}
|
|
7774
8108
|
return nodes;
|
|
7775
8109
|
}
|
|
@@ -8148,13 +8482,18 @@ function keyFetchErrorResult(keyId, result) {
|
|
|
8148
8482
|
}
|
|
8149
8483
|
};
|
|
8150
8484
|
}
|
|
8485
|
+
/**
|
|
8486
|
+
* Creates the resolvers of keys of FEP-ef61 portable actors, which HTTP
|
|
8487
|
+
* Signature verification alone passes to key lookups.
|
|
8488
|
+
*/
|
|
8489
|
+
function createPortableKeyResolvers(options) {
|
|
8490
|
+
return {
|
|
8491
|
+
gatewayKey: (document, actor, keyId) => verifyPortableGatewayKeyDocument(document, actor, keyId, options),
|
|
8492
|
+
actorKey: (keyId) => resolvePortableActorKey(keyId, options)
|
|
8493
|
+
};
|
|
8494
|
+
}
|
|
8151
8495
|
function parseKeyId(value) {
|
|
8152
|
-
|
|
8153
|
-
try {
|
|
8154
|
-
return new URL(value);
|
|
8155
|
-
} catch {
|
|
8156
|
-
return null;
|
|
8157
|
-
}
|
|
8496
|
+
return value == null ? null : parseKeyIdString(value);
|
|
8158
8497
|
}
|
|
8159
8498
|
function getKeyFetchErrorName(error) {
|
|
8160
8499
|
return error.name || error.constructor.name || "Error";
|
|
@@ -8412,7 +8751,7 @@ async function verifyRequestDraft(request, span, metricsContext, { documentLoade
|
|
|
8412
8751
|
keyCache,
|
|
8413
8752
|
tracerProvider,
|
|
8414
8753
|
meterProvider,
|
|
8415
|
-
|
|
8754
|
+
portableKeyResolvers: createPortableKeyResolvers({
|
|
8416
8755
|
documentLoader,
|
|
8417
8756
|
contextLoader,
|
|
8418
8757
|
keyCache,
|
|
@@ -8632,7 +8971,7 @@ async function verifyRequestRfc9421(request, span, metricsContext, { documentLoa
|
|
|
8632
8971
|
keyCache,
|
|
8633
8972
|
tracerProvider,
|
|
8634
8973
|
meterProvider,
|
|
8635
|
-
|
|
8974
|
+
portableKeyResolvers: createPortableKeyResolvers({
|
|
8636
8975
|
documentLoader,
|
|
8637
8976
|
contextLoader,
|
|
8638
8977
|
keyCache,
|
|
@@ -9156,6 +9495,12 @@ Object.defineProperty(exports, "isPortableId", {
|
|
|
9156
9495
|
return isPortableId;
|
|
9157
9496
|
}
|
|
9158
9497
|
});
|
|
9498
|
+
Object.defineProperty(exports, "isPortableKeyId", {
|
|
9499
|
+
enumerable: true,
|
|
9500
|
+
get: function() {
|
|
9501
|
+
return isPortableKeyId;
|
|
9502
|
+
}
|
|
9503
|
+
});
|
|
9159
9504
|
Object.defineProperty(exports, "isPortableUri", {
|
|
9160
9505
|
enumerable: true,
|
|
9161
9506
|
get: function() {
|
|
@@ -9276,6 +9621,12 @@ Object.defineProperty(exports, "registerQueueDepthGauge", {
|
|
|
9276
9621
|
return registerQueueDepthGauge;
|
|
9277
9622
|
}
|
|
9278
9623
|
});
|
|
9624
|
+
Object.defineProperty(exports, "resolvePortableActorKey", {
|
|
9625
|
+
enumerable: true,
|
|
9626
|
+
get: function() {
|
|
9627
|
+
return resolvePortableActorKey;
|
|
9628
|
+
}
|
|
9629
|
+
});
|
|
9279
9630
|
Object.defineProperty(exports, "selectRequestSignature", {
|
|
9280
9631
|
enumerable: true,
|
|
9281
9632
|
get: function() {
|