@fedify/fedify 2.4.0-dev.2108 → 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-BRlYEsrV.mjs → builder-CZcBWrBd.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-CHGINiJg.mjs → compound-proof-CDuP9MIX.mjs} +3 -3
- package/dist/{context-Cqx9M_zO.d.cts → context-SMNOiiY6.d.cts} +1 -1
- package/dist/{context-B4F4kGJq.d.ts → context-tOiJfBoD.d.ts} +1 -1
- package/dist/{deno-CC4RKEEp.mjs → deno-CCABdw7r.mjs} +1 -1
- package/dist/{docloader-D3_m98fe.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 +5 -5
- package/dist/federation/hashlink-media.test.mjs +2 -2
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/keycache.test.mjs +67 -1
- 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 +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 +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-dqo6ZzgT.cjs → federation-DYr384xz.cjs} +71 -3
- package/dist/{http-B7wEqv49.cjs → http-B4mQvNcv.cjs} +140 -55
- package/dist/{http-CHEYA5fH.mjs → http-BcLnr2do.mjs} +6 -12
- package/dist/{http-xlG-jRdB.d.cts → http-BrQgkJay.d.ts} +12 -7
- package/dist/{http-CaUBCHYJ.js → http-Ce6_Lf7k.js} +140 -55
- package/dist/{http-n2v6DPxu.d.ts → http-D76Py0M4.d.cts} +12 -7
- package/dist/{key-B8u1vdvF.mjs → key-CkNxztNV.mjs} +107 -16
- package/dist/{keycache-BUDHB5rY.mjs → keycache-BInaECGj.mjs} +68 -0
- package/dist/{kv-cache-0pYMPrkL.cjs → kv-cache-Dqp_E-1g.cjs} +1 -1
- package/dist/{kv-cache-ibncHN1D.js → kv-cache-DwzXzchj.js} +1 -1
- package/dist/{kv-cache-CTdvf_Iq.mjs → kv-cache-iFaRIcIL.mjs} +1 -1
- package/dist/{ld-BTMJHEGV.mjs → ld-CnT0f1nU.mjs} +4 -7
- package/dist/{metrics-CF1TEvdS.mjs → metrics-em_7lwhG.mjs} +1 -1
- package/dist/{middleware-B-rZ7oE-.mjs → middleware-BpwKvOpU.mjs} +15 -15
- package/dist/{middleware-Dl-Y_ZlC.js → middleware-DhIBngkj.js} +71 -3
- package/dist/{middleware-DCMTJT3_.mjs → middleware-U9IuzvDO.mjs} +1 -1
- package/dist/{mod-4s_XgF17.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-BuFmStNT.d.cts → mod-DnElMslb.d.cts} +1 -1
- package/dist/mod.cjs +5 -5
- package/dist/mod.d.cts +5 -5
- package/dist/mod.d.ts +5 -5
- package/dist/mod.js +5 -5
- package/dist/nodeinfo/handler.test.mjs +1 -1
- package/dist/{owner-CY--JSqO.mjs → owner-9LiHmOv1.mjs} +4 -4
- package/dist/{owner-CGnHPl4k.cjs → owner-BtoMCv6Z.cjs} +1 -1
- package/dist/{owner-M8dOKEl6.js → owner-Cb3ytrlq.js} +1 -1
- package/dist/{portable-key-C0-ghkgM.mjs → portable-key-9ck61lI0.mjs} +5 -4
- package/dist/{portable-key-id-Bva5iT3W.mjs → portable-key-id-T7lecIbt.mjs} +3 -2
- package/dist/{proof-DDw0i9b1.mjs → proof-xdPYGPaA.mjs} +32 -31
- package/dist/{send-CU3sHGPt.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-C2CORtvg.cjs → sig-Bj3fHnv0.cjs} +2 -2
- package/dist/{sig-fycq7sOr.js → sig-DN9pOglI.js} +2 -2
- package/dist/{temporal-NHiSpGcW.mjs → temporal-tAtwv6vX.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
|
@@ -14,7 +14,7 @@ import serialize from "json-canon";
|
|
|
14
14
|
import { ATTR_HTTP_REQUEST_HEADER, ATTR_HTTP_REQUEST_METHOD, ATTR_URL_FULL } from "@opentelemetry/semantic-conventions";
|
|
15
15
|
//#region deno.json
|
|
16
16
|
var name = "@fedify/fedify";
|
|
17
|
-
var version = "2.4.0-dev.
|
|
17
|
+
var version = "2.4.0-dev.2115+e2feaf2b";
|
|
18
18
|
//#endregion
|
|
19
19
|
//#region src/sig/portable-key-id.ts
|
|
20
20
|
const COMPATIBLE_KEY_ID_PATH_PATTERN = /^\/\.well-known\/apgateway\/did(?::|%3A)/i;
|
|
@@ -70,8 +70,9 @@ function hasPortableActor(activity) {
|
|
|
70
70
|
/**
|
|
71
71
|
* Checks whether a key ID looks like an [FEP-ef61] compatible identifier,
|
|
72
72
|
* i.e., an HTTP(S) URL under a gateway's `/.well-known/apgateway/did:` path.
|
|
73
|
-
* Such a key ID
|
|
74
|
-
*
|
|
73
|
+
* Such a key ID may name a key that a gateway holds for a portable actor,
|
|
74
|
+
* which only HTTP Signatures accept, so what it resolves to is cached apart
|
|
75
|
+
* for each purpose, never under the key ID itself.
|
|
75
76
|
*
|
|
76
77
|
* [FEP-ef61]: https://w3id.org/fep/ef61
|
|
77
78
|
* @internal
|
|
@@ -1315,6 +1316,32 @@ async function verifyKeyOwnership(key, options = {}) {
|
|
|
1315
1316
|
});
|
|
1316
1317
|
return null;
|
|
1317
1318
|
}
|
|
1319
|
+
/**
|
|
1320
|
+
* Wraps a key cache so that lookups through the wrapper miss, while
|
|
1321
|
+
* whatever they resolve is still written to the cache. It is used to retry
|
|
1322
|
+
* a verification that failed with a cached key with a freshly fetched one.
|
|
1323
|
+
* @param keyCache The key cache to wrap.
|
|
1324
|
+
* @returns The wrapped key cache.
|
|
1325
|
+
* @internal
|
|
1326
|
+
*/
|
|
1327
|
+
function bypassKeyCacheReads(keyCache) {
|
|
1328
|
+
const cache = keyCache;
|
|
1329
|
+
const bypassed = {
|
|
1330
|
+
get: () => Promise.resolve(void 0),
|
|
1331
|
+
set: async (keyId, key) => await cache?.set(keyId, key),
|
|
1332
|
+
getFetchError: () => Promise.resolve(void 0),
|
|
1333
|
+
setFetchError: async (keyId, error) => await cache?.setFetchError?.(keyId, error)
|
|
1334
|
+
};
|
|
1335
|
+
if (cache?.compatibleKeyScope != null) bypassed.compatibleKeyScope = (scope) => {
|
|
1336
|
+
const scoped = cache.compatibleKeyScope(scope);
|
|
1337
|
+
return {
|
|
1338
|
+
get: () => Promise.resolve(void 0),
|
|
1339
|
+
set: async (keyId, key, options) => await scoped.set(keyId, key, options),
|
|
1340
|
+
delete: async (keyId) => await scoped.delete(keyId)
|
|
1341
|
+
};
|
|
1342
|
+
};
|
|
1343
|
+
return bypassed;
|
|
1344
|
+
}
|
|
1318
1345
|
async function withFetchKeySpan(keyId, tracerProvider, fetcher) {
|
|
1319
1346
|
tracerProvider ??= trace.getTracerProvider();
|
|
1320
1347
|
return await tracerProvider.getTracer(name, version).startActiveSpan("activitypub.fetch_key", {
|
|
@@ -1351,9 +1378,9 @@ async function withFetchKeySpan(keyId, tracerProvider, fetcher) {
|
|
|
1351
1378
|
* Gateway keys of [FEP-ef61] portable actors, i.e., keys whose IDs are
|
|
1352
1379
|
* compatible identifiers that dereference to portable actor documents, are
|
|
1353
1380
|
* not resolved by this function, as they only authenticate HTTP requests;
|
|
1354
|
-
* use `verifyRequest()` or `getKeyOwner()` for them instead. Keys
|
|
1355
|
-
*
|
|
1356
|
-
*
|
|
1381
|
+
* use `verifyRequest()` or `getKeyOwner()` for them instead. Keys whose IDs
|
|
1382
|
+
* are compatible identifiers are never read from or written to the key
|
|
1383
|
+
* cache under their IDs, except for failures to fetch them.
|
|
1357
1384
|
*
|
|
1358
1385
|
* [FEP-ef61]: https://w3id.org/fep/ef61
|
|
1359
1386
|
* @template T The type of the key to fetch. Either {@link CryptographicKey}
|
|
@@ -1376,9 +1403,9 @@ function fetchKey(keyId, cls, options = {}) {
|
|
|
1376
1403
|
* Gateway keys of [FEP-ef61] portable actors, i.e., keys whose IDs are
|
|
1377
1404
|
* compatible identifiers that dereference to portable actor documents, are
|
|
1378
1405
|
* not resolved by this function, as they only authenticate HTTP requests;
|
|
1379
|
-
* use `verifyRequest()` or `getKeyOwner()` for them instead. Keys
|
|
1380
|
-
*
|
|
1381
|
-
*
|
|
1406
|
+
* use `verifyRequest()` or `getKeyOwner()` for them instead. Keys whose IDs
|
|
1407
|
+
* are compatible identifiers are never read from or written to the key
|
|
1408
|
+
* cache under their IDs, except for failures to fetch them.
|
|
1382
1409
|
*
|
|
1383
1410
|
* [FEP-ef61]: https://w3id.org/fep/ef61
|
|
1384
1411
|
*
|
|
@@ -1561,11 +1588,13 @@ async function resolveFetchedKey(document, cacheKey, documentUrl, keyId, cls, {
|
|
|
1561
1588
|
}
|
|
1562
1589
|
}
|
|
1563
1590
|
if (isCompatibleKeyId(cacheKey) && isPortableActorDocument(object)) {
|
|
1591
|
+
const scopedCache = keyCache;
|
|
1564
1592
|
if (portableGatewayKeyResolver == null || cls !== CryptographicKey) {
|
|
1565
1593
|
logger.debug("Failed to verify; key {keyId} is a gateway key of the portable actor {actorId}, which is only accepted for HTTP Signatures.", {
|
|
1566
1594
|
keyId,
|
|
1567
1595
|
actorId: object.id?.href
|
|
1568
1596
|
});
|
|
1597
|
+
await scopedCache?.set(cacheKey, null);
|
|
1569
1598
|
return {
|
|
1570
1599
|
key: null,
|
|
1571
1600
|
cached: false
|
|
@@ -1577,11 +1606,13 @@ async function resolveFetchedKey(document, cacheKey, documentUrl, keyId, cls, {
|
|
|
1577
1606
|
keyId,
|
|
1578
1607
|
actorId: object.id?.href
|
|
1579
1608
|
});
|
|
1609
|
+
await scopedCache?.set(cacheKey, null);
|
|
1580
1610
|
return {
|
|
1581
1611
|
key: null,
|
|
1582
1612
|
cached: false
|
|
1583
1613
|
};
|
|
1584
1614
|
}
|
|
1615
|
+
await scopedCache?.set(cacheKey, resolution.key, { expires: resolution.expires });
|
|
1585
1616
|
return {
|
|
1586
1617
|
key: resolution.key,
|
|
1587
1618
|
cached: false
|
|
@@ -1710,8 +1741,9 @@ async function resolveFetchedKey(document, cacheKey, documentUrl, keyId, cls, {
|
|
|
1710
1741
|
}
|
|
1711
1742
|
/**
|
|
1712
1743
|
* Tells whether a key claims a portable actor as its owner. Such a key can
|
|
1713
|
-
* only be a gateway key, which is never cached, so a cached one
|
|
1714
|
-
* by an older version that trusted web origins, and is fetched
|
|
1744
|
+
* only be a gateway key, which is never cached under its ID, so a cached one
|
|
1745
|
+
* was cached by an older version that trusted web origins, and is fetched
|
|
1746
|
+
* again.
|
|
1715
1747
|
*/
|
|
1716
1748
|
function claimsPortableOwner(key) {
|
|
1717
1749
|
const ownerId = key instanceof CryptographicKey ? key.ownerId : key?.controllerId;
|
|
@@ -1727,11 +1759,12 @@ async function fetchKeyWithResult(cacheKey, cls, options, onCachedUnavailable, o
|
|
|
1727
1759
|
"key"
|
|
1728
1760
|
]);
|
|
1729
1761
|
const keyId = cacheKey.href;
|
|
1730
|
-
if (isCompatibleKeyId(cacheKey)) options = {
|
|
1731
|
-
...options,
|
|
1732
|
-
keyCache: void 0
|
|
1733
|
-
};
|
|
1734
1762
|
const keyCache = options.keyCache;
|
|
1763
|
+
if (isCompatibleKeyId(cacheKey)) {
|
|
1764
|
+
const result = await fetchCompatibleKey(cacheKey, cls, options, onCachedUnavailable, onFetchError, logger);
|
|
1765
|
+
outcome = result.outcome;
|
|
1766
|
+
return result.result;
|
|
1767
|
+
}
|
|
1735
1768
|
const didKey = await resolveDidKey(cacheKey, cls, keyCache, logger);
|
|
1736
1769
|
if (didKey != null) {
|
|
1737
1770
|
outcome = { result: didKey.key == null ? "invalid" : didKey.cached ? "hit" : "fetched" };
|
|
@@ -1773,6 +1806,65 @@ async function fetchKeyWithResult(cacheKey, cls, options, onCachedUnavailable, o
|
|
|
1773
1806
|
});
|
|
1774
1807
|
}
|
|
1775
1808
|
}
|
|
1809
|
+
/**
|
|
1810
|
+
* Tells which purpose a lookup of a key at a compatible identifier serves.
|
|
1811
|
+
*/
|
|
1812
|
+
function getCompatibleKeyScope(cls, options) {
|
|
1813
|
+
if (cls !== CryptographicKey) return "multikey";
|
|
1814
|
+
return options.portableGatewayKeyResolver == null ? "cryptographicKey" : "httpSignature";
|
|
1815
|
+
}
|
|
1816
|
+
/**
|
|
1817
|
+
* Looks up a key at an [FEP-ef61] compatible identifier. Such a key may be
|
|
1818
|
+
* a gateway key of a portable actor, which is accepted for some purposes but
|
|
1819
|
+
* not for others, so everything resolved from the document at the key ID is
|
|
1820
|
+
* cached in a namespace of the lookup's purpose, and one purpose never reuses
|
|
1821
|
+
* what another resolved, nor what another rejected. A failure to fetch
|
|
1822
|
+
* the document fails every purpose alike, so it is cached in the shared
|
|
1823
|
+
* namespace, as for any other key.
|
|
1824
|
+
*
|
|
1825
|
+
* [FEP-ef61]: https://w3id.org/fep/ef61
|
|
1826
|
+
*/
|
|
1827
|
+
async function fetchCompatibleKey(cacheKey, cls, options, onCachedUnavailable, onFetchError, logger) {
|
|
1828
|
+
const keyId = cacheKey.href;
|
|
1829
|
+
const sharedCache = options.keyCache;
|
|
1830
|
+
const scopedCache = sharedCache?.compatibleKeyScope?.(getCompatibleKeyScope(cls, options));
|
|
1831
|
+
const cached = await getCachedFetchKey(cacheKey, keyId, cls, scopedCache, logger);
|
|
1832
|
+
if (cached != null) return {
|
|
1833
|
+
result: cached,
|
|
1834
|
+
outcome: { result: "hit" }
|
|
1835
|
+
};
|
|
1836
|
+
if (await sharedCache?.get(cacheKey) === null) {
|
|
1837
|
+
logger.debug("Entry {keyId} found in cache, but it could not be fetched.", { keyId });
|
|
1838
|
+
return {
|
|
1839
|
+
result: await onCachedUnavailable(cacheKey, keyId, sharedCache, logger),
|
|
1840
|
+
outcome: { result: "hit" }
|
|
1841
|
+
};
|
|
1842
|
+
}
|
|
1843
|
+
logger.debug("Fetching key {keyId} to verify signature...", { keyId });
|
|
1844
|
+
let document;
|
|
1845
|
+
let documentUrl;
|
|
1846
|
+
try {
|
|
1847
|
+
const remoteDocument = await (options.documentLoader ?? getDocumentLoader())(keyId);
|
|
1848
|
+
document = remoteDocument.document;
|
|
1849
|
+
documentUrl = new URL(remoteDocument.documentUrl ?? "", cacheKey);
|
|
1850
|
+
} catch (error) {
|
|
1851
|
+
const outcome = classifyFetchError(error);
|
|
1852
|
+
const result = await onFetchError(error, cacheKey, keyId, sharedCache, logger);
|
|
1853
|
+
await scopedCache?.delete(cacheKey);
|
|
1854
|
+
return {
|
|
1855
|
+
result,
|
|
1856
|
+
outcome
|
|
1857
|
+
};
|
|
1858
|
+
}
|
|
1859
|
+
const resolved = await resolveFetchedKey(document, cacheKey, documentUrl, keyId, cls, {
|
|
1860
|
+
...options,
|
|
1861
|
+
keyCache: scopedCache
|
|
1862
|
+
}, logger);
|
|
1863
|
+
return {
|
|
1864
|
+
result: resolved,
|
|
1865
|
+
outcome: { result: resolved.key != null ? "fetched" : "invalid" }
|
|
1866
|
+
};
|
|
1867
|
+
}
|
|
1776
1868
|
async function fetchKeyInternal(keyId, cls, options = {}) {
|
|
1777
1869
|
return await fetchKeyWithResult(typeof keyId === "string" ? new URL(keyId) : keyId, cls, options, (_cacheKey, _keyId, _keyCache, _logger) => {
|
|
1778
1870
|
return {
|
|
@@ -2289,10 +2381,7 @@ async function verifySignature(jsonLd, options = {}) {
|
|
|
2289
2381
|
});
|
|
2290
2382
|
const { key } = await measureSignatureKeyFetch(options.meterProvider, "linked_data", () => fetchKey(new URL(sig.creator), CryptographicKey, {
|
|
2291
2383
|
...options,
|
|
2292
|
-
keyCache:
|
|
2293
|
-
get: () => Promise.resolve(void 0),
|
|
2294
|
-
set: async (keyId, key) => await options.keyCache?.set(keyId, key)
|
|
2295
|
-
}
|
|
2384
|
+
keyCache: bypassKeyCacheReads(options.keyCache)
|
|
2296
2385
|
}));
|
|
2297
2386
|
if (key == null) return null;
|
|
2298
2387
|
return await crypto.subtle.verify("RSASSA-PKCS1-v1_5", key.publicKey, signature.slice(), messageBytes) ? key : null;
|
|
@@ -3338,38 +3427,42 @@ function equalStringSets(left, right) {
|
|
|
3338
3427
|
}
|
|
3339
3428
|
async function hasValidProofOptions(proofConfig, options, documentLoader) {
|
|
3340
3429
|
const expires = await getProofOption(proofConfig, SECURITY_EXPIRATION, ["expires", SECURITY_EXPIRATION], documentLoader);
|
|
3341
|
-
if (expires == null) return
|
|
3430
|
+
if (expires == null) return null;
|
|
3431
|
+
let expiration;
|
|
3342
3432
|
if (expires.present) {
|
|
3343
|
-
if (typeof expires.value !== "string") return
|
|
3344
|
-
let expiration;
|
|
3433
|
+
if (typeof expires.value !== "string") return null;
|
|
3345
3434
|
try {
|
|
3346
3435
|
expiration = Temporal.Instant.from(expires.value);
|
|
3347
3436
|
} catch {
|
|
3348
|
-
return
|
|
3437
|
+
return null;
|
|
3349
3438
|
}
|
|
3350
|
-
if (Temporal.Instant.compare(Temporal.Now.instant(), expiration) >= 0) return
|
|
3439
|
+
if (Temporal.Instant.compare(Temporal.Now.instant(), expiration) >= 0) return null;
|
|
3351
3440
|
}
|
|
3352
3441
|
const domain = await getProofOption(proofConfig, SECURITY_DOMAIN, ["domain", SECURITY_DOMAIN], documentLoader);
|
|
3353
|
-
if (domain == null) return
|
|
3442
|
+
if (domain == null) return null;
|
|
3354
3443
|
const proofDomains = domain.present ? parseStringSet(domain.value) : null;
|
|
3355
|
-
if (domain.present && proofDomains == null) return
|
|
3444
|
+
if (domain.present && proofDomains == null) return null;
|
|
3356
3445
|
if (options.domain != null) {
|
|
3357
3446
|
const expectedDomains = parseStringSet(options.domain);
|
|
3358
|
-
if (expectedDomains == null || proofDomains == null || !equalStringSets(proofDomains, expectedDomains)) return
|
|
3447
|
+
if (expectedDomains == null || proofDomains == null || !equalStringSets(proofDomains, expectedDomains)) return null;
|
|
3359
3448
|
}
|
|
3360
3449
|
const challenge = await getProofOption(proofConfig, SECURITY_CHALLENGE, ["challenge", SECURITY_CHALLENGE], documentLoader);
|
|
3361
|
-
if (challenge == null || challenge.present && typeof challenge.value !== "string" || options.challenge != null && (!challenge.present || challenge.value !== options.challenge)) return
|
|
3450
|
+
if (challenge == null || challenge.present && typeof challenge.value !== "string" || options.challenge != null && (!challenge.present || challenge.value !== options.challenge)) return null;
|
|
3362
3451
|
const nonce = await getProofOption(proofConfig, SECURITY_NONCE, ["nonce", SECURITY_NONCE], documentLoader);
|
|
3363
|
-
if (nonce == null || nonce.present && typeof nonce.value !== "string") return
|
|
3452
|
+
if (nonce == null || nonce.present && typeof nonce.value !== "string") return null;
|
|
3364
3453
|
const previousProof = await getProofOption(proofConfig, SECURITY_PREVIOUS_PROOF, ["previousProof", SECURITY_PREVIOUS_PROOF], documentLoader);
|
|
3365
|
-
if (previousProof == null || previousProof.present && typeof previousProof.value !== "string" && (!Array.isArray(previousProof.value) || previousProof.value.some((item) => typeof item !== "string"))) return
|
|
3366
|
-
return
|
|
3454
|
+
if (previousProof == null || previousProof.present && typeof previousProof.value !== "string" && (!Array.isArray(previousProof.value) || previousProof.value.some((item) => typeof item !== "string"))) return null;
|
|
3455
|
+
return { expires: expiration };
|
|
3367
3456
|
}
|
|
3368
3457
|
async function verifyProofInternal(jsonLd, proof, options, messageDigestCache, rawProofCandidate, keyIdBoundByCaller = false) {
|
|
3369
3458
|
if (!isJsonLdNode(jsonLd) || proof.cryptosuite !== "eddsa-jcs-2022" || proof.verificationMethodId == null || proof.proofPurpose !== "assertionMethod" || proof.proofValue == null || proof.created == null) return null;
|
|
3370
3459
|
const proofContextLoader = messageDigestCache.proofContextLoader ?? preloadedOnlyDocumentLoader;
|
|
3371
3460
|
const proofConfiguration = await createProofConfiguration(jsonLd, proof, options, proofContextLoader, rawProofCandidate);
|
|
3372
|
-
if (proofConfiguration == null
|
|
3461
|
+
if (proofConfiguration == null) return null;
|
|
3462
|
+
const validProofOptions = await hasValidProofOptions(proofConfiguration.value, options, proofContextLoader);
|
|
3463
|
+
if (validProofOptions == null) return null;
|
|
3464
|
+
const { expires } = validProofOptions;
|
|
3465
|
+
if (expires != null && (messageDigestCache.expires == null || Temporal.Instant.compare(expires, messageDigestCache.expires) < 0)) messageDigestCache.expires = expires;
|
|
3373
3466
|
const publicKeyPromise = measureSignatureKeyFetch(options.meterProvider, "object_integrity", () => fetchKey(proof.verificationMethodId, Multikey, {
|
|
3374
3467
|
...options,
|
|
3375
3468
|
keyIdBoundByCaller
|
|
@@ -3403,10 +3496,7 @@ async function verifyProofInternal(jsonLd, proof, options, messageDigestCache, r
|
|
|
3403
3496
|
});
|
|
3404
3497
|
return await verifyProofInternal(jsonLd, proof, {
|
|
3405
3498
|
...options,
|
|
3406
|
-
keyCache:
|
|
3407
|
-
get: () => Promise.resolve(void 0),
|
|
3408
|
-
set: async (keyId, key) => await options.keyCache?.set(keyId, key)
|
|
3409
|
-
}
|
|
3499
|
+
keyCache: bypassKeyCacheReads(options.keyCache)
|
|
3410
3500
|
}, messageDigestCache, rawProofCandidate, keyIdBoundByCaller);
|
|
3411
3501
|
}
|
|
3412
3502
|
logger$1.debug("The fetched key (verificationMethod) for the proof is not a valid Ed25519 key:\n{keyId}", {
|
|
@@ -3442,10 +3532,7 @@ async function verifyProofInternal(jsonLd, proof, options, messageDigestCache, r
|
|
|
3442
3532
|
});
|
|
3443
3533
|
return await verifyProofInternal(jsonLd, proof, {
|
|
3444
3534
|
...options,
|
|
3445
|
-
keyCache:
|
|
3446
|
-
get: () => Promise.resolve(void 0),
|
|
3447
|
-
set: async (keyId, key) => await options.keyCache?.set(keyId, key)
|
|
3448
|
-
}
|
|
3535
|
+
keyCache: bypassKeyCacheReads(options.keyCache)
|
|
3449
3536
|
}, messageDigestCache, rawProofCandidate, keyIdBoundByCaller);
|
|
3450
3537
|
}
|
|
3451
3538
|
logger$1.debug("Failed to verify the proof with the fetched key {keyId}:\n{proof}", {
|
|
@@ -3774,21 +3861,24 @@ async function verifyPortableObjectProofWithRoot(jsonLd, options = {}) {
|
|
|
3774
3861
|
...prepared.root == null ? {} : { root: prepared.root },
|
|
3775
3862
|
...prepared.objectType == null ? {} : { objectType: prepared.objectType }
|
|
3776
3863
|
};
|
|
3777
|
-
const { root, objectType } = prepared;
|
|
3864
|
+
const { root, objectType, proofContextLoader } = prepared;
|
|
3865
|
+
const messageDigestCache = { proofContextLoader };
|
|
3866
|
+
const result = await verifyPreparedPortableObjectProof(jsonLd, prepared, options, messageDigestCache);
|
|
3867
|
+
const { expires } = messageDigestCache;
|
|
3778
3868
|
return {
|
|
3779
|
-
result
|
|
3869
|
+
result,
|
|
3780
3870
|
root,
|
|
3781
|
-
objectType
|
|
3871
|
+
objectType,
|
|
3872
|
+
...result.verified && expires != null ? { expires } : {}
|
|
3782
3873
|
};
|
|
3783
3874
|
}
|
|
3784
|
-
async function verifyPreparedPortableObjectProof(jsonLd, prepared, options) {
|
|
3875
|
+
async function verifyPreparedPortableObjectProof(jsonLd, prepared, options, messageDigestCache) {
|
|
3785
3876
|
const { proofs, rawProofValues, proofContextLoader } = prepared;
|
|
3786
3877
|
const keys = [];
|
|
3787
3878
|
const rawProofCandidatePool = {
|
|
3788
3879
|
candidates: await parseRawProofCandidates(jsonLd, rawProofValues, options, proofContextLoader),
|
|
3789
3880
|
used: /* @__PURE__ */ new Set()
|
|
3790
3881
|
};
|
|
3791
|
-
const messageDigestCache = { proofContextLoader };
|
|
3792
3882
|
for (let proofIndex = 0; proofIndex < proofs.length; proofIndex++) {
|
|
3793
3883
|
const rawProofCandidate = takeRawProofCandidate(rawProofCandidatePool, proofs[proofIndex]);
|
|
3794
3884
|
if (rawProofCandidate == null) return {
|
|
@@ -3986,7 +4076,7 @@ async function verifyPortableGatewayKeyDocument(document, actor, keyId, options
|
|
|
3986
4076
|
if (!(error instanceof TypeError)) throw error;
|
|
3987
4077
|
return reject(`The actor document is malformed: ${error.message}`);
|
|
3988
4078
|
}
|
|
3989
|
-
const { result, root } = verification;
|
|
4079
|
+
const { result, root, expires } = verification;
|
|
3990
4080
|
if (!result.verified) return reject("The actor document does not satisfy the FEP-ef61 proof policy: " + result.reason.type);
|
|
3991
4081
|
if (root == null) return reject("The actor document could not be expanded.");
|
|
3992
4082
|
const refuse = (url) => Promise.reject(/* @__PURE__ */ new Error(`Refusing to fetch ${url}.`));
|
|
@@ -4058,7 +4148,8 @@ async function verifyPortableGatewayKeyDocument(document, actor, keyId, options
|
|
|
4058
4148
|
return {
|
|
4059
4149
|
type: "verified",
|
|
4060
4150
|
actor,
|
|
4061
|
-
key
|
|
4151
|
+
key,
|
|
4152
|
+
...expires == null ? {} : { expires }
|
|
4062
4153
|
};
|
|
4063
4154
|
}
|
|
4064
4155
|
/**
|
|
@@ -4787,10 +4878,7 @@ async function verifyRequestDraft(request, span, metricsContext, { documentLoade
|
|
|
4787
4878
|
contextLoader,
|
|
4788
4879
|
timeWindow,
|
|
4789
4880
|
currentTime,
|
|
4790
|
-
keyCache:
|
|
4791
|
-
get: () => Promise.resolve(void 0),
|
|
4792
|
-
set: async (keyId, key) => await keyCache?.set(keyId, key)
|
|
4793
|
-
},
|
|
4881
|
+
keyCache: bypassKeyCacheReads(keyCache),
|
|
4794
4882
|
meterProvider,
|
|
4795
4883
|
tracerProvider
|
|
4796
4884
|
});
|
|
@@ -5039,10 +5127,7 @@ async function verifyRequestRfc9421(request, span, metricsContext, { documentLoa
|
|
|
5039
5127
|
contextLoader,
|
|
5040
5128
|
timeWindow,
|
|
5041
5129
|
currentTime,
|
|
5042
|
-
keyCache:
|
|
5043
|
-
get: () => Promise.resolve(void 0),
|
|
5044
|
-
set: async (keyId, key) => await keyCache?.set(keyId, key)
|
|
5045
|
-
},
|
|
5130
|
+
keyCache: bypassKeyCacheReads(keyCache),
|
|
5046
5131
|
spec: "rfc9421",
|
|
5047
5132
|
meterProvider,
|
|
5048
5133
|
tracerProvider
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/// <reference lib="esnext.temporal" />
|
|
2
2
|
import { Actor, CryptographicKey, Multikey } from "@fedify/vocab";
|
|
3
|
-
import { MeterProvider, TracerProvider } from "@opentelemetry/api";
|
|
4
3
|
import { DocumentLoader } from "@fedify/vocab-runtime";
|
|
4
|
+
import { MeterProvider, TracerProvider } from "@opentelemetry/api";
|
|
5
5
|
|
|
6
6
|
//#region src/sig/portable-key.d.ts
|
|
7
7
|
/**
|
|
@@ -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
|
*
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import "temporal-polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { n as version, t as name } from "./deno-
|
|
5
|
-
import { _ as recordKeyLookup, n as getDurationMs, t as classifyFetchError } from "./metrics-
|
|
6
|
-
import { a as isCompatibleKeyId, o as isPortableActorDocument, s as isPortableId } from "./portable-key-id-
|
|
4
|
+
import { n as version, t as name } from "./deno-CCABdw7r.mjs";
|
|
5
|
+
import { _ as recordKeyLookup, n as getDurationMs, t as classifyFetchError } from "./metrics-em_7lwhG.mjs";
|
|
6
|
+
import { a as isCompatibleKeyId, o as isPortableActorDocument, s as isPortableId } from "./portable-key-id-T7lecIbt.mjs";
|
|
7
7
|
import { getLogger } from "@logtape/logtape";
|
|
8
8
|
import { CryptographicKey, Multikey, Object as Object$1, isActor } from "@fedify/vocab";
|
|
9
9
|
import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
|
|
@@ -210,6 +210,32 @@ async function verifyKeyOwnership(key, options = {}) {
|
|
|
210
210
|
});
|
|
211
211
|
return null;
|
|
212
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* Wraps a key cache so that lookups through the wrapper miss, while
|
|
215
|
+
* whatever they resolve is still written to the cache. It is used to retry
|
|
216
|
+
* a verification that failed with a cached key with a freshly fetched one.
|
|
217
|
+
* @param keyCache The key cache to wrap.
|
|
218
|
+
* @returns The wrapped key cache.
|
|
219
|
+
* @internal
|
|
220
|
+
*/
|
|
221
|
+
function bypassKeyCacheReads(keyCache) {
|
|
222
|
+
const cache = keyCache;
|
|
223
|
+
const bypassed = {
|
|
224
|
+
get: () => Promise.resolve(void 0),
|
|
225
|
+
set: async (keyId, key) => await cache?.set(keyId, key),
|
|
226
|
+
getFetchError: () => Promise.resolve(void 0),
|
|
227
|
+
setFetchError: async (keyId, error) => await cache?.setFetchError?.(keyId, error)
|
|
228
|
+
};
|
|
229
|
+
if (cache?.compatibleKeyScope != null) bypassed.compatibleKeyScope = (scope) => {
|
|
230
|
+
const scoped = cache.compatibleKeyScope(scope);
|
|
231
|
+
return {
|
|
232
|
+
get: () => Promise.resolve(void 0),
|
|
233
|
+
set: async (keyId, key, options) => await scoped.set(keyId, key, options),
|
|
234
|
+
delete: async (keyId) => await scoped.delete(keyId)
|
|
235
|
+
};
|
|
236
|
+
};
|
|
237
|
+
return bypassed;
|
|
238
|
+
}
|
|
213
239
|
async function withFetchKeySpan(keyId, tracerProvider, fetcher) {
|
|
214
240
|
tracerProvider ??= trace.getTracerProvider();
|
|
215
241
|
return await tracerProvider.getTracer(name, version).startActiveSpan("activitypub.fetch_key", {
|
|
@@ -246,9 +272,9 @@ async function withFetchKeySpan(keyId, tracerProvider, fetcher) {
|
|
|
246
272
|
* Gateway keys of [FEP-ef61] portable actors, i.e., keys whose IDs are
|
|
247
273
|
* compatible identifiers that dereference to portable actor documents, are
|
|
248
274
|
* not resolved by this function, as they only authenticate HTTP requests;
|
|
249
|
-
* use `verifyRequest()` or `getKeyOwner()` for them instead. Keys
|
|
250
|
-
*
|
|
251
|
-
*
|
|
275
|
+
* use `verifyRequest()` or `getKeyOwner()` for them instead. Keys whose IDs
|
|
276
|
+
* are compatible identifiers are never read from or written to the key
|
|
277
|
+
* cache under their IDs, except for failures to fetch them.
|
|
252
278
|
*
|
|
253
279
|
* [FEP-ef61]: https://w3id.org/fep/ef61
|
|
254
280
|
* @template T The type of the key to fetch. Either {@link CryptographicKey}
|
|
@@ -271,9 +297,9 @@ function fetchKey(keyId, cls, options = {}) {
|
|
|
271
297
|
* Gateway keys of [FEP-ef61] portable actors, i.e., keys whose IDs are
|
|
272
298
|
* compatible identifiers that dereference to portable actor documents, are
|
|
273
299
|
* not resolved by this function, as they only authenticate HTTP requests;
|
|
274
|
-
* use `verifyRequest()` or `getKeyOwner()` for them instead. Keys
|
|
275
|
-
*
|
|
276
|
-
*
|
|
300
|
+
* use `verifyRequest()` or `getKeyOwner()` for them instead. Keys whose IDs
|
|
301
|
+
* are compatible identifiers are never read from or written to the key
|
|
302
|
+
* cache under their IDs, except for failures to fetch them.
|
|
277
303
|
*
|
|
278
304
|
* [FEP-ef61]: https://w3id.org/fep/ef61
|
|
279
305
|
*
|
|
@@ -456,11 +482,13 @@ async function resolveFetchedKey(document, cacheKey, documentUrl, keyId, cls, {
|
|
|
456
482
|
}
|
|
457
483
|
}
|
|
458
484
|
if (isCompatibleKeyId(cacheKey) && isPortableActorDocument(object)) {
|
|
485
|
+
const scopedCache = keyCache;
|
|
459
486
|
if (portableGatewayKeyResolver == null || cls !== CryptographicKey) {
|
|
460
487
|
logger.debug("Failed to verify; key {keyId} is a gateway key of the portable actor {actorId}, which is only accepted for HTTP Signatures.", {
|
|
461
488
|
keyId,
|
|
462
489
|
actorId: object.id?.href
|
|
463
490
|
});
|
|
491
|
+
await scopedCache?.set(cacheKey, null);
|
|
464
492
|
return {
|
|
465
493
|
key: null,
|
|
466
494
|
cached: false
|
|
@@ -472,11 +500,13 @@ async function resolveFetchedKey(document, cacheKey, documentUrl, keyId, cls, {
|
|
|
472
500
|
keyId,
|
|
473
501
|
actorId: object.id?.href
|
|
474
502
|
});
|
|
503
|
+
await scopedCache?.set(cacheKey, null);
|
|
475
504
|
return {
|
|
476
505
|
key: null,
|
|
477
506
|
cached: false
|
|
478
507
|
};
|
|
479
508
|
}
|
|
509
|
+
await scopedCache?.set(cacheKey, resolution.key, { expires: resolution.expires });
|
|
480
510
|
return {
|
|
481
511
|
key: resolution.key,
|
|
482
512
|
cached: false
|
|
@@ -605,8 +635,9 @@ async function resolveFetchedKey(document, cacheKey, documentUrl, keyId, cls, {
|
|
|
605
635
|
}
|
|
606
636
|
/**
|
|
607
637
|
* Tells whether a key claims a portable actor as its owner. Such a key can
|
|
608
|
-
* only be a gateway key, which is never cached, so a cached one
|
|
609
|
-
* by an older version that trusted web origins, and is fetched
|
|
638
|
+
* only be a gateway key, which is never cached under its ID, so a cached one
|
|
639
|
+
* was cached by an older version that trusted web origins, and is fetched
|
|
640
|
+
* again.
|
|
610
641
|
*/
|
|
611
642
|
function claimsPortableOwner(key) {
|
|
612
643
|
const ownerId = key instanceof CryptographicKey ? key.ownerId : key?.controllerId;
|
|
@@ -622,11 +653,12 @@ async function fetchKeyWithResult(cacheKey, cls, options, onCachedUnavailable, o
|
|
|
622
653
|
"key"
|
|
623
654
|
]);
|
|
624
655
|
const keyId = cacheKey.href;
|
|
625
|
-
if (isCompatibleKeyId(cacheKey)) options = {
|
|
626
|
-
...options,
|
|
627
|
-
keyCache: void 0
|
|
628
|
-
};
|
|
629
656
|
const keyCache = options.keyCache;
|
|
657
|
+
if (isCompatibleKeyId(cacheKey)) {
|
|
658
|
+
const result = await fetchCompatibleKey(cacheKey, cls, options, onCachedUnavailable, onFetchError, logger);
|
|
659
|
+
outcome = result.outcome;
|
|
660
|
+
return result.result;
|
|
661
|
+
}
|
|
630
662
|
const didKey = await resolveDidKey(cacheKey, cls, keyCache, logger);
|
|
631
663
|
if (didKey != null) {
|
|
632
664
|
outcome = { result: didKey.key == null ? "invalid" : didKey.cached ? "hit" : "fetched" };
|
|
@@ -668,6 +700,65 @@ async function fetchKeyWithResult(cacheKey, cls, options, onCachedUnavailable, o
|
|
|
668
700
|
});
|
|
669
701
|
}
|
|
670
702
|
}
|
|
703
|
+
/**
|
|
704
|
+
* Tells which purpose a lookup of a key at a compatible identifier serves.
|
|
705
|
+
*/
|
|
706
|
+
function getCompatibleKeyScope(cls, options) {
|
|
707
|
+
if (cls !== CryptographicKey) return "multikey";
|
|
708
|
+
return options.portableGatewayKeyResolver == null ? "cryptographicKey" : "httpSignature";
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* Looks up a key at an [FEP-ef61] compatible identifier. Such a key may be
|
|
712
|
+
* a gateway key of a portable actor, which is accepted for some purposes but
|
|
713
|
+
* not for others, so everything resolved from the document at the key ID is
|
|
714
|
+
* cached in a namespace of the lookup's purpose, and one purpose never reuses
|
|
715
|
+
* what another resolved, nor what another rejected. A failure to fetch
|
|
716
|
+
* the document fails every purpose alike, so it is cached in the shared
|
|
717
|
+
* namespace, as for any other key.
|
|
718
|
+
*
|
|
719
|
+
* [FEP-ef61]: https://w3id.org/fep/ef61
|
|
720
|
+
*/
|
|
721
|
+
async function fetchCompatibleKey(cacheKey, cls, options, onCachedUnavailable, onFetchError, logger) {
|
|
722
|
+
const keyId = cacheKey.href;
|
|
723
|
+
const sharedCache = options.keyCache;
|
|
724
|
+
const scopedCache = sharedCache?.compatibleKeyScope?.(getCompatibleKeyScope(cls, options));
|
|
725
|
+
const cached = await getCachedFetchKey(cacheKey, keyId, cls, scopedCache, logger);
|
|
726
|
+
if (cached != null) return {
|
|
727
|
+
result: cached,
|
|
728
|
+
outcome: { result: "hit" }
|
|
729
|
+
};
|
|
730
|
+
if (await sharedCache?.get(cacheKey) === null) {
|
|
731
|
+
logger.debug("Entry {keyId} found in cache, but it could not be fetched.", { keyId });
|
|
732
|
+
return {
|
|
733
|
+
result: await onCachedUnavailable(cacheKey, keyId, sharedCache, logger),
|
|
734
|
+
outcome: { result: "hit" }
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
logger.debug("Fetching key {keyId} to verify signature...", { keyId });
|
|
738
|
+
let document;
|
|
739
|
+
let documentUrl;
|
|
740
|
+
try {
|
|
741
|
+
const remoteDocument = await (options.documentLoader ?? getDocumentLoader())(keyId);
|
|
742
|
+
document = remoteDocument.document;
|
|
743
|
+
documentUrl = new URL(remoteDocument.documentUrl ?? "", cacheKey);
|
|
744
|
+
} catch (error) {
|
|
745
|
+
const outcome = classifyFetchError(error);
|
|
746
|
+
const result = await onFetchError(error, cacheKey, keyId, sharedCache, logger);
|
|
747
|
+
await scopedCache?.delete(cacheKey);
|
|
748
|
+
return {
|
|
749
|
+
result,
|
|
750
|
+
outcome
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
const resolved = await resolveFetchedKey(document, cacheKey, documentUrl, keyId, cls, {
|
|
754
|
+
...options,
|
|
755
|
+
keyCache: scopedCache
|
|
756
|
+
}, logger);
|
|
757
|
+
return {
|
|
758
|
+
result: resolved,
|
|
759
|
+
outcome: { result: resolved.key != null ? "fetched" : "invalid" }
|
|
760
|
+
};
|
|
761
|
+
}
|
|
671
762
|
async function fetchKeyInternal(keyId, cls, options = {}) {
|
|
672
763
|
return await fetchKeyWithResult(typeof keyId === "string" ? new URL(keyId) : keyId, cls, options, (_cacheKey, _keyId, _keyCache, _logger) => {
|
|
673
764
|
return {
|
|
@@ -692,4 +783,4 @@ async function fetchKeyInternal(keyId, cls, options = {}) {
|
|
|
692
783
|
});
|
|
693
784
|
}
|
|
694
785
|
//#endregion
|
|
695
|
-
export {
|
|
786
|
+
export { fetchKeyDetailed as a, validateCryptoKey as c, fetchKey as i, verifyKeyOwnership as l, exportJwk as n, generateCryptoKeyPair as o, fetchActorDocument as r, importJwk as s, bypassKeyCacheReads as t };
|