@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.
Files changed (93) hide show
  1. package/dist/{builder-CHht75pX.mjs → builder-CZcBWrBd.mjs} +29 -5
  2. package/dist/compat/mod.d.cts +1 -1
  3. package/dist/compat/mod.d.ts +1 -1
  4. package/dist/compat/transformers.test.mjs +1 -1
  5. package/dist/{compound-proof-DKKH_Nr1.mjs → compound-proof-CDuP9MIX.mjs} +3 -3
  6. package/dist/{context-mDTxTw-S.d.cts → context-SMNOiiY6.d.cts} +93 -11
  7. package/dist/{context-CfJE_oQu.d.ts → context-tOiJfBoD.d.ts} +93 -11
  8. package/dist/{deno-CkjiYK_Z.mjs → deno-CCABdw7r.mjs} +1 -1
  9. package/dist/{docloader-C1hAJnWc.mjs → docloader-Bby5g2iu.mjs} +2 -2
  10. package/dist/federation/builder.test.mjs +1 -1
  11. package/dist/federation/compound-fanout.test.mjs +3 -3
  12. package/dist/federation/handler-compound-proof.test.mjs +3 -3
  13. package/dist/federation/handler.test.mjs +118 -5
  14. package/dist/federation/hashlink-media.test.mjs +2 -2
  15. package/dist/federation/idempotency.test.mjs +2 -2
  16. package/dist/federation/keycache.test.mjs +67 -1
  17. package/dist/federation/metrics.test.mjs +1 -1
  18. package/dist/federation/middleware.test.mjs +184 -7
  19. package/dist/federation/mod.cjs +1 -1
  20. package/dist/federation/mod.d.cts +3 -3
  21. package/dist/federation/mod.d.ts +3 -3
  22. package/dist/federation/mod.js +1 -1
  23. package/dist/federation/outgoing-proof.test.mjs +5 -5
  24. package/dist/federation/portable-actor.test.mjs +3 -3
  25. package/dist/federation/portable-gateway-keys.test.mjs +24 -7
  26. package/dist/federation/portable-inbox.test.mjs +4 -4
  27. package/dist/federation/portable.test.mjs +31 -4
  28. package/dist/federation/send.test.mjs +3 -3
  29. package/dist/federation/tasks/enqueue.test.mjs +1 -1
  30. package/dist/federation/tasks/tasks.test.mjs +2 -2
  31. package/dist/federation/temporal.test.mjs +1 -1
  32. package/dist/federation/webfinger.test.mjs +1 -1
  33. package/dist/{federation-C1dL6SiT.cjs → federation-DYr384xz.cjs} +116 -15
  34. package/dist/{http-Cala2Grs.cjs → http-B4mQvNcv.cjs} +140 -55
  35. package/dist/{http-BU-8fUrz.mjs → http-BcLnr2do.mjs} +6 -12
  36. package/dist/{http-xlG-jRdB.d.cts → http-BrQgkJay.d.ts} +12 -7
  37. package/dist/{http-tTlTeFa0.js → http-Ce6_Lf7k.js} +140 -55
  38. package/dist/{http-n2v6DPxu.d.ts → http-D76Py0M4.d.cts} +12 -7
  39. package/dist/{key-I8gLBkcf.mjs → key-CkNxztNV.mjs} +107 -16
  40. package/dist/{keycache-BUDHB5rY.mjs → keycache-BInaECGj.mjs} +68 -0
  41. package/dist/{kv-cache-Bl5EBFrU.cjs → kv-cache-Dqp_E-1g.cjs} +1 -1
  42. package/dist/{kv-cache-CxQu_cd4.js → kv-cache-DwzXzchj.js} +1 -1
  43. package/dist/{kv-cache-DJFoGRWQ.mjs → kv-cache-iFaRIcIL.mjs} +1 -1
  44. package/dist/{ld-wiJbqJHa.mjs → ld-CnT0f1nU.mjs} +4 -7
  45. package/dist/{metrics-BCAa5-kE.mjs → metrics-em_7lwhG.mjs} +1 -1
  46. package/dist/{middleware-FnITXhtz.mjs → middleware-BpwKvOpU.mjs} +26 -17
  47. package/dist/{middleware-B6IVTVL0.js → middleware-DhIBngkj.js} +108 -7
  48. package/dist/{middleware-6p2dYEVA.mjs → middleware-U9IuzvDO.mjs} +1 -1
  49. package/dist/{mod-BYvPz_T0.d.ts → mod-COWgSo2v.d.ts} +1 -1
  50. package/dist/{mod-Coacostr.d.ts → mod-CTh7ijx5.d.ts} +1 -1
  51. package/dist/{mod-xGFCHqq_.d.cts → mod-Cr79cQlc.d.cts} +1 -1
  52. package/dist/{mod-CVZrXSdm.d.ts → mod-Cwdt_CWU.d.ts} +1 -1
  53. package/dist/{mod-C8LieBzF.d.cts → mod-DMdGFMEl.d.cts} +1 -1
  54. package/dist/{mod-B3OzOobN.d.cts → mod-DnElMslb.d.cts} +1 -1
  55. package/dist/mod.cjs +5 -5
  56. package/dist/mod.d.cts +7 -7
  57. package/dist/mod.d.ts +7 -7
  58. package/dist/mod.js +5 -5
  59. package/dist/nodeinfo/handler.test.mjs +1 -1
  60. package/dist/{owner-BJp47LxN.mjs → owner-9LiHmOv1.mjs} +4 -4
  61. package/dist/{owner-DCt8W_Cv.cjs → owner-BtoMCv6Z.cjs} +1 -1
  62. package/dist/{owner-j5yrbXS1.js → owner-Cb3ytrlq.js} +1 -1
  63. package/dist/{portable-key-B5oKE_wZ.mjs → portable-key-9ck61lI0.mjs} +5 -4
  64. package/dist/{portable-key-id-Bva5iT3W.mjs → portable-key-id-T7lecIbt.mjs} +3 -2
  65. package/dist/{proof-BE4T5zF6.mjs → proof-xdPYGPaA.mjs} +32 -31
  66. package/dist/{send-BFITuKHw.mjs → send-Bf8rSWch.mjs} +3 -3
  67. package/dist/sig/compound-proof-discovery.test.mjs +1 -1
  68. package/dist/sig/compound-proof-producer.test.mjs +1 -1
  69. package/dist/sig/compound-proof-verification.test.mjs +2 -2
  70. package/dist/sig/compound-proof.test.mjs +2 -2
  71. package/dist/sig/http.test.mjs +2 -2
  72. package/dist/sig/key.test.mjs +1 -1
  73. package/dist/sig/ld.test.mjs +2 -2
  74. package/dist/sig/mod.cjs +3 -3
  75. package/dist/sig/mod.d.cts +2 -2
  76. package/dist/sig/mod.d.ts +2 -2
  77. package/dist/sig/mod.js +3 -3
  78. package/dist/sig/owner.test.mjs +1 -1
  79. package/dist/sig/portable-collection.test.mjs +2 -2
  80. package/dist/sig/portable-key.test.mjs +301 -23
  81. package/dist/sig/proof.test.mjs +3 -3
  82. package/dist/{sig-CRTRoouH.cjs → sig-Bj3fHnv0.cjs} +2 -2
  83. package/dist/{sig-CJHdGk9w.js → sig-DN9pOglI.js} +2 -2
  84. package/dist/{temporal-CyDDbE4i.mjs → temporal-tAtwv6vX.mjs} +1 -1
  85. package/dist/testing/kv.test.mjs +1 -1
  86. package/dist/testing/mod.d.mts +91 -9
  87. package/dist/utils/docloader.test.mjs +2 -2
  88. package/dist/utils/kv-cache.test.mjs +1 -1
  89. package/dist/utils/mod.cjs +1 -1
  90. package/dist/utils/mod.d.cts +1 -1
  91. package/dist/utils/mod.d.ts +1 -1
  92. package/dist/utils/mod.js +1 -1
  93. 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.2103+3be829c0";
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 names a key that a gateway holds for a portable actor, so it
76
- * is resolved only as a gateway key, and never cached in a key cache.
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
- * whose IDs are compatible identifiers are never read from or written to
4922
- * the key cache.
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
- * whose IDs are compatible identifiers are never read from or written to
4947
- * the key cache.
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 was cached
5280
- * by an older version that trusted web origins, and is fetched again.
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 false;
6997
+ if (expires == null) return null;
6998
+ let expiration;
6909
6999
  if (expires.present) {
6910
- if (typeof expires.value !== "string") return false;
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 false;
7004
+ return null;
6916
7005
  }
6917
- if (Temporal$1.Instant.compare(Temporal$1.Now.instant(), expiration) >= 0) return false;
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 false;
7009
+ if (domain == null) return null;
6921
7010
  const proofDomains = domain.present ? parseStringSet(domain.value) : null;
6922
- if (domain.present && proofDomains == null) return false;
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 false;
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 false;
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 false;
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 false;
6933
- return true;
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 || !await hasValidProofOptions(proofConfiguration.value, options, proofContextLoader)) return 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: await verifyPreparedPortableObjectProof(jsonLd, prepared, options),
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-CkjiYK_Z.mjs";
5
- import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-BCAa5-kE.mjs";
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 { i as fetchKeyDetailed, s as validateCryptoKey } from "./key-I8gLBkcf.mjs";
8
- import { n as verifyPortableGatewayKeyDocument } from "./portable-key-B5oKE_wZ.mjs";
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
- * whose IDs are compatible identifiers are never read from or written to
182
- * the key cache.
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
- * whose IDs are compatible identifiers are never read from or written to
204
- * the key cache.
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
  *