@fedify/fedify 2.4.0-dev.1727 → 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/dist/{builder-f5-YwHfj.mjs → builder-Bw6LVKGw.mjs} +2 -2
- package/dist/compat/transformers.test.mjs +1 -1
- package/dist/{deno-Deyoxesx.mjs → deno-tRdp8MCl.mjs} +1 -1
- package/dist/{docloader-B3kuDBg9.mjs → docloader-Dg_7gMC7.mjs} +2 -2
- package/dist/federation/builder.test.mjs +1 -1
- package/dist/federation/handler.test.mjs +3 -3
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/metrics.test.mjs +1 -1
- package/dist/federation/middleware.test.mjs +7 -7
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.js +1 -1
- package/dist/federation/send.test.mjs +3 -3
- package/dist/federation/tasks/enqueue.test.mjs +1 -1
- package/dist/federation/tasks/tasks.test.mjs +2 -2
- package/dist/federation/temporal.test.mjs +1 -1
- package/dist/federation/webfinger.test.mjs +1 -1
- package/dist/{federation-BzRjeOky.cjs → federation-BJBpt_Am.cjs} +3 -3
- package/dist/{http-Bw6s_dOB.js → http-BIxGa6AG.js} +1 -1
- package/dist/{http-TjhZfiSN.mjs → http-Day1kcpT.mjs} +3 -3
- package/dist/{http-AyKngJ_h.cjs → http-DggG1scN.cjs} +1 -1
- package/dist/{key-b4kf4yZT.mjs → key-igh0AMsL.mjs} +2 -2
- package/dist/{kv-cache-CQsh-KUw.js → kv-cache-BIAB0HKx.js} +1 -1
- package/dist/{kv-cache-CpILvhUV.mjs → kv-cache-Bz6aQOOX.mjs} +1 -1
- package/dist/{kv-cache-BTVaZrf-.cjs → kv-cache-C4U_uBqD.cjs} +1 -1
- package/dist/{ld-CKujkAfG.mjs → ld-DnT9sCWa.mjs} +3 -3
- package/dist/{metrics-AlCGTDrr.mjs → metrics-DbdNnWIt.mjs} +1 -1
- package/dist/{middleware-LPbZz6-1.mjs → middleware-B6GweVcg.mjs} +1 -1
- package/dist/{middleware-CZcuedl-.js → middleware-CnsPXG0O.js} +3 -3
- package/dist/{middleware-C9Y5lLZN.mjs → middleware-DPIS4fsB.mjs} +12 -12
- package/dist/{mod-CuDDaIIt.d.cts → mod-CnGLNoVw.d.cts} +85 -3
- package/dist/{mod-D-IhOEOP.d.ts → mod-DzRXACM5.d.ts} +85 -3
- package/dist/mod.cjs +5 -4
- package/dist/mod.d.cts +3 -3
- package/dist/mod.d.ts +3 -3
- package/dist/mod.js +5 -5
- package/dist/nodeinfo/handler.test.mjs +1 -1
- package/dist/{owner-D1O8v8FB.mjs → owner-B3ef8GfJ.mjs} +2 -2
- package/dist/{proof-BTBolSnd.js → proof-BBLQXRWw.js} +603 -31
- package/dist/{proof-DcSm-2eG.cjs → proof-Cqk2TcLX.cjs} +607 -29
- package/dist/proof-DVoDZ8d7.mjs +890 -0
- package/dist/{send-DG7JG280.mjs → send-PbBSZ7Qk.mjs} +3 -3
- package/dist/sig/http.test.mjs +2 -2
- package/dist/sig/key.test.mjs +1 -1
- package/dist/sig/ld.test.mjs +2 -2
- package/dist/sig/mod.cjs +3 -2
- package/dist/sig/mod.d.cts +2 -2
- package/dist/sig/mod.d.ts +2 -2
- package/dist/sig/mod.js +3 -3
- package/dist/sig/owner.test.mjs +1 -1
- package/dist/sig/proof.test.mjs +956 -8
- package/dist/{temporal-DA3jx8x1.mjs → temporal-DLQIjuTn.mjs} +1 -1
- package/dist/utils/docloader.test.mjs +2 -2
- package/dist/utils/kv-cache.test.mjs +1 -1
- package/dist/utils/mod.cjs +1 -1
- package/dist/utils/mod.js +1 -1
- package/package.json +6 -6
- package/dist/proof-DeMR7Akd.mjs +0 -316
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const { URLPattern } = require("urlpattern-polyfill");
|
|
2
2
|
const require_rolldown_runtime = require("./rolldown-runtime-Dmm3t92W.cjs");
|
|
3
|
-
const require_http = require("./http-
|
|
3
|
+
const require_http = require("./http-DggG1scN.cjs");
|
|
4
4
|
let _logtape_logtape = require("@logtape/logtape");
|
|
5
5
|
let _fedify_vocab = require("@fedify/vocab");
|
|
6
6
|
let _opentelemetry_api = require("@opentelemetry/api");
|
|
@@ -1129,6 +1129,16 @@ const logger = (0, _logtape_logtape.getLogger)([
|
|
|
1129
1129
|
"sig",
|
|
1130
1130
|
"proof"
|
|
1131
1131
|
]);
|
|
1132
|
+
const SECURITY_NAMESPACE = "https://w3id.org/security#";
|
|
1133
|
+
const SECURITY_PROOF = `${SECURITY_NAMESPACE}proof`;
|
|
1134
|
+
const SECURITY_PROOF_VALUE = `${SECURITY_NAMESPACE}proofValue`;
|
|
1135
|
+
const DATA_INTEGRITY_PROOF = `${SECURITY_NAMESPACE}DataIntegrityProof`;
|
|
1136
|
+
const SECURITY_VERIFICATION_METHOD = `${SECURITY_NAMESPACE}verificationMethod`;
|
|
1137
|
+
const SECURITY_EXPIRATION = `${SECURITY_NAMESPACE}expiration`;
|
|
1138
|
+
const SECURITY_DOMAIN = `${SECURITY_NAMESPACE}domain`;
|
|
1139
|
+
const SECURITY_CHALLENGE = `${SECURITY_NAMESPACE}challenge`;
|
|
1140
|
+
const SECURITY_NONCE = `${SECURITY_NAMESPACE}nonce`;
|
|
1141
|
+
const SECURITY_PREVIOUS_PROOF = `${SECURITY_NAMESPACE}previousProof`;
|
|
1132
1142
|
/**
|
|
1133
1143
|
* Checks if the given JSON-LD document has a DataIntegrityProof-like object,
|
|
1134
1144
|
* without fully deserializing it into vocabulary classes.
|
|
@@ -1240,15 +1250,21 @@ async function signObject(object, privateKey, keyId, options = {}) {
|
|
|
1240
1250
|
}
|
|
1241
1251
|
/**
|
|
1242
1252
|
* Verifies the given proof for the object.
|
|
1243
|
-
* @param jsonLd The JSON-LD object to verify the proof for.
|
|
1244
|
-
*
|
|
1253
|
+
* @param jsonLd The JSON-LD object to verify the proof for. Its proof
|
|
1254
|
+
* properties are excluded from the message, but matching proof
|
|
1255
|
+
* occurrences are used to authenticate the received proof
|
|
1256
|
+
* configuration.
|
|
1245
1257
|
* @param proof The proof to verify.
|
|
1246
1258
|
* @param options Additional options. See also {@link VerifyProofOptions}.
|
|
1247
1259
|
* @returns The public key that was used to sign the proof, or `null` if the
|
|
1248
1260
|
* proof is invalid.
|
|
1249
1261
|
* @since 0.10.0
|
|
1262
|
+
* @since 2.4.0 Matching proof configurations are authenticated.
|
|
1250
1263
|
*/
|
|
1251
1264
|
async function verifyProof(jsonLd, proof, options = {}) {
|
|
1265
|
+
return await verifyProofWithMessageDigestCache(jsonLd, proof, options);
|
|
1266
|
+
}
|
|
1267
|
+
async function verifyProofWithMessageDigestCache(jsonLd, proof, options, messageDigestCache = {}, rawProofCandidate) {
|
|
1252
1268
|
return await (options.tracerProvider ?? _opentelemetry_api.trace.getTracerProvider()).getTracer(require_http.name, require_http.version).startActiveSpan("object_integrity_proofs.verify", async (span) => {
|
|
1253
1269
|
const start = performance.now();
|
|
1254
1270
|
let verified = false;
|
|
@@ -1260,7 +1276,7 @@ async function verifyProof(jsonLd, proof, options = {}) {
|
|
|
1260
1276
|
if (proof.proofValue != null) span.setAttribute("object_integrity_proofs.signature", (0, byte_encodings_hex.encodeHex)(proof.proofValue));
|
|
1261
1277
|
}
|
|
1262
1278
|
try {
|
|
1263
|
-
const key = await verifyProofInternal(jsonLd, proof, options);
|
|
1279
|
+
const key = await verifyProofInternal(jsonLd, proof, options, messageDigestCache, rawProofCandidate);
|
|
1264
1280
|
if (key == null) span.setStatus({ code: _opentelemetry_api.SpanStatusCode.ERROR });
|
|
1265
1281
|
else verified = true;
|
|
1266
1282
|
return key;
|
|
@@ -1278,23 +1294,313 @@ async function verifyProof(jsonLd, proof, options = {}) {
|
|
|
1278
1294
|
}
|
|
1279
1295
|
});
|
|
1280
1296
|
}
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
const
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1297
|
+
function expandContextPropertyIri(activeContext, key) {
|
|
1298
|
+
const termId = _fedify_vocab_runtime_jsonld.default.getContextValue(activeContext, key, "@id");
|
|
1299
|
+
if (termId != null) return termId;
|
|
1300
|
+
const colon = key.indexOf(":");
|
|
1301
|
+
if (colon < 1) return key;
|
|
1302
|
+
const prefix = key.substring(0, colon);
|
|
1303
|
+
const suffix = key.substring(colon + 1);
|
|
1304
|
+
if (prefix === "_" || suffix.startsWith("//")) return key;
|
|
1305
|
+
const mapping = _fedify_vocab_runtime_jsonld.default.getContextValue(activeContext, prefix);
|
|
1306
|
+
if (typeof mapping === "object" && mapping != null && mapping._prefix === true && typeof mapping["@id"] === "string") return mapping["@id"] + suffix;
|
|
1307
|
+
return key;
|
|
1308
|
+
}
|
|
1309
|
+
async function getJsonLdPropertyNames(jsonLd, propertyIri, defaults, documentLoader = preloadedOnlyDocumentLoader, inheritedContext, rejectOnContextError = false) {
|
|
1310
|
+
const names = new Set(defaults);
|
|
1311
|
+
const context = jsonLd["@context"] ?? inheritedContext;
|
|
1312
|
+
if (context == null) return names;
|
|
1313
|
+
try {
|
|
1314
|
+
const options = { documentLoader };
|
|
1315
|
+
let activeContext = await _fedify_vocab_runtime_jsonld.default.processContext(null, null, options);
|
|
1316
|
+
activeContext = await _fedify_vocab_runtime_jsonld.default.processContext(activeContext, context, options);
|
|
1317
|
+
const typeScopedContext = activeContext;
|
|
1318
|
+
for (const key of globalThis.Object.keys(jsonLd).sort()) {
|
|
1319
|
+
if (key !== "@type" && expandContextPropertyIri(activeContext, key) !== "@type") continue;
|
|
1320
|
+
const value = jsonLd[key];
|
|
1321
|
+
const types = Array.isArray(value) ? value.slice().sort() : [value];
|
|
1322
|
+
for (const type of types) {
|
|
1323
|
+
if (typeof type !== "string") continue;
|
|
1324
|
+
const scopedContext = _fedify_vocab_runtime_jsonld.default.getContextValue(typeScopedContext, type, "@context");
|
|
1325
|
+
if (scopedContext != null) activeContext = await _fedify_vocab_runtime_jsonld.default.processContext(activeContext, scopedContext, options);
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
for (const key of globalThis.Object.keys(jsonLd)) if (expandContextPropertyIri(activeContext, key) === propertyIri) names.add(key);
|
|
1329
|
+
} catch {
|
|
1330
|
+
if (rejectOnContextError) return null;
|
|
1331
|
+
}
|
|
1332
|
+
return names;
|
|
1333
|
+
}
|
|
1334
|
+
async function getProofPropertyNames(jsonLd, documentLoader = preloadedOnlyDocumentLoader) {
|
|
1335
|
+
return await getJsonLdPropertyNames(jsonLd, SECURITY_PROOF, ["proof", SECURITY_PROOF], documentLoader) ?? /* @__PURE__ */ new Set(["proof", SECURITY_PROOF]);
|
|
1336
|
+
}
|
|
1337
|
+
async function createProofMessageDigests(jsonLd, proofContextLoader, context) {
|
|
1338
|
+
const msg = { ...jsonLd };
|
|
1339
|
+
for (const property of await getProofPropertyNames(msg, proofContextLoader)) delete msg[property];
|
|
1340
|
+
if (context != null) msg["@context"] = structuredClone(context);
|
|
1292
1341
|
const encoder = new TextEncoder();
|
|
1293
|
-
const
|
|
1342
|
+
const digest = async (value) => {
|
|
1343
|
+
const bytes = encoder.encode((0, json_canon.default)(value));
|
|
1344
|
+
return await crypto.subtle.digest("SHA-256", bytes);
|
|
1345
|
+
};
|
|
1346
|
+
const onWire = await digest(msg);
|
|
1347
|
+
let normalizedPromise;
|
|
1348
|
+
return {
|
|
1349
|
+
onWire,
|
|
1350
|
+
normalized() {
|
|
1351
|
+
normalizedPromise ??= (async () => {
|
|
1352
|
+
const normalized = await normalizeOutgoingActivityJsonLd(msg, preloadedOnlyDocumentLoader);
|
|
1353
|
+
return normalized === msg ? null : await digest(normalized);
|
|
1354
|
+
})();
|
|
1355
|
+
return normalizedPromise;
|
|
1356
|
+
}
|
|
1357
|
+
};
|
|
1358
|
+
}
|
|
1359
|
+
function contextValues(context) {
|
|
1360
|
+
return Array.isArray(context) ? context : [context];
|
|
1361
|
+
}
|
|
1362
|
+
function equalJsonValues(left, right) {
|
|
1363
|
+
try {
|
|
1364
|
+
return (0, json_canon.default)(left) === (0, json_canon.default)(right);
|
|
1365
|
+
} catch {
|
|
1366
|
+
return false;
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
function contextStartsWith(documentContext, proofContext) {
|
|
1370
|
+
if (documentContext == null) return false;
|
|
1371
|
+
const documentValues = contextValues(documentContext);
|
|
1372
|
+
const proofValues = contextValues(proofContext);
|
|
1373
|
+
return proofValues.length <= documentValues.length && proofValues.every((value, index) => equalJsonValues(value, documentValues[index]));
|
|
1374
|
+
}
|
|
1375
|
+
function appendProofValues(values, value) {
|
|
1376
|
+
if (Array.isArray(value)) for (const item of value) appendProofValues(values, item);
|
|
1377
|
+
else if (isJsonLdNode(value) && Array.isArray(value["@graph"])) for (const item of value["@graph"]) appendProofValues(values, item);
|
|
1378
|
+
else values.push(value);
|
|
1379
|
+
}
|
|
1380
|
+
async function getRawProofValues(jsonLd, documentLoader) {
|
|
1381
|
+
const propertyNames = await getProofPropertyNames(jsonLd, documentLoader);
|
|
1382
|
+
const values = [];
|
|
1383
|
+
for (const [property, value] of globalThis.Object.entries(jsonLd)) if (propertyNames.has(property)) appendProofValues(values, value);
|
|
1384
|
+
return values;
|
|
1385
|
+
}
|
|
1386
|
+
function sameBytes(left, right) {
|
|
1387
|
+
if (left == null || right == null) return left === right;
|
|
1388
|
+
return left.length === right.length && left.every((value, index) => value === right[index]);
|
|
1389
|
+
}
|
|
1390
|
+
function sameProof(left, right) {
|
|
1391
|
+
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();
|
|
1392
|
+
}
|
|
1393
|
+
function normalizeDocumentUrl(url) {
|
|
1394
|
+
try {
|
|
1395
|
+
return (0, _fedify_vocab_runtime.formatIri)((0, _fedify_vocab_runtime.parseIri)(url));
|
|
1396
|
+
} catch {
|
|
1397
|
+
return URL.canParse(url) ? new URL(url).href : url;
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
function getRawProofReference(value) {
|
|
1401
|
+
const node = Array.isArray(value) && value.length === 1 ? value[0] : value;
|
|
1402
|
+
if (typeof node === "string") return normalizeDocumentUrl(node);
|
|
1403
|
+
if (!isJsonLdNode(node)) return void 0;
|
|
1404
|
+
const id = node["@id"] ?? node.id;
|
|
1405
|
+
return typeof id === "string" ? normalizeDocumentUrl(id) : void 0;
|
|
1406
|
+
}
|
|
1407
|
+
async function parseRawProofCandidates(jsonLd, values, options, documentLoader) {
|
|
1408
|
+
const candidates = [];
|
|
1409
|
+
for (const value of values) {
|
|
1410
|
+
let parsed = null;
|
|
1411
|
+
if (isJsonLdNode(value)) {
|
|
1412
|
+
const proofJsonLd = value["@context"] == null && jsonLd["@context"] != null ? {
|
|
1413
|
+
"@context": jsonLd["@context"],
|
|
1414
|
+
...value
|
|
1415
|
+
} : value;
|
|
1416
|
+
try {
|
|
1417
|
+
parsed = await _fedify_vocab.DataIntegrityProof.fromJsonLd(proofJsonLd, {
|
|
1418
|
+
...options,
|
|
1419
|
+
contextLoader: documentLoader
|
|
1420
|
+
});
|
|
1421
|
+
} catch {}
|
|
1422
|
+
}
|
|
1423
|
+
candidates.push({
|
|
1424
|
+
value,
|
|
1425
|
+
proof: parsed,
|
|
1426
|
+
reference: getRawProofReference(value) ?? parsed?.id?.href
|
|
1427
|
+
});
|
|
1428
|
+
}
|
|
1429
|
+
return candidates;
|
|
1430
|
+
}
|
|
1431
|
+
async function findRawProofCandidate(jsonLd, proof, options, documentLoader) {
|
|
1432
|
+
const candidates = await parseRawProofCandidates(jsonLd, await getRawProofValues(jsonLd, documentLoader), options, documentLoader);
|
|
1433
|
+
const matches = [];
|
|
1434
|
+
for (const candidate of candidates) if (candidate.proof != null && sameProof(candidate.proof, proof)) matches.push(candidate);
|
|
1435
|
+
if (matches.length < 1) return void 0;
|
|
1436
|
+
const first = matches[0];
|
|
1437
|
+
const configurations = await Promise.all(matches.map((candidate) => normalizeProofConfiguration(candidate.value, jsonLd["@context"], documentLoader)));
|
|
1438
|
+
const firstConfiguration = configurations[0];
|
|
1439
|
+
return firstConfiguration != null && configurations.every((configuration) => configuration != null && equalJsonValues(configuration.value, firstConfiguration.value)) ? first : null;
|
|
1440
|
+
}
|
|
1441
|
+
function takeRawProofCandidate(pool, proof) {
|
|
1442
|
+
for (let index = 0; index < pool.candidates.length; index++) {
|
|
1443
|
+
if (pool.used.has(index)) continue;
|
|
1444
|
+
const candidate = pool.candidates[index];
|
|
1445
|
+
if (candidate.proof != null && sameProof(candidate.proof, proof)) {
|
|
1446
|
+
pool.used.add(index);
|
|
1447
|
+
return candidate;
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
const STANDARD_COMPACT_PROOF_PROPERTIES = /* @__PURE__ */ new Set([
|
|
1452
|
+
"@context",
|
|
1453
|
+
"type",
|
|
1454
|
+
"cryptosuite",
|
|
1455
|
+
"verificationMethod",
|
|
1456
|
+
"proofPurpose",
|
|
1457
|
+
"proofValue",
|
|
1458
|
+
"created"
|
|
1459
|
+
]);
|
|
1460
|
+
const STANDARD_EXPANDED_PROOF_PROPERTIES = /* @__PURE__ */ new Set([
|
|
1461
|
+
"@context",
|
|
1462
|
+
"@type",
|
|
1463
|
+
`${SECURITY_NAMESPACE}cryptosuite`,
|
|
1464
|
+
SECURITY_VERIFICATION_METHOD,
|
|
1465
|
+
`${SECURITY_NAMESPACE}proofPurpose`,
|
|
1466
|
+
`${SECURITY_NAMESPACE}proofValue`,
|
|
1467
|
+
"http://purl.org/dc/terms/created"
|
|
1468
|
+
]);
|
|
1469
|
+
function hasAdditionalProofOptions(value) {
|
|
1470
|
+
const node = Array.isArray(value) && value.length === 1 ? value[0] : value;
|
|
1471
|
+
return isJsonLdNode(node) && globalThis.Object.keys(node).some((property) => !STANDARD_COMPACT_PROOF_PROPERTIES.has(property) && !STANDARD_EXPANDED_PROOF_PROPERTIES.has(property));
|
|
1472
|
+
}
|
|
1473
|
+
function isExpandedJsonLdNode(value) {
|
|
1474
|
+
const properties = globalThis.Object.keys(value).filter((key) => !key.startsWith("@"));
|
|
1475
|
+
return properties.length > 0 && properties.every((property) => URL.canParse(property));
|
|
1476
|
+
}
|
|
1477
|
+
async function normalizeProofConfiguration(rawProof, documentContext, documentLoader) {
|
|
1478
|
+
let node = Array.isArray(rawProof) && rawProof.length === 1 ? rawProof[0] : rawProof;
|
|
1479
|
+
if (!isJsonLdNode(node)) return null;
|
|
1480
|
+
if (isExpandedJsonLdNode(node)) {
|
|
1481
|
+
if (documentContext == null) return null;
|
|
1482
|
+
node = await _fedify_vocab_runtime_jsonld.default.compact(node, documentContext, { documentLoader });
|
|
1483
|
+
if (!isJsonLdNode(node)) return null;
|
|
1484
|
+
} else node = structuredClone(node);
|
|
1485
|
+
const receivedContext = node["@context"];
|
|
1486
|
+
if (receivedContext != null && !contextStartsWith(documentContext, receivedContext)) return null;
|
|
1487
|
+
const context = receivedContext ?? documentContext;
|
|
1488
|
+
if (context != null) node["@context"] = structuredClone(context);
|
|
1489
|
+
const proofValueProperties = await getJsonLdPropertyNames(node, SECURITY_PROOF_VALUE, ["proofValue", SECURITY_PROOF_VALUE], documentLoader, context) ?? /* @__PURE__ */ new Set(["proofValue", SECURITY_PROOF_VALUE]);
|
|
1490
|
+
for (const property of proofValueProperties) delete node[property];
|
|
1491
|
+
return {
|
|
1492
|
+
value: node,
|
|
1493
|
+
context
|
|
1494
|
+
};
|
|
1495
|
+
}
|
|
1496
|
+
async function createProofConfiguration(jsonLd, proof, options, documentLoader, rawProofCandidate) {
|
|
1497
|
+
let rawProof;
|
|
1498
|
+
if (rawProofCandidate == null) {
|
|
1499
|
+
const match = await findRawProofCandidate(jsonLd, proof, options, documentLoader);
|
|
1500
|
+
if (match === null) return null;
|
|
1501
|
+
rawProof = match?.value;
|
|
1502
|
+
} else rawProof = rawProofCandidate.value;
|
|
1503
|
+
if (rawProof == null) {
|
|
1504
|
+
const serializedProof = await proof.toJsonLd();
|
|
1505
|
+
if (hasAdditionalProofOptions(serializedProof)) rawProof = serializedProof;
|
|
1506
|
+
}
|
|
1507
|
+
if (rawProof != null) return await normalizeProofConfiguration(rawProof, jsonLd["@context"], documentLoader);
|
|
1508
|
+
const context = jsonLd["@context"];
|
|
1509
|
+
return {
|
|
1510
|
+
value: {
|
|
1511
|
+
...context == null ? {} : { "@context": context },
|
|
1512
|
+
type: "DataIntegrityProof",
|
|
1513
|
+
cryptosuite: proof.cryptosuite,
|
|
1514
|
+
verificationMethod: proof.verificationMethodId.href,
|
|
1515
|
+
proofPurpose: proof.proofPurpose,
|
|
1516
|
+
created: proof.created.toString()
|
|
1517
|
+
},
|
|
1518
|
+
context
|
|
1519
|
+
};
|
|
1520
|
+
}
|
|
1521
|
+
const KNOWN_PROOF_CONFIGURATION_PROPERTIES = /* @__PURE__ */ new Set([
|
|
1522
|
+
"@context",
|
|
1523
|
+
"@id",
|
|
1524
|
+
"@type",
|
|
1525
|
+
"id",
|
|
1526
|
+
"type",
|
|
1527
|
+
"cryptosuite",
|
|
1528
|
+
`${SECURITY_NAMESPACE}cryptosuite`,
|
|
1529
|
+
"verificationMethod",
|
|
1530
|
+
SECURITY_VERIFICATION_METHOD,
|
|
1531
|
+
"proofPurpose",
|
|
1532
|
+
`${SECURITY_NAMESPACE}proofPurpose`,
|
|
1533
|
+
"created",
|
|
1534
|
+
"http://purl.org/dc/terms/created",
|
|
1535
|
+
"expires",
|
|
1536
|
+
SECURITY_EXPIRATION,
|
|
1537
|
+
"domain",
|
|
1538
|
+
SECURITY_DOMAIN,
|
|
1539
|
+
"challenge",
|
|
1540
|
+
SECURITY_CHALLENGE,
|
|
1541
|
+
"nonce",
|
|
1542
|
+
SECURITY_NONCE,
|
|
1543
|
+
"previousProof",
|
|
1544
|
+
SECURITY_PREVIOUS_PROOF
|
|
1545
|
+
]);
|
|
1546
|
+
async function getProofOption(proofConfig, propertyIri, defaults, documentLoader) {
|
|
1547
|
+
let names = await getJsonLdPropertyNames(proofConfig, propertyIri, defaults, documentLoader, proofConfig["@context"], true);
|
|
1548
|
+
if (names == null) {
|
|
1549
|
+
if (globalThis.Object.keys(proofConfig).some((property) => !KNOWN_PROOF_CONFIGURATION_PROPERTIES.has(property))) return null;
|
|
1550
|
+
names = new Set(defaults);
|
|
1551
|
+
}
|
|
1552
|
+
const present = globalThis.Object.keys(proofConfig).filter((property) => names.has(property));
|
|
1553
|
+
if (present.length > 1) return null;
|
|
1554
|
+
return present.length < 1 ? { present: false } : {
|
|
1555
|
+
present: true,
|
|
1556
|
+
value: proofConfig[present[0]]
|
|
1557
|
+
};
|
|
1558
|
+
}
|
|
1559
|
+
function parseStringSet(value) {
|
|
1560
|
+
if (typeof value === "string") return /* @__PURE__ */ new Set([value]);
|
|
1561
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) return null;
|
|
1562
|
+
return new Set(value);
|
|
1563
|
+
}
|
|
1564
|
+
function equalStringSets(left, right) {
|
|
1565
|
+
return left.size === right.size && [...left].every((value) => right.has(value));
|
|
1566
|
+
}
|
|
1567
|
+
async function hasValidProofOptions(proofConfig, options, documentLoader) {
|
|
1568
|
+
const expires = await getProofOption(proofConfig, SECURITY_EXPIRATION, ["expires", SECURITY_EXPIRATION], documentLoader);
|
|
1569
|
+
if (expires == null) return false;
|
|
1570
|
+
if (expires.present) {
|
|
1571
|
+
if (typeof expires.value !== "string") return false;
|
|
1572
|
+
let expiration;
|
|
1573
|
+
try {
|
|
1574
|
+
expiration = Temporal.Instant.from(expires.value);
|
|
1575
|
+
} catch {
|
|
1576
|
+
return false;
|
|
1577
|
+
}
|
|
1578
|
+
if (Temporal.Instant.compare(Temporal.Now.instant(), expiration) >= 0) return false;
|
|
1579
|
+
}
|
|
1580
|
+
const domain = await getProofOption(proofConfig, SECURITY_DOMAIN, ["domain", SECURITY_DOMAIN], documentLoader);
|
|
1581
|
+
if (domain == null) return false;
|
|
1582
|
+
const proofDomains = domain.present ? parseStringSet(domain.value) : null;
|
|
1583
|
+
if (domain.present && proofDomains == null) return false;
|
|
1584
|
+
if (options.domain != null) {
|
|
1585
|
+
const expectedDomains = parseStringSet(options.domain);
|
|
1586
|
+
if (expectedDomains == null || proofDomains == null || !equalStringSets(proofDomains, expectedDomains)) return false;
|
|
1587
|
+
}
|
|
1588
|
+
const challenge = await getProofOption(proofConfig, SECURITY_CHALLENGE, ["challenge", SECURITY_CHALLENGE], documentLoader);
|
|
1589
|
+
if (challenge == null || challenge.present && typeof challenge.value !== "string" || options.challenge != null && (!challenge.present || challenge.value !== options.challenge)) return false;
|
|
1590
|
+
const nonce = await getProofOption(proofConfig, SECURITY_NONCE, ["nonce", SECURITY_NONCE], documentLoader);
|
|
1591
|
+
if (nonce == null || nonce.present && typeof nonce.value !== "string") return false;
|
|
1592
|
+
const previousProof = await getProofOption(proofConfig, SECURITY_PREVIOUS_PROOF, ["previousProof", SECURITY_PREVIOUS_PROOF], documentLoader);
|
|
1593
|
+
if (previousProof == null || previousProof.present && typeof previousProof.value !== "string" && (!Array.isArray(previousProof.value) || previousProof.value.some((item) => typeof item !== "string"))) return false;
|
|
1594
|
+
return true;
|
|
1595
|
+
}
|
|
1596
|
+
async function verifyProofInternal(jsonLd, proof, options, messageDigestCache, rawProofCandidate) {
|
|
1597
|
+
if (!isJsonLdNode(jsonLd) || proof.cryptosuite !== "eddsa-jcs-2022" || proof.verificationMethodId == null || proof.proofPurpose !== "assertionMethod" || proof.proofValue == null || proof.created == null) return null;
|
|
1598
|
+
const proofContextLoader = messageDigestCache.proofContextLoader ?? preloadedOnlyDocumentLoader;
|
|
1599
|
+
const proofConfiguration = await createProofConfiguration(jsonLd, proof, options, proofContextLoader, rawProofCandidate);
|
|
1600
|
+
if (proofConfiguration == null || !await hasValidProofOptions(proofConfiguration.value, options, proofContextLoader)) return null;
|
|
1601
|
+
const publicKeyPromise = require_http.measureSignatureKeyFetch(options.meterProvider, "object_integrity", () => require_http.fetchKey(proof.verificationMethodId, _fedify_vocab.Multikey, options));
|
|
1602
|
+
const proofBytes = new TextEncoder().encode((0, json_canon.default)(proofConfiguration.value));
|
|
1294
1603
|
const proofDigest = await crypto.subtle.digest("SHA-256", proofBytes);
|
|
1295
|
-
const msg = { ...jsonLd };
|
|
1296
|
-
if ("proof" in msg) delete msg.proof;
|
|
1297
|
-
if ("https://w3id.org/security#proof" in msg) delete msg["https://w3id.org/security#proof"];
|
|
1298
1604
|
let fetchedKey;
|
|
1299
1605
|
try {
|
|
1300
1606
|
fetchedKey = await publicKeyPromise;
|
|
@@ -1326,7 +1632,7 @@ async function verifyProofInternal(jsonLd, proof, options) {
|
|
|
1326
1632
|
get: () => Promise.resolve(void 0),
|
|
1327
1633
|
set: async (keyId, key) => await options.keyCache?.set(keyId, key)
|
|
1328
1634
|
}
|
|
1329
|
-
});
|
|
1635
|
+
}, messageDigestCache, rawProofCandidate);
|
|
1330
1636
|
}
|
|
1331
1637
|
logger.debug("The fetched key (verificationMethod) for the proof is not a valid Ed25519 key:\n{keyId}", {
|
|
1332
1638
|
proof,
|
|
@@ -1337,15 +1643,21 @@ async function verifyProofInternal(jsonLd, proof, options) {
|
|
|
1337
1643
|
const digest = new Uint8Array(proofDigest.byteLength + 32);
|
|
1338
1644
|
digest.set(new Uint8Array(proofDigest), 0);
|
|
1339
1645
|
const proofValue = proof.proofValue;
|
|
1340
|
-
const verifyCandidate = async (
|
|
1341
|
-
const msgBytes = encoder.encode((0, json_canon.default)(candidate));
|
|
1342
|
-
const msgDigest = await crypto.subtle.digest("SHA-256", msgBytes);
|
|
1646
|
+
const verifyCandidate = async (msgDigest) => {
|
|
1343
1647
|
digest.set(new Uint8Array(msgDigest), proofDigest.byteLength);
|
|
1344
1648
|
return await crypto.subtle.verify("Ed25519", publicKey.publicKey, proofValue.slice(), digest);
|
|
1345
1649
|
};
|
|
1346
|
-
|
|
1347
|
-
const
|
|
1348
|
-
|
|
1650
|
+
const messageDigestKey = (0, json_canon.default)(proofConfiguration.context ?? null);
|
|
1651
|
+
const messageDigestValues = messageDigestCache.values ??= /* @__PURE__ */ new Map();
|
|
1652
|
+
let messageDigestsPromise = messageDigestValues.get(messageDigestKey);
|
|
1653
|
+
if (messageDigestsPromise == null) {
|
|
1654
|
+
messageDigestsPromise = createProofMessageDigests(jsonLd, messageDigestCache.proofContextLoader, proofConfiguration.context);
|
|
1655
|
+
messageDigestValues.set(messageDigestKey, messageDigestsPromise);
|
|
1656
|
+
}
|
|
1657
|
+
const messageDigests = await messageDigestsPromise;
|
|
1658
|
+
if (await verifyCandidate(messageDigests.onWire)) return publicKey;
|
|
1659
|
+
const normalizedDigest = await messageDigests.normalized();
|
|
1660
|
+
if (normalizedDigest != null && await verifyCandidate(normalizedDigest)) return publicKey;
|
|
1349
1661
|
if (fetchedKey.cached) {
|
|
1350
1662
|
logger.debug("Failed to verify the proof with the cached key {keyId}; retrying with the freshly fetched key...", {
|
|
1351
1663
|
keyId: proof.verificationMethodId.href,
|
|
@@ -1357,7 +1669,7 @@ async function verifyProofInternal(jsonLd, proof, options) {
|
|
|
1357
1669
|
get: () => Promise.resolve(void 0),
|
|
1358
1670
|
set: async (keyId, key) => await options.keyCache?.set(keyId, key)
|
|
1359
1671
|
}
|
|
1360
|
-
});
|
|
1672
|
+
}, messageDigestCache, rawProofCandidate);
|
|
1361
1673
|
}
|
|
1362
1674
|
logger.debug("Failed to verify the proof with the fetched key {keyId}:\n{proof}", {
|
|
1363
1675
|
keyId: proof.verificationMethodId.href,
|
|
@@ -1365,6 +1677,228 @@ async function verifyProofInternal(jsonLd, proof, options) {
|
|
|
1365
1677
|
});
|
|
1366
1678
|
return null;
|
|
1367
1679
|
}
|
|
1680
|
+
const AS_NAMESPACE = "https://www.w3.org/ns/activitystreams#";
|
|
1681
|
+
const FEP_2277_ACTOR_PROPERTIES = ["http://www.w3.org/ns/ldp#inbox", `${AS_NAMESPACE}outbox`];
|
|
1682
|
+
const FEP_2277_COLLECTION_PROPERTIES = [
|
|
1683
|
+
"items",
|
|
1684
|
+
"orderedItems",
|
|
1685
|
+
"totalItems",
|
|
1686
|
+
"partOf",
|
|
1687
|
+
"first",
|
|
1688
|
+
"last",
|
|
1689
|
+
"next",
|
|
1690
|
+
"prev",
|
|
1691
|
+
"current"
|
|
1692
|
+
].map((property) => AS_NAMESPACE + property);
|
|
1693
|
+
const PORTABLE_OBJECT_ID_PATTERN = /^ap(?:\+ef61)?:\/\//i;
|
|
1694
|
+
const FUNCTIONAL_PROOF_PROPERTIES = [
|
|
1695
|
+
`${SECURITY_NAMESPACE}cryptosuite`,
|
|
1696
|
+
SECURITY_VERIFICATION_METHOD,
|
|
1697
|
+
`${SECURITY_NAMESPACE}proofPurpose`,
|
|
1698
|
+
`${SECURITY_NAMESPACE}proofValue`,
|
|
1699
|
+
"http://purl.org/dc/terms/created"
|
|
1700
|
+
];
|
|
1701
|
+
function isJsonLdNode(value) {
|
|
1702
|
+
return typeof value === "object" && value != null && !Array.isArray(value);
|
|
1703
|
+
}
|
|
1704
|
+
function hasValidPortableProofShape(proofValue) {
|
|
1705
|
+
if (!isJsonLdNode(proofValue)) return false;
|
|
1706
|
+
let proofNode = proofValue;
|
|
1707
|
+
if ("@graph" in proofNode) {
|
|
1708
|
+
const graph = proofNode["@graph"];
|
|
1709
|
+
if (!Array.isArray(graph) || graph.length !== 1 || !isJsonLdNode(graph[0])) return false;
|
|
1710
|
+
proofNode = graph[0];
|
|
1711
|
+
}
|
|
1712
|
+
const types = proofNode["@type"];
|
|
1713
|
+
return Array.isArray(types) && types.length === 1 && types[0] === DATA_INTEGRITY_PROOF && FUNCTIONAL_PROOF_PROPERTIES.every((property) => {
|
|
1714
|
+
const values = proofNode[property];
|
|
1715
|
+
return Array.isArray(values) && values.length === 1 && !(isJsonLdNode(values[0]) && "@list" in values[0]);
|
|
1716
|
+
});
|
|
1717
|
+
}
|
|
1718
|
+
function classifyFep2277CoreType(node) {
|
|
1719
|
+
if (FEP_2277_ACTOR_PROPERTIES.every((property) => property in node)) return "actor";
|
|
1720
|
+
if (`${SECURITY_NAMESPACE}publicKeyMultibase` in node) return "verificationMethod";
|
|
1721
|
+
if (`${SECURITY_NAMESPACE}publicKeyPem` in node) return "publicKey";
|
|
1722
|
+
if (`${AS_NAMESPACE}href` in node) return "link";
|
|
1723
|
+
if (`${AS_NAMESPACE}actor` in node) return "activity";
|
|
1724
|
+
if (FEP_2277_COLLECTION_PROPERTIES.some((property) => property in node)) return "collection";
|
|
1725
|
+
return "object";
|
|
1726
|
+
}
|
|
1727
|
+
async function expandPortableObjectRoot(jsonLd, contextLoader) {
|
|
1728
|
+
if (!isJsonLdNode(jsonLd)) throw new TypeError("Expected a single JSON-LD object.");
|
|
1729
|
+
const loadedContexts = /* @__PURE__ */ new Map();
|
|
1730
|
+
const loader = getNormalizationContextLoader(contextLoader);
|
|
1731
|
+
const recordingLoader = async (url, options) => {
|
|
1732
|
+
const remoteDocument = await loader(url, options);
|
|
1733
|
+
const key = URL.canParse(url) ? new URL(url).href : url;
|
|
1734
|
+
loadedContexts.set(key, structuredClone(remoteDocument));
|
|
1735
|
+
return remoteDocument;
|
|
1736
|
+
};
|
|
1737
|
+
const expanded = await _fedify_vocab_runtime_jsonld.default.expand(jsonLd, {
|
|
1738
|
+
documentLoader: recordingLoader,
|
|
1739
|
+
keepFreeFloatingNodes: true
|
|
1740
|
+
});
|
|
1741
|
+
if (expanded.length !== 1 || !isJsonLdNode(expanded[0])) throw new TypeError("Expected a single JSON-LD object.");
|
|
1742
|
+
return {
|
|
1743
|
+
root: expanded[0],
|
|
1744
|
+
proofContextLoader: async (url, options) => {
|
|
1745
|
+
const key = URL.canParse(url) ? new URL(url).href : url;
|
|
1746
|
+
const remoteDocument = loadedContexts.get(key);
|
|
1747
|
+
if (remoteDocument != null) return structuredClone(remoteDocument);
|
|
1748
|
+
return await preloadedOnlyDocumentLoader(url, options);
|
|
1749
|
+
}
|
|
1750
|
+
};
|
|
1751
|
+
}
|
|
1752
|
+
/**
|
|
1753
|
+
* Verifies the FEP-ef61 Object Integrity Proof policy for a portable object.
|
|
1754
|
+
*
|
|
1755
|
+
* This applies the FEP-2277 core-type classification to the top-level JSON-LD
|
|
1756
|
+
* node. Portable actors, activities, and objects require proofs. A portable
|
|
1757
|
+
* collection without a proof is reported separately so a caller can apply a
|
|
1758
|
+
* gateway trust policy. Embedded portable objects are not traversed.
|
|
1759
|
+
*
|
|
1760
|
+
* Every proof must use a DID URL whose DID matches the portable object's
|
|
1761
|
+
* authority, and every proof must pass {@link verifyProof}.
|
|
1762
|
+
*
|
|
1763
|
+
* @param jsonLd The JSON-LD document to verify.
|
|
1764
|
+
* @param options Additional options. See also
|
|
1765
|
+
* {@link VerifyPortableObjectProofOptions}.
|
|
1766
|
+
* @returns The detailed portable proof-policy result.
|
|
1767
|
+
* @throws {TypeError} If the input is not a single JSON-LD object or has a
|
|
1768
|
+
* malformed portable ID.
|
|
1769
|
+
* @since 2.4.0
|
|
1770
|
+
*/
|
|
1771
|
+
async function verifyPortableObjectProof(jsonLd, options = {}) {
|
|
1772
|
+
if (isJsonLdNode(jsonLd) && typeof jsonLd["@id"] === "string" && !PORTABLE_OBJECT_ID_PATTERN.test(jsonLd["@id"])) return {
|
|
1773
|
+
verified: false,
|
|
1774
|
+
reason: { type: "notPortableObject" }
|
|
1775
|
+
};
|
|
1776
|
+
const { root, proofContextLoader } = await expandPortableObjectRoot(jsonLd, options.contextLoader);
|
|
1777
|
+
const id = root["@id"];
|
|
1778
|
+
if (typeof id !== "string" || !PORTABLE_OBJECT_ID_PATTERN.test(id)) return {
|
|
1779
|
+
verified: false,
|
|
1780
|
+
reason: { type: "notPortableObject" }
|
|
1781
|
+
};
|
|
1782
|
+
const objectId = (0, _fedify_vocab_runtime.parseIri)(id);
|
|
1783
|
+
(0, _fedify_vocab_runtime.getFe34Origin)(objectId);
|
|
1784
|
+
const objectType = classifyFep2277CoreType(root);
|
|
1785
|
+
if (objectType === "verificationMethod" || objectType === "publicKey" || objectType === "link") return {
|
|
1786
|
+
verified: false,
|
|
1787
|
+
reason: {
|
|
1788
|
+
type: "unsupportedObjectType",
|
|
1789
|
+
objectType
|
|
1790
|
+
}
|
|
1791
|
+
};
|
|
1792
|
+
const proofValues = root[SECURITY_PROOF];
|
|
1793
|
+
if (proofValues == null || Array.isArray(proofValues) && proofValues.length < 1) return objectType === "collection" ? {
|
|
1794
|
+
verified: false,
|
|
1795
|
+
reason: { type: "unsecuredCollection" }
|
|
1796
|
+
} : {
|
|
1797
|
+
verified: false,
|
|
1798
|
+
reason: { type: "missingProof" }
|
|
1799
|
+
};
|
|
1800
|
+
if (!Array.isArray(proofValues)) return {
|
|
1801
|
+
verified: false,
|
|
1802
|
+
reason: {
|
|
1803
|
+
type: "invalidProof",
|
|
1804
|
+
proofIndex: 0
|
|
1805
|
+
}
|
|
1806
|
+
};
|
|
1807
|
+
const rawProofValues = isJsonLdNode(jsonLd) ? await getRawProofValues(jsonLd, proofContextLoader) : [];
|
|
1808
|
+
const proofs = [];
|
|
1809
|
+
for (let proofIndex = 0; proofIndex < proofValues.length; proofIndex++) {
|
|
1810
|
+
const proofValue = proofValues[proofIndex];
|
|
1811
|
+
if (!hasValidPortableProofShape(proofValue)) return {
|
|
1812
|
+
verified: false,
|
|
1813
|
+
reason: {
|
|
1814
|
+
type: "invalidProof",
|
|
1815
|
+
proofIndex
|
|
1816
|
+
}
|
|
1817
|
+
};
|
|
1818
|
+
let proof;
|
|
1819
|
+
try {
|
|
1820
|
+
proof = await _fedify_vocab.DataIntegrityProof.fromJsonLd(proofValue, options);
|
|
1821
|
+
} catch {
|
|
1822
|
+
return {
|
|
1823
|
+
verified: false,
|
|
1824
|
+
reason: {
|
|
1825
|
+
type: "invalidProof",
|
|
1826
|
+
proofIndex
|
|
1827
|
+
}
|
|
1828
|
+
};
|
|
1829
|
+
}
|
|
1830
|
+
proofs.push(proof);
|
|
1831
|
+
}
|
|
1832
|
+
for (let proofIndex = 0; proofIndex < proofs.length; proofIndex++) {
|
|
1833
|
+
const verificationMethod = proofs[proofIndex].verificationMethodId;
|
|
1834
|
+
if (verificationMethod == null) return {
|
|
1835
|
+
verified: false,
|
|
1836
|
+
reason: {
|
|
1837
|
+
type: "invalidProof",
|
|
1838
|
+
proofIndex
|
|
1839
|
+
}
|
|
1840
|
+
};
|
|
1841
|
+
if (verificationMethod.protocol !== "did:") return {
|
|
1842
|
+
verified: false,
|
|
1843
|
+
reason: {
|
|
1844
|
+
type: "unsupportedVerificationMethod",
|
|
1845
|
+
proofIndex,
|
|
1846
|
+
verificationMethod
|
|
1847
|
+
}
|
|
1848
|
+
};
|
|
1849
|
+
try {
|
|
1850
|
+
(0, _fedify_vocab_runtime.getFe34Origin)(verificationMethod);
|
|
1851
|
+
} catch (error) {
|
|
1852
|
+
if (!(error instanceof TypeError)) throw error;
|
|
1853
|
+
return {
|
|
1854
|
+
verified: false,
|
|
1855
|
+
reason: {
|
|
1856
|
+
type: "unsupportedVerificationMethod",
|
|
1857
|
+
proofIndex,
|
|
1858
|
+
verificationMethod
|
|
1859
|
+
}
|
|
1860
|
+
};
|
|
1861
|
+
}
|
|
1862
|
+
if (!(0, _fedify_vocab_runtime.haveSameFe34Origin)(objectId, verificationMethod)) return {
|
|
1863
|
+
verified: false,
|
|
1864
|
+
reason: {
|
|
1865
|
+
type: "verificationMethodMismatch",
|
|
1866
|
+
proofIndex,
|
|
1867
|
+
objectId,
|
|
1868
|
+
verificationMethod
|
|
1869
|
+
}
|
|
1870
|
+
};
|
|
1871
|
+
}
|
|
1872
|
+
const keys = [];
|
|
1873
|
+
const rawProofCandidatePool = {
|
|
1874
|
+
candidates: await parseRawProofCandidates(jsonLd, rawProofValues, options, proofContextLoader),
|
|
1875
|
+
used: /* @__PURE__ */ new Set()
|
|
1876
|
+
};
|
|
1877
|
+
const messageDigestCache = { proofContextLoader };
|
|
1878
|
+
for (let proofIndex = 0; proofIndex < proofs.length; proofIndex++) {
|
|
1879
|
+
const rawProofCandidate = takeRawProofCandidate(rawProofCandidatePool, proofs[proofIndex]);
|
|
1880
|
+
if (rawProofCandidate == null) return {
|
|
1881
|
+
verified: false,
|
|
1882
|
+
reason: {
|
|
1883
|
+
type: "invalidProof",
|
|
1884
|
+
proofIndex
|
|
1885
|
+
}
|
|
1886
|
+
};
|
|
1887
|
+
const key = await verifyProofWithMessageDigestCache(jsonLd, proofs[proofIndex], options, messageDigestCache, rawProofCandidate);
|
|
1888
|
+
if (key == null) return {
|
|
1889
|
+
verified: false,
|
|
1890
|
+
reason: {
|
|
1891
|
+
type: "invalidProof",
|
|
1892
|
+
proofIndex
|
|
1893
|
+
}
|
|
1894
|
+
};
|
|
1895
|
+
keys.push(key);
|
|
1896
|
+
}
|
|
1897
|
+
return {
|
|
1898
|
+
verified: true,
|
|
1899
|
+
keys
|
|
1900
|
+
};
|
|
1901
|
+
}
|
|
1368
1902
|
/**
|
|
1369
1903
|
* Verifies the given object. It will verify all the proofs in the object,
|
|
1370
1904
|
* and succeed only if all the proofs are valid and all attributions and
|
|
@@ -1386,10 +1920,48 @@ async function verifyObject(cls, jsonLd, options = {}) {
|
|
|
1386
1920
|
"proof"
|
|
1387
1921
|
]);
|
|
1388
1922
|
const object = await cls.fromJsonLd(jsonLd, options);
|
|
1923
|
+
const defaultDocumentLoader = (0, _fedify_vocab_runtime.getDocumentLoader)();
|
|
1924
|
+
const proofContextLoader = options.contextLoader ?? defaultDocumentLoader;
|
|
1389
1925
|
const attributions = new Set(object.attributionIds.map((uri) => uri.href));
|
|
1390
1926
|
if (object instanceof _fedify_vocab.Activity) for (const uri of object.actorIds) attributions.add(uri.href);
|
|
1391
|
-
|
|
1392
|
-
|
|
1927
|
+
const rawProofValues = isJsonLdNode(jsonLd) ? await getRawProofValues(jsonLd, proofContextLoader) : [];
|
|
1928
|
+
const rawProofCandidates = isJsonLdNode(jsonLd) ? await parseRawProofCandidates(jsonLd, rawProofValues, options, proofContextLoader) : [];
|
|
1929
|
+
const rawProofCandidatePool = {
|
|
1930
|
+
candidates: rawProofCandidates,
|
|
1931
|
+
used: /* @__PURE__ */ new Set()
|
|
1932
|
+
};
|
|
1933
|
+
const baseDocumentLoader = options.documentLoader ?? defaultDocumentLoader;
|
|
1934
|
+
const hydratedCandidates = /* @__PURE__ */ new Set();
|
|
1935
|
+
const proofDocumentLoader = async (url, loaderOptions) => {
|
|
1936
|
+
const remoteDocument = await baseDocumentLoader(url, loaderOptions);
|
|
1937
|
+
const reference = normalizeDocumentUrl(url);
|
|
1938
|
+
const candidateIndex = rawProofCandidates.findIndex((candidate, index) => !hydratedCandidates.has(index) && !rawProofCandidatePool.used.has(index) && candidate.reference === reference);
|
|
1939
|
+
if (candidateIndex >= 0) {
|
|
1940
|
+
hydratedCandidates.add(candidateIndex);
|
|
1941
|
+
let parsed = null;
|
|
1942
|
+
try {
|
|
1943
|
+
parsed = await _fedify_vocab.DataIntegrityProof.fromJsonLd(remoteDocument.document, {
|
|
1944
|
+
documentLoader: baseDocumentLoader,
|
|
1945
|
+
contextLoader: proofContextLoader,
|
|
1946
|
+
tracerProvider: options.tracerProvider,
|
|
1947
|
+
baseUrl: (0, _fedify_vocab_runtime.parseIri)(remoteDocument.documentUrl)
|
|
1948
|
+
});
|
|
1949
|
+
} catch {}
|
|
1950
|
+
rawProofCandidates[candidateIndex] = {
|
|
1951
|
+
value: structuredClone(remoteDocument.document),
|
|
1952
|
+
proof: parsed,
|
|
1953
|
+
reference
|
|
1954
|
+
};
|
|
1955
|
+
}
|
|
1956
|
+
return remoteDocument;
|
|
1957
|
+
};
|
|
1958
|
+
for await (const proof of object.getProofs({
|
|
1959
|
+
...options,
|
|
1960
|
+
documentLoader: proofDocumentLoader
|
|
1961
|
+
})) {
|
|
1962
|
+
const rawProofCandidate = takeRawProofCandidate(rawProofCandidatePool, proof);
|
|
1963
|
+
if (rawProofCandidate == null) return null;
|
|
1964
|
+
const key = await verifyProofWithMessageDigestCache(jsonLd, proof, options, { proofContextLoader }, rawProofCandidate);
|
|
1393
1965
|
if (key === null) return null;
|
|
1394
1966
|
if (proof.verificationMethodId == null) return null;
|
|
1395
1967
|
if (key.controllerId == null) {
|
|
@@ -1541,6 +2113,12 @@ Object.defineProperty(exports, "verifyObject", {
|
|
|
1541
2113
|
return verifyObject;
|
|
1542
2114
|
}
|
|
1543
2115
|
});
|
|
2116
|
+
Object.defineProperty(exports, "verifyPortableObjectProof", {
|
|
2117
|
+
enumerable: true,
|
|
2118
|
+
get: function() {
|
|
2119
|
+
return verifyPortableObjectProof;
|
|
2120
|
+
}
|
|
2121
|
+
});
|
|
1544
2122
|
Object.defineProperty(exports, "verifyProof", {
|
|
1545
2123
|
enumerable: true,
|
|
1546
2124
|
get: function() {
|