@fedify/fedify 2.4.0-dev.1964 → 2.4.0-dev.1988
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-B4zpKIFU.mjs → builder-Cts78G9N.mjs} +2 -2
- package/dist/{client-CtxvmazV.mjs → client-DpnpgjyX.mjs} +78 -4
- 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/{context-COLwHogJ.d.cts → context-BeDnDuF3.d.cts} +4 -3
- package/dist/{context-DiiCt4S7.d.ts → context-uyRUWoZQ.d.ts} +4 -3
- package/dist/{deno-CtG93ohj.mjs → deno-BgMEefEr.mjs} +1 -1
- package/dist/{docloader-dk_k3S8d.mjs → docloader-DEYs99FZ.mjs} +2 -2
- package/dist/federation/builder.test.mjs +1 -1
- package/dist/federation/handler.test.mjs +122 -4
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/keycache.test.mjs +52 -9
- package/dist/federation/metrics.test.mjs +1 -1
- package/dist/federation/middleware.test.mjs +111 -15
- 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/send.test.mjs +139 -4
- 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-Drjlrrax.cjs → federation-DzPUG_AP.cjs} +93 -17
- package/dist/{http-6TI0tI03.d.cts → http-65PYi9jY.d.ts} +34 -2
- package/dist/{http-CdrSn2ip.d.ts → http-CP-7OdSq.d.cts} +34 -2
- package/dist/{http-Dj4s1UQ0.js → http-CgwqtxAy.js} +169 -7
- package/dist/{http-D7LX-BL9.mjs → http-CtidZS_t.mjs} +3 -3
- package/dist/{http-B3skRvGo.cjs → http-Z3anvCd-.cjs} +180 -6
- package/dist/{key-BVjgbs_9.mjs → key-Cwbg5ggV.mjs} +169 -7
- package/dist/{keycache-ZLqIfrkL.mjs → keycache-BUDHB5rY.mjs} +12 -4
- package/dist/{kv-cache-DtsjH_bY.cjs → kv-cache-BM2-w-Wq.cjs} +2 -2
- package/dist/{kv-cache-tGNbvDCF.js → kv-cache-CykuL0ya.js} +2 -2
- package/dist/{kv-cache-xRcC_xxP.mjs → kv-cache-DHiR8yTf.mjs} +1 -1
- package/dist/{ld-OuCeQFny.mjs → ld-CzhdWyTt.mjs} +3 -3
- package/dist/{metrics-B9N27cCB.mjs → metrics-CrqsHP0l.mjs} +1 -1
- package/dist/{middleware-Cd_vPqDH.mjs → middleware-CZpYxSKD.mjs} +1 -1
- package/dist/{middleware-CPTDFMmJ.js → middleware-CeCj5caM.js} +101 -25
- package/dist/{middleware-TeLjzoxn.mjs → middleware-DCpiAsZi.mjs} +34 -23
- package/dist/{mod-DypxPS9H.d.ts → mod-C0BYc0OU.d.ts} +1 -1
- package/dist/{mod-CnGLNoVw.d.cts → mod-CbKDOnCt.d.cts} +1 -1
- package/dist/{mod-CLHkCwLm.d.cts → mod-CyiSjRkk.d.cts} +1 -1
- package/dist/{mod-CSH1Ee-I.d.ts → mod-D882_Xsg.d.ts} +1 -1
- package/dist/{mod-DzRXACM5.d.ts → mod-UDwMjuHM.d.ts} +1 -1
- package/dist/{mod-BZldCEU0.d.cts → mod-x9W0d0Pk.d.cts} +1 -1
- package/dist/mod.cjs +5 -5
- package/dist/mod.d.cts +6 -6
- package/dist/mod.d.ts +6 -6
- package/dist/mod.js +5 -5
- package/dist/nodeinfo/client.test.mjs +28 -1
- package/dist/nodeinfo/handler.test.mjs +1 -1
- package/dist/nodeinfo/mod.cjs +1 -1
- package/dist/nodeinfo/mod.js +1 -1
- package/dist/{owner-DGSfntI-.d.ts → owner-BF0L9cMt.d.cts} +12 -1
- package/dist/{owner-r_waTiQj.d.cts → owner-BSzr5-I8.d.ts} +12 -1
- package/dist/owner-BqVXJLW8.mjs +175 -0
- package/dist/{proof-BO_esQii.js → proof-CUghqhAf.js} +122 -65
- package/dist/{proof-Dua0d906.cjs → proof-DHu5zI0Z.cjs} +122 -65
- package/dist/{proof-D5Ajk4DJ.mjs → proof-DL8Iid3a.mjs} +14 -11
- package/dist/{send-DVXyUqhO.mjs → send-6jS4xFsX.mjs} +68 -11
- package/dist/sig/http.test.mjs +2 -2
- package/dist/sig/key.test.mjs +169 -2
- package/dist/sig/ld.test.mjs +55 -2
- package/dist/sig/mod.cjs +2 -2
- package/dist/sig/mod.d.cts +3 -3
- package/dist/sig/mod.d.ts +3 -3
- package/dist/sig/mod.js +2 -2
- package/dist/sig/owner.test.mjs +103 -4
- package/dist/sig/proof.test.mjs +120 -1
- package/dist/{temporal-BoDw51A-.mjs → temporal-DcyzeytP.mjs} +1 -1
- package/dist/testing/kv.test.mjs +1 -1
- package/dist/testing/mod.d.mts +2 -1
- package/dist/{types-RXm5YxpC.cjs → types-Be2zbyeG.cjs} +94 -2
- package/dist/{types-Dtyf8lVj.js → types-C_cNlNVK.js} +78 -4
- package/dist/utils/docloader.test.mjs +27 -3
- package/dist/utils/kv-cache.test.mjs +1 -1
- package/dist/utils/mod.cjs +1 -1
- package/dist/utils/mod.d.cts +1 -1
- package/dist/utils/mod.d.ts +1 -1
- package/dist/utils/mod.js +1 -1
- package/package.json +6 -6
- package/dist/owner-DTEqTzSr.mjs +0 -120
package/dist/sig/key.test.mjs
CHANGED
|
@@ -5,7 +5,8 @@ import { t as assertEquals } from "../assert_equals-CBlRfA-_.mjs";
|
|
|
5
5
|
import { t as assertThrows } from "../assert_throws-BJ5wuvR2.mjs";
|
|
6
6
|
import "../std__assert-CxFT0MNt.mjs";
|
|
7
7
|
import { t as assertRejects } from "../assert_rejects-wFbSPn1O.mjs";
|
|
8
|
-
import {
|
|
8
|
+
import { t as assert } from "../assert-CebeUf0C.mjs";
|
|
9
|
+
import { a as generateCryptoKeyPair, i as fetchKeyDetailed, o as importJwk, r as fetchKey, s as validateCryptoKey, t as exportJwk } from "../key-Cwbg5ggV.mjs";
|
|
9
10
|
import { c as rsaPublicKey3, i as rsaPrivateKey2, o as rsaPublicKey1, r as ed25519PublicKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-DIJNHW-Y.mjs";
|
|
10
11
|
import { CryptographicKey, Multikey } from "@fedify/vocab";
|
|
11
12
|
import { createTestMeterProvider, createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
|
|
@@ -267,7 +268,7 @@ test("fetchKeyDetailed()", async () => {
|
|
|
267
268
|
key: rsaPublicKey1,
|
|
268
269
|
cached: false
|
|
269
270
|
});
|
|
270
|
-
assertEquals(documentLoaderCalls,
|
|
271
|
+
assertEquals(documentLoaderCalls, 2);
|
|
271
272
|
const spans = exporter.getSpans("activitypub.fetch_key");
|
|
272
273
|
assertEquals(spans.length, 2);
|
|
273
274
|
assertEquals(spans[0].attributes["activitypub.actor.key.cached"], true);
|
|
@@ -679,5 +680,171 @@ test("fetchKey() returns null for a malformed actor publicKey", async () => {
|
|
|
679
680
|
cached: true
|
|
680
681
|
});
|
|
681
682
|
});
|
|
683
|
+
test("fetchKey() rejects a key whose owner does not link back", async () => {
|
|
684
|
+
const keyId = "https://attacker.example/key";
|
|
685
|
+
const keyDocument = await rsaPublicKey1.toJsonLd({ contextLoader: mockDocumentLoader });
|
|
686
|
+
const serveKeyOwnedBy = (owner) => ({
|
|
687
|
+
documentLoader(resource) {
|
|
688
|
+
if (resource === keyId) return Promise.resolve({
|
|
689
|
+
contextUrl: null,
|
|
690
|
+
documentUrl: resource,
|
|
691
|
+
document: {
|
|
692
|
+
...keyDocument,
|
|
693
|
+
id: keyId,
|
|
694
|
+
owner
|
|
695
|
+
}
|
|
696
|
+
});
|
|
697
|
+
return mockDocumentLoader(resource);
|
|
698
|
+
},
|
|
699
|
+
contextLoader: mockDocumentLoader
|
|
700
|
+
});
|
|
701
|
+
assertEquals(await fetchKey(keyId, CryptographicKey, serveKeyOwnedBy("https://example.com/person")), {
|
|
702
|
+
key: null,
|
|
703
|
+
cached: false
|
|
704
|
+
});
|
|
705
|
+
assertEquals(await fetchKey(keyId, CryptographicKey, serveKeyOwnedBy("https://impersonated.invalid/users/victim")), {
|
|
706
|
+
key: null,
|
|
707
|
+
cached: false
|
|
708
|
+
});
|
|
709
|
+
});
|
|
710
|
+
test("fetchKey() rejects a Multikey whose controller does not link back", async () => {
|
|
711
|
+
const keyId = "https://attacker.example/multikey";
|
|
712
|
+
const keyDocument = await ed25519Multikey.toJsonLd({ contextLoader: mockDocumentLoader });
|
|
713
|
+
assertEquals(await fetchKey(keyId, Multikey, {
|
|
714
|
+
documentLoader(resource) {
|
|
715
|
+
if (resource === keyId) return Promise.resolve({
|
|
716
|
+
contextUrl: null,
|
|
717
|
+
documentUrl: resource,
|
|
718
|
+
document: {
|
|
719
|
+
...keyDocument,
|
|
720
|
+
id: keyId,
|
|
721
|
+
controller: "https://example.com/person2"
|
|
722
|
+
}
|
|
723
|
+
});
|
|
724
|
+
return mockDocumentLoader(resource);
|
|
725
|
+
},
|
|
726
|
+
contextLoader: mockDocumentLoader
|
|
727
|
+
}), {
|
|
728
|
+
key: null,
|
|
729
|
+
cached: false
|
|
730
|
+
});
|
|
731
|
+
});
|
|
732
|
+
test("fetchKey() rejects an actor document from another origin", async () => {
|
|
733
|
+
const keyId = "https://attacker.example/key";
|
|
734
|
+
const impersonated = "https://example.com/person";
|
|
735
|
+
const { publicKeyPem } = await rsaPublicKey1.toJsonLd({ contextLoader: mockDocumentLoader });
|
|
736
|
+
assertEquals(await fetchKey(keyId, CryptographicKey, {
|
|
737
|
+
documentLoader(resource) {
|
|
738
|
+
if (resource === keyId) return Promise.resolve({
|
|
739
|
+
contextUrl: null,
|
|
740
|
+
documentUrl: resource,
|
|
741
|
+
document: {
|
|
742
|
+
"@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1"],
|
|
743
|
+
id: impersonated,
|
|
744
|
+
type: "Person",
|
|
745
|
+
publicKey: [{
|
|
746
|
+
id: `${impersonated}#main-key`,
|
|
747
|
+
type: "CryptographicKey",
|
|
748
|
+
owner: impersonated,
|
|
749
|
+
publicKeyPem
|
|
750
|
+
}]
|
|
751
|
+
}
|
|
752
|
+
});
|
|
753
|
+
return mockDocumentLoader(resource);
|
|
754
|
+
},
|
|
755
|
+
contextLoader: mockDocumentLoader
|
|
756
|
+
}), {
|
|
757
|
+
key: null,
|
|
758
|
+
cached: false
|
|
759
|
+
});
|
|
760
|
+
});
|
|
761
|
+
test("fetchKey() rejects a key that disowns the actor document holding it", async () => {
|
|
762
|
+
const actorId = "https://attacker.example/actor";
|
|
763
|
+
const keyId = `${actorId}#main-key`;
|
|
764
|
+
const { publicKeyPem } = await rsaPublicKey1.toJsonLd({ contextLoader: mockDocumentLoader });
|
|
765
|
+
assertEquals(await fetchKey(keyId, CryptographicKey, {
|
|
766
|
+
documentLoader(resource) {
|
|
767
|
+
if (resource === keyId || resource === actorId) return Promise.resolve({
|
|
768
|
+
contextUrl: null,
|
|
769
|
+
documentUrl: resource,
|
|
770
|
+
document: {
|
|
771
|
+
"@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1"],
|
|
772
|
+
id: actorId,
|
|
773
|
+
type: "Person",
|
|
774
|
+
publicKey: [{
|
|
775
|
+
id: keyId,
|
|
776
|
+
type: "CryptographicKey",
|
|
777
|
+
owner: "https://example.com/person",
|
|
778
|
+
publicKeyPem
|
|
779
|
+
}]
|
|
780
|
+
}
|
|
781
|
+
});
|
|
782
|
+
return mockDocumentLoader(resource);
|
|
783
|
+
},
|
|
784
|
+
contextLoader: mockDocumentLoader
|
|
785
|
+
}), {
|
|
786
|
+
key: null,
|
|
787
|
+
cached: false
|
|
788
|
+
});
|
|
789
|
+
});
|
|
790
|
+
test("fetchKey() records the owner its actor document establishes", async () => {
|
|
791
|
+
const { key } = await fetchKey("https://example.com/users/handle#main-key", CryptographicKey, {
|
|
792
|
+
documentLoader: mockDocumentLoader,
|
|
793
|
+
contextLoader: mockDocumentLoader
|
|
794
|
+
});
|
|
795
|
+
assertEquals(key?.ownerId, new URL("https://example.com/users/handle"));
|
|
796
|
+
});
|
|
797
|
+
test("fetchKey() accepts a key document that leaves its id implicit", async () => {
|
|
798
|
+
const keyId = "https://example.com/key";
|
|
799
|
+
const keyDocument = await rsaPublicKey1.toJsonLd({ contextLoader: mockDocumentLoader });
|
|
800
|
+
delete keyDocument.id;
|
|
801
|
+
const { key } = await fetchKey(keyId, CryptographicKey, {
|
|
802
|
+
documentLoader(resource) {
|
|
803
|
+
if (resource === keyId) return Promise.resolve({
|
|
804
|
+
contextUrl: null,
|
|
805
|
+
documentUrl: resource,
|
|
806
|
+
document: keyDocument
|
|
807
|
+
});
|
|
808
|
+
return mockDocumentLoader(resource);
|
|
809
|
+
},
|
|
810
|
+
contextLoader: mockDocumentLoader
|
|
811
|
+
});
|
|
812
|
+
assertEquals(key?.id, new URL(keyId));
|
|
813
|
+
assertEquals(key?.ownerId, new URL("https://example.com/person"));
|
|
814
|
+
assert(key?.publicKey != null);
|
|
815
|
+
});
|
|
816
|
+
test("fetchKey() records the controller an actor document establishes", async () => {
|
|
817
|
+
const actorId = "https://example.com/multikey-actor";
|
|
818
|
+
const keyId = `${actorId}#assertion`;
|
|
819
|
+
const { publicKeyMultibase } = await ed25519Multikey.toJsonLd({ contextLoader: mockDocumentLoader });
|
|
820
|
+
const { key } = await fetchKey(keyId, Multikey, {
|
|
821
|
+
documentLoader(resource) {
|
|
822
|
+
if (resource === actorId || resource === keyId) return Promise.resolve({
|
|
823
|
+
contextUrl: null,
|
|
824
|
+
documentUrl: actorId,
|
|
825
|
+
document: {
|
|
826
|
+
"@context": [
|
|
827
|
+
"https://www.w3.org/ns/activitystreams",
|
|
828
|
+
"https://w3id.org/security/v1",
|
|
829
|
+
"https://w3id.org/security/multikey/v1",
|
|
830
|
+
"https://w3id.org/security/data-integrity/v1",
|
|
831
|
+
"https://www.w3.org/ns/did/v1"
|
|
832
|
+
],
|
|
833
|
+
id: actorId,
|
|
834
|
+
type: "Person",
|
|
835
|
+
assertionMethod: [{
|
|
836
|
+
id: keyId,
|
|
837
|
+
type: "Multikey",
|
|
838
|
+
publicKeyMultibase
|
|
839
|
+
}]
|
|
840
|
+
}
|
|
841
|
+
});
|
|
842
|
+
return mockDocumentLoader(resource);
|
|
843
|
+
},
|
|
844
|
+
contextLoader: mockDocumentLoader
|
|
845
|
+
});
|
|
846
|
+
assertEquals(key?.controllerId, new URL(actorId));
|
|
847
|
+
assert(key?.publicKey != null);
|
|
848
|
+
});
|
|
682
849
|
//#endregion
|
|
683
850
|
export {};
|
package/dist/sig/ld.test.mjs
CHANGED
|
@@ -4,12 +4,13 @@ globalThis.addEventListener = () => {};
|
|
|
4
4
|
import { t as assertEquals } from "../assert_equals-CBlRfA-_.mjs";
|
|
5
5
|
import { t as assertFalse } from "../assert_false-3pLLLsO4.mjs";
|
|
6
6
|
import { t as assertGreaterOrEqual } from "../assert_greater_or_equal-BApOTh1h.mjs";
|
|
7
|
+
import { t as assertInstanceOf } from "../assert_instance_of-g3WcyYNR.mjs";
|
|
7
8
|
import { t as assertThrows } from "../assert_throws-BJ5wuvR2.mjs";
|
|
8
9
|
import { t as assertRejects } from "../assert_rejects-wFbSPn1O.mjs";
|
|
9
10
|
import { t as assert } from "../assert-CebeUf0C.mjs";
|
|
10
|
-
import {
|
|
11
|
+
import { a as generateCryptoKeyPair } from "../key-Cwbg5ggV.mjs";
|
|
11
12
|
import { a as rsaPrivateKey3, c as rsaPublicKey3, i as rsaPrivateKey2, n as ed25519PrivateKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-DIJNHW-Y.mjs";
|
|
12
|
-
import { a as compactJsonLd, f as isInvalidUrlTypeError, g as verifySignature, h as verifyJsonLd, i as attachSignature, n as UnsafeJsonLdError, o as createSignature, p as signJsonLd, s as detachSignature, u as hasSignatureLike } from "../ld-
|
|
13
|
+
import { a as compactJsonLd, f as isInvalidUrlTypeError, g as verifySignature, h as verifyJsonLd, i as attachSignature, n as UnsafeJsonLdError, o as createSignature, p as signJsonLd, s as detachSignature, u as hasSignatureLike } from "../ld-CzhdWyTt.mjs";
|
|
13
14
|
import { CryptographicKey } from "@fedify/vocab";
|
|
14
15
|
import { createTestMeterProvider, mockDocumentLoader, test } from "@fedify/fixture";
|
|
15
16
|
import { encodeBase64 } from "byte-encodings/base64";
|
|
@@ -940,5 +941,57 @@ test("compactJsonLd() rejects inputs that compact into @graph wrappers", async (
|
|
|
940
941
|
content: "two"
|
|
941
942
|
}], mockDocumentLoader), UnsafeJsonLdError, "Unsupported JSON-LD keyword: @graph.");
|
|
942
943
|
});
|
|
944
|
+
test("verifyJsonLd() rejects a key that claims a forged owner", async () => {
|
|
945
|
+
const keyId = new URL("https://attacker.example/key");
|
|
946
|
+
const impersonated = "https://example.com/person";
|
|
947
|
+
const { publicKeyPem } = await rsaPublicKey2.toJsonLd({ contextLoader: mockDocumentLoader });
|
|
948
|
+
const signed = await signJsonLd({
|
|
949
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
950
|
+
id: "https://attacker.example/activities/1",
|
|
951
|
+
type: "Create",
|
|
952
|
+
actor: impersonated
|
|
953
|
+
}, rsaPrivateKey2, keyId, { contextLoader: mockDocumentLoader });
|
|
954
|
+
const serveKeyOwnedBy = (owner) => (resource) => {
|
|
955
|
+
if (resource === keyId.href) return Promise.resolve({
|
|
956
|
+
contextUrl: null,
|
|
957
|
+
documentUrl: resource,
|
|
958
|
+
document: {
|
|
959
|
+
"@context": "https://w3id.org/security/v1",
|
|
960
|
+
id: keyId.href,
|
|
961
|
+
type: "CryptographicKey",
|
|
962
|
+
owner,
|
|
963
|
+
publicKeyPem
|
|
964
|
+
}
|
|
965
|
+
});
|
|
966
|
+
if (resource === "https://attacker.example/actor") return Promise.resolve({
|
|
967
|
+
contextUrl: null,
|
|
968
|
+
documentUrl: resource,
|
|
969
|
+
document: {
|
|
970
|
+
"@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1"],
|
|
971
|
+
id: resource,
|
|
972
|
+
type: "Person",
|
|
973
|
+
publicKey: [keyId.href]
|
|
974
|
+
}
|
|
975
|
+
});
|
|
976
|
+
return mockDocumentLoader(resource);
|
|
977
|
+
};
|
|
978
|
+
assertEquals(await verifySignature(signed, {
|
|
979
|
+
documentLoader: serveKeyOwnedBy(impersonated),
|
|
980
|
+
contextLoader: mockDocumentLoader
|
|
981
|
+
}), null);
|
|
982
|
+
assertFalse(await verifyJsonLd(signed, {
|
|
983
|
+
documentLoader: serveKeyOwnedBy(impersonated),
|
|
984
|
+
contextLoader: mockDocumentLoader
|
|
985
|
+
}));
|
|
986
|
+
const honestLoader = serveKeyOwnedBy("https://attacker.example/actor");
|
|
987
|
+
assertInstanceOf(await verifySignature(signed, {
|
|
988
|
+
documentLoader: honestLoader,
|
|
989
|
+
contextLoader: mockDocumentLoader
|
|
990
|
+
}), CryptographicKey);
|
|
991
|
+
assertFalse(await verifyJsonLd(signed, {
|
|
992
|
+
documentLoader: honestLoader,
|
|
993
|
+
contextLoader: mockDocumentLoader
|
|
994
|
+
}));
|
|
995
|
+
});
|
|
943
996
|
//#endregion
|
|
944
997
|
export {};
|
package/dist/sig/mod.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const { URLPattern } = require("urlpattern-polyfill");
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const require_http = require("../http-
|
|
4
|
-
const require_proof = require("../proof-
|
|
3
|
+
const require_http = require("../http-Z3anvCd-.cjs");
|
|
4
|
+
const require_proof = require("../proof-DHu5zI0Z.cjs");
|
|
5
5
|
exports.attachSignature = require_proof.attachSignature;
|
|
6
6
|
exports.createProof = require_proof.createProof;
|
|
7
7
|
exports.createSignature = require_proof.createSignature;
|
package/dist/sig/mod.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference lib="esnext.temporal" />
|
|
2
|
-
import { C as exportJwk, D as importJwk, E as generateCryptoKeyPair, S as KeyCache, T as fetchKeyDetailed, _ as validateAcceptSignature, a as VerifyRequestDetailedResult, b as FetchKeyOptions, c as signRequest, d as AcceptSignatureMember, f as AcceptSignatureParameters, g as parseAcceptSignature, h as fulfillAcceptSignature, i as SignRequestOptions, l as verifyRequest, m as formatAcceptSignature, n as HttpMessageSignaturesSpecDeterminer, o as VerifyRequestFailureReason, p as FulfillAcceptSignatureResult, r as Rfc9421SignRequestOptions, s as VerifyRequestOptions, t as HttpMessageSignaturesSpec, u as verifyRequestDetailed, v as FetchKeyDetailedResult, w as fetchKey, x as FetchKeyResult, y as FetchKeyErrorResult } from "../http-
|
|
3
|
-
import { i as getKeyOwner, n as GetKeyOwnerOptions, r as doesActorOwnKey, t as DoesActorOwnKeyOptions } from "../owner-
|
|
4
|
-
import { C as verifyJsonLd, S as signJsonLd, _ as VerifySignatureOptions, a as VerifyPortableObjectProofOptions, b as detachSignature, c as createProof, d as verifyObject, f as verifyPortableObjectProof, g as VerifyJsonLdOptions, h as SignJsonLdOptions, i as VerifyPortableObjectProofFailureReason, l as hasProofLike, m as CreateSignatureOptions, n as SignObjectOptions, o as VerifyPortableObjectProofResult, p as verifyProof, r as VerifyObjectOptions, s as VerifyProofOptions, t as CreateProofOptions, u as signObject, v as attachSignature, w as verifySignature, x as hasSignatureLike, y as createSignature } from "../mod-
|
|
2
|
+
import { C as exportJwk, D as importJwk, E as generateCryptoKeyPair, S as KeyCache, T as fetchKeyDetailed, _ as validateAcceptSignature, a as VerifyRequestDetailedResult, b as FetchKeyOptions, c as signRequest, d as AcceptSignatureMember, f as AcceptSignatureParameters, g as parseAcceptSignature, h as fulfillAcceptSignature, i as SignRequestOptions, l as verifyRequest, m as formatAcceptSignature, n as HttpMessageSignaturesSpecDeterminer, o as VerifyRequestFailureReason, p as FulfillAcceptSignatureResult, r as Rfc9421SignRequestOptions, s as VerifyRequestOptions, t as HttpMessageSignaturesSpec, u as verifyRequestDetailed, v as FetchKeyDetailedResult, w as fetchKey, x as FetchKeyResult, y as FetchKeyErrorResult } from "../http-CP-7OdSq.cjs";
|
|
3
|
+
import { i as getKeyOwner, n as GetKeyOwnerOptions, r as doesActorOwnKey, t as DoesActorOwnKeyOptions } from "../owner-BF0L9cMt.cjs";
|
|
4
|
+
import { C as verifyJsonLd, S as signJsonLd, _ as VerifySignatureOptions, a as VerifyPortableObjectProofOptions, b as detachSignature, c as createProof, d as verifyObject, f as verifyPortableObjectProof, g as VerifyJsonLdOptions, h as SignJsonLdOptions, i as VerifyPortableObjectProofFailureReason, l as hasProofLike, m as CreateSignatureOptions, n as SignObjectOptions, o as VerifyPortableObjectProofResult, p as verifyProof, r as VerifyObjectOptions, s as VerifyProofOptions, t as CreateProofOptions, u as signObject, v as attachSignature, w as verifySignature, x as hasSignatureLike, y as createSignature } from "../mod-CbKDOnCt.cjs";
|
|
5
5
|
export { AcceptSignatureMember, AcceptSignatureParameters, CreateProofOptions, CreateSignatureOptions, DoesActorOwnKeyOptions, FetchKeyDetailedResult, FetchKeyErrorResult, FetchKeyOptions, FetchKeyResult, FulfillAcceptSignatureResult, GetKeyOwnerOptions, HttpMessageSignaturesSpec, HttpMessageSignaturesSpecDeterminer, KeyCache, Rfc9421SignRequestOptions, SignJsonLdOptions, SignObjectOptions, SignRequestOptions, VerifyJsonLdOptions, VerifyObjectOptions, VerifyPortableObjectProofFailureReason, VerifyPortableObjectProofOptions, VerifyPortableObjectProofResult, VerifyProofOptions, VerifyRequestDetailedResult, VerifyRequestFailureReason, VerifyRequestOptions, VerifySignatureOptions, attachSignature, createProof, createSignature, detachSignature, doesActorOwnKey, exportJwk, fetchKey, fetchKeyDetailed, formatAcceptSignature, fulfillAcceptSignature, generateCryptoKeyPair, getKeyOwner, hasProofLike, hasSignatureLike, importJwk, parseAcceptSignature, signJsonLd, signObject, signRequest, validateAcceptSignature, verifyJsonLd, verifyObject, verifyPortableObjectProof, verifyProof, verifyRequest, verifyRequestDetailed, verifySignature };
|
package/dist/sig/mod.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference lib="esnext.temporal" />
|
|
2
|
-
import { C as exportJwk, D as importJwk, E as generateCryptoKeyPair, S as KeyCache, T as fetchKeyDetailed, _ as validateAcceptSignature, a as VerifyRequestDetailedResult, b as FetchKeyOptions, c as signRequest, d as AcceptSignatureMember, f as AcceptSignatureParameters, g as parseAcceptSignature, h as fulfillAcceptSignature, i as SignRequestOptions, l as verifyRequest, m as formatAcceptSignature, n as HttpMessageSignaturesSpecDeterminer, o as VerifyRequestFailureReason, p as FulfillAcceptSignatureResult, r as Rfc9421SignRequestOptions, s as VerifyRequestOptions, t as HttpMessageSignaturesSpec, u as verifyRequestDetailed, v as FetchKeyDetailedResult, w as fetchKey, x as FetchKeyResult, y as FetchKeyErrorResult } from "../http-
|
|
3
|
-
import { i as getKeyOwner, n as GetKeyOwnerOptions, r as doesActorOwnKey, t as DoesActorOwnKeyOptions } from "../owner-
|
|
4
|
-
import { C as verifyJsonLd, S as signJsonLd, _ as VerifySignatureOptions, a as VerifyPortableObjectProofOptions, b as detachSignature, c as createProof, d as verifyObject, f as verifyPortableObjectProof, g as VerifyJsonLdOptions, h as SignJsonLdOptions, i as VerifyPortableObjectProofFailureReason, l as hasProofLike, m as CreateSignatureOptions, n as SignObjectOptions, o as VerifyPortableObjectProofResult, p as verifyProof, r as VerifyObjectOptions, s as VerifyProofOptions, t as CreateProofOptions, u as signObject, v as attachSignature, w as verifySignature, x as hasSignatureLike, y as createSignature } from "../mod-
|
|
2
|
+
import { C as exportJwk, D as importJwk, E as generateCryptoKeyPair, S as KeyCache, T as fetchKeyDetailed, _ as validateAcceptSignature, a as VerifyRequestDetailedResult, b as FetchKeyOptions, c as signRequest, d as AcceptSignatureMember, f as AcceptSignatureParameters, g as parseAcceptSignature, h as fulfillAcceptSignature, i as SignRequestOptions, l as verifyRequest, m as formatAcceptSignature, n as HttpMessageSignaturesSpecDeterminer, o as VerifyRequestFailureReason, p as FulfillAcceptSignatureResult, r as Rfc9421SignRequestOptions, s as VerifyRequestOptions, t as HttpMessageSignaturesSpec, u as verifyRequestDetailed, v as FetchKeyDetailedResult, w as fetchKey, x as FetchKeyResult, y as FetchKeyErrorResult } from "../http-65PYi9jY.js";
|
|
3
|
+
import { i as getKeyOwner, n as GetKeyOwnerOptions, r as doesActorOwnKey, t as DoesActorOwnKeyOptions } from "../owner-BSzr5-I8.js";
|
|
4
|
+
import { C as verifyJsonLd, S as signJsonLd, _ as VerifySignatureOptions, a as VerifyPortableObjectProofOptions, b as detachSignature, c as createProof, d as verifyObject, f as verifyPortableObjectProof, g as VerifyJsonLdOptions, h as SignJsonLdOptions, i as VerifyPortableObjectProofFailureReason, l as hasProofLike, m as CreateSignatureOptions, n as SignObjectOptions, o as VerifyPortableObjectProofResult, p as verifyProof, r as VerifyObjectOptions, s as VerifyProofOptions, t as CreateProofOptions, u as signObject, v as attachSignature, w as verifySignature, x as hasSignatureLike, y as createSignature } from "../mod-UDwMjuHM.js";
|
|
5
5
|
export { AcceptSignatureMember, AcceptSignatureParameters, CreateProofOptions, CreateSignatureOptions, DoesActorOwnKeyOptions, FetchKeyDetailedResult, FetchKeyErrorResult, FetchKeyOptions, FetchKeyResult, FulfillAcceptSignatureResult, GetKeyOwnerOptions, HttpMessageSignaturesSpec, HttpMessageSignaturesSpecDeterminer, KeyCache, Rfc9421SignRequestOptions, SignJsonLdOptions, SignObjectOptions, SignRequestOptions, VerifyJsonLdOptions, VerifyObjectOptions, VerifyPortableObjectProofFailureReason, VerifyPortableObjectProofOptions, VerifyPortableObjectProofResult, VerifyProofOptions, VerifyRequestDetailedResult, VerifyRequestFailureReason, VerifyRequestOptions, VerifySignatureOptions, attachSignature, createProof, createSignature, detachSignature, doesActorOwnKey, exportJwk, fetchKey, fetchKeyDetailed, formatAcceptSignature, fulfillAcceptSignature, generateCryptoKeyPair, getKeyOwner, hasProofLike, hasSignatureLike, importJwk, parseAcceptSignature, signJsonLd, signObject, signRequest, validateAcceptSignature, verifyJsonLd, verifyObject, verifyPortableObjectProof, verifyProof, verifyRequest, verifyRequestDetailed, verifySignature };
|
package/dist/sig/mod.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
import { URLPattern } from "urlpattern-polyfill";
|
|
3
|
-
import {
|
|
4
|
-
import { C as verifyJsonLd, a as verifyPortableObjectProof, c as doesActorOwnKey, f as attachSignature, h as detachSignature, i as verifyObject, l as getKeyOwner, m as createSignature, n as hasProofLike, o as verifyProof, r as signObject, t as createProof, v as hasSignatureLike, w as verifySignature, x as signJsonLd } from "../proof-
|
|
3
|
+
import { F as validateAcceptSignature, M as formatAcceptSignature, N as fulfillAcceptSignature, P as parseAcceptSignature, a as verifyRequestDetailed, c as fetchKey, d as importJwk, i as verifyRequest, l as fetchKeyDetailed, o as exportJwk, r as signRequest, u as generateCryptoKeyPair } from "../http-CgwqtxAy.js";
|
|
4
|
+
import { C as verifyJsonLd, a as verifyPortableObjectProof, c as doesActorOwnKey, f as attachSignature, h as detachSignature, i as verifyObject, l as getKeyOwner, m as createSignature, n as hasProofLike, o as verifyProof, r as signObject, t as createProof, v as hasSignatureLike, w as verifySignature, x as signJsonLd } from "../proof-CUghqhAf.js";
|
|
5
5
|
export { attachSignature, createProof, createSignature, detachSignature, doesActorOwnKey, exportJwk, fetchKey, fetchKeyDetailed, formatAcceptSignature, fulfillAcceptSignature, generateCryptoKeyPair, getKeyOwner, hasProofLike, hasSignatureLike, importJwk, parseAcceptSignature, signJsonLd, signObject, signRequest, validateAcceptSignature, verifyJsonLd, verifyObject, verifyPortableObjectProof, verifyProof, verifyRequest, verifyRequestDetailed, verifySignature };
|
package/dist/sig/owner.test.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { t as assertFalse } from "../assert_false-3pLLLsO4.mjs";
|
|
|
6
6
|
import "../std__assert-CxFT0MNt.mjs";
|
|
7
7
|
import { t as assert } from "../assert-CebeUf0C.mjs";
|
|
8
8
|
import { o as rsaPublicKey1, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
|
|
9
|
-
import { n as getKeyOwner, t as doesActorOwnKey } from "../owner-
|
|
9
|
+
import { n as getKeyOwner, t as doesActorOwnKey } from "../owner-BqVXJLW8.mjs";
|
|
10
10
|
import { Create, CryptographicKey, lookupObject } from "@fedify/vocab";
|
|
11
11
|
import { createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
|
|
12
12
|
//#region src/sig/owner.test.ts
|
|
@@ -40,7 +40,7 @@ test("doesActorOwnKey() records OpenTelemetry span", async () => {
|
|
|
40
40
|
id: new URL("https://example.com/activity"),
|
|
41
41
|
actor: new URL("https://example.com/person")
|
|
42
42
|
}), new CryptographicKey({
|
|
43
|
-
id: new URL("https://example.com/
|
|
43
|
+
id: new URL("https://example.com/key"),
|
|
44
44
|
owner: new URL("https://example.com/person")
|
|
45
45
|
}), {
|
|
46
46
|
documentLoader: mockDocumentLoader,
|
|
@@ -50,9 +50,108 @@ test("doesActorOwnKey() records OpenTelemetry span", async () => {
|
|
|
50
50
|
assertEquals(spans.length, 1);
|
|
51
51
|
const span = spans[0];
|
|
52
52
|
assertEquals(span.attributes["activitypub.actor.id"], "https://example.com/person");
|
|
53
|
-
assertEquals(span.attributes["activitypub.key.id"], "https://example.com/
|
|
53
|
+
assertEquals(span.attributes["activitypub.key.id"], "https://example.com/key");
|
|
54
54
|
assertEquals(span.attributes["activitypub.key_ownership.verified"], true);
|
|
55
|
-
assertEquals(span.attributes["activitypub.key_ownership.method"], "
|
|
55
|
+
assertEquals(span.attributes["activitypub.key_ownership.method"], "key_owner");
|
|
56
|
+
});
|
|
57
|
+
test("doesActorOwnKey() rejects a key that only claims to be owned", async () => {
|
|
58
|
+
const options = {
|
|
59
|
+
documentLoader: mockDocumentLoader,
|
|
60
|
+
contextLoader: mockDocumentLoader
|
|
61
|
+
};
|
|
62
|
+
assertFalse(await doesActorOwnKey(new Create({ actor: new URL("https://example.com/person") }), new CryptographicKey({
|
|
63
|
+
id: new URL("https://attacker.example/key"),
|
|
64
|
+
owner: new URL("https://example.com/person")
|
|
65
|
+
}), options));
|
|
66
|
+
const unresolvable = new URL("https://impersonated.invalid/users/victim");
|
|
67
|
+
assertFalse(await doesActorOwnKey(new Create({ actor: unresolvable }), new CryptographicKey({
|
|
68
|
+
id: new URL("https://attacker.example/key"),
|
|
69
|
+
owner: unresolvable
|
|
70
|
+
}), options));
|
|
71
|
+
});
|
|
72
|
+
test("doesActorOwnKey() ignores an actor embedded in the activity", async () => {
|
|
73
|
+
const { publicKeyPem } = await rsaPublicKey1.toJsonLd({ contextLoader: mockDocumentLoader });
|
|
74
|
+
const activity = await Create.fromJsonLd({
|
|
75
|
+
"@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1"],
|
|
76
|
+
id: "https://example.com/activity",
|
|
77
|
+
type: "Create",
|
|
78
|
+
actor: {
|
|
79
|
+
id: "https://example.com/person",
|
|
80
|
+
type: "Person",
|
|
81
|
+
publicKey: {
|
|
82
|
+
id: "https://attacker.example/key",
|
|
83
|
+
type: "CryptographicKey",
|
|
84
|
+
owner: "https://example.com/person",
|
|
85
|
+
publicKeyPem
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}, {
|
|
89
|
+
documentLoader: mockDocumentLoader,
|
|
90
|
+
contextLoader: mockDocumentLoader
|
|
91
|
+
});
|
|
92
|
+
assertEquals(activity.actorId, new URL("https://example.com/person"));
|
|
93
|
+
assertFalse(await doesActorOwnKey(activity, new CryptographicKey({ id: new URL("https://attacker.example/key") }), {
|
|
94
|
+
documentLoader: mockDocumentLoader,
|
|
95
|
+
contextLoader: mockDocumentLoader
|
|
96
|
+
}));
|
|
97
|
+
});
|
|
98
|
+
test("getKeyOwner() rejects an actor document from another origin", async () => {
|
|
99
|
+
const keyId = "https://attacker.example/key";
|
|
100
|
+
const { publicKeyPem } = await rsaPublicKey1.toJsonLd({ contextLoader: mockDocumentLoader });
|
|
101
|
+
const options = {
|
|
102
|
+
documentLoader(resource) {
|
|
103
|
+
if (resource === keyId) return Promise.resolve({
|
|
104
|
+
contextUrl: null,
|
|
105
|
+
documentUrl: resource,
|
|
106
|
+
document: {
|
|
107
|
+
"@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1"],
|
|
108
|
+
id: "https://example.com/person",
|
|
109
|
+
type: "Person",
|
|
110
|
+
publicKey: [{
|
|
111
|
+
id: keyId,
|
|
112
|
+
type: "CryptographicKey",
|
|
113
|
+
publicKeyPem
|
|
114
|
+
}]
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
return mockDocumentLoader(resource);
|
|
118
|
+
},
|
|
119
|
+
contextLoader: mockDocumentLoader
|
|
120
|
+
};
|
|
121
|
+
assertEquals(await getKeyOwner(new URL(keyId), options), null);
|
|
122
|
+
});
|
|
123
|
+
test("doesActorOwnKey() matches a key that leaves its id implicit", async () => {
|
|
124
|
+
const actorId = "https://example.com/implicit-key-actor";
|
|
125
|
+
const { publicKeyPem } = await rsaPublicKey1.toJsonLd({ contextLoader: mockDocumentLoader });
|
|
126
|
+
const options = {
|
|
127
|
+
documentLoader(resource) {
|
|
128
|
+
if (resource === actorId) return Promise.resolve({
|
|
129
|
+
contextUrl: null,
|
|
130
|
+
documentUrl: resource,
|
|
131
|
+
document: {
|
|
132
|
+
"@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1"],
|
|
133
|
+
id: actorId,
|
|
134
|
+
type: "Person",
|
|
135
|
+
publicKey: [{
|
|
136
|
+
type: "CryptographicKey",
|
|
137
|
+
owner: actorId,
|
|
138
|
+
publicKeyPem
|
|
139
|
+
}]
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
return mockDocumentLoader(resource);
|
|
143
|
+
},
|
|
144
|
+
contextLoader: mockDocumentLoader
|
|
145
|
+
};
|
|
146
|
+
const activity = new Create({ actor: new URL(actorId) });
|
|
147
|
+
assert(await doesActorOwnKey(activity, new CryptographicKey({
|
|
148
|
+
owner: new URL(actorId),
|
|
149
|
+
publicKey: rsaPublicKey1.publicKey
|
|
150
|
+
}), options));
|
|
151
|
+
assertFalse(await doesActorOwnKey(activity, new CryptographicKey({
|
|
152
|
+
owner: new URL(actorId),
|
|
153
|
+
publicKey: rsaPublicKey2.publicKey
|
|
154
|
+
}), options));
|
|
56
155
|
});
|
|
57
156
|
//#endregion
|
|
58
157
|
export {};
|
package/dist/sig/proof.test.mjs
CHANGED
|
@@ -10,7 +10,7 @@ import { t as assertRejects } from "../assert_rejects-wFbSPn1O.mjs";
|
|
|
10
10
|
import { t as assert } from "../assert-CebeUf0C.mjs";
|
|
11
11
|
import { i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-DIJNHW-Y.mjs";
|
|
12
12
|
import { r as normalizeOutgoingActivityJsonLd } from "../outgoing-jsonld-B5Ne7JoL.mjs";
|
|
13
|
-
import { a as verifyPortableObjectProof, i as verifyObject, n as hasProofLike, o as verifyProof, r as signObject, t as createProof } from "../proof-
|
|
13
|
+
import { a as verifyPortableObjectProof, i as verifyObject, n as hasProofLike, o as verifyProof, r as signObject, t as createProof } from "../proof-DL8Iid3a.mjs";
|
|
14
14
|
import { Create, DataIntegrityProof, Document, Multikey, Note, PUBLIC_COLLECTION, Place, Translation } from "@fedify/vocab";
|
|
15
15
|
import { createTestMeterProvider, mockDocumentLoader, test } from "@fedify/fixture";
|
|
16
16
|
import { decodeMultibase, encodeMultibase, exportDidKey, exportMultibaseKey, formatIri, importMultibaseKey, parseIri } from "@fedify/vocab-runtime";
|
|
@@ -1005,6 +1005,56 @@ test("verifyPortableObjectProof()", async (t) => {
|
|
|
1005
1005
|
})).verified);
|
|
1006
1006
|
assertEquals(fetches, 1);
|
|
1007
1007
|
});
|
|
1008
|
+
await t.step("caches keys owned by their own DID", async () => {
|
|
1009
|
+
const verificationMethod = new URL("did:web:example.com#key");
|
|
1010
|
+
const document = {
|
|
1011
|
+
...unsignedObject,
|
|
1012
|
+
id: "ap://did:web:example.com/objects/1"
|
|
1013
|
+
};
|
|
1014
|
+
const cached = [];
|
|
1015
|
+
assert((await verifyPortableObjectProof(await signPortableJsonLd(document, { verificationMethod }), {
|
|
1016
|
+
contextLoader: mockDocumentLoader,
|
|
1017
|
+
keyCache: {
|
|
1018
|
+
get: () => Promise.resolve(void 0),
|
|
1019
|
+
set: (keyId) => {
|
|
1020
|
+
cached.push(keyId.href);
|
|
1021
|
+
return Promise.resolve();
|
|
1022
|
+
}
|
|
1023
|
+
},
|
|
1024
|
+
documentLoader: async (url) => ({
|
|
1025
|
+
contextUrl: null,
|
|
1026
|
+
documentUrl: url,
|
|
1027
|
+
document: {
|
|
1028
|
+
"@context": "https://w3id.org/security/multikey/v1",
|
|
1029
|
+
id: verificationMethod.href,
|
|
1030
|
+
type: "Multikey",
|
|
1031
|
+
controller: "did:web:example.com",
|
|
1032
|
+
publicKeyMultibase: await exportMultibaseKey(ed25519PublicKey.publicKey)
|
|
1033
|
+
}
|
|
1034
|
+
})
|
|
1035
|
+
})).verified);
|
|
1036
|
+
assertEquals(cached, [verificationMethod.href]);
|
|
1037
|
+
});
|
|
1038
|
+
await t.step("rejects a portable key that names another controller", async () => {
|
|
1039
|
+
const verificationMethod = new URL("did:web:example.com#key");
|
|
1040
|
+
assertEquals((await verifyPortableObjectProof(await signPortableJsonLd({
|
|
1041
|
+
...unsignedObject,
|
|
1042
|
+
id: "ap://did:web:example.com/objects/1"
|
|
1043
|
+
}, { verificationMethod }), {
|
|
1044
|
+
contextLoader: mockDocumentLoader,
|
|
1045
|
+
documentLoader: async (url) => ({
|
|
1046
|
+
contextUrl: null,
|
|
1047
|
+
documentUrl: url,
|
|
1048
|
+
document: {
|
|
1049
|
+
"@context": "https://w3id.org/security/multikey/v1",
|
|
1050
|
+
id: verificationMethod.href,
|
|
1051
|
+
type: "Multikey",
|
|
1052
|
+
controller: "did:web:attacker.example",
|
|
1053
|
+
publicKeyMultibase: await exportMultibaseKey(ed25519PublicKey.publicKey)
|
|
1054
|
+
}
|
|
1055
|
+
})
|
|
1056
|
+
})).verified, false);
|
|
1057
|
+
});
|
|
1008
1058
|
await t.step("reports a missing proof on portable objects", async () => {
|
|
1009
1059
|
assertEquals(await verifyPortableObjectProof(unsignedObject, options), {
|
|
1010
1060
|
verified: false,
|
|
@@ -1699,5 +1749,74 @@ test("signObject() preserves FEP-22cd contexts through proof verification", asyn
|
|
|
1699
1749
|
assertInstanceOf(verified, Note);
|
|
1700
1750
|
assertEquals(verified.translations[0].language?.baseName, "ko");
|
|
1701
1751
|
});
|
|
1752
|
+
test("verifyObject() rejects a key that claims a forged controller", async () => {
|
|
1753
|
+
const keyId = new URL("https://attacker.example/multikey");
|
|
1754
|
+
const impersonated = "https://example.com/person2";
|
|
1755
|
+
const attackerActor = "https://attacker.example/actor";
|
|
1756
|
+
const multikeyDocument = await ed25519Multikey.toJsonLd({ contextLoader: mockDocumentLoader });
|
|
1757
|
+
const options = {
|
|
1758
|
+
format: "compact",
|
|
1759
|
+
contextLoader: mockDocumentLoader,
|
|
1760
|
+
documentLoader: mockDocumentLoader,
|
|
1761
|
+
context: ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/data-integrity/v1"]
|
|
1762
|
+
};
|
|
1763
|
+
const signed = await signObject(new Create({
|
|
1764
|
+
id: new URL("https://attacker.example/activities/1"),
|
|
1765
|
+
actor: new URL(impersonated),
|
|
1766
|
+
object: new Note({
|
|
1767
|
+
id: new URL("https://attacker.example/notes/1"),
|
|
1768
|
+
attribution: new URL(impersonated),
|
|
1769
|
+
content: "Hello world"
|
|
1770
|
+
})
|
|
1771
|
+
}), ed25519PrivateKey, keyId, options);
|
|
1772
|
+
const jsonLd = await signed.toJsonLd(options);
|
|
1773
|
+
const serveKeyControlledBy = (controller) => (resource) => {
|
|
1774
|
+
if (resource === keyId.href) return Promise.resolve({
|
|
1775
|
+
contextUrl: null,
|
|
1776
|
+
documentUrl: resource,
|
|
1777
|
+
document: {
|
|
1778
|
+
...multikeyDocument,
|
|
1779
|
+
id: keyId.href,
|
|
1780
|
+
controller
|
|
1781
|
+
}
|
|
1782
|
+
});
|
|
1783
|
+
if (resource === attackerActor) return Promise.resolve({
|
|
1784
|
+
contextUrl: null,
|
|
1785
|
+
documentUrl: resource,
|
|
1786
|
+
document: {
|
|
1787
|
+
"@context": [
|
|
1788
|
+
"https://www.w3.org/ns/activitystreams",
|
|
1789
|
+
"https://w3id.org/security/v1",
|
|
1790
|
+
"https://w3id.org/security/multikey/v1",
|
|
1791
|
+
"https://w3id.org/security/data-integrity/v1",
|
|
1792
|
+
"https://www.w3.org/ns/did/v1"
|
|
1793
|
+
],
|
|
1794
|
+
id: resource,
|
|
1795
|
+
type: "Person",
|
|
1796
|
+
assertionMethod: [keyId.href]
|
|
1797
|
+
}
|
|
1798
|
+
});
|
|
1799
|
+
return mockDocumentLoader(resource);
|
|
1800
|
+
};
|
|
1801
|
+
assertEquals(await verifyObject(Create, jsonLd, {
|
|
1802
|
+
documentLoader: serveKeyControlledBy(impersonated),
|
|
1803
|
+
contextLoader: mockDocumentLoader
|
|
1804
|
+
}), null);
|
|
1805
|
+
const honestLoader = serveKeyControlledBy(attackerActor);
|
|
1806
|
+
let proof = null;
|
|
1807
|
+
for await (const p of signed.getProofs(options)) {
|
|
1808
|
+
proof = p;
|
|
1809
|
+
break;
|
|
1810
|
+
}
|
|
1811
|
+
assertInstanceOf(proof, DataIntegrityProof);
|
|
1812
|
+
assertInstanceOf(await verifyProof(jsonLd, proof, {
|
|
1813
|
+
documentLoader: honestLoader,
|
|
1814
|
+
contextLoader: mockDocumentLoader
|
|
1815
|
+
}), Multikey);
|
|
1816
|
+
assertEquals(await verifyObject(Create, jsonLd, {
|
|
1817
|
+
documentLoader: honestLoader,
|
|
1818
|
+
contextLoader: mockDocumentLoader
|
|
1819
|
+
}), null);
|
|
1820
|
+
});
|
|
1702
1821
|
//#endregion
|
|
1703
1822
|
export {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { c as getNormalizationContextLoader } from "./ld-
|
|
4
|
+
import { c as getNormalizationContextLoader } from "./ld-CzhdWyTt.mjs";
|
|
5
5
|
import jsonld from "@fedify/vocab-runtime/jsonld";
|
|
6
6
|
//#region src/federation/temporal.ts
|
|
7
7
|
function isPlainObject(value) {
|
package/dist/testing/kv.test.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
|
|
|
6
6
|
import { t as ManualClockKvStore } from "../kv-DBRWVx3Q.mjs";
|
|
7
7
|
import { n as baseOptions, o as stringSchema, t as MockQueue } from "../tasks-dbCShlCb.mjs";
|
|
8
8
|
import { t as assertEquals } from "../assert_equals-CBlRfA-_.mjs";
|
|
9
|
-
import { o as createFederation } from "../middleware-
|
|
9
|
+
import { o as createFederation } from "../middleware-DCpiAsZi.mjs";
|
|
10
10
|
//#region src/testing/kv.test.ts
|
|
11
11
|
test("ManualClockKvStore keeps an entry at its exact expiration instant", async () => {
|
|
12
12
|
const kv = new ManualClockKvStore();
|
package/dist/testing/mod.d.mts
CHANGED
|
@@ -2021,7 +2021,8 @@ interface FederationOptions<TContextData> {
|
|
|
2021
2021
|
*/
|
|
2022
2022
|
authenticatedDocumentLoaderFactory?: AuthenticatedDocumentLoaderFactory;
|
|
2023
2023
|
/**
|
|
2024
|
-
* Whether to allow
|
|
2024
|
+
* Whether to allow private network addresses in the document loader and
|
|
2025
|
+
* outbound activity delivery, including redirects.
|
|
2025
2026
|
*
|
|
2026
2027
|
* If turned on, {@link FederationOptions.documentLoader},
|
|
2027
2028
|
* {@link FederationOptions.contextLoader}, and
|