@fedify/fedify 2.4.0-dev.2077 → 2.4.0-dev.2093
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-i3jxVe9m.mjs → builder-Ld4Y9ymR.mjs} +34 -3
- package/dist/compat/transformers.test.mjs +1 -1
- package/dist/{compound-proof-Cayy-ECj.mjs → compound-proof-CTx_lewQ.mjs} +191 -13
- package/dist/{deno-Djkzd-G_.mjs → deno-8t8k_6Yv.mjs} +1 -1
- package/dist/{docloader-dA0RswUV.mjs → docloader-DJKcsAiF.mjs} +2 -2
- package/dist/federation/builder.test.mjs +1 -1
- package/dist/federation/compound-fanout.test.mjs +3 -3
- package/dist/federation/handler-compound-proof.test.mjs +3 -3
- package/dist/federation/handler.test.mjs +5 -5
- package/dist/federation/hashlink-media.test.mjs +2 -2
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/metrics.test.mjs +1 -1
- package/dist/federation/middleware.test.mjs +7 -7
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.js +1 -1
- package/dist/federation/outgoing-proof.test.mjs +254 -5
- package/dist/federation/portable-gateway-keys.test.mjs +72 -11
- package/dist/federation/portable-inbox.test.mjs +513 -6
- package/dist/federation/portable.test.mjs +182 -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 +89 -1
- package/dist/{federation-CFd0Jhhv.cjs → federation-aSHO04ob.cjs} +472 -74
- package/dist/{http-CCJ97Jql.mjs → http-B2ftY0Ha.mjs} +4 -4
- package/dist/{http-Dvy_IwRa.cjs → http-DSaSAN7X.cjs} +1609 -1457
- package/dist/{http-aqh3yFdM.js → http-DyT7DP1Y.js} +1518 -1390
- package/dist/{key-IxTTC8Yf.mjs → key-Dwm4VswT.mjs} +39 -99
- package/dist/{kv-cache-BP69NV0P.cjs → kv-cache-BSJAlP4C.cjs} +1 -1
- package/dist/{kv-cache-Bwwbh-xR.mjs → kv-cache-D0yYukqk.mjs} +1 -1
- package/dist/{kv-cache-Y6SDRSX6.js → kv-cache-DmroAz3M.js} +1 -1
- package/dist/{ld-l0B9nREb.mjs → ld-BYZOoDzZ.mjs} +3 -3
- package/dist/{metrics-D1p6hv4z.mjs → metrics-B7YwKTFr.mjs} +1 -1
- package/dist/{middleware-BDjCUHMS.mjs → middleware-BNeqKSsL.mjs} +1 -1
- package/dist/{middleware-SSfryVzY.js → middleware-Blq0882d.js} +480 -82
- package/dist/{middleware-BMgzqBtr.mjs → middleware-E_0IH72m.mjs} +278 -80
- package/dist/{mod-HDSvS-ww.d.cts → mod-C8LieBzF.d.cts} +16 -3
- package/dist/{mod-C9WSpZWm.d.ts → mod-Coacostr.d.ts} +16 -3
- package/dist/mod.cjs +5 -5
- package/dist/mod.d.cts +1 -1
- package/dist/mod.d.ts +1 -1
- package/dist/mod.js +5 -5
- package/dist/nodeinfo/handler.test.mjs +1 -1
- package/dist/{owner-DGbYmXqU.mjs → owner-Cgx2Dc7I.mjs} +5 -4
- package/dist/{owner-jH1z_YfD.js → owner-DAGSC5gX.js} +2 -2
- package/dist/{owner-BpOxL5PG.cjs → owner-szIZl4qI.cjs} +2 -2
- package/dist/{portable-key-CCwJGSsU.mjs → portable-key-Cn1WJSN1.mjs} +60 -41
- package/dist/portable-key-id-Bva5iT3W.mjs +136 -0
- package/dist/{proof-CVL7JnZd.mjs → proof-DVXC6nYz.mjs} +59 -18
- package/dist/{send-Ejy1jOz9.mjs → send-D-hOvYKy.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 +160 -3
- 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 +1 -1
- package/dist/sig/mod.d.ts +1 -1
- package/dist/sig/mod.js +3 -3
- package/dist/sig/owner.test.mjs +1 -1
- package/dist/sig/portable-collection.test.mjs +69 -5
- package/dist/sig/portable-key.test.mjs +176 -10
- package/dist/sig/proof.test.mjs +107 -4
- package/dist/{sig-Dpy1B1Ad.js → sig-B5Ell7pO.js} +19 -3
- package/dist/{sig-xoqy3ATq.cjs → sig-CXOKN4Br.cjs} +19 -3
- package/dist/{temporal-DNJ1cNpr.mjs → temporal-CqpKGEPk.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.js +1 -1
- package/package.json +7 -7
|
@@ -289,7 +289,11 @@ interface VerifyPortableObjectProofOptions extends VerifyProofOptions {}
|
|
|
289
289
|
* @since 2.4.0
|
|
290
290
|
*/
|
|
291
291
|
type VerifyPortableObjectProofFailureReason = {
|
|
292
|
-
/**
|
|
292
|
+
/**
|
|
293
|
+
* The document has neither a portable `ap:` or `ap+ef61:` ID nor an
|
|
294
|
+
* FEP-ef61 compatible identifier.
|
|
295
|
+
*/
|
|
296
|
+
readonly type: "notPortableObject";
|
|
293
297
|
} | {
|
|
294
298
|
/**
|
|
295
299
|
* The document is a portable collection without an Object Integrity
|
|
@@ -347,15 +351,24 @@ declare function verifyProof(jsonLd: unknown, proof: DataIntegrityProof, options
|
|
|
347
351
|
* collection without a proof is reported separately so a caller can apply a
|
|
348
352
|
* gateway trust policy. Embedded portable objects are not traversed.
|
|
349
353
|
*
|
|
354
|
+
* A portable object is a document whose ID is an `ap:` or `ap+ef61:` URI, or
|
|
355
|
+
* an [FEP-ef61] compatible identifier such as
|
|
356
|
+
* `https://gw.example/.well-known/apgateway/did:key:z6Mk…/actor`, which
|
|
357
|
+
* stands for the portable ID `ap+ef61://did:key:z6Mk…/actor`.
|
|
358
|
+
*
|
|
350
359
|
* Every proof must use a DID URL whose DID matches the portable object's
|
|
351
|
-
* authority, and every proof must
|
|
360
|
+
* authority, i.e., the DID of its canonical portable ID, and every proof must
|
|
361
|
+
* pass {@link verifyProof}. The proofs are verified over the document as
|
|
362
|
+
* given; a compatible identifier is never rewritten into a portable ID.
|
|
363
|
+
*
|
|
364
|
+
* [FEP-ef61]: https://w3id.org/fep/ef61
|
|
352
365
|
*
|
|
353
366
|
* @param jsonLd The JSON-LD document to verify.
|
|
354
367
|
* @param options Additional options. See also
|
|
355
368
|
* {@link VerifyPortableObjectProofOptions}.
|
|
356
369
|
* @returns The detailed portable proof-policy result.
|
|
357
370
|
* @throws {TypeError} If the input is not a single JSON-LD object or has a
|
|
358
|
-
* malformed portable ID.
|
|
371
|
+
* malformed portable ID or compatible identifier.
|
|
359
372
|
* @since 2.4.0
|
|
360
373
|
*/
|
|
361
374
|
declare function verifyPortableObjectProof(jsonLd: unknown, options?: VerifyPortableObjectProofOptions): Promise<VerifyPortableObjectProofResult>;
|
|
@@ -289,7 +289,11 @@ interface VerifyPortableObjectProofOptions extends VerifyProofOptions {}
|
|
|
289
289
|
* @since 2.4.0
|
|
290
290
|
*/
|
|
291
291
|
type VerifyPortableObjectProofFailureReason = {
|
|
292
|
-
/**
|
|
292
|
+
/**
|
|
293
|
+
* The document has neither a portable `ap:` or `ap+ef61:` ID nor an
|
|
294
|
+
* FEP-ef61 compatible identifier.
|
|
295
|
+
*/
|
|
296
|
+
readonly type: "notPortableObject";
|
|
293
297
|
} | {
|
|
294
298
|
/**
|
|
295
299
|
* The document is a portable collection without an Object Integrity
|
|
@@ -347,15 +351,24 @@ declare function verifyProof(jsonLd: unknown, proof: DataIntegrityProof, options
|
|
|
347
351
|
* collection without a proof is reported separately so a caller can apply a
|
|
348
352
|
* gateway trust policy. Embedded portable objects are not traversed.
|
|
349
353
|
*
|
|
354
|
+
* A portable object is a document whose ID is an `ap:` or `ap+ef61:` URI, or
|
|
355
|
+
* an [FEP-ef61] compatible identifier such as
|
|
356
|
+
* `https://gw.example/.well-known/apgateway/did:key:z6Mk…/actor`, which
|
|
357
|
+
* stands for the portable ID `ap+ef61://did:key:z6Mk…/actor`.
|
|
358
|
+
*
|
|
350
359
|
* Every proof must use a DID URL whose DID matches the portable object's
|
|
351
|
-
* authority, and every proof must
|
|
360
|
+
* authority, i.e., the DID of its canonical portable ID, and every proof must
|
|
361
|
+
* pass {@link verifyProof}. The proofs are verified over the document as
|
|
362
|
+
* given; a compatible identifier is never rewritten into a portable ID.
|
|
363
|
+
*
|
|
364
|
+
* [FEP-ef61]: https://w3id.org/fep/ef61
|
|
352
365
|
*
|
|
353
366
|
* @param jsonLd The JSON-LD document to verify.
|
|
354
367
|
* @param options Additional options. See also
|
|
355
368
|
* {@link VerifyPortableObjectProofOptions}.
|
|
356
369
|
* @returns The detailed portable proof-policy result.
|
|
357
370
|
* @throws {TypeError} If the input is not a single JSON-LD object or has a
|
|
358
|
-
* malformed portable ID.
|
|
371
|
+
* malformed portable ID or compatible identifier.
|
|
359
372
|
* @since 2.4.0
|
|
360
373
|
*/
|
|
361
374
|
declare function verifyPortableObjectProof(jsonLd: unknown, options?: VerifyPortableObjectProofOptions): Promise<VerifyPortableObjectProofResult>;
|
package/dist/mod.cjs
CHANGED
|
@@ -3,14 +3,14 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
3
3
|
require("./rolldown-runtime-Dmm3t92W.cjs");
|
|
4
4
|
const require_transformers = require("./transformers-BalCrSsr.cjs");
|
|
5
5
|
require("./compat/mod.cjs");
|
|
6
|
-
const require_http = require("./http-
|
|
7
|
-
const require_federation = require("./federation-
|
|
6
|
+
const require_http = require("./http-DSaSAN7X.cjs");
|
|
7
|
+
const require_federation = require("./federation-aSHO04ob.cjs");
|
|
8
8
|
const require_types = require("./types-Be2zbyeG.cjs");
|
|
9
|
-
const require_owner = require("./owner-
|
|
10
|
-
const require_kv_cache = require("./kv-cache-
|
|
9
|
+
const require_owner = require("./owner-szIZl4qI.cjs");
|
|
10
|
+
const require_kv_cache = require("./kv-cache-BSJAlP4C.cjs");
|
|
11
11
|
require("./nodeinfo/mod.cjs");
|
|
12
12
|
require("./runtime/mod.cjs");
|
|
13
|
-
const require_sig = require("./sig-
|
|
13
|
+
const require_sig = require("./sig-CXOKN4Br.cjs");
|
|
14
14
|
require("./utils/mod.cjs");
|
|
15
15
|
//#endregion
|
|
16
16
|
exports.CircuitBreaker = require_federation.CircuitBreaker;
|
package/dist/mod.d.cts
CHANGED
|
@@ -6,7 +6,7 @@ import { $ as InProcessMessageQueueOptions, A as ObjectCallbackSetters, At as In
|
|
|
6
6
|
import { a as MemoryKvStore, i as KvStoreSetOptions, n as KvStore, r as KvStoreListEntry, t as KvKey } from "./kv-BhNbN8Ed.cjs";
|
|
7
7
|
import { actorDehydrator, autoIdAssigner, getDefaultActivityTransformers } from "./compat/mod.cjs";
|
|
8
8
|
import { a as RouterOptions, i as RouterError, n as handleWebFinger, o as RouterRouteResult, r as Router, t as WebFingerHandlerParameters } from "./mod-JzkaEXsi.cjs";
|
|
9
|
-
import { C as createSignature, D as verifyJsonLd, E as signJsonLd, O as verifySignature, S as attachSignature, T as hasSignatureLike, _ as verifyProof, a as CreateProofOptions, b as VerifyJsonLdOptions, c as VerifyPortableObjectProofFailureReason, d as VerifyProofOptions, f as createProof, g as verifyPortableObjectProof, h as verifyObject, i as verifyPortableObject, l as VerifyPortableObjectProofOptions, m as signObject, n as VerifyPortableObjectOptions, o as SignObjectOptions, p as hasProofLike, r as VerifyPortableObjectResult, s as VerifyObjectOptions, t as VerifyPortableObjectFailureReason, u as VerifyPortableObjectProofResult, v as CreateSignatureOptions, w as detachSignature, x as VerifySignatureOptions, y as SignJsonLdOptions } from "./mod-
|
|
9
|
+
import { C as createSignature, D as verifyJsonLd, E as signJsonLd, O as verifySignature, S as attachSignature, T as hasSignatureLike, _ as verifyProof, a as CreateProofOptions, b as VerifyJsonLdOptions, c as VerifyPortableObjectProofFailureReason, d as VerifyProofOptions, f as createProof, g as verifyPortableObjectProof, h as verifyObject, i as verifyPortableObject, l as VerifyPortableObjectProofOptions, m as signObject, n as VerifyPortableObjectOptions, o as SignObjectOptions, p as hasProofLike, r as VerifyPortableObjectResult, s as VerifyObjectOptions, t as VerifyPortableObjectFailureReason, u as VerifyPortableObjectProofResult, v as CreateSignatureOptions, w as detachSignature, x as VerifySignatureOptions, y as SignJsonLdOptions } from "./mod-C8LieBzF.cjs";
|
|
10
10
|
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./mod-xGFCHqq_.cjs";
|
|
11
11
|
export * from "@fedify/vocab-runtime";
|
|
12
12
|
|
package/dist/mod.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { $ as InProcessMessageQueueOptions, A as ObjectCallbackSetters, At as In
|
|
|
6
6
|
import { a as MemoryKvStore, i as KvStoreSetOptions, n as KvStore, r as KvStoreListEntry, t as KvKey } from "./kv-BhNbN8Ed.js";
|
|
7
7
|
import { actorDehydrator, autoIdAssigner, getDefaultActivityTransformers } from "./compat/mod.js";
|
|
8
8
|
import { a as RouterOptions, i as RouterError, n as handleWebFinger, o as RouterRouteResult, r as Router, t as WebFingerHandlerParameters } from "./mod-B2h2KY0q.js";
|
|
9
|
-
import { C as createSignature, D as verifyJsonLd, E as signJsonLd, O as verifySignature, S as attachSignature, T as hasSignatureLike, _ as verifyProof, a as CreateProofOptions, b as VerifyJsonLdOptions, c as VerifyPortableObjectProofFailureReason, d as VerifyProofOptions, f as createProof, g as verifyPortableObjectProof, h as verifyObject, i as verifyPortableObject, l as VerifyPortableObjectProofOptions, m as signObject, n as VerifyPortableObjectOptions, o as SignObjectOptions, p as hasProofLike, r as VerifyPortableObjectResult, s as VerifyObjectOptions, t as VerifyPortableObjectFailureReason, u as VerifyPortableObjectProofResult, v as CreateSignatureOptions, w as detachSignature, x as VerifySignatureOptions, y as SignJsonLdOptions } from "./mod-
|
|
9
|
+
import { C as createSignature, D as verifyJsonLd, E as signJsonLd, O as verifySignature, S as attachSignature, T as hasSignatureLike, _ as verifyProof, a as CreateProofOptions, b as VerifyJsonLdOptions, c as VerifyPortableObjectProofFailureReason, d as VerifyProofOptions, f as createProof, g as verifyPortableObjectProof, h as verifyObject, i as verifyPortableObject, l as VerifyPortableObjectProofOptions, m as signObject, n as VerifyPortableObjectOptions, o as SignObjectOptions, p as hasProofLike, r as VerifyPortableObjectResult, s as VerifyObjectOptions, t as VerifyPortableObjectFailureReason, u as VerifyPortableObjectProofResult, v as CreateSignatureOptions, w as detachSignature, x as VerifySignatureOptions, y as SignJsonLdOptions } from "./mod-Coacostr.js";
|
|
10
10
|
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./mod-CVZrXSdm.js";
|
|
11
11
|
export * from "@fedify/vocab-runtime";
|
|
12
12
|
|
package/dist/mod.js
CHANGED
|
@@ -3,15 +3,15 @@ import { URLPattern } from "urlpattern-polyfill";
|
|
|
3
3
|
import "./rolldown-runtime-BC-Eo5Mj.js";
|
|
4
4
|
import { n as autoIdAssigner, r as getDefaultActivityTransformers, t as actorDehydrator } from "./transformers-DAtXl_Kc.js";
|
|
5
5
|
import "./compat/mod.js";
|
|
6
|
-
import {
|
|
7
|
-
import { a as respondWithObject, c as digest, d as parseCircuitBreakerKvState, f as createFederationBuilder, i as SendActivityError, l as CircuitBreaker, n as handleWebFinger, o as respondWithObjectIfAcceptable, r as createExponentialBackoffPolicy, s as buildCollectionSynchronizationHeader, t as createFederation, u as normalizeCircuitBreakerOptions } from "./middleware-
|
|
6
|
+
import { C as detachSignature, E as hasSignatureLike, I as fetchKey, L as fetchKeyDetailed, M as verifySignature, P as exportJwk, R as generateCryptoKeyPair, S as createSignature, a as verifyRequestDetailed, b as attachSignature, c as createProof, ct as parseAcceptSignature, f as verifyObject, g as verifyProof, i as verifyRequest, j as verifyJsonLd, k as signJsonLd, l as hasProofLike, lt as validateAcceptSignature, ot as formatAcceptSignature, p as verifyPortableObjectProof, r as signRequest, st as fulfillAcceptSignature, u as signObject, z as importJwk } from "./http-DyT7DP1Y.js";
|
|
7
|
+
import { a as respondWithObject, c as digest, d as parseCircuitBreakerKvState, f as createFederationBuilder, i as SendActivityError, l as CircuitBreaker, n as handleWebFinger, o as respondWithObjectIfAcceptable, r as createExponentialBackoffPolicy, s as buildCollectionSynchronizationHeader, t as createFederation, u as normalizeCircuitBreakerOptions } from "./middleware-Blq0882d.js";
|
|
8
8
|
import { n as getNodeInfo, r as parseNodeInfo, t as nodeInfoToJson } from "./types-C_cNlNVK.js";
|
|
9
|
-
import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-
|
|
10
|
-
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-
|
|
9
|
+
import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-DAGSC5gX.js";
|
|
10
|
+
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-DmroAz3M.js";
|
|
11
11
|
import { InProcessMessageQueue, MemoryKvStore, ParallelMessageQueue, Router, RouterError } from "./federation/mod.js";
|
|
12
12
|
import "./nodeinfo/mod.js";
|
|
13
13
|
import "./runtime/mod.js";
|
|
14
|
-
import { t as verifyPortableObject } from "./sig-
|
|
14
|
+
import { t as verifyPortableObject } from "./sig-B5Ell7pO.js";
|
|
15
15
|
import "./utils/mod.js";
|
|
16
16
|
export * from "@fedify/vocab-runtime";
|
|
17
17
|
//#endregion
|
|
@@ -5,7 +5,7 @@ import { r as createRequestContext } from "../context-DlZhxdFT.mjs";
|
|
|
5
5
|
import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
|
|
6
6
|
import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
|
|
7
7
|
import "../std__assert-DvMDRPes.mjs";
|
|
8
|
-
import {
|
|
8
|
+
import { C as handleNodeInfoJrd, S as handleNodeInfo, o as createFederation } from "../middleware-E_0IH72m.mjs";
|
|
9
9
|
import { test } from "@fedify/fixture";
|
|
10
10
|
//#region src/nodeinfo/handler.test.ts
|
|
11
11
|
test("handleNodeInfo()", async () => {
|
|
@@ -1,9 +1,10 @@
|
|
|
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 {
|
|
6
|
-
import { t as
|
|
4
|
+
import { n as version, t as name } from "./deno-8t8k_6Yv.mjs";
|
|
5
|
+
import { a as isCompatibleKeyId, l as isSamePublicKey$1, s as isPortableId, t as getCanonicalPortableId } from "./portable-key-id-Bva5iT3W.mjs";
|
|
6
|
+
import { c as verifyKeyOwnership, n as fetchActorDocument, t as exportJwk } from "./key-Dwm4VswT.mjs";
|
|
7
|
+
import { t as fetchPortableGatewayKey } from "./portable-key-Cn1WJSN1.mjs";
|
|
7
8
|
import { getLogger } from "@logtape/logtape";
|
|
8
9
|
import { CryptographicKey, Object as Object$1, isActor } from "@fedify/vocab";
|
|
9
10
|
import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
|
|
@@ -176,7 +177,7 @@ async function getKeyOwner(keyId, options) {
|
|
|
176
177
|
return await verifyKeyOwnership(object.id == null ? object.clone({ id: keyId }) : object, fetchOptions);
|
|
177
178
|
}
|
|
178
179
|
if (!isActor(object)) return null;
|
|
179
|
-
if (object.id == null || object.id.origin !== documentUrl.origin) {
|
|
180
|
+
if (object.id == null || isPortableId(object.id) || object.id.origin !== documentUrl.origin) {
|
|
180
181
|
logger.debug("The document served at {documentUrl} claims to be the actor {actorId}, which belongs to another origin; refusing to treat it as the owner of key {keyId}.", {
|
|
181
182
|
documentUrl: documentUrl.href,
|
|
182
183
|
actorId: object.id?.href,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
import { URLPattern } from "urlpattern-polyfill";
|
|
3
|
-
import {
|
|
3
|
+
import { F as fetchActorDocument, P as exportJwk, V as verifyKeyOwnership, _t as isSamePublicKey$1, ht as isPortableId, mt as isCompatibleKeyId, o as fetchPortableGatewayKey, ut as getCanonicalPortableId, vt as name, yt as version } from "./http-DyT7DP1Y.js";
|
|
4
4
|
import { getLogger } from "@logtape/logtape";
|
|
5
5
|
import { CryptographicKey, Object as Object$1, isActor } from "@fedify/vocab";
|
|
6
6
|
import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
|
|
@@ -173,7 +173,7 @@ async function getKeyOwner(keyId, options) {
|
|
|
173
173
|
return await verifyKeyOwnership(object.id == null ? object.clone({ id: keyId }) : object, fetchOptions);
|
|
174
174
|
}
|
|
175
175
|
if (!isActor(object)) return null;
|
|
176
|
-
if (object.id == null || object.id.origin !== documentUrl.origin) {
|
|
176
|
+
if (object.id == null || isPortableId(object.id) || object.id.origin !== documentUrl.origin) {
|
|
177
177
|
logger.debug("The document served at {documentUrl} claims to be the actor {actorId}, which belongs to another origin; refusing to treat it as the owner of key {keyId}.", {
|
|
178
178
|
documentUrl: documentUrl.href,
|
|
179
179
|
actorId: object.id?.href,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const { URLPattern } = require("urlpattern-polyfill");
|
|
2
|
-
const require_http = require("./http-
|
|
2
|
+
const require_http = require("./http-DSaSAN7X.cjs");
|
|
3
3
|
let _logtape_logtape = require("@logtape/logtape");
|
|
4
4
|
let _fedify_vocab = require("@fedify/vocab");
|
|
5
5
|
let _opentelemetry_api = require("@opentelemetry/api");
|
|
@@ -172,7 +172,7 @@ async function getKeyOwner(keyId, options) {
|
|
|
172
172
|
return await require_http.verifyKeyOwnership(object.id == null ? object.clone({ id: keyId }) : object, fetchOptions);
|
|
173
173
|
}
|
|
174
174
|
if (!(0, _fedify_vocab.isActor)(object)) return null;
|
|
175
|
-
if (object.id == null || object.id.origin !== documentUrl.origin) {
|
|
175
|
+
if (object.id == null || require_http.isPortableId(object.id) || object.id.origin !== documentUrl.origin) {
|
|
176
176
|
logger.debug("The document served at {documentUrl} claims to be the actor {actorId}, which belongs to another origin; refusing to treat it as the owner of key {keyId}.", {
|
|
177
177
|
documentUrl: documentUrl.href,
|
|
178
178
|
actorId: object.id?.href,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import "temporal-polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { l as isSamePublicKey, o as isPortableActorDocument, t as getCanonicalPortableId } from "./portable-key-id-Bva5iT3W.mjs";
|
|
5
|
+
import { l as verifyPortableObjectProofWithRoot } from "./proof-DVXC6nYz.mjs";
|
|
6
6
|
import { getLogger } from "@logtape/logtape";
|
|
7
7
|
import { CryptographicKey, Multikey, Object as Object$1 } from "@fedify/vocab";
|
|
8
8
|
import { getDocumentLoader, isGatewayUrl } from "@fedify/vocab-runtime";
|
|
@@ -19,13 +19,19 @@ const SEC = "https://w3id.org/security#";
|
|
|
19
19
|
*
|
|
20
20
|
* A gateway key is accepted only if:
|
|
21
21
|
*
|
|
22
|
-
* - the document is an actor whose portable ID
|
|
23
|
-
* the compatible identifier
|
|
22
|
+
* - the document is an actor whose portable ID, i.e., its `ap:` ID or
|
|
23
|
+
* the canonical portable ID of its compatible identifier, is the one
|
|
24
|
+
* the key ID is the compatible identifier of;
|
|
24
25
|
* - the document has a valid [FEP-8b32] Object Integrity Proof made by
|
|
25
26
|
* the DID of its ID, i.e., it satisfies the [FEP-ef61] proof policy;
|
|
26
27
|
* - the document embeds a `Multikey` with that ID in its `assertionMethod`,
|
|
27
|
-
* whose `controller` is the actor, as [FEP-521a] requires
|
|
28
|
-
* embedded in its `publicKey` with the same ID
|
|
28
|
+
* whose `controller` is the actor, as [FEP-521a] requires, and a key
|
|
29
|
+
* embedded in its `publicKey` with the same ID agrees with it; or, if
|
|
30
|
+
* no `assertionMethod` entry has that ID, the document embeds
|
|
31
|
+
* a `CryptographicKey` with that ID in its `publicKey`, whose `owner` is
|
|
32
|
+
* the actor, as some publishers, e.g., tootik, list their RSA keys only
|
|
33
|
+
* there;
|
|
34
|
+
* - no more than one entry of either property has that ID; and
|
|
29
35
|
* - the key ID's origin is one of the actor's `gateways`.
|
|
30
36
|
*
|
|
31
37
|
* [FEP-8b32]: https://w3id.org/fep/8b32
|
|
@@ -74,6 +80,21 @@ async function verifyPortableGatewayKeyDocument(document, actor, keyId, options
|
|
|
74
80
|
if (!result.verified) return reject("The actor document does not satisfy the FEP-ef61 proof policy: " + result.reason.type);
|
|
75
81
|
if (root == null) return reject("The actor document could not be expanded.");
|
|
76
82
|
const refuse = (url) => Promise.reject(/* @__PURE__ */ new Error(`Refusing to fetch ${url}.`));
|
|
83
|
+
const parseEmbeddedPublicKey = async (node) => {
|
|
84
|
+
if (isReference(node)) return null;
|
|
85
|
+
let key;
|
|
86
|
+
try {
|
|
87
|
+
key = await CryptographicKey.fromJsonLd(node, {
|
|
88
|
+
documentLoader: refuse,
|
|
89
|
+
contextLoader: options.contextLoader,
|
|
90
|
+
tracerProvider: options.tracerProvider
|
|
91
|
+
});
|
|
92
|
+
} catch (error) {
|
|
93
|
+
if (!(error instanceof TypeError)) throw error;
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
return key.publicKey == null ? null : key;
|
|
97
|
+
};
|
|
77
98
|
let verifiedActor;
|
|
78
99
|
try {
|
|
79
100
|
verifiedActor = await Object$1.fromJsonLd(root, {
|
|
@@ -87,41 +108,42 @@ async function verifyPortableGatewayKeyDocument(document, actor, keyId, options
|
|
|
87
108
|
}
|
|
88
109
|
if (!isPortableActorDocument(verifiedActor) || getCanonicalPortableId(verifiedActor.id) !== claimedActorId) return reject("The key ID is not a compatible identifier of the actor whose document is signed.");
|
|
89
110
|
actor = verifiedActor;
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
tracerProvider: options.tracerProvider
|
|
98
|
-
});
|
|
99
|
-
} catch (error) {
|
|
100
|
-
if (!(error instanceof TypeError)) throw error;
|
|
101
|
-
return reject("The key in the assertionMethod is malformed.");
|
|
102
|
-
}
|
|
103
|
-
if (multikey.publicKey == null) return reject("The key in the assertionMethod has no public key.");
|
|
104
|
-
if (multikey.controllerId == null || getCanonicalPortableId(multikey.controllerId) !== actorId) return reject("The controller of the key is not the actor.");
|
|
105
|
-
const publicKeyNode = findEmbeddedNode(root[`${SEC}publicKey`], keyId);
|
|
106
|
-
if (publicKeyNode != null) {
|
|
107
|
-
let publicKey;
|
|
111
|
+
const assertionNodes = findNodes(root[`${SEC}assertionMethod`], keyId);
|
|
112
|
+
const publicKeyNodes = findNodes(root[`${SEC}publicKey`], keyId);
|
|
113
|
+
if (assertionNodes.length > 1 || publicKeyNodes.length > 1) return reject("The actor document has more than one key with the ID.");
|
|
114
|
+
let publicKey;
|
|
115
|
+
if (assertionNodes.length > 0) {
|
|
116
|
+
if (isReference(assertionNodes[0])) return reject("The actor document does not embed the key in its assertionMethod.");
|
|
117
|
+
let multikey;
|
|
108
118
|
try {
|
|
109
|
-
|
|
119
|
+
multikey = await Multikey.fromJsonLd(assertionNodes[0], {
|
|
110
120
|
documentLoader: refuse,
|
|
111
121
|
contextLoader: options.contextLoader,
|
|
112
122
|
tracerProvider: options.tracerProvider
|
|
113
123
|
});
|
|
114
124
|
} catch (error) {
|
|
115
125
|
if (!(error instanceof TypeError)) throw error;
|
|
116
|
-
return reject("The key in the
|
|
126
|
+
return reject("The key in the assertionMethod is malformed.");
|
|
127
|
+
}
|
|
128
|
+
if (multikey.publicKey == null) return reject("The key in the assertionMethod has no public key.");
|
|
129
|
+
if (multikey.controllerId == null || getCanonicalPortableId(multikey.controllerId) !== actorId) return reject("The controller of the key is not the actor.");
|
|
130
|
+
publicKey = multikey.publicKey;
|
|
131
|
+
if (publicKeyNodes.length > 0 && !isReference(publicKeyNodes[0])) {
|
|
132
|
+
const embedded = await parseEmbeddedPublicKey(publicKeyNodes[0]);
|
|
133
|
+
if (embedded == null || !await isSamePublicKey(embedded.publicKey, publicKey) || embedded.ownerId != null && getCanonicalPortableId(embedded.ownerId) !== actorId) return reject("The key in the publicKey does not agree with the one in the assertionMethod.");
|
|
117
134
|
}
|
|
118
|
-
|
|
135
|
+
} else {
|
|
136
|
+
if (publicKeyNodes.length < 1) return reject("The actor document embeds the key in neither its assertionMethod nor its publicKey.");
|
|
137
|
+
const embedded = await parseEmbeddedPublicKey(publicKeyNodes[0]);
|
|
138
|
+
if (embedded == null) return reject("The key in the publicKey is not embedded or malformed.");
|
|
139
|
+
if (embedded.ownerId == null || getCanonicalPortableId(embedded.ownerId) !== actorId) return reject("The owner of the key is not the actor.");
|
|
140
|
+
publicKey = embedded.publicKey;
|
|
119
141
|
}
|
|
120
142
|
if (!actor.gateways.some((gateway) => isGatewayUrl(gateway) && gateway.origin === keyId.origin)) return reject(`The gateway ${keyId.origin} is not listed in the actor's gateways.`);
|
|
121
143
|
const key = new CryptographicKey({
|
|
122
144
|
id: keyId,
|
|
123
145
|
owner: actor.id,
|
|
124
|
-
publicKey
|
|
146
|
+
publicKey
|
|
125
147
|
});
|
|
126
148
|
return {
|
|
127
149
|
type: "verified",
|
|
@@ -171,24 +193,21 @@ async function fetchPortableGatewayKey(keyId, options = {}) {
|
|
|
171
193
|
contextLoader
|
|
172
194
|
});
|
|
173
195
|
}
|
|
174
|
-
function
|
|
175
|
-
if (!Array.isArray(values)) return
|
|
196
|
+
function findNodes(values, id) {
|
|
197
|
+
if (!Array.isArray(values)) return [];
|
|
198
|
+
const nodes = [];
|
|
176
199
|
for (const value of values) {
|
|
177
200
|
if (value == null || typeof value !== "object" || Array.isArray(value)) continue;
|
|
178
201
|
const node = value;
|
|
179
202
|
const nodeId = node["@id"];
|
|
180
|
-
if (typeof nodeId !== "string") continue;
|
|
181
|
-
|
|
182
|
-
try {
|
|
183
|
-
parsed = new URL(nodeId);
|
|
184
|
-
} catch {
|
|
185
|
-
continue;
|
|
186
|
-
}
|
|
187
|
-
if (parsed.href !== id.href) continue;
|
|
188
|
-
if (Object.keys(node).every((key) => key === "@id")) continue;
|
|
189
|
-
return node;
|
|
203
|
+
if (typeof nodeId !== "string" || !URL.canParse(nodeId)) continue;
|
|
204
|
+
if (new URL(nodeId).href === id.href) nodes.push(node);
|
|
190
205
|
}
|
|
191
|
-
return
|
|
206
|
+
return nodes;
|
|
207
|
+
}
|
|
208
|
+
/** A node with nothing but an `@id` is a reference, not an embedded key. */
|
|
209
|
+
function isReference(node) {
|
|
210
|
+
return Object.keys(node).every((key) => key === "@id");
|
|
192
211
|
}
|
|
193
212
|
//#endregion
|
|
194
213
|
export { verifyPortableGatewayKeyDocument as n, fetchPortableGatewayKey as t };
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import "temporal-polyfill";
|
|
2
|
+
import "urlpattern-polyfill";
|
|
3
|
+
globalThis.addEventListener = () => {};
|
|
4
|
+
import { isActor } from "@fedify/vocab";
|
|
5
|
+
import { canonicalizePortableUri, formatIri, fromCompatibleEf61Id, getFe34Origin, toCompatibleEf61Id } from "@fedify/vocab-runtime";
|
|
6
|
+
import { isCompatibleEf61Iri } from "@fedify/vocab-runtime/internal/portable-dereference";
|
|
7
|
+
//#region src/sig/portable-key-id.ts
|
|
8
|
+
const COMPATIBLE_KEY_ID_PATH_PATTERN = /^\/\.well-known\/apgateway\/did(?::|%3A)/i;
|
|
9
|
+
/**
|
|
10
|
+
* Checks whether the URL is an `ap:` or `ap+ef61:` URI.
|
|
11
|
+
* @internal
|
|
12
|
+
*/
|
|
13
|
+
function isPortableUri(url) {
|
|
14
|
+
return url.protocol === "ap:" || url.protocol === "ap+ef61:";
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Checks whether an ID identifies an [FEP-ef61] portable object, i.e., it is
|
|
18
|
+
* an `ap:` or `ap+ef61:` URI, or looks like a compatible identifier.
|
|
19
|
+
* A malformed compatible identifier counts too, so that it is rejected as
|
|
20
|
+
* a portable object that no proof can match, rather than trusted by its web
|
|
21
|
+
* origin.
|
|
22
|
+
*
|
|
23
|
+
* [FEP-ef61]: https://w3id.org/fep/ef61
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
function isPortableId(id) {
|
|
27
|
+
if (typeof id === "string") {
|
|
28
|
+
if (/^ap(?:\+ef61)?:\/\//i.test(id)) return true;
|
|
29
|
+
} else if (isPortableUri(id)) return true;
|
|
30
|
+
return isCompatibleEf61Iri(id);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Gets the DID, i.e., the FEP-fe34 cryptographic origin, of an `ap:` or
|
|
34
|
+
* `ap+ef61:` URI, a DID URL, or a compatible identifier.
|
|
35
|
+
* @returns The DID, or `null` if the ID is none of them, or is malformed.
|
|
36
|
+
* @internal
|
|
37
|
+
*/
|
|
38
|
+
function getPortableDid(id) {
|
|
39
|
+
try {
|
|
40
|
+
const raw = typeof id === "string" ? id : id.href;
|
|
41
|
+
if (/^(?:did:|ap(?:\+ef61)?:\/\/)/i.test(raw)) return getFe34Origin(id);
|
|
42
|
+
const portable = fromCompatibleEf61Id(id);
|
|
43
|
+
return portable == null ? null : getFe34Origin(portable);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
if (error instanceof TypeError) return null;
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Checks whether an activity is performed by an FEP-ef61 portable actor,
|
|
51
|
+
* i.e., any of its actors has an `ap:` or `ap+ef61:` ID, or a compatible
|
|
52
|
+
* identifier.
|
|
53
|
+
* @internal
|
|
54
|
+
*/
|
|
55
|
+
function hasPortableActor(activity) {
|
|
56
|
+
return activity.actorIds.some((id) => isPortableId(id));
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Checks whether a key ID looks like an [FEP-ef61] compatible identifier,
|
|
60
|
+
* i.e., an HTTP(S) URL under a gateway's `/.well-known/apgateway/did:` path.
|
|
61
|
+
* Such a key ID names a key that a gateway holds for a portable actor, so it
|
|
62
|
+
* is resolved only as a gateway key, and never cached in a key cache.
|
|
63
|
+
*
|
|
64
|
+
* [FEP-ef61]: https://w3id.org/fep/ef61
|
|
65
|
+
* @internal
|
|
66
|
+
*/
|
|
67
|
+
function isCompatibleKeyId(keyId) {
|
|
68
|
+
return (keyId.protocol === "http:" || keyId.protocol === "https:") && COMPATIBLE_KEY_ID_PATH_PATTERN.test(keyId.pathname);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Gets the canonical portable ID of an actor ID that is either an `ap:` or
|
|
72
|
+
* `ap+ef61:` URI or a compatible identifier.
|
|
73
|
+
* @returns The canonical portable ID, or `null` if the ID is neither, or is
|
|
74
|
+
* malformed.
|
|
75
|
+
* @internal
|
|
76
|
+
*/
|
|
77
|
+
function getCanonicalPortableId(id) {
|
|
78
|
+
try {
|
|
79
|
+
if (isPortableUri(id)) return canonicalizePortableUri(formatIri(id));
|
|
80
|
+
const portable = fromCompatibleEf61Id(id);
|
|
81
|
+
if (portable == null) return null;
|
|
82
|
+
return canonicalizePortableUri(formatIri(portable));
|
|
83
|
+
} catch (error) {
|
|
84
|
+
if (error instanceof TypeError) return null;
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Builds the base of the key IDs of a gateway's keys for a portable actor:
|
|
90
|
+
* the actor's compatible identifier on the gateway.
|
|
91
|
+
* @param actorId The portable actor's ID, either an `ap:` or `ap+ef61:` URI
|
|
92
|
+
* or a compatible identifier. It must not have a fragment.
|
|
93
|
+
* @param gateway The gateway's origin, e.g., `https://example.com`.
|
|
94
|
+
* @returns The compatible identifier without query and fragment.
|
|
95
|
+
* @throws {TypeError} If the actor ID is not a portable ID, or has
|
|
96
|
+
* a fragment.
|
|
97
|
+
* @internal
|
|
98
|
+
*/
|
|
99
|
+
function getGatewayKeyBase(actorId, gateway) {
|
|
100
|
+
let portable;
|
|
101
|
+
if (isPortableUri(actorId)) portable = actorId;
|
|
102
|
+
else try {
|
|
103
|
+
portable = fromCompatibleEf61Id(actorId);
|
|
104
|
+
} catch (error) {
|
|
105
|
+
if (!(error instanceof TypeError)) throw error;
|
|
106
|
+
portable = null;
|
|
107
|
+
}
|
|
108
|
+
if (portable == null) throw new TypeError(`The portable actor ID ${actorId.href} must be an ap: or ap+ef61: URI, or an FEP-ef61 compatible identifier.`);
|
|
109
|
+
if (portable.hash !== "") throw new TypeError(`The portable actor ID ${actorId.href} must not have a fragment.`);
|
|
110
|
+
const base = toCompatibleEf61Id(portable, gateway);
|
|
111
|
+
base.search = "";
|
|
112
|
+
base.hash = "";
|
|
113
|
+
return base;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Tells whether a parsed document is a portable actor, i.e., an actor whose
|
|
117
|
+
* ID is an `ap:` or `ap+ef61:` URI or a compatible identifier. Such an actor
|
|
118
|
+
* is never authenticated by the web origin that served it; at a compatible
|
|
119
|
+
* key ID, it makes the key a gateway key that only
|
|
120
|
+
* {@link verifyPortableGatewayKeyDocument} can vouch for.
|
|
121
|
+
* @internal
|
|
122
|
+
*/
|
|
123
|
+
function isPortableActorDocument(object) {
|
|
124
|
+
return isActor(object) && object.id != null && isPortableId(object.id);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Compares two public keys by their key material.
|
|
128
|
+
* @internal
|
|
129
|
+
*/
|
|
130
|
+
async function isSamePublicKey(a, b) {
|
|
131
|
+
if (a.algorithm.name !== b.algorithm.name) return false;
|
|
132
|
+
const [x, y] = await Promise.all([crypto.subtle.exportKey("jwk", a), crypto.subtle.exportKey("jwk", b)]);
|
|
133
|
+
return x.kty === y.kty && x.crv === y.crv && x.n === y.n && x.e === y.e && x.x === y.x && x.y === y.y;
|
|
134
|
+
}
|
|
135
|
+
//#endregion
|
|
136
|
+
export { isCompatibleKeyId as a, isPortableUri as c, hasPortableActor as i, isSamePublicKey as l, getGatewayKeyBase as n, isPortableActorDocument as o, getPortableDid as r, isPortableId as s, getCanonicalPortableId as t };
|