@fedify/fedify 2.4.0-dev.1668 → 2.4.0-dev.1740
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/README.md +1 -1
- package/dist/{assert-CJ1KXcvv.mjs → assert-CebeUf0C.mjs} +1 -1
- package/dist/assert_exists-DtzkXC47.mjs +28 -0
- package/dist/assert_false-3pLLLsO4.mjs +23 -0
- package/dist/assert_greater_or_equal-BApOTh1h.mjs +30 -0
- package/dist/{assert_instance_of-Q9KTBnrf.mjs → assert_instance_of-g3WcyYNR.mjs} +1 -1
- package/dist/{assert_not_equals-EPJPoxuf.mjs → assert_not_equals-BN7yDMWe.mjs} +1 -1
- package/dist/assert_rejects-wFbSPn1O.mjs +39 -0
- package/dist/{assert_strict_equals-Ec20AUwa.mjs → assert_strict_equals-Bpjl4Psw.mjs} +1 -1
- package/dist/{assert_throws-NiFfqbnH.mjs → assert_throws-BJ5wuvR2.mjs} +1 -1
- package/dist/{builder-iXZ5jNVg.mjs → builder-Bw6LVKGw.mjs} +5 -3
- package/dist/{circuit-breaker-CBGyz5F7.mjs → circuit-breaker-BT7CiPbz.mjs} +191 -9
- package/dist/circuit-breaker-test-utils-BYIHoliq.mjs +14 -0
- package/dist/{client-CjF5IYQS.mjs → client-CtxvmazV.mjs} +63 -5
- package/dist/{collection-BIhlldqK.mjs → collection-CnnP1UAv.mjs} +1 -1
- package/dist/compat/mod.cjs +1 -1
- package/dist/compat/mod.d.cts +1 -1
- package/dist/compat/mod.d.ts +1 -1
- package/dist/compat/mod.js +1 -1
- package/dist/compat/outgoing-jsonld.test.mjs +3 -3
- package/dist/compat/public-audience.test.mjs +3 -3
- package/dist/compat/transformers.test.mjs +5 -5
- package/dist/{context-C05Rgxmu.d.cts → context-B-dsBjZ9.d.cts} +281 -7
- package/dist/{context-Cjnr0ig1.d.ts → context-DrLXudbA.d.ts} +281 -7
- package/dist/{deno-DtHcfB0f.mjs → deno-tRdp8MCl.mjs} +1 -1
- package/dist/{docloader-0vAfF6XW.mjs → docloader-Dg_7gMC7.mjs} +2 -2
- package/dist/{esm-yYQfPhIY.mjs → esm-BHXXx4Wx.mjs} +13 -6
- package/dist/federation/builder.test.mjs +18 -5
- package/dist/federation/circuit-breaker.test.mjs +626 -7
- package/dist/federation/collection.test.mjs +3 -3
- package/dist/federation/handler.test.mjs +14 -13
- package/dist/federation/idempotency.test.mjs +6 -6
- package/dist/federation/inbox.test.mjs +3 -3
- package/dist/federation/keycache.test.mjs +5 -5
- package/dist/federation/kv.test.mjs +3 -3
- package/dist/federation/metrics.test.mjs +4 -4
- package/dist/federation/middleware.test.mjs +37 -28
- package/dist/federation/mod.cjs +18 -540
- package/dist/federation/mod.d.cts +3 -4
- package/dist/federation/mod.d.ts +3 -4
- package/dist/federation/mod.js +1 -1
- package/dist/federation/mq.test.mjs +32 -5
- package/dist/federation/negotiation.test.mjs +4 -4
- package/dist/federation/retry.test.mjs +3 -3
- package/dist/federation/router.test.mjs +7 -7
- package/dist/federation/send.test.mjs +28 -26
- package/dist/federation/tasks/codec.test.mjs +4 -4
- package/dist/federation/tasks/enqueue.test.mjs +4 -4
- package/dist/federation/tasks/tasks.test.mjs +6 -6
- package/dist/federation/temporal.test.mjs +4 -4
- package/dist/federation/webfinger.test.mjs +74 -12
- package/dist/{middleware-DzIUFRO2.cjs → federation-BJBpt_Am.cjs} +855 -114
- package/dist/{http-BbnOMQoy.js → http-BIxGa6AG.js} +3 -3
- package/dist/{http-DUmWq5Pw.mjs → http-Day1kcpT.mjs} +5 -5
- package/dist/{temporal-polyfill-BCdTHexd.cjs → http-DggG1scN.cjs} +2801 -104
- package/dist/{key-CM7wDnZG.mjs → key-igh0AMsL.mjs} +2 -2
- package/dist/{kv-cache-ZQ22hm44.js → kv-cache-BIAB0HKx.js} +1 -1
- package/dist/{kv-cache-DEXKemJ9.mjs → kv-cache-Bz6aQOOX.mjs} +1 -1
- package/dist/{kv-cache-zmgG1HUp.cjs → kv-cache-C4U_uBqD.cjs} +2 -4
- package/dist/{ld-BttH_-8K.mjs → ld-DnT9sCWa.mjs} +5 -5
- package/dist/metrics-DVKYY6K1.d.cts +36 -0
- package/dist/{metrics-DT-CnD1g.mjs → metrics-DbdNnWIt.mjs} +2 -2
- package/dist/metrics-DoRtI0qU.d.ts +36 -0
- package/dist/{middleware-ChlYS0Af.mjs → middleware-B6GweVcg.mjs} +1 -1
- package/dist/{middleware-D3IAVMcT.js → middleware-CnsPXG0O.js} +241 -40
- package/dist/{middleware-DrroX3aD.mjs → middleware-DPIS4fsB.mjs} +60 -43
- package/dist/{mod-BrpVsSfY.d.cts → mod-CLHkCwLm.d.cts} +3 -3
- package/dist/{mod-B0hW12_O.d.cts → mod-CnGLNoVw.d.cts} +86 -4
- package/dist/{mod-DdONo-ep.d.cts → mod-D-abvu2C.d.cts} +1 -1
- package/dist/{mod-BsCE5Ihq.d.ts → mod-D6O8C7eN.d.ts} +1 -1
- package/dist/{mod-CNef4in0.d.ts → mod-DypxPS9H.d.ts} +3 -3
- package/dist/{mod-COIAjwRS.d.ts → mod-DzRXACM5.d.ts} +86 -4
- package/dist/mod.cjs +25 -25
- package/dist/mod.d.cts +10 -11
- package/dist/mod.d.ts +10 -11
- package/dist/mod.js +8 -8
- package/dist/nodeinfo/client.test.mjs +64 -4
- package/dist/nodeinfo/handler.test.mjs +5 -5
- package/dist/nodeinfo/mod.cjs +1 -1
- package/dist/nodeinfo/mod.d.cts +1 -1
- package/dist/nodeinfo/mod.d.ts +1 -1
- package/dist/nodeinfo/mod.js +1 -1
- package/dist/nodeinfo/types.test.mjs +4 -4
- package/dist/otel/exporter.test.mjs +3 -3
- package/dist/otel/mod.cjs +0 -1
- package/dist/otel/mod.d.cts +1 -1
- package/dist/otel/mod.d.ts +1 -1
- package/dist/{outgoing-jsonld-URuqsICz.mjs → outgoing-jsonld-BQSHRLhg.mjs} +2 -2
- package/dist/{owner-DZALmhdl.mjs → owner-B3ef8GfJ.mjs} +2 -2
- package/dist/{proof-CqA-2nfW.js → proof-BBLQXRWw.js} +608 -36
- package/dist/{proof-a60oVJ_4.cjs → proof-Cqk2TcLX.cjs} +616 -39
- package/dist/proof-DVoDZ8d7.mjs +890 -0
- package/dist/{public-audience-Cr5MsnMq.mjs → public-audience--9hx7IGx.mjs} +1 -1
- package/dist/runtime/mod.cjs +1 -1
- package/dist/runtime/mod.js +1 -1
- package/dist/{send-DcwZnQWG.mjs → send-PbBSZ7Qk.mjs} +3 -3
- package/dist/sig/accept.test.mjs +1 -1
- package/dist/sig/http.test.mjs +37 -11
- package/dist/sig/key.test.mjs +6 -6
- package/dist/sig/ld.test.mjs +9 -7
- package/dist/sig/mod.cjs +3 -2
- package/dist/sig/mod.d.cts +4 -4
- package/dist/sig/mod.d.ts +4 -4
- package/dist/sig/mod.js +3 -3
- package/dist/sig/owner.test.mjs +6 -6
- package/dist/sig/proof.test.mjs +965 -15
- package/dist/std__assert-CxFT0MNt.mjs +8 -0
- package/dist/{temporal-Bpxpsl2k.mjs → temporal-DLQIjuTn.mjs} +3 -3
- package/dist/testing/mod.d.mts +10 -0
- package/dist/testing/mod.mjs +2 -2
- package/dist/{testing-CO7ftO8A.mjs → testing-BX0Nicyq.mjs} +2 -2
- package/dist/{transformers-DGwl-hD1.cjs → transformers-BalCrSsr.cjs} +0 -1
- package/dist/{types-DC1pNgJA.js → types-Dtyf8lVj.js} +63 -5
- package/dist/{types-CPym83zc.cjs → types-RXm5YxpC.cjs} +62 -5
- package/dist/utils/docloader.test.mjs +7 -7
- 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 +7 -7
- package/skills/fedify/SKILL.md +462 -0
- package/dist/assert_rejects-EgstcQy5.mjs +0 -82
- package/dist/http-BpT5cE6r.cjs +0 -2694
- package/dist/metrics-DN13mCAw.d.cts +0 -294
- package/dist/metrics-v_e0n5GQ.d.ts +0 -294
- package/dist/proof-DiQ0uBal.mjs +0 -316
- package/dist/std__assert-B2NnY9bq.mjs +0 -79
- /package/dist/{accept-8PiolDP7.mjs → accept-CPVqki78.mjs} +0 -0
- /package/dist/{activity-listener-BtTSxZWA.mjs → activity-listener-C-BSbKI7.mjs} +0 -0
- /package/dist/{assert_equals-BV7oLMxm.mjs → assert_equals-CBlRfA-_.mjs} +0 -0
- /package/dist/{client-CAM_bQXx.d.cts → client-D-XP_Ccb.d.cts} +0 -0
- /package/dist/{client-CSddvgWN.d.ts → client-D-XP_Ccb.d.ts} +0 -0
- /package/dist/{codec-BBga-FWI.mjs → codec-peqOdBaE.mjs} +0 -0
- /package/dist/{context-BJXxPElm.mjs → context-Dt36KFEJ.mjs} +0 -0
- /package/dist/{http-VyDTd4G3.d.cts → http-6TI0tI03.d.cts} +0 -0
- /package/dist/{http-lf8Hsd91.d.ts → http-CdrSn2ip.d.ts} +0 -0
- /package/dist/{keycache-_DHqz5z_.mjs → keycache-0Jk78XmB.mjs} +0 -0
- /package/dist/{keys-Dbyd7bhl.mjs → keys-DIJNHW-Y.mjs} +0 -0
- /package/dist/{kv-D6hNiMTK.d.ts → kv-BhNbN8Ed.d.cts} +0 -0
- /package/dist/{kv-gJ8LYbxX.d.cts → kv-BhNbN8Ed.d.ts} +0 -0
- /package/dist/{kv-CXBAok3k.mjs → kv-DCHkaJ9-.mjs} +0 -0
- /package/dist/{mq-Bo7Etgh6.mjs → mq-Dm1Wmtm-.mjs} +0 -0
- /package/dist/{negotiation-RuwrPFqi.mjs → negotiation-CCocWT_N.mjs} +0 -0
- /package/dist/{owner-CnngXDNJ.d.ts → owner-DGSfntI-.d.ts} +0 -0
- /package/dist/{owner-DEvZuyOE.d.cts → owner-r_waTiQj.d.cts} +0 -0
- /package/dist/{retry-DgFqeS5k.mjs → retry-zX5xw5e0.mjs} +0 -0
- /package/dist/{chunk-D5IdezsZ.js → rolldown-runtime-BC-Eo5Mj.js} +0 -0
- /package/dist/{chunk-Czpeue4M.cjs → rolldown-runtime-Dmm3t92W.cjs} +0 -0
- /package/dist/{transformers-DXze6FUB.js → transformers-DAtXl_Kc.js} +0 -0
- /package/dist/{types-CrtdVuDP.mjs → types-DE_LV1JB.mjs} +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
import { URLPattern } from "urlpattern-polyfill";
|
|
3
|
-
import { F as version, P as name, _ as measureSignatureKeyFetch, d as validateCryptoKey, f as getDurationMs, p as getFederationMetrics, s as fetchKey } from "./http-
|
|
3
|
+
import { F as version, P as name, _ as measureSignatureKeyFetch, d as validateCryptoKey, f as getDurationMs, p as getFederationMetrics, s as fetchKey } from "./http-BIxGa6AG.js";
|
|
4
4
|
import { getLogger } from "@logtape/logtape";
|
|
5
5
|
import { Activity, CryptographicKey, DataIntegrityProof, Multikey, Object as Object$1, PUBLIC_COLLECTION, getTypeId, isActor } from "@fedify/vocab";
|
|
6
6
|
import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
|
|
7
7
|
import { encodeHex } from "byte-encodings/hex";
|
|
8
|
-
import { getDocumentLoader, haveSameFe34Origin, preloadedContexts } from "@fedify/vocab-runtime";
|
|
8
|
+
import { formatIri, getDocumentLoader, getFe34Origin, haveSameFe34Origin, parseIri, preloadedContexts } from "@fedify/vocab-runtime";
|
|
9
9
|
import { decodeBase64, encodeBase64 } from "byte-encodings/base64";
|
|
10
10
|
import jsonld from "@fedify/vocab-runtime/jsonld";
|
|
11
11
|
import serialize from "json-canon";
|
|
@@ -23,7 +23,7 @@ const localContext = [
|
|
|
23
23
|
];
|
|
24
24
|
const localContextUrls = new Set(localContext);
|
|
25
25
|
const builtInContextLoader = getDocumentLoader();
|
|
26
|
-
const disallowedJsonLdKeywords = new Set([
|
|
26
|
+
const disallowedJsonLdKeywords = /* @__PURE__ */ new Set([
|
|
27
27
|
"@graph",
|
|
28
28
|
"@included",
|
|
29
29
|
"@reverse"
|
|
@@ -523,7 +523,7 @@ async function verifySignature(jsonLd, options = {}) {
|
|
|
523
523
|
* only from external documents and are dropped from the metric attribute to
|
|
524
524
|
* avoid attacker-controlled cardinality.
|
|
525
525
|
*/
|
|
526
|
-
const LD_KNOWN_SIGNATURE_TYPES = new Set(["RsaSignature2017"]);
|
|
526
|
+
const LD_KNOWN_SIGNATURE_TYPES = /* @__PURE__ */ new Set(["RsaSignature2017"]);
|
|
527
527
|
/**
|
|
528
528
|
* Reports only whether a `signature` key is present on the document, with
|
|
529
529
|
* no shape check on its value. This is intentionally looser than
|
|
@@ -757,7 +757,7 @@ const logger$2 = getLogger([
|
|
|
757
757
|
"compat",
|
|
758
758
|
"public-audience"
|
|
759
759
|
]);
|
|
760
|
-
const PUBLIC_ADDRESSING_FIELDS = new Set([
|
|
760
|
+
const PUBLIC_ADDRESSING_FIELDS = /* @__PURE__ */ new Set([
|
|
761
761
|
"to",
|
|
762
762
|
"cc",
|
|
763
763
|
"bto",
|
|
@@ -922,7 +922,7 @@ const logger$1 = getLogger([
|
|
|
922
922
|
"compat",
|
|
923
923
|
"outgoing-jsonld"
|
|
924
924
|
]);
|
|
925
|
-
const ATTACHMENT_FIELDS = new Set(["attachment", "https://www.w3.org/ns/activitystreams#attachment"]);
|
|
925
|
+
const ATTACHMENT_FIELDS = /* @__PURE__ */ new Set(["attachment", "https://www.w3.org/ns/activitystreams#attachment"]);
|
|
926
926
|
const AS_CONTEXT_URL = "https://www.w3.org/ns/activitystreams";
|
|
927
927
|
const KNOWN_SAFE_CONTEXT_URLS = getKnownSafeContextUrls();
|
|
928
928
|
const MAX_TRAVERSAL_DEPTH = 64;
|
|
@@ -1120,12 +1120,22 @@ async function normalizeOutgoingActivityJsonLd(jsonLd, contextLoader) {
|
|
|
1120
1120
|
* dropped from the metric attribute to avoid attacker-controlled
|
|
1121
1121
|
* cardinality.
|
|
1122
1122
|
*/
|
|
1123
|
-
const OIP_KNOWN_CRYPTOSUITES = new Set(["eddsa-jcs-2022"]);
|
|
1123
|
+
const OIP_KNOWN_CRYPTOSUITES = /* @__PURE__ */ new Set(["eddsa-jcs-2022"]);
|
|
1124
1124
|
const logger = getLogger([
|
|
1125
1125
|
"fedify",
|
|
1126
1126
|
"sig",
|
|
1127
1127
|
"proof"
|
|
1128
1128
|
]);
|
|
1129
|
+
const SECURITY_NAMESPACE = "https://w3id.org/security#";
|
|
1130
|
+
const SECURITY_PROOF = `${SECURITY_NAMESPACE}proof`;
|
|
1131
|
+
const SECURITY_PROOF_VALUE = `${SECURITY_NAMESPACE}proofValue`;
|
|
1132
|
+
const DATA_INTEGRITY_PROOF = `${SECURITY_NAMESPACE}DataIntegrityProof`;
|
|
1133
|
+
const SECURITY_VERIFICATION_METHOD = `${SECURITY_NAMESPACE}verificationMethod`;
|
|
1134
|
+
const SECURITY_EXPIRATION = `${SECURITY_NAMESPACE}expiration`;
|
|
1135
|
+
const SECURITY_DOMAIN = `${SECURITY_NAMESPACE}domain`;
|
|
1136
|
+
const SECURITY_CHALLENGE = `${SECURITY_NAMESPACE}challenge`;
|
|
1137
|
+
const SECURITY_NONCE = `${SECURITY_NAMESPACE}nonce`;
|
|
1138
|
+
const SECURITY_PREVIOUS_PROOF = `${SECURITY_NAMESPACE}previousProof`;
|
|
1129
1139
|
/**
|
|
1130
1140
|
* Checks if the given JSON-LD document has a DataIntegrityProof-like object,
|
|
1131
1141
|
* without fully deserializing it into vocabulary classes.
|
|
@@ -1237,15 +1247,21 @@ async function signObject(object, privateKey, keyId, options = {}) {
|
|
|
1237
1247
|
}
|
|
1238
1248
|
/**
|
|
1239
1249
|
* Verifies the given proof for the object.
|
|
1240
|
-
* @param jsonLd The JSON-LD object to verify the proof for.
|
|
1241
|
-
*
|
|
1250
|
+
* @param jsonLd The JSON-LD object to verify the proof for. Its proof
|
|
1251
|
+
* properties are excluded from the message, but matching proof
|
|
1252
|
+
* occurrences are used to authenticate the received proof
|
|
1253
|
+
* configuration.
|
|
1242
1254
|
* @param proof The proof to verify.
|
|
1243
1255
|
* @param options Additional options. See also {@link VerifyProofOptions}.
|
|
1244
1256
|
* @returns The public key that was used to sign the proof, or `null` if the
|
|
1245
1257
|
* proof is invalid.
|
|
1246
1258
|
* @since 0.10.0
|
|
1259
|
+
* @since 2.4.0 Matching proof configurations are authenticated.
|
|
1247
1260
|
*/
|
|
1248
1261
|
async function verifyProof(jsonLd, proof, options = {}) {
|
|
1262
|
+
return await verifyProofWithMessageDigestCache(jsonLd, proof, options);
|
|
1263
|
+
}
|
|
1264
|
+
async function verifyProofWithMessageDigestCache(jsonLd, proof, options, messageDigestCache = {}, rawProofCandidate) {
|
|
1249
1265
|
return await (options.tracerProvider ?? trace.getTracerProvider()).getTracer(name, version).startActiveSpan("object_integrity_proofs.verify", async (span) => {
|
|
1250
1266
|
const start = performance.now();
|
|
1251
1267
|
let verified = false;
|
|
@@ -1257,7 +1273,7 @@ async function verifyProof(jsonLd, proof, options = {}) {
|
|
|
1257
1273
|
if (proof.proofValue != null) span.setAttribute("object_integrity_proofs.signature", encodeHex(proof.proofValue));
|
|
1258
1274
|
}
|
|
1259
1275
|
try {
|
|
1260
|
-
const key = await verifyProofInternal(jsonLd, proof, options);
|
|
1276
|
+
const key = await verifyProofInternal(jsonLd, proof, options, messageDigestCache, rawProofCandidate);
|
|
1261
1277
|
if (key == null) span.setStatus({ code: SpanStatusCode.ERROR });
|
|
1262
1278
|
else verified = true;
|
|
1263
1279
|
return key;
|
|
@@ -1275,23 +1291,313 @@ async function verifyProof(jsonLd, proof, options = {}) {
|
|
|
1275
1291
|
}
|
|
1276
1292
|
});
|
|
1277
1293
|
}
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
const
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1294
|
+
function expandContextPropertyIri(activeContext, key) {
|
|
1295
|
+
const termId = jsonld.getContextValue(activeContext, key, "@id");
|
|
1296
|
+
if (termId != null) return termId;
|
|
1297
|
+
const colon = key.indexOf(":");
|
|
1298
|
+
if (colon < 1) return key;
|
|
1299
|
+
const prefix = key.substring(0, colon);
|
|
1300
|
+
const suffix = key.substring(colon + 1);
|
|
1301
|
+
if (prefix === "_" || suffix.startsWith("//")) return key;
|
|
1302
|
+
const mapping = jsonld.getContextValue(activeContext, prefix);
|
|
1303
|
+
if (typeof mapping === "object" && mapping != null && mapping._prefix === true && typeof mapping["@id"] === "string") return mapping["@id"] + suffix;
|
|
1304
|
+
return key;
|
|
1305
|
+
}
|
|
1306
|
+
async function getJsonLdPropertyNames(jsonLd, propertyIri, defaults, documentLoader = preloadedOnlyDocumentLoader, inheritedContext, rejectOnContextError = false) {
|
|
1307
|
+
const names = new Set(defaults);
|
|
1308
|
+
const context = jsonLd["@context"] ?? inheritedContext;
|
|
1309
|
+
if (context == null) return names;
|
|
1310
|
+
try {
|
|
1311
|
+
const options = { documentLoader };
|
|
1312
|
+
let activeContext = await jsonld.processContext(null, null, options);
|
|
1313
|
+
activeContext = await jsonld.processContext(activeContext, context, options);
|
|
1314
|
+
const typeScopedContext = activeContext;
|
|
1315
|
+
for (const key of globalThis.Object.keys(jsonLd).sort()) {
|
|
1316
|
+
if (key !== "@type" && expandContextPropertyIri(activeContext, key) !== "@type") continue;
|
|
1317
|
+
const value = jsonLd[key];
|
|
1318
|
+
const types = Array.isArray(value) ? value.slice().sort() : [value];
|
|
1319
|
+
for (const type of types) {
|
|
1320
|
+
if (typeof type !== "string") continue;
|
|
1321
|
+
const scopedContext = jsonld.getContextValue(typeScopedContext, type, "@context");
|
|
1322
|
+
if (scopedContext != null) activeContext = await jsonld.processContext(activeContext, scopedContext, options);
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
for (const key of globalThis.Object.keys(jsonLd)) if (expandContextPropertyIri(activeContext, key) === propertyIri) names.add(key);
|
|
1326
|
+
} catch {
|
|
1327
|
+
if (rejectOnContextError) return null;
|
|
1328
|
+
}
|
|
1329
|
+
return names;
|
|
1330
|
+
}
|
|
1331
|
+
async function getProofPropertyNames(jsonLd, documentLoader = preloadedOnlyDocumentLoader) {
|
|
1332
|
+
return await getJsonLdPropertyNames(jsonLd, SECURITY_PROOF, ["proof", SECURITY_PROOF], documentLoader) ?? /* @__PURE__ */ new Set(["proof", SECURITY_PROOF]);
|
|
1333
|
+
}
|
|
1334
|
+
async function createProofMessageDigests(jsonLd, proofContextLoader, context) {
|
|
1335
|
+
const msg = { ...jsonLd };
|
|
1336
|
+
for (const property of await getProofPropertyNames(msg, proofContextLoader)) delete msg[property];
|
|
1337
|
+
if (context != null) msg["@context"] = structuredClone(context);
|
|
1289
1338
|
const encoder = new TextEncoder();
|
|
1290
|
-
const
|
|
1339
|
+
const digest = async (value) => {
|
|
1340
|
+
const bytes = encoder.encode(serialize(value));
|
|
1341
|
+
return await crypto.subtle.digest("SHA-256", bytes);
|
|
1342
|
+
};
|
|
1343
|
+
const onWire = await digest(msg);
|
|
1344
|
+
let normalizedPromise;
|
|
1345
|
+
return {
|
|
1346
|
+
onWire,
|
|
1347
|
+
normalized() {
|
|
1348
|
+
normalizedPromise ??= (async () => {
|
|
1349
|
+
const normalized = await normalizeOutgoingActivityJsonLd(msg, preloadedOnlyDocumentLoader);
|
|
1350
|
+
return normalized === msg ? null : await digest(normalized);
|
|
1351
|
+
})();
|
|
1352
|
+
return normalizedPromise;
|
|
1353
|
+
}
|
|
1354
|
+
};
|
|
1355
|
+
}
|
|
1356
|
+
function contextValues(context) {
|
|
1357
|
+
return Array.isArray(context) ? context : [context];
|
|
1358
|
+
}
|
|
1359
|
+
function equalJsonValues(left, right) {
|
|
1360
|
+
try {
|
|
1361
|
+
return serialize(left) === serialize(right);
|
|
1362
|
+
} catch {
|
|
1363
|
+
return false;
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
function contextStartsWith(documentContext, proofContext) {
|
|
1367
|
+
if (documentContext == null) return false;
|
|
1368
|
+
const documentValues = contextValues(documentContext);
|
|
1369
|
+
const proofValues = contextValues(proofContext);
|
|
1370
|
+
return proofValues.length <= documentValues.length && proofValues.every((value, index) => equalJsonValues(value, documentValues[index]));
|
|
1371
|
+
}
|
|
1372
|
+
function appendProofValues(values, value) {
|
|
1373
|
+
if (Array.isArray(value)) for (const item of value) appendProofValues(values, item);
|
|
1374
|
+
else if (isJsonLdNode(value) && Array.isArray(value["@graph"])) for (const item of value["@graph"]) appendProofValues(values, item);
|
|
1375
|
+
else values.push(value);
|
|
1376
|
+
}
|
|
1377
|
+
async function getRawProofValues(jsonLd, documentLoader) {
|
|
1378
|
+
const propertyNames = await getProofPropertyNames(jsonLd, documentLoader);
|
|
1379
|
+
const values = [];
|
|
1380
|
+
for (const [property, value] of globalThis.Object.entries(jsonLd)) if (propertyNames.has(property)) appendProofValues(values, value);
|
|
1381
|
+
return values;
|
|
1382
|
+
}
|
|
1383
|
+
function sameBytes(left, right) {
|
|
1384
|
+
if (left == null || right == null) return left === right;
|
|
1385
|
+
return left.length === right.length && left.every((value, index) => value === right[index]);
|
|
1386
|
+
}
|
|
1387
|
+
function sameProof(left, right) {
|
|
1388
|
+
return left.cryptosuite === right.cryptosuite && left.verificationMethodId?.href === right.verificationMethodId?.href && left.proofPurpose === right.proofPurpose && sameBytes(left.proofValue, right.proofValue) && left.created?.toString() === right.created?.toString();
|
|
1389
|
+
}
|
|
1390
|
+
function normalizeDocumentUrl(url) {
|
|
1391
|
+
try {
|
|
1392
|
+
return formatIri(parseIri(url));
|
|
1393
|
+
} catch {
|
|
1394
|
+
return URL.canParse(url) ? new URL(url).href : url;
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
function getRawProofReference(value) {
|
|
1398
|
+
const node = Array.isArray(value) && value.length === 1 ? value[0] : value;
|
|
1399
|
+
if (typeof node === "string") return normalizeDocumentUrl(node);
|
|
1400
|
+
if (!isJsonLdNode(node)) return void 0;
|
|
1401
|
+
const id = node["@id"] ?? node.id;
|
|
1402
|
+
return typeof id === "string" ? normalizeDocumentUrl(id) : void 0;
|
|
1403
|
+
}
|
|
1404
|
+
async function parseRawProofCandidates(jsonLd, values, options, documentLoader) {
|
|
1405
|
+
const candidates = [];
|
|
1406
|
+
for (const value of values) {
|
|
1407
|
+
let parsed = null;
|
|
1408
|
+
if (isJsonLdNode(value)) {
|
|
1409
|
+
const proofJsonLd = value["@context"] == null && jsonLd["@context"] != null ? {
|
|
1410
|
+
"@context": jsonLd["@context"],
|
|
1411
|
+
...value
|
|
1412
|
+
} : value;
|
|
1413
|
+
try {
|
|
1414
|
+
parsed = await DataIntegrityProof.fromJsonLd(proofJsonLd, {
|
|
1415
|
+
...options,
|
|
1416
|
+
contextLoader: documentLoader
|
|
1417
|
+
});
|
|
1418
|
+
} catch {}
|
|
1419
|
+
}
|
|
1420
|
+
candidates.push({
|
|
1421
|
+
value,
|
|
1422
|
+
proof: parsed,
|
|
1423
|
+
reference: getRawProofReference(value) ?? parsed?.id?.href
|
|
1424
|
+
});
|
|
1425
|
+
}
|
|
1426
|
+
return candidates;
|
|
1427
|
+
}
|
|
1428
|
+
async function findRawProofCandidate(jsonLd, proof, options, documentLoader) {
|
|
1429
|
+
const candidates = await parseRawProofCandidates(jsonLd, await getRawProofValues(jsonLd, documentLoader), options, documentLoader);
|
|
1430
|
+
const matches = [];
|
|
1431
|
+
for (const candidate of candidates) if (candidate.proof != null && sameProof(candidate.proof, proof)) matches.push(candidate);
|
|
1432
|
+
if (matches.length < 1) return void 0;
|
|
1433
|
+
const first = matches[0];
|
|
1434
|
+
const configurations = await Promise.all(matches.map((candidate) => normalizeProofConfiguration(candidate.value, jsonLd["@context"], documentLoader)));
|
|
1435
|
+
const firstConfiguration = configurations[0];
|
|
1436
|
+
return firstConfiguration != null && configurations.every((configuration) => configuration != null && equalJsonValues(configuration.value, firstConfiguration.value)) ? first : null;
|
|
1437
|
+
}
|
|
1438
|
+
function takeRawProofCandidate(pool, proof) {
|
|
1439
|
+
for (let index = 0; index < pool.candidates.length; index++) {
|
|
1440
|
+
if (pool.used.has(index)) continue;
|
|
1441
|
+
const candidate = pool.candidates[index];
|
|
1442
|
+
if (candidate.proof != null && sameProof(candidate.proof, proof)) {
|
|
1443
|
+
pool.used.add(index);
|
|
1444
|
+
return candidate;
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
const STANDARD_COMPACT_PROOF_PROPERTIES = /* @__PURE__ */ new Set([
|
|
1449
|
+
"@context",
|
|
1450
|
+
"type",
|
|
1451
|
+
"cryptosuite",
|
|
1452
|
+
"verificationMethod",
|
|
1453
|
+
"proofPurpose",
|
|
1454
|
+
"proofValue",
|
|
1455
|
+
"created"
|
|
1456
|
+
]);
|
|
1457
|
+
const STANDARD_EXPANDED_PROOF_PROPERTIES = /* @__PURE__ */ new Set([
|
|
1458
|
+
"@context",
|
|
1459
|
+
"@type",
|
|
1460
|
+
`${SECURITY_NAMESPACE}cryptosuite`,
|
|
1461
|
+
SECURITY_VERIFICATION_METHOD,
|
|
1462
|
+
`${SECURITY_NAMESPACE}proofPurpose`,
|
|
1463
|
+
`${SECURITY_NAMESPACE}proofValue`,
|
|
1464
|
+
"http://purl.org/dc/terms/created"
|
|
1465
|
+
]);
|
|
1466
|
+
function hasAdditionalProofOptions(value) {
|
|
1467
|
+
const node = Array.isArray(value) && value.length === 1 ? value[0] : value;
|
|
1468
|
+
return isJsonLdNode(node) && globalThis.Object.keys(node).some((property) => !STANDARD_COMPACT_PROOF_PROPERTIES.has(property) && !STANDARD_EXPANDED_PROOF_PROPERTIES.has(property));
|
|
1469
|
+
}
|
|
1470
|
+
function isExpandedJsonLdNode(value) {
|
|
1471
|
+
const properties = globalThis.Object.keys(value).filter((key) => !key.startsWith("@"));
|
|
1472
|
+
return properties.length > 0 && properties.every((property) => URL.canParse(property));
|
|
1473
|
+
}
|
|
1474
|
+
async function normalizeProofConfiguration(rawProof, documentContext, documentLoader) {
|
|
1475
|
+
let node = Array.isArray(rawProof) && rawProof.length === 1 ? rawProof[0] : rawProof;
|
|
1476
|
+
if (!isJsonLdNode(node)) return null;
|
|
1477
|
+
if (isExpandedJsonLdNode(node)) {
|
|
1478
|
+
if (documentContext == null) return null;
|
|
1479
|
+
node = await jsonld.compact(node, documentContext, { documentLoader });
|
|
1480
|
+
if (!isJsonLdNode(node)) return null;
|
|
1481
|
+
} else node = structuredClone(node);
|
|
1482
|
+
const receivedContext = node["@context"];
|
|
1483
|
+
if (receivedContext != null && !contextStartsWith(documentContext, receivedContext)) return null;
|
|
1484
|
+
const context = receivedContext ?? documentContext;
|
|
1485
|
+
if (context != null) node["@context"] = structuredClone(context);
|
|
1486
|
+
const proofValueProperties = await getJsonLdPropertyNames(node, SECURITY_PROOF_VALUE, ["proofValue", SECURITY_PROOF_VALUE], documentLoader, context) ?? /* @__PURE__ */ new Set(["proofValue", SECURITY_PROOF_VALUE]);
|
|
1487
|
+
for (const property of proofValueProperties) delete node[property];
|
|
1488
|
+
return {
|
|
1489
|
+
value: node,
|
|
1490
|
+
context
|
|
1491
|
+
};
|
|
1492
|
+
}
|
|
1493
|
+
async function createProofConfiguration(jsonLd, proof, options, documentLoader, rawProofCandidate) {
|
|
1494
|
+
let rawProof;
|
|
1495
|
+
if (rawProofCandidate == null) {
|
|
1496
|
+
const match = await findRawProofCandidate(jsonLd, proof, options, documentLoader);
|
|
1497
|
+
if (match === null) return null;
|
|
1498
|
+
rawProof = match?.value;
|
|
1499
|
+
} else rawProof = rawProofCandidate.value;
|
|
1500
|
+
if (rawProof == null) {
|
|
1501
|
+
const serializedProof = await proof.toJsonLd();
|
|
1502
|
+
if (hasAdditionalProofOptions(serializedProof)) rawProof = serializedProof;
|
|
1503
|
+
}
|
|
1504
|
+
if (rawProof != null) return await normalizeProofConfiguration(rawProof, jsonLd["@context"], documentLoader);
|
|
1505
|
+
const context = jsonLd["@context"];
|
|
1506
|
+
return {
|
|
1507
|
+
value: {
|
|
1508
|
+
...context == null ? {} : { "@context": context },
|
|
1509
|
+
type: "DataIntegrityProof",
|
|
1510
|
+
cryptosuite: proof.cryptosuite,
|
|
1511
|
+
verificationMethod: proof.verificationMethodId.href,
|
|
1512
|
+
proofPurpose: proof.proofPurpose,
|
|
1513
|
+
created: proof.created.toString()
|
|
1514
|
+
},
|
|
1515
|
+
context
|
|
1516
|
+
};
|
|
1517
|
+
}
|
|
1518
|
+
const KNOWN_PROOF_CONFIGURATION_PROPERTIES = /* @__PURE__ */ new Set([
|
|
1519
|
+
"@context",
|
|
1520
|
+
"@id",
|
|
1521
|
+
"@type",
|
|
1522
|
+
"id",
|
|
1523
|
+
"type",
|
|
1524
|
+
"cryptosuite",
|
|
1525
|
+
`${SECURITY_NAMESPACE}cryptosuite`,
|
|
1526
|
+
"verificationMethod",
|
|
1527
|
+
SECURITY_VERIFICATION_METHOD,
|
|
1528
|
+
"proofPurpose",
|
|
1529
|
+
`${SECURITY_NAMESPACE}proofPurpose`,
|
|
1530
|
+
"created",
|
|
1531
|
+
"http://purl.org/dc/terms/created",
|
|
1532
|
+
"expires",
|
|
1533
|
+
SECURITY_EXPIRATION,
|
|
1534
|
+
"domain",
|
|
1535
|
+
SECURITY_DOMAIN,
|
|
1536
|
+
"challenge",
|
|
1537
|
+
SECURITY_CHALLENGE,
|
|
1538
|
+
"nonce",
|
|
1539
|
+
SECURITY_NONCE,
|
|
1540
|
+
"previousProof",
|
|
1541
|
+
SECURITY_PREVIOUS_PROOF
|
|
1542
|
+
]);
|
|
1543
|
+
async function getProofOption(proofConfig, propertyIri, defaults, documentLoader) {
|
|
1544
|
+
let names = await getJsonLdPropertyNames(proofConfig, propertyIri, defaults, documentLoader, proofConfig["@context"], true);
|
|
1545
|
+
if (names == null) {
|
|
1546
|
+
if (globalThis.Object.keys(proofConfig).some((property) => !KNOWN_PROOF_CONFIGURATION_PROPERTIES.has(property))) return null;
|
|
1547
|
+
names = new Set(defaults);
|
|
1548
|
+
}
|
|
1549
|
+
const present = globalThis.Object.keys(proofConfig).filter((property) => names.has(property));
|
|
1550
|
+
if (present.length > 1) return null;
|
|
1551
|
+
return present.length < 1 ? { present: false } : {
|
|
1552
|
+
present: true,
|
|
1553
|
+
value: proofConfig[present[0]]
|
|
1554
|
+
};
|
|
1555
|
+
}
|
|
1556
|
+
function parseStringSet(value) {
|
|
1557
|
+
if (typeof value === "string") return /* @__PURE__ */ new Set([value]);
|
|
1558
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) return null;
|
|
1559
|
+
return new Set(value);
|
|
1560
|
+
}
|
|
1561
|
+
function equalStringSets(left, right) {
|
|
1562
|
+
return left.size === right.size && [...left].every((value) => right.has(value));
|
|
1563
|
+
}
|
|
1564
|
+
async function hasValidProofOptions(proofConfig, options, documentLoader) {
|
|
1565
|
+
const expires = await getProofOption(proofConfig, SECURITY_EXPIRATION, ["expires", SECURITY_EXPIRATION], documentLoader);
|
|
1566
|
+
if (expires == null) return false;
|
|
1567
|
+
if (expires.present) {
|
|
1568
|
+
if (typeof expires.value !== "string") return false;
|
|
1569
|
+
let expiration;
|
|
1570
|
+
try {
|
|
1571
|
+
expiration = Temporal.Instant.from(expires.value);
|
|
1572
|
+
} catch {
|
|
1573
|
+
return false;
|
|
1574
|
+
}
|
|
1575
|
+
if (Temporal.Instant.compare(Temporal.Now.instant(), expiration) >= 0) return false;
|
|
1576
|
+
}
|
|
1577
|
+
const domain = await getProofOption(proofConfig, SECURITY_DOMAIN, ["domain", SECURITY_DOMAIN], documentLoader);
|
|
1578
|
+
if (domain == null) return false;
|
|
1579
|
+
const proofDomains = domain.present ? parseStringSet(domain.value) : null;
|
|
1580
|
+
if (domain.present && proofDomains == null) return false;
|
|
1581
|
+
if (options.domain != null) {
|
|
1582
|
+
const expectedDomains = parseStringSet(options.domain);
|
|
1583
|
+
if (expectedDomains == null || proofDomains == null || !equalStringSets(proofDomains, expectedDomains)) return false;
|
|
1584
|
+
}
|
|
1585
|
+
const challenge = await getProofOption(proofConfig, SECURITY_CHALLENGE, ["challenge", SECURITY_CHALLENGE], documentLoader);
|
|
1586
|
+
if (challenge == null || challenge.present && typeof challenge.value !== "string" || options.challenge != null && (!challenge.present || challenge.value !== options.challenge)) return false;
|
|
1587
|
+
const nonce = await getProofOption(proofConfig, SECURITY_NONCE, ["nonce", SECURITY_NONCE], documentLoader);
|
|
1588
|
+
if (nonce == null || nonce.present && typeof nonce.value !== "string") return false;
|
|
1589
|
+
const previousProof = await getProofOption(proofConfig, SECURITY_PREVIOUS_PROOF, ["previousProof", SECURITY_PREVIOUS_PROOF], documentLoader);
|
|
1590
|
+
if (previousProof == null || previousProof.present && typeof previousProof.value !== "string" && (!Array.isArray(previousProof.value) || previousProof.value.some((item) => typeof item !== "string"))) return false;
|
|
1591
|
+
return true;
|
|
1592
|
+
}
|
|
1593
|
+
async function verifyProofInternal(jsonLd, proof, options, messageDigestCache, rawProofCandidate) {
|
|
1594
|
+
if (!isJsonLdNode(jsonLd) || proof.cryptosuite !== "eddsa-jcs-2022" || proof.verificationMethodId == null || proof.proofPurpose !== "assertionMethod" || proof.proofValue == null || proof.created == null) return null;
|
|
1595
|
+
const proofContextLoader = messageDigestCache.proofContextLoader ?? preloadedOnlyDocumentLoader;
|
|
1596
|
+
const proofConfiguration = await createProofConfiguration(jsonLd, proof, options, proofContextLoader, rawProofCandidate);
|
|
1597
|
+
if (proofConfiguration == null || !await hasValidProofOptions(proofConfiguration.value, options, proofContextLoader)) return null;
|
|
1598
|
+
const publicKeyPromise = measureSignatureKeyFetch(options.meterProvider, "object_integrity", () => fetchKey(proof.verificationMethodId, Multikey, options));
|
|
1599
|
+
const proofBytes = new TextEncoder().encode(serialize(proofConfiguration.value));
|
|
1291
1600
|
const proofDigest = await crypto.subtle.digest("SHA-256", proofBytes);
|
|
1292
|
-
const msg = { ...jsonLd };
|
|
1293
|
-
if ("proof" in msg) delete msg.proof;
|
|
1294
|
-
if ("https://w3id.org/security#proof" in msg) delete msg["https://w3id.org/security#proof"];
|
|
1295
1601
|
let fetchedKey;
|
|
1296
1602
|
try {
|
|
1297
1603
|
fetchedKey = await publicKeyPromise;
|
|
@@ -1323,7 +1629,7 @@ async function verifyProofInternal(jsonLd, proof, options) {
|
|
|
1323
1629
|
get: () => Promise.resolve(void 0),
|
|
1324
1630
|
set: async (keyId, key) => await options.keyCache?.set(keyId, key)
|
|
1325
1631
|
}
|
|
1326
|
-
});
|
|
1632
|
+
}, messageDigestCache, rawProofCandidate);
|
|
1327
1633
|
}
|
|
1328
1634
|
logger.debug("The fetched key (verificationMethod) for the proof is not a valid Ed25519 key:\n{keyId}", {
|
|
1329
1635
|
proof,
|
|
@@ -1334,15 +1640,21 @@ async function verifyProofInternal(jsonLd, proof, options) {
|
|
|
1334
1640
|
const digest = new Uint8Array(proofDigest.byteLength + 32);
|
|
1335
1641
|
digest.set(new Uint8Array(proofDigest), 0);
|
|
1336
1642
|
const proofValue = proof.proofValue;
|
|
1337
|
-
const verifyCandidate = async (
|
|
1338
|
-
const msgBytes = encoder.encode(serialize(candidate));
|
|
1339
|
-
const msgDigest = await crypto.subtle.digest("SHA-256", msgBytes);
|
|
1643
|
+
const verifyCandidate = async (msgDigest) => {
|
|
1340
1644
|
digest.set(new Uint8Array(msgDigest), proofDigest.byteLength);
|
|
1341
1645
|
return await crypto.subtle.verify("Ed25519", publicKey.publicKey, proofValue.slice(), digest);
|
|
1342
1646
|
};
|
|
1343
|
-
|
|
1344
|
-
const
|
|
1345
|
-
|
|
1647
|
+
const messageDigestKey = serialize(proofConfiguration.context ?? null);
|
|
1648
|
+
const messageDigestValues = messageDigestCache.values ??= /* @__PURE__ */ new Map();
|
|
1649
|
+
let messageDigestsPromise = messageDigestValues.get(messageDigestKey);
|
|
1650
|
+
if (messageDigestsPromise == null) {
|
|
1651
|
+
messageDigestsPromise = createProofMessageDigests(jsonLd, messageDigestCache.proofContextLoader, proofConfiguration.context);
|
|
1652
|
+
messageDigestValues.set(messageDigestKey, messageDigestsPromise);
|
|
1653
|
+
}
|
|
1654
|
+
const messageDigests = await messageDigestsPromise;
|
|
1655
|
+
if (await verifyCandidate(messageDigests.onWire)) return publicKey;
|
|
1656
|
+
const normalizedDigest = await messageDigests.normalized();
|
|
1657
|
+
if (normalizedDigest != null && await verifyCandidate(normalizedDigest)) return publicKey;
|
|
1346
1658
|
if (fetchedKey.cached) {
|
|
1347
1659
|
logger.debug("Failed to verify the proof with the cached key {keyId}; retrying with the freshly fetched key...", {
|
|
1348
1660
|
keyId: proof.verificationMethodId.href,
|
|
@@ -1354,7 +1666,7 @@ async function verifyProofInternal(jsonLd, proof, options) {
|
|
|
1354
1666
|
get: () => Promise.resolve(void 0),
|
|
1355
1667
|
set: async (keyId, key) => await options.keyCache?.set(keyId, key)
|
|
1356
1668
|
}
|
|
1357
|
-
});
|
|
1669
|
+
}, messageDigestCache, rawProofCandidate);
|
|
1358
1670
|
}
|
|
1359
1671
|
logger.debug("Failed to verify the proof with the fetched key {keyId}:\n{proof}", {
|
|
1360
1672
|
keyId: proof.verificationMethodId.href,
|
|
@@ -1362,6 +1674,228 @@ async function verifyProofInternal(jsonLd, proof, options) {
|
|
|
1362
1674
|
});
|
|
1363
1675
|
return null;
|
|
1364
1676
|
}
|
|
1677
|
+
const AS_NAMESPACE = "https://www.w3.org/ns/activitystreams#";
|
|
1678
|
+
const FEP_2277_ACTOR_PROPERTIES = ["http://www.w3.org/ns/ldp#inbox", `${AS_NAMESPACE}outbox`];
|
|
1679
|
+
const FEP_2277_COLLECTION_PROPERTIES = [
|
|
1680
|
+
"items",
|
|
1681
|
+
"orderedItems",
|
|
1682
|
+
"totalItems",
|
|
1683
|
+
"partOf",
|
|
1684
|
+
"first",
|
|
1685
|
+
"last",
|
|
1686
|
+
"next",
|
|
1687
|
+
"prev",
|
|
1688
|
+
"current"
|
|
1689
|
+
].map((property) => AS_NAMESPACE + property);
|
|
1690
|
+
const PORTABLE_OBJECT_ID_PATTERN = /^ap(?:\+ef61)?:\/\//i;
|
|
1691
|
+
const FUNCTIONAL_PROOF_PROPERTIES = [
|
|
1692
|
+
`${SECURITY_NAMESPACE}cryptosuite`,
|
|
1693
|
+
SECURITY_VERIFICATION_METHOD,
|
|
1694
|
+
`${SECURITY_NAMESPACE}proofPurpose`,
|
|
1695
|
+
`${SECURITY_NAMESPACE}proofValue`,
|
|
1696
|
+
"http://purl.org/dc/terms/created"
|
|
1697
|
+
];
|
|
1698
|
+
function isJsonLdNode(value) {
|
|
1699
|
+
return typeof value === "object" && value != null && !Array.isArray(value);
|
|
1700
|
+
}
|
|
1701
|
+
function hasValidPortableProofShape(proofValue) {
|
|
1702
|
+
if (!isJsonLdNode(proofValue)) return false;
|
|
1703
|
+
let proofNode = proofValue;
|
|
1704
|
+
if ("@graph" in proofNode) {
|
|
1705
|
+
const graph = proofNode["@graph"];
|
|
1706
|
+
if (!Array.isArray(graph) || graph.length !== 1 || !isJsonLdNode(graph[0])) return false;
|
|
1707
|
+
proofNode = graph[0];
|
|
1708
|
+
}
|
|
1709
|
+
const types = proofNode["@type"];
|
|
1710
|
+
return Array.isArray(types) && types.length === 1 && types[0] === DATA_INTEGRITY_PROOF && FUNCTIONAL_PROOF_PROPERTIES.every((property) => {
|
|
1711
|
+
const values = proofNode[property];
|
|
1712
|
+
return Array.isArray(values) && values.length === 1 && !(isJsonLdNode(values[0]) && "@list" in values[0]);
|
|
1713
|
+
});
|
|
1714
|
+
}
|
|
1715
|
+
function classifyFep2277CoreType(node) {
|
|
1716
|
+
if (FEP_2277_ACTOR_PROPERTIES.every((property) => property in node)) return "actor";
|
|
1717
|
+
if (`${SECURITY_NAMESPACE}publicKeyMultibase` in node) return "verificationMethod";
|
|
1718
|
+
if (`${SECURITY_NAMESPACE}publicKeyPem` in node) return "publicKey";
|
|
1719
|
+
if (`${AS_NAMESPACE}href` in node) return "link";
|
|
1720
|
+
if (`${AS_NAMESPACE}actor` in node) return "activity";
|
|
1721
|
+
if (FEP_2277_COLLECTION_PROPERTIES.some((property) => property in node)) return "collection";
|
|
1722
|
+
return "object";
|
|
1723
|
+
}
|
|
1724
|
+
async function expandPortableObjectRoot(jsonLd, contextLoader) {
|
|
1725
|
+
if (!isJsonLdNode(jsonLd)) throw new TypeError("Expected a single JSON-LD object.");
|
|
1726
|
+
const loadedContexts = /* @__PURE__ */ new Map();
|
|
1727
|
+
const loader = getNormalizationContextLoader(contextLoader);
|
|
1728
|
+
const recordingLoader = async (url, options) => {
|
|
1729
|
+
const remoteDocument = await loader(url, options);
|
|
1730
|
+
const key = URL.canParse(url) ? new URL(url).href : url;
|
|
1731
|
+
loadedContexts.set(key, structuredClone(remoteDocument));
|
|
1732
|
+
return remoteDocument;
|
|
1733
|
+
};
|
|
1734
|
+
const expanded = await jsonld.expand(jsonLd, {
|
|
1735
|
+
documentLoader: recordingLoader,
|
|
1736
|
+
keepFreeFloatingNodes: true
|
|
1737
|
+
});
|
|
1738
|
+
if (expanded.length !== 1 || !isJsonLdNode(expanded[0])) throw new TypeError("Expected a single JSON-LD object.");
|
|
1739
|
+
return {
|
|
1740
|
+
root: expanded[0],
|
|
1741
|
+
proofContextLoader: async (url, options) => {
|
|
1742
|
+
const key = URL.canParse(url) ? new URL(url).href : url;
|
|
1743
|
+
const remoteDocument = loadedContexts.get(key);
|
|
1744
|
+
if (remoteDocument != null) return structuredClone(remoteDocument);
|
|
1745
|
+
return await preloadedOnlyDocumentLoader(url, options);
|
|
1746
|
+
}
|
|
1747
|
+
};
|
|
1748
|
+
}
|
|
1749
|
+
/**
|
|
1750
|
+
* Verifies the FEP-ef61 Object Integrity Proof policy for a portable object.
|
|
1751
|
+
*
|
|
1752
|
+
* This applies the FEP-2277 core-type classification to the top-level JSON-LD
|
|
1753
|
+
* node. Portable actors, activities, and objects require proofs. A portable
|
|
1754
|
+
* collection without a proof is reported separately so a caller can apply a
|
|
1755
|
+
* gateway trust policy. Embedded portable objects are not traversed.
|
|
1756
|
+
*
|
|
1757
|
+
* Every proof must use a DID URL whose DID matches the portable object's
|
|
1758
|
+
* authority, and every proof must pass {@link verifyProof}.
|
|
1759
|
+
*
|
|
1760
|
+
* @param jsonLd The JSON-LD document to verify.
|
|
1761
|
+
* @param options Additional options. See also
|
|
1762
|
+
* {@link VerifyPortableObjectProofOptions}.
|
|
1763
|
+
* @returns The detailed portable proof-policy result.
|
|
1764
|
+
* @throws {TypeError} If the input is not a single JSON-LD object or has a
|
|
1765
|
+
* malformed portable ID.
|
|
1766
|
+
* @since 2.4.0
|
|
1767
|
+
*/
|
|
1768
|
+
async function verifyPortableObjectProof(jsonLd, options = {}) {
|
|
1769
|
+
if (isJsonLdNode(jsonLd) && typeof jsonLd["@id"] === "string" && !PORTABLE_OBJECT_ID_PATTERN.test(jsonLd["@id"])) return {
|
|
1770
|
+
verified: false,
|
|
1771
|
+
reason: { type: "notPortableObject" }
|
|
1772
|
+
};
|
|
1773
|
+
const { root, proofContextLoader } = await expandPortableObjectRoot(jsonLd, options.contextLoader);
|
|
1774
|
+
const id = root["@id"];
|
|
1775
|
+
if (typeof id !== "string" || !PORTABLE_OBJECT_ID_PATTERN.test(id)) return {
|
|
1776
|
+
verified: false,
|
|
1777
|
+
reason: { type: "notPortableObject" }
|
|
1778
|
+
};
|
|
1779
|
+
const objectId = parseIri(id);
|
|
1780
|
+
getFe34Origin(objectId);
|
|
1781
|
+
const objectType = classifyFep2277CoreType(root);
|
|
1782
|
+
if (objectType === "verificationMethod" || objectType === "publicKey" || objectType === "link") return {
|
|
1783
|
+
verified: false,
|
|
1784
|
+
reason: {
|
|
1785
|
+
type: "unsupportedObjectType",
|
|
1786
|
+
objectType
|
|
1787
|
+
}
|
|
1788
|
+
};
|
|
1789
|
+
const proofValues = root[SECURITY_PROOF];
|
|
1790
|
+
if (proofValues == null || Array.isArray(proofValues) && proofValues.length < 1) return objectType === "collection" ? {
|
|
1791
|
+
verified: false,
|
|
1792
|
+
reason: { type: "unsecuredCollection" }
|
|
1793
|
+
} : {
|
|
1794
|
+
verified: false,
|
|
1795
|
+
reason: { type: "missingProof" }
|
|
1796
|
+
};
|
|
1797
|
+
if (!Array.isArray(proofValues)) return {
|
|
1798
|
+
verified: false,
|
|
1799
|
+
reason: {
|
|
1800
|
+
type: "invalidProof",
|
|
1801
|
+
proofIndex: 0
|
|
1802
|
+
}
|
|
1803
|
+
};
|
|
1804
|
+
const rawProofValues = isJsonLdNode(jsonLd) ? await getRawProofValues(jsonLd, proofContextLoader) : [];
|
|
1805
|
+
const proofs = [];
|
|
1806
|
+
for (let proofIndex = 0; proofIndex < proofValues.length; proofIndex++) {
|
|
1807
|
+
const proofValue = proofValues[proofIndex];
|
|
1808
|
+
if (!hasValidPortableProofShape(proofValue)) return {
|
|
1809
|
+
verified: false,
|
|
1810
|
+
reason: {
|
|
1811
|
+
type: "invalidProof",
|
|
1812
|
+
proofIndex
|
|
1813
|
+
}
|
|
1814
|
+
};
|
|
1815
|
+
let proof;
|
|
1816
|
+
try {
|
|
1817
|
+
proof = await DataIntegrityProof.fromJsonLd(proofValue, options);
|
|
1818
|
+
} catch {
|
|
1819
|
+
return {
|
|
1820
|
+
verified: false,
|
|
1821
|
+
reason: {
|
|
1822
|
+
type: "invalidProof",
|
|
1823
|
+
proofIndex
|
|
1824
|
+
}
|
|
1825
|
+
};
|
|
1826
|
+
}
|
|
1827
|
+
proofs.push(proof);
|
|
1828
|
+
}
|
|
1829
|
+
for (let proofIndex = 0; proofIndex < proofs.length; proofIndex++) {
|
|
1830
|
+
const verificationMethod = proofs[proofIndex].verificationMethodId;
|
|
1831
|
+
if (verificationMethod == null) return {
|
|
1832
|
+
verified: false,
|
|
1833
|
+
reason: {
|
|
1834
|
+
type: "invalidProof",
|
|
1835
|
+
proofIndex
|
|
1836
|
+
}
|
|
1837
|
+
};
|
|
1838
|
+
if (verificationMethod.protocol !== "did:") return {
|
|
1839
|
+
verified: false,
|
|
1840
|
+
reason: {
|
|
1841
|
+
type: "unsupportedVerificationMethod",
|
|
1842
|
+
proofIndex,
|
|
1843
|
+
verificationMethod
|
|
1844
|
+
}
|
|
1845
|
+
};
|
|
1846
|
+
try {
|
|
1847
|
+
getFe34Origin(verificationMethod);
|
|
1848
|
+
} catch (error) {
|
|
1849
|
+
if (!(error instanceof TypeError)) throw error;
|
|
1850
|
+
return {
|
|
1851
|
+
verified: false,
|
|
1852
|
+
reason: {
|
|
1853
|
+
type: "unsupportedVerificationMethod",
|
|
1854
|
+
proofIndex,
|
|
1855
|
+
verificationMethod
|
|
1856
|
+
}
|
|
1857
|
+
};
|
|
1858
|
+
}
|
|
1859
|
+
if (!haveSameFe34Origin(objectId, verificationMethod)) return {
|
|
1860
|
+
verified: false,
|
|
1861
|
+
reason: {
|
|
1862
|
+
type: "verificationMethodMismatch",
|
|
1863
|
+
proofIndex,
|
|
1864
|
+
objectId,
|
|
1865
|
+
verificationMethod
|
|
1866
|
+
}
|
|
1867
|
+
};
|
|
1868
|
+
}
|
|
1869
|
+
const keys = [];
|
|
1870
|
+
const rawProofCandidatePool = {
|
|
1871
|
+
candidates: await parseRawProofCandidates(jsonLd, rawProofValues, options, proofContextLoader),
|
|
1872
|
+
used: /* @__PURE__ */ new Set()
|
|
1873
|
+
};
|
|
1874
|
+
const messageDigestCache = { proofContextLoader };
|
|
1875
|
+
for (let proofIndex = 0; proofIndex < proofs.length; proofIndex++) {
|
|
1876
|
+
const rawProofCandidate = takeRawProofCandidate(rawProofCandidatePool, proofs[proofIndex]);
|
|
1877
|
+
if (rawProofCandidate == null) return {
|
|
1878
|
+
verified: false,
|
|
1879
|
+
reason: {
|
|
1880
|
+
type: "invalidProof",
|
|
1881
|
+
proofIndex
|
|
1882
|
+
}
|
|
1883
|
+
};
|
|
1884
|
+
const key = await verifyProofWithMessageDigestCache(jsonLd, proofs[proofIndex], options, messageDigestCache, rawProofCandidate);
|
|
1885
|
+
if (key == null) return {
|
|
1886
|
+
verified: false,
|
|
1887
|
+
reason: {
|
|
1888
|
+
type: "invalidProof",
|
|
1889
|
+
proofIndex
|
|
1890
|
+
}
|
|
1891
|
+
};
|
|
1892
|
+
keys.push(key);
|
|
1893
|
+
}
|
|
1894
|
+
return {
|
|
1895
|
+
verified: true,
|
|
1896
|
+
keys
|
|
1897
|
+
};
|
|
1898
|
+
}
|
|
1365
1899
|
/**
|
|
1366
1900
|
* Verifies the given object. It will verify all the proofs in the object,
|
|
1367
1901
|
* and succeed only if all the proofs are valid and all attributions and
|
|
@@ -1383,10 +1917,48 @@ async function verifyObject(cls, jsonLd, options = {}) {
|
|
|
1383
1917
|
"proof"
|
|
1384
1918
|
]);
|
|
1385
1919
|
const object = await cls.fromJsonLd(jsonLd, options);
|
|
1920
|
+
const defaultDocumentLoader = getDocumentLoader();
|
|
1921
|
+
const proofContextLoader = options.contextLoader ?? defaultDocumentLoader;
|
|
1386
1922
|
const attributions = new Set(object.attributionIds.map((uri) => uri.href));
|
|
1387
1923
|
if (object instanceof Activity) for (const uri of object.actorIds) attributions.add(uri.href);
|
|
1388
|
-
|
|
1389
|
-
|
|
1924
|
+
const rawProofValues = isJsonLdNode(jsonLd) ? await getRawProofValues(jsonLd, proofContextLoader) : [];
|
|
1925
|
+
const rawProofCandidates = isJsonLdNode(jsonLd) ? await parseRawProofCandidates(jsonLd, rawProofValues, options, proofContextLoader) : [];
|
|
1926
|
+
const rawProofCandidatePool = {
|
|
1927
|
+
candidates: rawProofCandidates,
|
|
1928
|
+
used: /* @__PURE__ */ new Set()
|
|
1929
|
+
};
|
|
1930
|
+
const baseDocumentLoader = options.documentLoader ?? defaultDocumentLoader;
|
|
1931
|
+
const hydratedCandidates = /* @__PURE__ */ new Set();
|
|
1932
|
+
const proofDocumentLoader = async (url, loaderOptions) => {
|
|
1933
|
+
const remoteDocument = await baseDocumentLoader(url, loaderOptions);
|
|
1934
|
+
const reference = normalizeDocumentUrl(url);
|
|
1935
|
+
const candidateIndex = rawProofCandidates.findIndex((candidate, index) => !hydratedCandidates.has(index) && !rawProofCandidatePool.used.has(index) && candidate.reference === reference);
|
|
1936
|
+
if (candidateIndex >= 0) {
|
|
1937
|
+
hydratedCandidates.add(candidateIndex);
|
|
1938
|
+
let parsed = null;
|
|
1939
|
+
try {
|
|
1940
|
+
parsed = await DataIntegrityProof.fromJsonLd(remoteDocument.document, {
|
|
1941
|
+
documentLoader: baseDocumentLoader,
|
|
1942
|
+
contextLoader: proofContextLoader,
|
|
1943
|
+
tracerProvider: options.tracerProvider,
|
|
1944
|
+
baseUrl: parseIri(remoteDocument.documentUrl)
|
|
1945
|
+
});
|
|
1946
|
+
} catch {}
|
|
1947
|
+
rawProofCandidates[candidateIndex] = {
|
|
1948
|
+
value: structuredClone(remoteDocument.document),
|
|
1949
|
+
proof: parsed,
|
|
1950
|
+
reference
|
|
1951
|
+
};
|
|
1952
|
+
}
|
|
1953
|
+
return remoteDocument;
|
|
1954
|
+
};
|
|
1955
|
+
for await (const proof of object.getProofs({
|
|
1956
|
+
...options,
|
|
1957
|
+
documentLoader: proofDocumentLoader
|
|
1958
|
+
})) {
|
|
1959
|
+
const rawProofCandidate = takeRawProofCandidate(rawProofCandidatePool, proof);
|
|
1960
|
+
if (rawProofCandidate == null) return null;
|
|
1961
|
+
const key = await verifyProofWithMessageDigestCache(jsonLd, proof, options, { proofContextLoader }, rawProofCandidate);
|
|
1390
1962
|
if (key === null) return null;
|
|
1391
1963
|
if (proof.verificationMethodId == null) return null;
|
|
1392
1964
|
if (key.controllerId == null) {
|
|
@@ -1412,4 +1984,4 @@ function hasCryptographicOrigin(iri) {
|
|
|
1412
1984
|
return /^did:/i.test(iri) || /^ap(?:\+ef61)?:\/\//i.test(iri);
|
|
1413
1985
|
}
|
|
1414
1986
|
//#endregion
|
|
1415
|
-
export {
|
|
1987
|
+
export { verifyJsonLd as C, verifyCompactJsonLd as S, wrapContextLoaderForJsonLd as T, hasSignature as _, verifyPortableObjectProof as a, isInvalidUrlTypeError as b, doesActorOwnKey as c, assertSafeJsonLd as d, attachSignature as f, getNormalizationContextLoader as g, detachSignature as h, verifyObject as i, getKeyOwner as l, createSignature as m, hasProofLike as n, verifyProof as o, compactJsonLd as p, signObject as r, normalizeOutgoingActivityJsonLd as s, createProof as t, InvalidContextReferenceError as u, hasSignatureLike as v, verifySignature as w, signJsonLd as x, isClearlyMalformedContextReference as y };
|