@fedify/fedify 2.4.0-dev.2103 → 2.4.0-dev.2115
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-CHht75pX.mjs → builder-CZcBWrBd.mjs} +29 -5
- 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-DKKH_Nr1.mjs → compound-proof-CDuP9MIX.mjs} +3 -3
- package/dist/{context-mDTxTw-S.d.cts → context-SMNOiiY6.d.cts} +93 -11
- package/dist/{context-CfJE_oQu.d.ts → context-tOiJfBoD.d.ts} +93 -11
- package/dist/{deno-CkjiYK_Z.mjs → deno-CCABdw7r.mjs} +1 -1
- package/dist/{docloader-C1hAJnWc.mjs → docloader-Bby5g2iu.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 +118 -5
- package/dist/federation/hashlink-media.test.mjs +2 -2
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/keycache.test.mjs +67 -1
- package/dist/federation/metrics.test.mjs +1 -1
- package/dist/federation/middleware.test.mjs +184 -7
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.d.cts +3 -3
- package/dist/federation/mod.d.ts +3 -3
- 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-gateway-keys.test.mjs +24 -7
- package/dist/federation/portable-inbox.test.mjs +4 -4
- package/dist/federation/portable.test.mjs +31 -4
- 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-C1dL6SiT.cjs → federation-DYr384xz.cjs} +116 -15
- package/dist/{http-Cala2Grs.cjs → http-B4mQvNcv.cjs} +140 -55
- package/dist/{http-BU-8fUrz.mjs → http-BcLnr2do.mjs} +6 -12
- package/dist/{http-xlG-jRdB.d.cts → http-BrQgkJay.d.ts} +12 -7
- package/dist/{http-tTlTeFa0.js → http-Ce6_Lf7k.js} +140 -55
- package/dist/{http-n2v6DPxu.d.ts → http-D76Py0M4.d.cts} +12 -7
- package/dist/{key-I8gLBkcf.mjs → key-CkNxztNV.mjs} +107 -16
- package/dist/{keycache-BUDHB5rY.mjs → keycache-BInaECGj.mjs} +68 -0
- package/dist/{kv-cache-Bl5EBFrU.cjs → kv-cache-Dqp_E-1g.cjs} +1 -1
- package/dist/{kv-cache-CxQu_cd4.js → kv-cache-DwzXzchj.js} +1 -1
- package/dist/{kv-cache-DJFoGRWQ.mjs → kv-cache-iFaRIcIL.mjs} +1 -1
- package/dist/{ld-wiJbqJHa.mjs → ld-CnT0f1nU.mjs} +4 -7
- package/dist/{metrics-BCAa5-kE.mjs → metrics-em_7lwhG.mjs} +1 -1
- package/dist/{middleware-FnITXhtz.mjs → middleware-BpwKvOpU.mjs} +26 -17
- package/dist/{middleware-B6IVTVL0.js → middleware-DhIBngkj.js} +108 -7
- package/dist/{middleware-6p2dYEVA.mjs → middleware-U9IuzvDO.mjs} +1 -1
- package/dist/{mod-BYvPz_T0.d.ts → mod-COWgSo2v.d.ts} +1 -1
- package/dist/{mod-Coacostr.d.ts → mod-CTh7ijx5.d.ts} +1 -1
- package/dist/{mod-xGFCHqq_.d.cts → mod-Cr79cQlc.d.cts} +1 -1
- package/dist/{mod-CVZrXSdm.d.ts → mod-Cwdt_CWU.d.ts} +1 -1
- package/dist/{mod-C8LieBzF.d.cts → mod-DMdGFMEl.d.cts} +1 -1
- package/dist/{mod-B3OzOobN.d.cts → mod-DnElMslb.d.cts} +1 -1
- package/dist/mod.cjs +5 -5
- package/dist/mod.d.cts +7 -7
- package/dist/mod.d.ts +7 -7
- package/dist/mod.js +5 -5
- package/dist/nodeinfo/handler.test.mjs +1 -1
- package/dist/{owner-BJp47LxN.mjs → owner-9LiHmOv1.mjs} +4 -4
- package/dist/{owner-DCt8W_Cv.cjs → owner-BtoMCv6Z.cjs} +1 -1
- package/dist/{owner-j5yrbXS1.js → owner-Cb3ytrlq.js} +1 -1
- package/dist/{portable-key-B5oKE_wZ.mjs → portable-key-9ck61lI0.mjs} +5 -4
- package/dist/{portable-key-id-Bva5iT3W.mjs → portable-key-id-T7lecIbt.mjs} +3 -2
- package/dist/{proof-BE4T5zF6.mjs → proof-xdPYGPaA.mjs} +32 -31
- package/dist/{send-BFITuKHw.mjs → send-Bf8rSWch.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 +3 -3
- package/dist/sig/mod.d.cts +2 -2
- package/dist/sig/mod.d.ts +2 -2
- package/dist/sig/mod.js +3 -3
- package/dist/sig/owner.test.mjs +1 -1
- package/dist/sig/portable-collection.test.mjs +2 -2
- package/dist/sig/portable-key.test.mjs +301 -23
- package/dist/sig/proof.test.mjs +3 -3
- package/dist/{sig-CRTRoouH.cjs → sig-Bj3fHnv0.cjs} +2 -2
- package/dist/{sig-CJHdGk9w.js → sig-DN9pOglI.js} +2 -2
- package/dist/{temporal-CyDDbE4i.mjs → temporal-tAtwv6vX.mjs} +1 -1
- package/dist/testing/kv.test.mjs +1 -1
- package/dist/testing/mod.d.mts +91 -9
- 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 +7 -7
|
@@ -16,7 +16,7 @@ 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.2115+e2feaf2b";
|
|
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;
|
|
@@ -72,8 +72,9 @@ function hasPortableActor(activity) {
|
|
|
72
72
|
/**
|
|
73
73
|
* Checks whether a key ID looks like an [FEP-ef61] compatible identifier,
|
|
74
74
|
* i.e., an HTTP(S) URL under a gateway's `/.well-known/apgateway/did:` path.
|
|
75
|
-
* Such a key ID
|
|
76
|
-
*
|
|
75
|
+
* Such a key ID may name a key that a gateway holds for a portable actor,
|
|
76
|
+
* which only HTTP Signatures accept, so what it resolves to is cached apart
|
|
77
|
+
* for each purpose, never under the key ID itself.
|
|
77
78
|
*
|
|
78
79
|
* [FEP-ef61]: https://w3id.org/fep/ef61
|
|
79
80
|
* @internal
|
|
@@ -4881,6 +4882,32 @@ async function verifyKeyOwnership(key, options = {}) {
|
|
|
4881
4882
|
});
|
|
4882
4883
|
return null;
|
|
4883
4884
|
}
|
|
4885
|
+
/**
|
|
4886
|
+
* Wraps a key cache so that lookups through the wrapper miss, while
|
|
4887
|
+
* whatever they resolve is still written to the cache. It is used to retry
|
|
4888
|
+
* a verification that failed with a cached key with a freshly fetched one.
|
|
4889
|
+
* @param keyCache The key cache to wrap.
|
|
4890
|
+
* @returns The wrapped key cache.
|
|
4891
|
+
* @internal
|
|
4892
|
+
*/
|
|
4893
|
+
function bypassKeyCacheReads(keyCache) {
|
|
4894
|
+
const cache = keyCache;
|
|
4895
|
+
const bypassed = {
|
|
4896
|
+
get: () => Promise.resolve(void 0),
|
|
4897
|
+
set: async (keyId, key) => await cache?.set(keyId, key),
|
|
4898
|
+
getFetchError: () => Promise.resolve(void 0),
|
|
4899
|
+
setFetchError: async (keyId, error) => await cache?.setFetchError?.(keyId, error)
|
|
4900
|
+
};
|
|
4901
|
+
if (cache?.compatibleKeyScope != null) bypassed.compatibleKeyScope = (scope) => {
|
|
4902
|
+
const scoped = cache.compatibleKeyScope(scope);
|
|
4903
|
+
return {
|
|
4904
|
+
get: () => Promise.resolve(void 0),
|
|
4905
|
+
set: async (keyId, key, options) => await scoped.set(keyId, key, options),
|
|
4906
|
+
delete: async (keyId) => await scoped.delete(keyId)
|
|
4907
|
+
};
|
|
4908
|
+
};
|
|
4909
|
+
return bypassed;
|
|
4910
|
+
}
|
|
4884
4911
|
async function withFetchKeySpan(keyId, tracerProvider, fetcher) {
|
|
4885
4912
|
tracerProvider ??= _opentelemetry_api.trace.getTracerProvider();
|
|
4886
4913
|
return await tracerProvider.getTracer(name, version).startActiveSpan("activitypub.fetch_key", {
|
|
@@ -4917,9 +4944,9 @@ async function withFetchKeySpan(keyId, tracerProvider, fetcher) {
|
|
|
4917
4944
|
* Gateway keys of [FEP-ef61] portable actors, i.e., keys whose IDs are
|
|
4918
4945
|
* compatible identifiers that dereference to portable actor documents, are
|
|
4919
4946
|
* not resolved by this function, as they only authenticate HTTP requests;
|
|
4920
|
-
* use `verifyRequest()` or `getKeyOwner()` for them instead. Keys
|
|
4921
|
-
*
|
|
4922
|
-
*
|
|
4947
|
+
* use `verifyRequest()` or `getKeyOwner()` for them instead. Keys whose IDs
|
|
4948
|
+
* are compatible identifiers are never read from or written to the key
|
|
4949
|
+
* cache under their IDs, except for failures to fetch them.
|
|
4923
4950
|
*
|
|
4924
4951
|
* [FEP-ef61]: https://w3id.org/fep/ef61
|
|
4925
4952
|
* @template T The type of the key to fetch. Either {@link CryptographicKey}
|
|
@@ -4942,9 +4969,9 @@ function fetchKey(keyId, cls, options = {}) {
|
|
|
4942
4969
|
* Gateway keys of [FEP-ef61] portable actors, i.e., keys whose IDs are
|
|
4943
4970
|
* compatible identifiers that dereference to portable actor documents, are
|
|
4944
4971
|
* not resolved by this function, as they only authenticate HTTP requests;
|
|
4945
|
-
* use `verifyRequest()` or `getKeyOwner()` for them instead. Keys
|
|
4946
|
-
*
|
|
4947
|
-
*
|
|
4972
|
+
* use `verifyRequest()` or `getKeyOwner()` for them instead. Keys whose IDs
|
|
4973
|
+
* are compatible identifiers are never read from or written to the key
|
|
4974
|
+
* cache under their IDs, except for failures to fetch them.
|
|
4948
4975
|
*
|
|
4949
4976
|
* [FEP-ef61]: https://w3id.org/fep/ef61
|
|
4950
4977
|
*
|
|
@@ -5127,11 +5154,13 @@ async function resolveFetchedKey(document, cacheKey, documentUrl, keyId, cls, {
|
|
|
5127
5154
|
}
|
|
5128
5155
|
}
|
|
5129
5156
|
if (isCompatibleKeyId(cacheKey) && isPortableActorDocument(object)) {
|
|
5157
|
+
const scopedCache = keyCache;
|
|
5130
5158
|
if (portableGatewayKeyResolver == null || cls !== _fedify_vocab.CryptographicKey) {
|
|
5131
5159
|
logger.debug("Failed to verify; key {keyId} is a gateway key of the portable actor {actorId}, which is only accepted for HTTP Signatures.", {
|
|
5132
5160
|
keyId,
|
|
5133
5161
|
actorId: object.id?.href
|
|
5134
5162
|
});
|
|
5163
|
+
await scopedCache?.set(cacheKey, null);
|
|
5135
5164
|
return {
|
|
5136
5165
|
key: null,
|
|
5137
5166
|
cached: false
|
|
@@ -5143,11 +5172,13 @@ async function resolveFetchedKey(document, cacheKey, documentUrl, keyId, cls, {
|
|
|
5143
5172
|
keyId,
|
|
5144
5173
|
actorId: object.id?.href
|
|
5145
5174
|
});
|
|
5175
|
+
await scopedCache?.set(cacheKey, null);
|
|
5146
5176
|
return {
|
|
5147
5177
|
key: null,
|
|
5148
5178
|
cached: false
|
|
5149
5179
|
};
|
|
5150
5180
|
}
|
|
5181
|
+
await scopedCache?.set(cacheKey, resolution.key, { expires: resolution.expires });
|
|
5151
5182
|
return {
|
|
5152
5183
|
key: resolution.key,
|
|
5153
5184
|
cached: false
|
|
@@ -5276,8 +5307,9 @@ async function resolveFetchedKey(document, cacheKey, documentUrl, keyId, cls, {
|
|
|
5276
5307
|
}
|
|
5277
5308
|
/**
|
|
5278
5309
|
* Tells whether a key claims a portable actor as its owner. Such a key can
|
|
5279
|
-
* only be a gateway key, which is never cached, so a cached one
|
|
5280
|
-
* by an older version that trusted web origins, and is fetched
|
|
5310
|
+
* only be a gateway key, which is never cached under its ID, so a cached one
|
|
5311
|
+
* was cached by an older version that trusted web origins, and is fetched
|
|
5312
|
+
* again.
|
|
5281
5313
|
*/
|
|
5282
5314
|
function claimsPortableOwner(key) {
|
|
5283
5315
|
const ownerId = key instanceof _fedify_vocab.CryptographicKey ? key.ownerId : key?.controllerId;
|
|
@@ -5293,11 +5325,12 @@ async function fetchKeyWithResult(cacheKey, cls, options, onCachedUnavailable, o
|
|
|
5293
5325
|
"key"
|
|
5294
5326
|
]);
|
|
5295
5327
|
const keyId = cacheKey.href;
|
|
5296
|
-
if (isCompatibleKeyId(cacheKey)) options = {
|
|
5297
|
-
...options,
|
|
5298
|
-
keyCache: void 0
|
|
5299
|
-
};
|
|
5300
5328
|
const keyCache = options.keyCache;
|
|
5329
|
+
if (isCompatibleKeyId(cacheKey)) {
|
|
5330
|
+
const result = await fetchCompatibleKey(cacheKey, cls, options, onCachedUnavailable, onFetchError, logger);
|
|
5331
|
+
outcome = result.outcome;
|
|
5332
|
+
return result.result;
|
|
5333
|
+
}
|
|
5301
5334
|
const didKey = await resolveDidKey(cacheKey, cls, keyCache, logger);
|
|
5302
5335
|
if (didKey != null) {
|
|
5303
5336
|
outcome = { result: didKey.key == null ? "invalid" : didKey.cached ? "hit" : "fetched" };
|
|
@@ -5339,6 +5372,65 @@ async function fetchKeyWithResult(cacheKey, cls, options, onCachedUnavailable, o
|
|
|
5339
5372
|
});
|
|
5340
5373
|
}
|
|
5341
5374
|
}
|
|
5375
|
+
/**
|
|
5376
|
+
* Tells which purpose a lookup of a key at a compatible identifier serves.
|
|
5377
|
+
*/
|
|
5378
|
+
function getCompatibleKeyScope(cls, options) {
|
|
5379
|
+
if (cls !== _fedify_vocab.CryptographicKey) return "multikey";
|
|
5380
|
+
return options.portableGatewayKeyResolver == null ? "cryptographicKey" : "httpSignature";
|
|
5381
|
+
}
|
|
5382
|
+
/**
|
|
5383
|
+
* Looks up a key at an [FEP-ef61] compatible identifier. Such a key may be
|
|
5384
|
+
* a gateway key of a portable actor, which is accepted for some purposes but
|
|
5385
|
+
* not for others, so everything resolved from the document at the key ID is
|
|
5386
|
+
* cached in a namespace of the lookup's purpose, and one purpose never reuses
|
|
5387
|
+
* what another resolved, nor what another rejected. A failure to fetch
|
|
5388
|
+
* the document fails every purpose alike, so it is cached in the shared
|
|
5389
|
+
* namespace, as for any other key.
|
|
5390
|
+
*
|
|
5391
|
+
* [FEP-ef61]: https://w3id.org/fep/ef61
|
|
5392
|
+
*/
|
|
5393
|
+
async function fetchCompatibleKey(cacheKey, cls, options, onCachedUnavailable, onFetchError, logger) {
|
|
5394
|
+
const keyId = cacheKey.href;
|
|
5395
|
+
const sharedCache = options.keyCache;
|
|
5396
|
+
const scopedCache = sharedCache?.compatibleKeyScope?.(getCompatibleKeyScope(cls, options));
|
|
5397
|
+
const cached = await getCachedFetchKey(cacheKey, keyId, cls, scopedCache, logger);
|
|
5398
|
+
if (cached != null) return {
|
|
5399
|
+
result: cached,
|
|
5400
|
+
outcome: { result: "hit" }
|
|
5401
|
+
};
|
|
5402
|
+
if (await sharedCache?.get(cacheKey) === null) {
|
|
5403
|
+
logger.debug("Entry {keyId} found in cache, but it could not be fetched.", { keyId });
|
|
5404
|
+
return {
|
|
5405
|
+
result: await onCachedUnavailable(cacheKey, keyId, sharedCache, logger),
|
|
5406
|
+
outcome: { result: "hit" }
|
|
5407
|
+
};
|
|
5408
|
+
}
|
|
5409
|
+
logger.debug("Fetching key {keyId} to verify signature...", { keyId });
|
|
5410
|
+
let document;
|
|
5411
|
+
let documentUrl;
|
|
5412
|
+
try {
|
|
5413
|
+
const remoteDocument = await (options.documentLoader ?? (0, _fedify_vocab_runtime.getDocumentLoader)())(keyId);
|
|
5414
|
+
document = remoteDocument.document;
|
|
5415
|
+
documentUrl = new URL(remoteDocument.documentUrl ?? "", cacheKey);
|
|
5416
|
+
} catch (error) {
|
|
5417
|
+
const outcome = classifyFetchError(error);
|
|
5418
|
+
const result = await onFetchError(error, cacheKey, keyId, sharedCache, logger);
|
|
5419
|
+
await scopedCache?.delete(cacheKey);
|
|
5420
|
+
return {
|
|
5421
|
+
result,
|
|
5422
|
+
outcome
|
|
5423
|
+
};
|
|
5424
|
+
}
|
|
5425
|
+
const resolved = await resolveFetchedKey(document, cacheKey, documentUrl, keyId, cls, {
|
|
5426
|
+
...options,
|
|
5427
|
+
keyCache: scopedCache
|
|
5428
|
+
}, logger);
|
|
5429
|
+
return {
|
|
5430
|
+
result: resolved,
|
|
5431
|
+
outcome: { result: resolved.key != null ? "fetched" : "invalid" }
|
|
5432
|
+
};
|
|
5433
|
+
}
|
|
5342
5434
|
async function fetchKeyInternal(keyId, cls, options = {}) {
|
|
5343
5435
|
return await fetchKeyWithResult(typeof keyId === "string" ? new URL(keyId) : keyId, cls, options, (_cacheKey, _keyId, _keyCache, _logger) => {
|
|
5344
5436
|
return {
|
|
@@ -5855,10 +5947,7 @@ async function verifySignature(jsonLd, options = {}) {
|
|
|
5855
5947
|
});
|
|
5856
5948
|
const { key } = await measureSignatureKeyFetch(options.meterProvider, "linked_data", () => fetchKey(new URL(sig.creator), _fedify_vocab.CryptographicKey, {
|
|
5857
5949
|
...options,
|
|
5858
|
-
keyCache:
|
|
5859
|
-
get: () => Promise.resolve(void 0),
|
|
5860
|
-
set: async (keyId, key) => await options.keyCache?.set(keyId, key)
|
|
5861
|
-
}
|
|
5950
|
+
keyCache: bypassKeyCacheReads(options.keyCache)
|
|
5862
5951
|
}));
|
|
5863
5952
|
if (key == null) return null;
|
|
5864
5953
|
return await crypto.subtle.verify("RSASSA-PKCS1-v1_5", key.publicKey, signature.slice(), messageBytes) ? key : null;
|
|
@@ -6905,38 +6994,42 @@ function equalStringSets(left, right) {
|
|
|
6905
6994
|
}
|
|
6906
6995
|
async function hasValidProofOptions(proofConfig, options, documentLoader) {
|
|
6907
6996
|
const expires = await getProofOption(proofConfig, SECURITY_EXPIRATION, ["expires", SECURITY_EXPIRATION], documentLoader);
|
|
6908
|
-
if (expires == null) return
|
|
6997
|
+
if (expires == null) return null;
|
|
6998
|
+
let expiration;
|
|
6909
6999
|
if (expires.present) {
|
|
6910
|
-
if (typeof expires.value !== "string") return
|
|
6911
|
-
let expiration;
|
|
7000
|
+
if (typeof expires.value !== "string") return null;
|
|
6912
7001
|
try {
|
|
6913
7002
|
expiration = Temporal$1.Instant.from(expires.value);
|
|
6914
7003
|
} catch {
|
|
6915
|
-
return
|
|
7004
|
+
return null;
|
|
6916
7005
|
}
|
|
6917
|
-
if (Temporal$1.Instant.compare(Temporal$1.Now.instant(), expiration) >= 0) return
|
|
7006
|
+
if (Temporal$1.Instant.compare(Temporal$1.Now.instant(), expiration) >= 0) return null;
|
|
6918
7007
|
}
|
|
6919
7008
|
const domain = await getProofOption(proofConfig, SECURITY_DOMAIN, ["domain", SECURITY_DOMAIN], documentLoader);
|
|
6920
|
-
if (domain == null) return
|
|
7009
|
+
if (domain == null) return null;
|
|
6921
7010
|
const proofDomains = domain.present ? parseStringSet(domain.value) : null;
|
|
6922
|
-
if (domain.present && proofDomains == null) return
|
|
7011
|
+
if (domain.present && proofDomains == null) return null;
|
|
6923
7012
|
if (options.domain != null) {
|
|
6924
7013
|
const expectedDomains = parseStringSet(options.domain);
|
|
6925
|
-
if (expectedDomains == null || proofDomains == null || !equalStringSets(proofDomains, expectedDomains)) return
|
|
7014
|
+
if (expectedDomains == null || proofDomains == null || !equalStringSets(proofDomains, expectedDomains)) return null;
|
|
6926
7015
|
}
|
|
6927
7016
|
const challenge = await getProofOption(proofConfig, SECURITY_CHALLENGE, ["challenge", SECURITY_CHALLENGE], documentLoader);
|
|
6928
|
-
if (challenge == null || challenge.present && typeof challenge.value !== "string" || options.challenge != null && (!challenge.present || challenge.value !== options.challenge)) return
|
|
7017
|
+
if (challenge == null || challenge.present && typeof challenge.value !== "string" || options.challenge != null && (!challenge.present || challenge.value !== options.challenge)) return null;
|
|
6929
7018
|
const nonce = await getProofOption(proofConfig, SECURITY_NONCE, ["nonce", SECURITY_NONCE], documentLoader);
|
|
6930
|
-
if (nonce == null || nonce.present && typeof nonce.value !== "string") return
|
|
7019
|
+
if (nonce == null || nonce.present && typeof nonce.value !== "string") return null;
|
|
6931
7020
|
const previousProof = await getProofOption(proofConfig, SECURITY_PREVIOUS_PROOF, ["previousProof", SECURITY_PREVIOUS_PROOF], documentLoader);
|
|
6932
|
-
if (previousProof == null || previousProof.present && typeof previousProof.value !== "string" && (!Array.isArray(previousProof.value) || previousProof.value.some((item) => typeof item !== "string"))) return
|
|
6933
|
-
return
|
|
7021
|
+
if (previousProof == null || previousProof.present && typeof previousProof.value !== "string" && (!Array.isArray(previousProof.value) || previousProof.value.some((item) => typeof item !== "string"))) return null;
|
|
7022
|
+
return { expires: expiration };
|
|
6934
7023
|
}
|
|
6935
7024
|
async function verifyProofInternal(jsonLd, proof, options, messageDigestCache, rawProofCandidate, keyIdBoundByCaller = false) {
|
|
6936
7025
|
if (!isJsonLdNode(jsonLd) || proof.cryptosuite !== "eddsa-jcs-2022" || proof.verificationMethodId == null || proof.proofPurpose !== "assertionMethod" || proof.proofValue == null || proof.created == null) return null;
|
|
6937
7026
|
const proofContextLoader = messageDigestCache.proofContextLoader ?? preloadedOnlyDocumentLoader;
|
|
6938
7027
|
const proofConfiguration = await createProofConfiguration(jsonLd, proof, options, proofContextLoader, rawProofCandidate);
|
|
6939
|
-
if (proofConfiguration == null
|
|
7028
|
+
if (proofConfiguration == null) return null;
|
|
7029
|
+
const validProofOptions = await hasValidProofOptions(proofConfiguration.value, options, proofContextLoader);
|
|
7030
|
+
if (validProofOptions == null) return null;
|
|
7031
|
+
const { expires } = validProofOptions;
|
|
7032
|
+
if (expires != null && (messageDigestCache.expires == null || Temporal$1.Instant.compare(expires, messageDigestCache.expires) < 0)) messageDigestCache.expires = expires;
|
|
6940
7033
|
const publicKeyPromise = measureSignatureKeyFetch(options.meterProvider, "object_integrity", () => fetchKey(proof.verificationMethodId, _fedify_vocab.Multikey, {
|
|
6941
7034
|
...options,
|
|
6942
7035
|
keyIdBoundByCaller
|
|
@@ -6970,10 +7063,7 @@ async function verifyProofInternal(jsonLd, proof, options, messageDigestCache, r
|
|
|
6970
7063
|
});
|
|
6971
7064
|
return await verifyProofInternal(jsonLd, proof, {
|
|
6972
7065
|
...options,
|
|
6973
|
-
keyCache:
|
|
6974
|
-
get: () => Promise.resolve(void 0),
|
|
6975
|
-
set: async (keyId, key) => await options.keyCache?.set(keyId, key)
|
|
6976
|
-
}
|
|
7066
|
+
keyCache: bypassKeyCacheReads(options.keyCache)
|
|
6977
7067
|
}, messageDigestCache, rawProofCandidate, keyIdBoundByCaller);
|
|
6978
7068
|
}
|
|
6979
7069
|
logger$1.debug("The fetched key (verificationMethod) for the proof is not a valid Ed25519 key:\n{keyId}", {
|
|
@@ -7009,10 +7099,7 @@ async function verifyProofInternal(jsonLd, proof, options, messageDigestCache, r
|
|
|
7009
7099
|
});
|
|
7010
7100
|
return await verifyProofInternal(jsonLd, proof, {
|
|
7011
7101
|
...options,
|
|
7012
|
-
keyCache:
|
|
7013
|
-
get: () => Promise.resolve(void 0),
|
|
7014
|
-
set: async (keyId, key) => await options.keyCache?.set(keyId, key)
|
|
7015
|
-
}
|
|
7102
|
+
keyCache: bypassKeyCacheReads(options.keyCache)
|
|
7016
7103
|
}, messageDigestCache, rawProofCandidate, keyIdBoundByCaller);
|
|
7017
7104
|
}
|
|
7018
7105
|
logger$1.debug("Failed to verify the proof with the fetched key {keyId}:\n{proof}", {
|
|
@@ -7341,21 +7428,24 @@ async function verifyPortableObjectProofWithRoot(jsonLd, options = {}) {
|
|
|
7341
7428
|
...prepared.root == null ? {} : { root: prepared.root },
|
|
7342
7429
|
...prepared.objectType == null ? {} : { objectType: prepared.objectType }
|
|
7343
7430
|
};
|
|
7344
|
-
const { root, objectType } = prepared;
|
|
7431
|
+
const { root, objectType, proofContextLoader } = prepared;
|
|
7432
|
+
const messageDigestCache = { proofContextLoader };
|
|
7433
|
+
const result = await verifyPreparedPortableObjectProof(jsonLd, prepared, options, messageDigestCache);
|
|
7434
|
+
const { expires } = messageDigestCache;
|
|
7345
7435
|
return {
|
|
7346
|
-
result
|
|
7436
|
+
result,
|
|
7347
7437
|
root,
|
|
7348
|
-
objectType
|
|
7438
|
+
objectType,
|
|
7439
|
+
...result.verified && expires != null ? { expires } : {}
|
|
7349
7440
|
};
|
|
7350
7441
|
}
|
|
7351
|
-
async function verifyPreparedPortableObjectProof(jsonLd, prepared, options) {
|
|
7442
|
+
async function verifyPreparedPortableObjectProof(jsonLd, prepared, options, messageDigestCache) {
|
|
7352
7443
|
const { proofs, rawProofValues, proofContextLoader } = prepared;
|
|
7353
7444
|
const keys = [];
|
|
7354
7445
|
const rawProofCandidatePool = {
|
|
7355
7446
|
candidates: await parseRawProofCandidates(jsonLd, rawProofValues, options, proofContextLoader),
|
|
7356
7447
|
used: /* @__PURE__ */ new Set()
|
|
7357
7448
|
};
|
|
7358
|
-
const messageDigestCache = { proofContextLoader };
|
|
7359
7449
|
for (let proofIndex = 0; proofIndex < proofs.length; proofIndex++) {
|
|
7360
7450
|
const rawProofCandidate = takeRawProofCandidate(rawProofCandidatePool, proofs[proofIndex]);
|
|
7361
7451
|
if (rawProofCandidate == null) return {
|
|
@@ -7553,7 +7643,7 @@ async function verifyPortableGatewayKeyDocument(document, actor, keyId, options
|
|
|
7553
7643
|
if (!(error instanceof TypeError)) throw error;
|
|
7554
7644
|
return reject(`The actor document is malformed: ${error.message}`);
|
|
7555
7645
|
}
|
|
7556
|
-
const { result, root } = verification;
|
|
7646
|
+
const { result, root, expires } = verification;
|
|
7557
7647
|
if (!result.verified) return reject("The actor document does not satisfy the FEP-ef61 proof policy: " + result.reason.type);
|
|
7558
7648
|
if (root == null) return reject("The actor document could not be expanded.");
|
|
7559
7649
|
const refuse = (url) => Promise.reject(/* @__PURE__ */ new Error(`Refusing to fetch ${url}.`));
|
|
@@ -7625,7 +7715,8 @@ async function verifyPortableGatewayKeyDocument(document, actor, keyId, options
|
|
|
7625
7715
|
return {
|
|
7626
7716
|
type: "verified",
|
|
7627
7717
|
actor,
|
|
7628
|
-
key
|
|
7718
|
+
key,
|
|
7719
|
+
...expires == null ? {} : { expires }
|
|
7629
7720
|
};
|
|
7630
7721
|
}
|
|
7631
7722
|
/**
|
|
@@ -8355,10 +8446,7 @@ async function verifyRequestDraft(request, span, metricsContext, { documentLoade
|
|
|
8355
8446
|
contextLoader,
|
|
8356
8447
|
timeWindow,
|
|
8357
8448
|
currentTime,
|
|
8358
|
-
keyCache:
|
|
8359
|
-
get: () => Promise.resolve(void 0),
|
|
8360
|
-
set: async (keyId, key) => await keyCache?.set(keyId, key)
|
|
8361
|
-
},
|
|
8449
|
+
keyCache: bypassKeyCacheReads(keyCache),
|
|
8362
8450
|
meterProvider,
|
|
8363
8451
|
tracerProvider
|
|
8364
8452
|
});
|
|
@@ -8607,10 +8695,7 @@ async function verifyRequestRfc9421(request, span, metricsContext, { documentLoa
|
|
|
8607
8695
|
contextLoader,
|
|
8608
8696
|
timeWindow,
|
|
8609
8697
|
currentTime,
|
|
8610
|
-
keyCache:
|
|
8611
|
-
get: () => Promise.resolve(void 0),
|
|
8612
|
-
set: async (keyId, key) => await keyCache?.set(keyId, key)
|
|
8613
|
-
},
|
|
8698
|
+
keyCache: bypassKeyCacheReads(keyCache),
|
|
8614
8699
|
spec: "rfc9421",
|
|
8615
8700
|
meterProvider,
|
|
8616
8701
|
tracerProvider
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { n as version, t as name } from "./deno-
|
|
5
|
-
import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-
|
|
4
|
+
import { n as version, t as name } from "./deno-CCABdw7r.mjs";
|
|
5
|
+
import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-em_7lwhG.mjs";
|
|
6
6
|
import { i as validateAcceptSignature, n as fulfillAcceptSignature, r as parseAcceptSignature } from "./accept-CPVqki78.mjs";
|
|
7
|
-
import {
|
|
8
|
-
import { n as verifyPortableGatewayKeyDocument } from "./portable-key-
|
|
7
|
+
import { a as fetchKeyDetailed, c as validateCryptoKey, t as bypassKeyCacheReads } from "./key-CkNxztNV.mjs";
|
|
8
|
+
import { n as verifyPortableGatewayKeyDocument } from "./portable-key-9ck61lI0.mjs";
|
|
9
9
|
import { getLogger } from "@logtape/logtape";
|
|
10
10
|
import { CryptographicKey } from "@fedify/vocab";
|
|
11
11
|
import { SpanStatusCode, trace } from "@opentelemetry/api";
|
|
@@ -681,10 +681,7 @@ async function verifyRequestDraft(request, span, metricsContext, { documentLoade
|
|
|
681
681
|
contextLoader,
|
|
682
682
|
timeWindow,
|
|
683
683
|
currentTime,
|
|
684
|
-
keyCache:
|
|
685
|
-
get: () => Promise.resolve(void 0),
|
|
686
|
-
set: async (keyId, key) => await keyCache?.set(keyId, key)
|
|
687
|
-
},
|
|
684
|
+
keyCache: bypassKeyCacheReads(keyCache),
|
|
688
685
|
meterProvider,
|
|
689
686
|
tracerProvider
|
|
690
687
|
});
|
|
@@ -933,10 +930,7 @@ async function verifyRequestRfc9421(request, span, metricsContext, { documentLoa
|
|
|
933
930
|
contextLoader,
|
|
934
931
|
timeWindow,
|
|
935
932
|
currentTime,
|
|
936
|
-
keyCache:
|
|
937
|
-
get: () => Promise.resolve(void 0),
|
|
938
|
-
set: async (keyId, key) => await keyCache?.set(keyId, key)
|
|
939
|
-
},
|
|
933
|
+
keyCache: bypassKeyCacheReads(keyCache),
|
|
940
934
|
spec: "rfc9421",
|
|
941
935
|
meterProvider,
|
|
942
936
|
tracerProvider
|
|
@@ -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
|
/**
|
|
@@ -23,6 +23,11 @@ type PortableGatewayKeyResolution = {
|
|
|
23
23
|
readonly key: CryptographicKey & {
|
|
24
24
|
publicKey: CryptoKey;
|
|
25
25
|
};
|
|
26
|
+
/**
|
|
27
|
+
* When the proof of the actor's document expires, if it does. The
|
|
28
|
+
* document does not vouch for the key after that.
|
|
29
|
+
*/
|
|
30
|
+
readonly expires?: Temporal.Instant;
|
|
26
31
|
};
|
|
27
32
|
//#endregion
|
|
28
33
|
//#region src/sig/key.d.ts
|
|
@@ -177,9 +182,9 @@ type FetchableKeyClass<T extends CryptographicKey | Multikey> = (new (...args: a
|
|
|
177
182
|
* Gateway keys of [FEP-ef61] portable actors, i.e., keys whose IDs are
|
|
178
183
|
* compatible identifiers that dereference to portable actor documents, are
|
|
179
184
|
* not resolved by this function, as they only authenticate HTTP requests;
|
|
180
|
-
* use `verifyRequest()` or `getKeyOwner()` for them instead. Keys
|
|
181
|
-
*
|
|
182
|
-
*
|
|
185
|
+
* use `verifyRequest()` or `getKeyOwner()` for them instead. Keys whose IDs
|
|
186
|
+
* are compatible identifiers are never read from or written to the key
|
|
187
|
+
* cache under their IDs, except for failures to fetch them.
|
|
183
188
|
*
|
|
184
189
|
* [FEP-ef61]: https://w3id.org/fep/ef61
|
|
185
190
|
* @template T The type of the key to fetch. Either {@link CryptographicKey}
|
|
@@ -199,9 +204,9 @@ declare function fetchKey<T extends CryptographicKey | Multikey>(keyId: URL | st
|
|
|
199
204
|
* Gateway keys of [FEP-ef61] portable actors, i.e., keys whose IDs are
|
|
200
205
|
* compatible identifiers that dereference to portable actor documents, are
|
|
201
206
|
* not resolved by this function, as they only authenticate HTTP requests;
|
|
202
|
-
* use `verifyRequest()` or `getKeyOwner()` for them instead. Keys
|
|
203
|
-
*
|
|
204
|
-
*
|
|
207
|
+
* use `verifyRequest()` or `getKeyOwner()` for them instead. Keys whose IDs
|
|
208
|
+
* are compatible identifiers are never read from or written to the key
|
|
209
|
+
* cache under their IDs, except for failures to fetch them.
|
|
205
210
|
*
|
|
206
211
|
* [FEP-ef61]: https://w3id.org/fep/ef61
|
|
207
212
|
*
|