@fedify/fedify 2.4.0-dev.1964 → 2.4.0-dev.1995
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-D0DxgOV5.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/compound-proof-CJnWc6c-.mjs +495 -0
- 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-CABhzCb_.mjs} +1 -1
- package/dist/{docloader-dk_k3S8d.mjs → docloader-BbGSL0Xq.mjs} +2 -2
- package/dist/federation/builder.test.mjs +1 -1
- package/dist/federation/handler-compound-proof.test.d.mts +2 -0
- package/dist/federation/handler-compound-proof.test.mjs +152 -0
- 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-DCEHQk4G.cjs} +632 -57
- package/dist/{http-6TI0tI03.d.cts → http-65PYi9jY.d.ts} +34 -2
- package/dist/{http-Dj4s1UQ0.js → http-9WyuldYh.js} +170 -8
- package/dist/{http-CdrSn2ip.d.ts → http-CP-7OdSq.d.cts} +34 -2
- package/dist/{http-B3skRvGo.cjs → http-CRFns-UE.cjs} +181 -7
- package/dist/{http-D7LX-BL9.mjs → http-D9urSO2D.mjs} +3 -3
- package/dist/{key-BVjgbs_9.mjs → key-Ccs8cnd7.mjs} +169 -7
- package/dist/{keycache-ZLqIfrkL.mjs → keycache-BUDHB5rY.mjs} +12 -4
- package/dist/{kv-cache-DtsjH_bY.cjs → kv-cache-DXT9vmIg.cjs} +2 -2
- package/dist/{kv-cache-tGNbvDCF.js → kv-cache-DtmXhPXG.js} +2 -2
- package/dist/{kv-cache-xRcC_xxP.mjs → kv-cache-lKyFn_Dn.mjs} +1 -1
- package/dist/{ld-OuCeQFny.mjs → ld-qup__MAW.mjs} +3 -3
- package/dist/map-local-context-conflict-DOYEEJ3u.mjs +204 -0
- package/dist/map-local-create-note-B0SeXJms.mjs +214 -0
- package/dist/{metrics-B9N27cCB.mjs → metrics-CISXCvB1.mjs} +1 -1
- package/dist/{middleware-TeLjzoxn.mjs → middleware-CzvJvPHK.mjs} +58 -15
- package/dist/{middleware-Cd_vPqDH.mjs → middleware-DkrU2WhY.mjs} +1 -1
- package/dist/{middleware-CPTDFMmJ.js → middleware-DsfWajaD.js} +593 -18
- 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 +20 -20
- 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/{proof-BO_esQii.js → owner-BIPEZGiU.js} +1255 -1098
- package/dist/{owner-r_waTiQj.d.cts → owner-BSzr5-I8.d.ts} +12 -1
- package/dist/owner-CRoq8Spb.mjs +175 -0
- package/dist/{proof-Dua0d906.cjs → owner-CVA7L9MA.cjs} +1266 -1097
- package/dist/{proof-D5Ajk4DJ.mjs → proof-BDxNVZ3E.mjs} +186 -83
- package/dist/{send-DVXyUqhO.mjs → send-BzM5GLyn.mjs} +68 -11
- package/dist/sig/compound-proof-discovery.test.d.mts +2 -0
- package/dist/sig/compound-proof-discovery.test.mjs +331 -0
- package/dist/sig/compound-proof-verification.test.d.mts +2 -0
- package/dist/sig/compound-proof-verification.test.mjs +465 -0
- package/dist/sig/compound-proof.test.d.mts +2 -0
- package/dist/sig/compound-proof.test.mjs +324 -0
- 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 +17 -17
- 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-Dv6Qv-8k.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
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import { Temporal } from "temporal-polyfill";
|
|
2
|
+
import "urlpattern-polyfill";
|
|
3
|
+
globalThis.addEventListener = () => {};
|
|
4
|
+
import { t as assertEquals } from "../assert_equals-CBlRfA-_.mjs";
|
|
5
|
+
import "../std__assert-CxFT0MNt.mjs";
|
|
6
|
+
import { t as assert } from "../assert-CebeUf0C.mjs";
|
|
7
|
+
import { n as ed25519PrivateKey, r as ed25519PublicKey } from "../keys-DIJNHW-Y.mjs";
|
|
8
|
+
import { n as preloadedOnlyDocumentLoader } from "../public-audience-Cc2YJ3ip.mjs";
|
|
9
|
+
import { r as normalizeOutgoingActivityJsonLd } from "../outgoing-jsonld-B5Ne7JoL.mjs";
|
|
10
|
+
import { c as verifyProof, r as signObject } from "../proof-BDxNVZ3E.mjs";
|
|
11
|
+
import { t as map_local_create_note_default } from "../map-local-create-note-B0SeXJms.mjs";
|
|
12
|
+
import { t as map_local_context_conflict_default } from "../map-local-context-conflict-DOYEEJ3u.mjs";
|
|
13
|
+
import { Create, DataIntegrityProof, Note } from "@fedify/vocab";
|
|
14
|
+
import { mockDocumentLoader, test } from "@fedify/fixture";
|
|
15
|
+
import { encodeMultibase, exportDidKey, parseIri } from "@fedify/vocab-runtime";
|
|
16
|
+
import { encodeHex } from "byte-encodings/hex";
|
|
17
|
+
import jsonld from "@fedify/vocab-runtime/jsonld";
|
|
18
|
+
import serialize from "json-canon";
|
|
19
|
+
//#region src/sig/compound-proof.test.ts
|
|
20
|
+
const outerPrivateKey = await crypto.subtle.importKey("jwk", {
|
|
21
|
+
kty: "OKP",
|
|
22
|
+
crv: "Ed25519",
|
|
23
|
+
d: "yW756hDF5BTEcXI6_53nLDX6W3D66X6IMuysfS4rjtY",
|
|
24
|
+
x: "sA2Nk45_dz1RVlqtNqYj9TRPf10ZYPnPPo4SYg6igQ8",
|
|
25
|
+
key_ops: ["sign"],
|
|
26
|
+
ext: true
|
|
27
|
+
}, "Ed25519", true, ["sign"]);
|
|
28
|
+
const outerDid = await exportDidKey(await crypto.subtle.importKey("jwk", {
|
|
29
|
+
kty: "OKP",
|
|
30
|
+
crv: "Ed25519",
|
|
31
|
+
x: "sA2Nk45_dz1RVlqtNqYj9TRPf10ZYPnPPo4SYg6igQ8",
|
|
32
|
+
key_ops: ["verify"],
|
|
33
|
+
ext: true
|
|
34
|
+
}, "Ed25519", true, ["verify"]));
|
|
35
|
+
const innerDid = await exportDidKey(ed25519PublicKey.publicKey);
|
|
36
|
+
const outerMethod = outerDid.substring(8);
|
|
37
|
+
const innerMethod = innerDid.substring(8);
|
|
38
|
+
const outerKeyId = new URL(`${outerDid}#${outerMethod}`);
|
|
39
|
+
const innerKeyId = new URL(`${innerDid}#${innerMethod}`);
|
|
40
|
+
const vectorInnerKeyId = new URL(map_local_create_note_default.keys.inner.verificationMethod);
|
|
41
|
+
const conflictVectorInnerKeyId = new URL(map_local_context_conflict_default.keys.inner.verificationMethod);
|
|
42
|
+
const context = ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/data-integrity/v1"];
|
|
43
|
+
const distinctInnerContext = [...context, { ex: "https://example.com/ns#" }];
|
|
44
|
+
const options = {
|
|
45
|
+
context,
|
|
46
|
+
contextLoader: mockDocumentLoader,
|
|
47
|
+
documentLoader: mockDocumentLoader
|
|
48
|
+
};
|
|
49
|
+
async function parseProof(document) {
|
|
50
|
+
const proof = document.proof;
|
|
51
|
+
assert(proof != null && typeof proof === "object" && !Array.isArray(proof));
|
|
52
|
+
return await DataIntegrityProof.fromJsonLd({
|
|
53
|
+
"@context": document["@context"],
|
|
54
|
+
...proof
|
|
55
|
+
}, options);
|
|
56
|
+
}
|
|
57
|
+
function asRecord(value) {
|
|
58
|
+
assert(value != null && typeof value === "object" && !Array.isArray(value));
|
|
59
|
+
return value;
|
|
60
|
+
}
|
|
61
|
+
function withoutDirectProof(document) {
|
|
62
|
+
const unsecuredDocument = structuredClone(document);
|
|
63
|
+
delete unsecuredDocument.proof;
|
|
64
|
+
return unsecuredDocument;
|
|
65
|
+
}
|
|
66
|
+
function withoutProofValue(proof) {
|
|
67
|
+
const proofConfiguration = structuredClone(proof);
|
|
68
|
+
delete proofConfiguration.proofValue;
|
|
69
|
+
return proofConfiguration;
|
|
70
|
+
}
|
|
71
|
+
async function sha256(value) {
|
|
72
|
+
return new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(value)));
|
|
73
|
+
}
|
|
74
|
+
async function assertRecordedProof(entry) {
|
|
75
|
+
assertEquals(withoutProofValue(entry.proof), entry.proofConfiguration);
|
|
76
|
+
assertEquals(entry.proof.proofValue, entry.proofValue);
|
|
77
|
+
const documentJcs = serialize(entry.document);
|
|
78
|
+
const proofConfigurationJcs = serialize(entry.proofConfiguration);
|
|
79
|
+
assertEquals(documentJcs, entry.canonicalization.documentJcs);
|
|
80
|
+
assertEquals(proofConfigurationJcs, entry.canonicalization.proofConfigurationJcs);
|
|
81
|
+
const documentDigest = await sha256(documentJcs);
|
|
82
|
+
const proofConfigurationDigest = await sha256(proofConfigurationJcs);
|
|
83
|
+
assertEquals(encodeHex(documentDigest), entry.hashes.documentSha256);
|
|
84
|
+
assertEquals(encodeHex(proofConfigurationDigest), entry.hashes.proofConfigurationSha256);
|
|
85
|
+
const combinedSigningInput = new Uint8Array(proofConfigurationDigest.length + documentDigest.length);
|
|
86
|
+
combinedSigningInput.set(proofConfigurationDigest);
|
|
87
|
+
combinedSigningInput.set(documentDigest, proofConfigurationDigest.length);
|
|
88
|
+
assertEquals(encodeHex(combinedSigningInput), entry.hashes.combinedSigningInput);
|
|
89
|
+
assertEquals(entry.hashes.combinedSigningInput, entry.hashes.proofConfigurationSha256 + entry.hashes.documentSha256);
|
|
90
|
+
const privateJwk = structuredClone(entry.key.testPrivateKeyJwk);
|
|
91
|
+
const publicJwk = structuredClone(entry.key.publicKeyJwk);
|
|
92
|
+
assertEquals(privateJwk.x, publicJwk.x);
|
|
93
|
+
const privateKey = await crypto.subtle.importKey("jwk", privateJwk, "Ed25519", true, ["sign"]);
|
|
94
|
+
const publicKey = await crypto.subtle.importKey("jwk", publicJwk, "Ed25519", true, ["verify"]);
|
|
95
|
+
const controller = await exportDidKey(publicKey);
|
|
96
|
+
assertEquals(controller, entry.key.controller);
|
|
97
|
+
const publicKeyMultibase = controller.replace(/^did:key:/, "");
|
|
98
|
+
assertEquals(publicKeyMultibase, entry.key.publicKeyMultibase);
|
|
99
|
+
assertEquals(entry.key.verificationMethod, `${controller}#${publicKeyMultibase}`);
|
|
100
|
+
assertEquals(entry.proofConfiguration.verificationMethod, entry.key.verificationMethod);
|
|
101
|
+
const signature = new Uint8Array(await crypto.subtle.sign("Ed25519", privateKey, combinedSigningInput));
|
|
102
|
+
assert(await crypto.subtle.verify("Ed25519", publicKey, signature, combinedSigningInput));
|
|
103
|
+
assertEquals(new TextDecoder().decode(encodeMultibase("base58btc", signature)), entry.proofValue);
|
|
104
|
+
}
|
|
105
|
+
test("map-local compound vector records independently reproducible proofs", async () => {
|
|
106
|
+
const securedOuter = asRecord(map_local_create_note_default.documents.finalSecuredCompound);
|
|
107
|
+
const securedInner = asRecord(map_local_create_note_default.documents.securedInner);
|
|
108
|
+
const outerProof = asRecord(securedOuter.proof);
|
|
109
|
+
const innerProof = asRecord(securedInner.proof);
|
|
110
|
+
const replacementInnerProof = asRecord(map_local_create_note_default.documents.replacementInnerProof);
|
|
111
|
+
const replacementInnerProofConfiguration = withoutProofValue(replacementInnerProof);
|
|
112
|
+
assertEquals(withoutDirectProof(securedOuter), map_local_create_note_default.documents.outerUnsecuredDocument);
|
|
113
|
+
assertEquals(withoutDirectProof(securedInner), map_local_create_note_default.documents.innerUnsecuredDocument);
|
|
114
|
+
assertEquals(withoutProofValue(outerProof), map_local_create_note_default.documents.outerProofConfiguration);
|
|
115
|
+
assertEquals(withoutProofValue(innerProof), map_local_create_note_default.documents.innerProofConfiguration);
|
|
116
|
+
const entries = [
|
|
117
|
+
{
|
|
118
|
+
document: map_local_create_note_default.documents.outerUnsecuredDocument,
|
|
119
|
+
proofConfiguration: map_local_create_note_default.documents.outerProofConfiguration,
|
|
120
|
+
proof: outerProof,
|
|
121
|
+
key: map_local_create_note_default.keys.outer,
|
|
122
|
+
canonicalization: map_local_create_note_default.canonicalization.outer,
|
|
123
|
+
hashes: map_local_create_note_default.hashes.outer,
|
|
124
|
+
proofValue: map_local_create_note_default.proofValues.outer
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
document: map_local_create_note_default.documents.innerUnsecuredDocument,
|
|
128
|
+
proofConfiguration: map_local_create_note_default.documents.innerProofConfiguration,
|
|
129
|
+
proof: innerProof,
|
|
130
|
+
key: map_local_create_note_default.keys.inner,
|
|
131
|
+
canonicalization: map_local_create_note_default.canonicalization.inner,
|
|
132
|
+
hashes: map_local_create_note_default.hashes.inner,
|
|
133
|
+
proofValue: map_local_create_note_default.proofValues.inner
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
document: map_local_create_note_default.documents.innerUnsecuredDocument,
|
|
137
|
+
proofConfiguration: replacementInnerProofConfiguration,
|
|
138
|
+
proof: replacementInnerProof,
|
|
139
|
+
key: map_local_create_note_default.keys.inner,
|
|
140
|
+
canonicalization: map_local_create_note_default.canonicalization.replacementInner,
|
|
141
|
+
hashes: map_local_create_note_default.hashes.replacementInner,
|
|
142
|
+
proofValue: map_local_create_note_default.proofValues.replacementInner
|
|
143
|
+
}
|
|
144
|
+
];
|
|
145
|
+
for (const entry of entries) await assertRecordedProof(entry);
|
|
146
|
+
});
|
|
147
|
+
test("the context-conflict vector records valid proofs and divergent semantics", async () => {
|
|
148
|
+
const compound = asRecord(map_local_context_conflict_default.documents.finalSecuredCompound);
|
|
149
|
+
const embedded = asRecord(compound.object);
|
|
150
|
+
const securedInner = asRecord(map_local_context_conflict_default.documents.securedInner);
|
|
151
|
+
const outerProof = asRecord(compound.proof);
|
|
152
|
+
const innerProof = asRecord(embedded.proof);
|
|
153
|
+
assertEquals(embedded, securedInner);
|
|
154
|
+
assertEquals(withoutDirectProof(compound), map_local_context_conflict_default.documents.outerUnsecuredDocument);
|
|
155
|
+
assertEquals(withoutDirectProof(embedded), map_local_context_conflict_default.documents.innerUnsecuredDocument);
|
|
156
|
+
await assertRecordedProof({
|
|
157
|
+
document: map_local_context_conflict_default.documents.outerUnsecuredDocument,
|
|
158
|
+
proofConfiguration: map_local_context_conflict_default.documents.outerProofConfiguration,
|
|
159
|
+
proof: outerProof,
|
|
160
|
+
key: map_local_context_conflict_default.keys.outer,
|
|
161
|
+
canonicalization: map_local_context_conflict_default.canonicalization.outer,
|
|
162
|
+
hashes: map_local_context_conflict_default.hashes.outer,
|
|
163
|
+
proofValue: map_local_context_conflict_default.proofValues.outer
|
|
164
|
+
});
|
|
165
|
+
await assertRecordedProof({
|
|
166
|
+
document: map_local_context_conflict_default.documents.innerUnsecuredDocument,
|
|
167
|
+
proofConfiguration: map_local_context_conflict_default.documents.innerProofConfiguration,
|
|
168
|
+
proof: innerProof,
|
|
169
|
+
key: map_local_context_conflict_default.keys.inner,
|
|
170
|
+
canonicalization: map_local_context_conflict_default.canonicalization.inner,
|
|
171
|
+
hashes: map_local_context_conflict_default.hashes.inner,
|
|
172
|
+
proofValue: map_local_context_conflict_default.proofValues.inner
|
|
173
|
+
});
|
|
174
|
+
const inboundOptions = {
|
|
175
|
+
contextLoader: preloadedOnlyDocumentLoader,
|
|
176
|
+
documentLoader: preloadedOnlyDocumentLoader
|
|
177
|
+
};
|
|
178
|
+
const verifiedInner = await verifyProof(embedded, await parseProof(embedded), inboundOptions);
|
|
179
|
+
assertEquals(verifiedInner != null, map_local_context_conflict_default.expectedVerification.original.inner);
|
|
180
|
+
assertEquals(verifiedInner?.id, conflictVectorInnerKeyId);
|
|
181
|
+
const verifiedOuter = await verifyProof(compound, await parseProof(compound), inboundOptions);
|
|
182
|
+
assertEquals(verifiedOuter != null, map_local_context_conflict_default.expectedVerification.original.outer);
|
|
183
|
+
assertEquals(verifiedOuter?.id, outerKeyId);
|
|
184
|
+
const standaloneExpansion = await jsonld.expand(embedded, { documentLoader: preloadedOnlyDocumentLoader });
|
|
185
|
+
const compoundExpansion = await jsonld.expand(compound, { documentLoader: preloadedOnlyDocumentLoader });
|
|
186
|
+
const content = "https://www.w3.org/ns/activitystreams#content";
|
|
187
|
+
const object = "https://www.w3.org/ns/activitystreams#object";
|
|
188
|
+
const standaloneContent = asRecord(standaloneExpansion[0])[content];
|
|
189
|
+
const inParentObject = asRecord(asRecord(compoundExpansion[0])[object][0]);
|
|
190
|
+
const inParentContent = inParentObject[content];
|
|
191
|
+
assertEquals(serialize(standaloneExpansion[0]) === serialize(inParentObject), map_local_context_conflict_default.expectedInterpretation.standaloneAndInParentExpansionsEqual);
|
|
192
|
+
assertEquals(standaloneContent, [{ "@value": "A portable note with inherited language" }]);
|
|
193
|
+
assertEquals(inParentContent, [{
|
|
194
|
+
"@value": "A portable note with inherited language",
|
|
195
|
+
"@language": map_local_context_conflict_default.expectedInterpretation.inParentContentLanguage
|
|
196
|
+
}]);
|
|
197
|
+
assertEquals(standaloneContent[0]["@language"] ?? null, map_local_context_conflict_default.expectedInterpretation.standaloneContentLanguage);
|
|
198
|
+
assertEquals(serialize(standaloneContent) !== serialize(inParentContent), !map_local_context_conflict_default.expectedInterpretation.standaloneAndInParentExpansionsEqual);
|
|
199
|
+
});
|
|
200
|
+
test("the raw same-context baseline verifies map-locally", async () => {
|
|
201
|
+
const compound = structuredClone(map_local_create_note_default.documents.finalSecuredCompound);
|
|
202
|
+
const embedded = compound.object;
|
|
203
|
+
const embeddedProof = embedded.proof;
|
|
204
|
+
assertEquals(embedded, map_local_create_note_default.documents.securedInner);
|
|
205
|
+
assertEquals(embeddedProof["@context"], context);
|
|
206
|
+
assertEquals(embedded["@context"], context);
|
|
207
|
+
assert("proof" in embedded);
|
|
208
|
+
const verifiedInnerKey = await verifyProof(embedded, await parseProof(embedded), options);
|
|
209
|
+
assertEquals(verifiedInnerKey != null, map_local_create_note_default.expectedVerification.original.inner);
|
|
210
|
+
assertEquals(verifiedInnerKey != null, map_local_create_note_default.expectedVerification.standaloneInner);
|
|
211
|
+
assertEquals(verifiedInnerKey?.id, vectorInnerKeyId);
|
|
212
|
+
const verifiedOuterKey = await verifyProof(compound, await parseProof(compound), options);
|
|
213
|
+
assertEquals(verifiedOuterKey != null, map_local_create_note_default.expectedVerification.original.outer);
|
|
214
|
+
assertEquals(verifiedOuterKey != null, map_local_create_note_default.expectedVerification.standaloneOuter);
|
|
215
|
+
assertEquals(verifiedOuterKey?.id, outerKeyId);
|
|
216
|
+
const tampered = structuredClone(compound);
|
|
217
|
+
const tamperedInner = tampered.object;
|
|
218
|
+
tamperedInner.content = "A tampered portable note";
|
|
219
|
+
assertEquals(await verifyProof(tamperedInner, await parseProof(tamperedInner), options) != null, map_local_create_note_default.expectedVerification.tamperedInnerContent.inner);
|
|
220
|
+
assertEquals(await verifyProof(tampered, await parseProof(tampered), options) != null, map_local_create_note_default.expectedVerification.tamperedInnerContent.outer);
|
|
221
|
+
const replaced = structuredClone(compound);
|
|
222
|
+
const replacedInner = replaced.object;
|
|
223
|
+
replacedInner.proof = structuredClone(map_local_create_note_default.documents.replacementInnerProof);
|
|
224
|
+
const replacementInnerKey = await verifyProof(replacedInner, await parseProof(replacedInner), options);
|
|
225
|
+
assertEquals(replacementInnerKey != null, map_local_create_note_default.expectedVerification.replacedInnerProof.inner);
|
|
226
|
+
assertEquals(replacementInnerKey?.id, vectorInnerKeyId);
|
|
227
|
+
assertEquals(await verifyProof(replaced, await parseProof(replaced), options) != null, map_local_create_note_default.expectedVerification.replacedInnerProof.outer);
|
|
228
|
+
});
|
|
229
|
+
test("typed Create serialization bypasses a parsed child's JSON-LD cache", async () => {
|
|
230
|
+
const created = Temporal.Instant.from("2023-02-24T23:36:38Z");
|
|
231
|
+
const inner = await signObject(new Note({
|
|
232
|
+
id: parseIri(`ap+ef61://${innerDid}/objects/distinct-context`),
|
|
233
|
+
attribution: parseIri(`ap+ef61://${innerDid}/actor`),
|
|
234
|
+
content: "A portable note with its own context"
|
|
235
|
+
}), ed25519PrivateKey, innerKeyId, {
|
|
236
|
+
...options,
|
|
237
|
+
context: distinctInnerContext,
|
|
238
|
+
created
|
|
239
|
+
});
|
|
240
|
+
const standaloneInner = await normalizeOutgoingActivityJsonLd(await inner.toJsonLd({
|
|
241
|
+
format: "compact",
|
|
242
|
+
...options,
|
|
243
|
+
context: distinctInnerContext
|
|
244
|
+
}), mockDocumentLoader);
|
|
245
|
+
const compound = await normalizeOutgoingActivityJsonLd(await (await signObject(new Create({
|
|
246
|
+
id: parseIri(`ap+ef61://${outerDid}/activities/distinct-context`),
|
|
247
|
+
actor: parseIri(`ap+ef61://${outerDid}/actor`),
|
|
248
|
+
object: inner
|
|
249
|
+
}), outerPrivateKey, outerKeyId, {
|
|
250
|
+
...options,
|
|
251
|
+
created
|
|
252
|
+
})).toJsonLd({
|
|
253
|
+
format: "compact",
|
|
254
|
+
...options
|
|
255
|
+
}), mockDocumentLoader);
|
|
256
|
+
const embedded = compound.object;
|
|
257
|
+
const embeddedProof = embedded.proof;
|
|
258
|
+
const standaloneProof = await parseProof(standaloneInner);
|
|
259
|
+
assertEquals(standaloneInner["@context"], distinctInnerContext);
|
|
260
|
+
assertEquals(standaloneInner.proof["@context"], distinctInnerContext);
|
|
261
|
+
assertEquals(embedded["@context"], void 0);
|
|
262
|
+
assertEquals(embeddedProof["@context"], void 0);
|
|
263
|
+
assertEquals(embeddedProof.proofValue, standaloneInner.proof.proofValue);
|
|
264
|
+
const compactedStandalone = structuredClone(standaloneInner);
|
|
265
|
+
const compactedStandaloneProof = compactedStandalone.proof;
|
|
266
|
+
delete compactedStandalone["@context"];
|
|
267
|
+
delete compactedStandaloneProof["@context"];
|
|
268
|
+
assertEquals(embedded, compactedStandalone);
|
|
269
|
+
assertEquals((await verifyProof(standaloneInner, standaloneProof, options))?.id, innerKeyId);
|
|
270
|
+
const rewrittenContext = structuredClone(standaloneInner);
|
|
271
|
+
rewrittenContext["@context"] = context;
|
|
272
|
+
assertEquals(await verifyProof(rewrittenContext, standaloneProof, options), null);
|
|
273
|
+
assertEquals(await verifyProof(embedded, standaloneProof, options), null);
|
|
274
|
+
assertEquals((await verifyProof(compound, await parseProof(compound), options))?.id, outerKeyId);
|
|
275
|
+
const reparsedInner = await Note.fromJsonLd(standaloneInner, {
|
|
276
|
+
contextLoader: mockDocumentLoader,
|
|
277
|
+
documentLoader: mockDocumentLoader
|
|
278
|
+
});
|
|
279
|
+
assert(reparsedInner instanceof Note);
|
|
280
|
+
assertEquals(await reparsedInner.toJsonLd(), standaloneInner);
|
|
281
|
+
const reparsedCompound = await normalizeOutgoingActivityJsonLd(await (await signObject(new Create({
|
|
282
|
+
id: parseIri(`ap+ef61://${outerDid}/activities/reparsed-context`),
|
|
283
|
+
actor: parseIri(`ap+ef61://${outerDid}/actor`),
|
|
284
|
+
object: reparsedInner
|
|
285
|
+
}), outerPrivateKey, outerKeyId, {
|
|
286
|
+
...options,
|
|
287
|
+
created
|
|
288
|
+
})).toJsonLd({
|
|
289
|
+
format: "compact",
|
|
290
|
+
...options
|
|
291
|
+
}), mockDocumentLoader);
|
|
292
|
+
const reparsedEmbedded = reparsedCompound.object;
|
|
293
|
+
const reparsedEmbeddedProof = reparsedEmbedded.proof;
|
|
294
|
+
assertEquals(reparsedEmbedded["@context"], void 0);
|
|
295
|
+
assertEquals(reparsedEmbeddedProof["@context"], void 0);
|
|
296
|
+
assertEquals(reparsedEmbeddedProof.proofValue, standaloneInner.proof.proofValue);
|
|
297
|
+
assertEquals(reparsedEmbedded, compactedStandalone);
|
|
298
|
+
assertEquals(reparsedEmbedded, embedded);
|
|
299
|
+
assertEquals(await verifyProof(reparsedEmbedded, standaloneProof, options), null);
|
|
300
|
+
assertEquals((await verifyProof(reparsedCompound, await parseProof(reparsedCompound), options))?.id, outerKeyId);
|
|
301
|
+
});
|
|
302
|
+
test("verifyProof() does not invent a missing child context", async () => {
|
|
303
|
+
const received = structuredClone(map_local_create_note_default.documents.finalSecuredCompound);
|
|
304
|
+
const receivedInner = received.object;
|
|
305
|
+
const receivedInnerProof = receivedInner.proof;
|
|
306
|
+
delete receivedInner["@context"];
|
|
307
|
+
delete receivedInnerProof["@context"];
|
|
308
|
+
const inboundOptions = {
|
|
309
|
+
contextLoader: preloadedOnlyDocumentLoader,
|
|
310
|
+
documentLoader: preloadedOnlyDocumentLoader
|
|
311
|
+
};
|
|
312
|
+
const snapshot = structuredClone(received);
|
|
313
|
+
const proof = await DataIntegrityProof.fromJsonLd(received.proof, inboundOptions);
|
|
314
|
+
assertEquals(await verifyProof(received, proof, inboundOptions), null);
|
|
315
|
+
assertEquals(received, snapshot);
|
|
316
|
+
const guessed = structuredClone(received);
|
|
317
|
+
const guessedInner = guessed.object;
|
|
318
|
+
const guessedInnerProof = guessedInner.proof;
|
|
319
|
+
guessedInner["@context"] = structuredClone(guessed["@context"]);
|
|
320
|
+
guessedInnerProof["@context"] = structuredClone(guessed["@context"]);
|
|
321
|
+
assertEquals((await verifyProof(guessed, proof, inboundOptions))?.id, outerKeyId);
|
|
322
|
+
});
|
|
323
|
+
//#endregion
|
|
324
|
+
export {};
|
package/dist/sig/http.test.mjs
CHANGED
|
@@ -10,8 +10,8 @@ import { t as assertThrows } from "../assert_throws-BJ5wuvR2.mjs";
|
|
|
10
10
|
import "../std__assert-CxFT0MNt.mjs";
|
|
11
11
|
import { t as assertRejects } from "../assert_rejects-wFbSPn1O.mjs";
|
|
12
12
|
import { t as assert } from "../assert-CebeUf0C.mjs";
|
|
13
|
-
import { t as exportJwk } from "../key-
|
|
14
|
-
import { a as parseRfc9421Signature, c as timingSafeEqual, i as formatRfc9421SignatureParameters, l as verifyRequest, n as doubleKnock, o as parseRfc9421SignatureInput, r as formatRfc9421Signature, s as signRequest, t as createRfc9421SignatureBase, u as verifyRequestDetailed } from "../http-
|
|
13
|
+
import { t as exportJwk } from "../key-Ccs8cnd7.mjs";
|
|
14
|
+
import { a as parseRfc9421Signature, c as timingSafeEqual, i as formatRfc9421SignatureParameters, l as verifyRequest, n as doubleKnock, o as parseRfc9421SignatureInput, r as formatRfc9421Signature, s as signRequest, t as createRfc9421SignatureBase, u as verifyRequestDetailed } from "../http-D9urSO2D.mjs";
|
|
15
15
|
import { i as rsaPrivateKey2, l as rsaPublicKey5, o as rsaPublicKey1, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
|
|
16
16
|
import { createTestMeterProvider, createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
|
|
17
17
|
import { FetchError, exportSpki } from "@fedify/vocab-runtime";
|
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-Ccs8cnd7.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-Ccs8cnd7.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-qup__MAW.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,31 +1,31 @@
|
|
|
1
1
|
const { URLPattern } = require("urlpattern-polyfill");
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const require_http = require("../http-
|
|
4
|
-
const
|
|
5
|
-
exports.attachSignature =
|
|
6
|
-
exports.createProof =
|
|
7
|
-
exports.createSignature =
|
|
8
|
-
exports.detachSignature =
|
|
9
|
-
exports.doesActorOwnKey =
|
|
3
|
+
const require_http = require("../http-CRFns-UE.cjs");
|
|
4
|
+
const require_owner = require("../owner-CVA7L9MA.cjs");
|
|
5
|
+
exports.attachSignature = require_owner.attachSignature;
|
|
6
|
+
exports.createProof = require_owner.createProof;
|
|
7
|
+
exports.createSignature = require_owner.createSignature;
|
|
8
|
+
exports.detachSignature = require_owner.detachSignature;
|
|
9
|
+
exports.doesActorOwnKey = require_owner.doesActorOwnKey;
|
|
10
10
|
exports.exportJwk = require_http.exportJwk;
|
|
11
11
|
exports.fetchKey = require_http.fetchKey;
|
|
12
12
|
exports.fetchKeyDetailed = require_http.fetchKeyDetailed;
|
|
13
13
|
exports.formatAcceptSignature = require_http.formatAcceptSignature;
|
|
14
14
|
exports.fulfillAcceptSignature = require_http.fulfillAcceptSignature;
|
|
15
15
|
exports.generateCryptoKeyPair = require_http.generateCryptoKeyPair;
|
|
16
|
-
exports.getKeyOwner =
|
|
17
|
-
exports.hasProofLike =
|
|
18
|
-
exports.hasSignatureLike =
|
|
16
|
+
exports.getKeyOwner = require_owner.getKeyOwner;
|
|
17
|
+
exports.hasProofLike = require_owner.hasProofLike;
|
|
18
|
+
exports.hasSignatureLike = require_owner.hasSignatureLike;
|
|
19
19
|
exports.importJwk = require_http.importJwk;
|
|
20
20
|
exports.parseAcceptSignature = require_http.parseAcceptSignature;
|
|
21
|
-
exports.signJsonLd =
|
|
22
|
-
exports.signObject =
|
|
21
|
+
exports.signJsonLd = require_owner.signJsonLd;
|
|
22
|
+
exports.signObject = require_owner.signObject;
|
|
23
23
|
exports.signRequest = require_http.signRequest;
|
|
24
24
|
exports.validateAcceptSignature = require_http.validateAcceptSignature;
|
|
25
|
-
exports.verifyJsonLd =
|
|
26
|
-
exports.verifyObject =
|
|
27
|
-
exports.verifyPortableObjectProof =
|
|
28
|
-
exports.verifyProof =
|
|
25
|
+
exports.verifyJsonLd = require_owner.verifyJsonLd;
|
|
26
|
+
exports.verifyObject = require_owner.verifyObject;
|
|
27
|
+
exports.verifyPortableObjectProof = require_owner.verifyPortableObjectProof;
|
|
28
|
+
exports.verifyProof = require_owner.verifyProof;
|
|
29
29
|
exports.verifyRequest = require_http.verifyRequest;
|
|
30
30
|
exports.verifyRequestDetailed = require_http.verifyRequestDetailed;
|
|
31
|
-
exports.verifySignature =
|
|
31
|
+
exports.verifySignature = require_owner.verifySignature;
|
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 };
|