@fedify/fedify 1.6.9 → 1.6.11

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.
Files changed (98) hide show
  1. package/dist/{actor-DY_ediuQ.js → actor-CxnKxfwH.js} +1059 -2276
  2. package/dist/{actor-qNVQigMJ.js → actor-KrOvzFz1.js} +6 -9
  3. package/dist/{assert-DmFG7ppO.js → assert-LOEeCUK5.js} +1 -1
  4. package/dist/{assert_equals-CTYbeopb.js → assert_equals-B44MxcIj.js} +2 -4
  5. package/dist/{assert_instance_of-CF09JHYM.js → assert_instance_of-XtuFevV_.js} +1 -1
  6. package/dist/{assert_is_error-nrwA1GeT.js → assert_is_error-BTlryvT0.js} +1 -1
  7. package/dist/{assert_not_equals-Dc7y-V5Q.js → assert_not_equals-C685gKx6.js} +1 -1
  8. package/dist/{assert_rejects-C-sxEMM5.js → assert_rejects-DWQ4jaf9.js} +2 -2
  9. package/dist/{assert_throws-Cn9C6Jur.js → assert_throws-YetpVSc-.js} +2 -2
  10. package/dist/{authdocloader-DTpSS6vq.js → authdocloader-CsnXlm3V.js} +4 -4
  11. package/dist/{authdocloader-ColUu5fh.js → authdocloader-DsULyH5D.js} +3 -3
  12. package/dist/{builder-CTAic_n3.js → builder-BjJSVOvs.js} +7 -10
  13. package/dist/{chunk-HsBuZ-b2.js → chunk-Cx8LTkjm.js} +3 -1
  14. package/dist/{client-Bp-TqQI6.js → client-BlntqhE9.js} +6 -6
  15. package/dist/compat/transformers.test.js +27 -28
  16. package/dist/{context-BAx407GT.js → context-CiyuTkCx.js} +2 -2
  17. package/dist/{docloader-UqcHJRjL.js → docloader-BYgIBZvh.js} +4 -6
  18. package/dist/{docloader-NmruKwBX.js → docloader-DOrb0fRp.js} +5 -9
  19. package/dist/{esm-Db4De7AS.js → esm-DO9PrujO.js} +23 -34
  20. package/dist/federation/builder.test.js +20 -23
  21. package/dist/federation/collection.test.js +9 -9
  22. package/dist/federation/handler.test.js +33 -33
  23. package/dist/federation/inbox.test.js +10 -10
  24. package/dist/federation/keycache.test.js +9 -9
  25. package/dist/federation/kv.test.js +10 -9
  26. package/dist/federation/middleware.test.js +53 -68
  27. package/dist/federation/mod.js +11 -11
  28. package/dist/federation/mq.test.js +10 -11
  29. package/dist/federation/retry.test.js +3 -3
  30. package/dist/federation/router.test.js +9 -9
  31. package/dist/federation/send.test.js +22 -22
  32. package/dist/{federation-3B6BDKCK.js → federation-DyRlaC4X.js} +1 -2
  33. package/dist/fixtures/oeee.cafe/ap/users/3609fd4e-d51d-4db8-9f04-4189815864dd.json +24 -0
  34. package/dist/{http-DjFoawa6.js → http-Dc8RIaDE.js} +56 -17
  35. package/dist/{http-XWz7prGl.js → http-LsE7i_Zq.js} +56 -17
  36. package/dist/{inbox-eAHML3xw.js → inbox-BVwejjm8.js} +5 -7
  37. package/dist/key-BP-Oky5H.js +16 -0
  38. package/dist/{key-CUEyp_yz.js → key-BTcmhZp7.js} +6 -9
  39. package/dist/{key-B4UFmNvL.js → key-Bg_PKGpe.js} +5 -8
  40. package/dist/{key-C1UhN1LT.js → key-DxKqQgGm.js} +4 -4
  41. package/dist/{keycache-YqFJn7EL.js → keycache-BnCTs9sG.js} +2 -2
  42. package/dist/{keys-Dd_v3HDs.js → keys-pbGG00jU.js} +1 -1
  43. package/dist/{ld-D8WCx-VU.js → ld-n_kUCeXG.js} +9 -16
  44. package/dist/{lookup-DggOHyrB.js → lookup-1pupHJbK.js} +7 -11
  45. package/dist/{lookup-DEN8lvde.js → lookup-Cutk-FXA.js} +3 -5
  46. package/dist/{lookup-DHlMvC3Q.js → lookup-DrAS097Q.js} +2 -4
  47. package/dist/middleware-CDYmpOKW.js +33 -0
  48. package/dist/{middleware-D_j7IO2V.js → middleware-CMTirIyg.js} +34 -52
  49. package/dist/middleware-CXZvlTVn.js +17 -0
  50. package/dist/{middleware-mmR5eUDI.js → middleware-kjrPMCqA.js} +34 -58
  51. package/dist/mod.js +11 -11
  52. package/dist/{multibase-DeCHcK8L.js → multibase-CnLHszip.js} +1 -2
  53. package/dist/nodeinfo/client.test.js +20 -23
  54. package/dist/nodeinfo/handler.test.js +32 -32
  55. package/dist/nodeinfo/mod.js +2 -2
  56. package/dist/nodeinfo/semver.test.js +23 -30
  57. package/dist/nodeinfo/types.test.js +11 -11
  58. package/dist/{owner-lYBh2f5Z.js → owner-c-Zzpacz.js} +3 -3
  59. package/dist/{proof-CPamyAcG.js → proof-BTx3Maj-.js} +12 -25
  60. package/dist/{proof-BAoGg0DQ.js → proof-CTOU2zFF.js} +8 -14
  61. package/dist/runtime/authdocloader.test.js +21 -21
  62. package/dist/runtime/docloader.test.js +16 -18
  63. package/dist/runtime/key.test.js +17 -17
  64. package/dist/runtime/langstr.test.js +9 -9
  65. package/dist/runtime/mod.js +6 -6
  66. package/dist/runtime/multibase/multibase.test.js +11 -12
  67. package/dist/runtime/url.test.js +6 -6
  68. package/dist/{semver-DWClQt_5.js → semver-BNrOOAs9.js} +1 -3
  69. package/dist/{send-CrbpYrfD.js → send-SniNSIKd.js} +3 -4
  70. package/dist/sig/http.test.js +55 -53
  71. package/dist/sig/key.test.js +18 -18
  72. package/dist/sig/ld.test.js +20 -22
  73. package/dist/sig/mod.js +6 -6
  74. package/dist/sig/owner.test.js +20 -20
  75. package/dist/sig/proof.test.js +19 -19
  76. package/dist/{std__assert-vp0TKMS1.js → std__assert-o_r9vqm1.js} +1 -1
  77. package/dist/testing/docloader.test.js +9 -9
  78. package/dist/testing/mod.js +1 -1
  79. package/dist/{testing-BZ0dJ4qn.js → testing-DLyvtiiW.js} +3 -5
  80. package/dist/{type-D2s5lmbZ.js → type-CFuiGLz9.js} +1 -2
  81. package/dist/{types-C7C_l-jz.js → types-CJHS5pXl.js} +1 -1
  82. package/dist/{types-Bl1hBU1t.js → types-DG1qZLU9.js} +6 -8
  83. package/dist/{url-kTAI6_KP.js → url-C2xuoQD1.js} +2 -4
  84. package/dist/vocab/actor.test.js +104 -156
  85. package/dist/vocab/lookup.test.js +17 -17
  86. package/dist/vocab/mod.js +4 -4
  87. package/dist/vocab/type.test.js +11 -15
  88. package/dist/vocab/vocab.test.js +45 -49
  89. package/dist/{vocab-Cs9KbJAx.js → vocab-lzEJg2bH.js} +1057 -2270
  90. package/dist/{vocab-DSVoOj6P.js → vocab-oKloyO0F.js} +6 -10
  91. package/dist/webfinger/handler.test.js +33 -34
  92. package/dist/webfinger/lookup.test.js +13 -13
  93. package/dist/webfinger/mod.js +2 -2
  94. package/dist/x/cfworkers.test.js +9 -9
  95. package/package.json +1 -1
  96. package/dist/key-ByAyOg3m.js +0 -16
  97. package/dist/middleware-B-ysz1Gc.js +0 -17
  98. package/dist/middleware-Bmr-V4-W.js +0 -33
@@ -3,30 +3,30 @@
3
3
  import { URLPattern } from "urlpattern-polyfill";
4
4
  globalThis.addEventListener = () => {};
5
5
 
6
- import { assertEquals } from "../assert_equals-CTYbeopb.js";
7
- import { assert } from "../assert-DmFG7ppO.js";
8
- import "../assert_instance_of-CF09JHYM.js";
9
- import "../docloader-UqcHJRjL.js";
10
- import "../url-kTAI6_KP.js";
11
- import "../multibase-DeCHcK8L.js";
12
- import { Activity, Application, Endpoints, Group, Person, Service } from "../vocab-Cs9KbJAx.js";
6
+ import { assertEquals } from "../assert_equals-B44MxcIj.js";
7
+ import { assert } from "../assert-LOEeCUK5.js";
8
+ import "../assert_instance_of-XtuFevV_.js";
9
+ import "../docloader-BYgIBZvh.js";
10
+ import "../url-C2xuoQD1.js";
11
+ import "../multibase-CnLHszip.js";
12
+ import { Activity, Application, Endpoints, Group, Person, Service } from "../vocab-lzEJg2bH.js";
13
13
  import "../langstr-DbWheeIS.js";
14
- import "../lookup-DEN8lvde.js";
15
- import "../type-D2s5lmbZ.js";
16
- import "../actor-qNVQigMJ.js";
17
- import "../key-CUEyp_yz.js";
18
- import { verifyRequest } from "../http-DjFoawa6.js";
19
- import { doesActorOwnKey } from "../owner-lYBh2f5Z.js";
20
- import { extractInboxes, sendActivity } from "../send-CrbpYrfD.js";
21
- import { test } from "../testing-BZ0dJ4qn.js";
22
- import "../std__assert-vp0TKMS1.js";
23
- import { assertFalse, assertRejects } from "../assert_rejects-C-sxEMM5.js";
24
- import "../assert_is_error-nrwA1GeT.js";
25
- import { assertNotEquals } from "../assert_not_equals-Dc7y-V5Q.js";
26
- import "../assert_throws-Cn9C6Jur.js";
14
+ import "../lookup-Cutk-FXA.js";
15
+ import "../type-CFuiGLz9.js";
16
+ import "../actor-KrOvzFz1.js";
17
+ import "../key-BTcmhZp7.js";
18
+ import { verifyRequest } from "../http-Dc8RIaDE.js";
19
+ import { doesActorOwnKey } from "../owner-c-Zzpacz.js";
20
+ import { extractInboxes, sendActivity } from "../send-SniNSIKd.js";
21
+ import { test } from "../testing-DLyvtiiW.js";
22
+ import "../std__assert-o_r9vqm1.js";
23
+ import { assertFalse, assertRejects } from "../assert_rejects-DWQ4jaf9.js";
24
+ import "../assert_is_error-BTlryvT0.js";
25
+ import { assertNotEquals } from "../assert_not_equals-C685gKx6.js";
26
+ import "../assert_throws-YetpVSc-.js";
27
27
  import { mockDocumentLoader } from "../docloader-09nVWLAZ.js";
28
- import { ed25519Multikey, ed25519PrivateKey, rsaPrivateKey2, rsaPublicKey2 } from "../keys-Dd_v3HDs.js";
29
- import { esm_default } from "../esm-Db4De7AS.js";
28
+ import { ed25519Multikey, ed25519PrivateKey, rsaPrivateKey2, rsaPublicKey2 } from "../keys-pbGG00jU.js";
29
+ import { esm_default } from "../esm-DO9PrujO.js";
30
30
 
31
31
  //#region federation/send.test.ts
32
32
  test("extractInboxes()", () => {
@@ -159,8 +159,7 @@ var ParallelMessageQueue = class ParallelMessageQueue {
159
159
  }
160
160
  async enqueueMany(messages, options) {
161
161
  if (this.queue.enqueueMany == null) {
162
- const results = await Promise.allSettled(messages.map((message) => this.queue.enqueue(message, options)));
163
- const errors = results.filter((r) => r.status === "rejected").map((r) => r.reason);
162
+ const errors = (await Promise.allSettled(messages.map((message) => this.queue.enqueue(message, options)))).filter((r) => r.status === "rejected").map((r) => r.reason);
164
163
  if (errors.length > 1) throw new AggregateError(errors, "Failed to enqueue messages.");
165
164
  else if (errors.length === 1) throw errors[0];
166
165
  return;
@@ -0,0 +1,24 @@
1
+ {
2
+ "@context": [
3
+ "https://www.w3.org/ns/activitystreams",
4
+ "https://w3id.org/security/v1"
5
+ ],
6
+ "id": "https://oeee.cafe/ap/users/3609fd4e-d51d-4db8-9f04-4189815864dd",
7
+ "type": "Person",
8
+ "preferredUsername": "hongminhee",
9
+ "name": "洪兔",
10
+ "inbox": "https://oeee.cafe/ap/users/3609fd4e-d51d-4db8-9f04-4189815864dd/inbox",
11
+ "outbox": "https://oeee.cafe/ap/users/3609fd4e-d51d-4db8-9f04-4189815864dd/outbox",
12
+ "publicKey": {
13
+ "id": "https://oeee.cafe/ap/users/3609fd4e-d51d-4db8-9f04-4189815864dd#main-key",
14
+ "owner": "https://oeee.cafe/ap/users/3609fd4e-d51d-4db8-9f04-4189815864dd",
15
+ "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAowJfOzpA/nAYyL0bVDTm\niCAOlhFCIBnqwk1jvGrbkDhMzxlsgyoDqUSlmcJdKaPwu24YdFajDtJIgto27Ju7\nIC3hB7OFchnZ4JZrdYFo7CJABOzK58o12sdmmkCdY5hXWf1604E+mzyIdBAJ1FFJ\nL8vP07VEUsZ7yo9x0iVNg7HpCOK+y6BqI2GHS2dq9qkqQEIhC2TKHXn/RQVXwYB6\nG+YQmVUtcsbCVKdcWyTKhItLRGnepu3BqBSbieLxV27B1O9NFSoPu8xiBUnYwMoe\nsUQCE5tGcqxc75HzcVCbq7PqVqHZ1NW9RYssaSUqi4FYcjXxQrR08DrAl8rR4eXT\n4QIDAQAB\n-----END PUBLIC KEY-----\n"
16
+ },
17
+ "endpoints": {
18
+ "type": "as:Endpoints",
19
+ "sharedInbox": "https://oeee.cafe/inbox"
20
+ },
21
+ "followers": "https://oeee.cafe/ap/users/3609fd4e-d51d-4db8-9f04-4189815864dd/followers",
22
+ "manuallyApprovesFollowers": false,
23
+ "url": "https://oeee.cafe/@hongminhee"
24
+ }
@@ -3,9 +3,9 @@
3
3
  import { URLPattern } from "urlpattern-polyfill";
4
4
  globalThis.addEventListener = () => {};
5
5
 
6
- import { deno_default } from "./docloader-UqcHJRjL.js";
7
- import { CryptographicKey } from "./vocab-Cs9KbJAx.js";
8
- import { fetchKey, validateCryptoKey } from "./key-CUEyp_yz.js";
6
+ import { deno_default } from "./docloader-BYgIBZvh.js";
7
+ import { CryptographicKey } from "./vocab-lzEJg2bH.js";
8
+ import { fetchKey, validateCryptoKey } from "./key-BTcmhZp7.js";
9
9
  import { getLogger } from "@logtape/logtape";
10
10
  import { SpanStatusCode, trace } from "@opentelemetry/api";
11
11
  import { ATTR_HTTP_REQUEST_HEADER, ATTR_HTTP_REQUEST_METHOD, ATTR_URL_FULL } from "@opentelemetry/semantic-conventions";
@@ -25,9 +25,7 @@ import { Item, decodeDict, encodeItem } from "structured-field-values";
25
25
  */
26
26
  async function signRequest(request, privateKey, keyId, options = {}) {
27
27
  validateCryptoKey(privateKey, "private");
28
- const tracerProvider = options.tracerProvider ?? trace.getTracerProvider();
29
- const tracer = tracerProvider.getTracer(deno_default.name, deno_default.version);
30
- return await tracer.startActiveSpan("http_signatures.sign", async (span) => {
28
+ return await (options.tracerProvider ?? trace.getTracerProvider()).getTracer(deno_default.name, deno_default.version).startActiveSpan("http_signatures.sign", async (span) => {
31
29
  try {
32
30
  const spec = options.spec ?? "draft-cavage-http-signatures-12";
33
31
  let signed;
@@ -254,9 +252,7 @@ const supportedHashAlgorithms = {
254
252
  * could not be verified.
255
253
  */
256
254
  async function verifyRequest(request, options = {}) {
257
- const tracerProvider = options.tracerProvider ?? trace.getTracerProvider();
258
- const tracer = tracerProvider.getTracer(deno_default.name, deno_default.version);
259
- return await tracer.startActiveSpan("http_signatures.verify", async (span) => {
255
+ return await (options.tracerProvider ?? trace.getTracerProvider()).getTracer(deno_default.name, deno_default.version).startActiveSpan("http_signatures.verify", async (span) => {
260
256
  if (span.isRecording()) {
261
257
  span.setAttribute(ATTR_HTTP_REQUEST_METHOD, request.method);
262
258
  span.setAttribute(ATTR_URL_FULL, request.url);
@@ -367,7 +363,7 @@ async function verifyRequestDraft(request, span, { documentLoader, contextLoader
367
363
  return null;
368
364
  }
369
365
  }
370
- const sigValues = Object.fromEntries(sigHeader.split(",").map((pair) => pair.match(/^\s*([A-Za-z]+)="([^"]*)"\s*$/)).filter((m) => m != null).map((m) => m.slice(1, 3)));
366
+ const sigValues = Object.fromEntries(sigHeader.split(",").map((pair) => pair.match(/^\s*([A-Za-z]+)=(?:"([^"]*)"|(\d+))\s*$/)).filter((m) => m != null).map((m) => [m[1], m[2] ?? m[3]]));
371
367
  if (!("keyId" in sigValues)) {
372
368
  logger.debug("Failed to verify; no keyId field found in the Signature header.", { signature: sigHeader });
373
369
  return null;
@@ -378,6 +374,52 @@ async function verifyRequestDraft(request, span, { documentLoader, contextLoader
378
374
  logger.debug("Failed to verify; no signature field found in the Signature header.", { signature: sigHeader });
379
375
  return null;
380
376
  }
377
+ if ("expires" in sigValues) {
378
+ const expiresSeconds = parseInt(sigValues.expires);
379
+ if (!Number.isInteger(expiresSeconds)) {
380
+ logger.debug("Failed to verify; invalid expires field in the Signature header: {expires}.", {
381
+ expires: sigValues.expires,
382
+ signature: sigHeader
383
+ });
384
+ return null;
385
+ }
386
+ const expires = Temporal.Instant.fromEpochMilliseconds(expiresSeconds * 1e3);
387
+ if (Temporal.Instant.compare(now, expires) > 0) {
388
+ logger.debug("Failed to verify; signature expired at {expires} (now: {now}).", {
389
+ expires: expires.toString(),
390
+ now: now.toString(),
391
+ signature: sigHeader
392
+ });
393
+ return null;
394
+ }
395
+ }
396
+ if ("created" in sigValues) {
397
+ const createdSeconds = parseInt(sigValues.created);
398
+ if (!Number.isInteger(createdSeconds)) {
399
+ logger.debug("Failed to verify; invalid created field in the Signature header: {created}.", {
400
+ created: sigValues.created,
401
+ signature: sigHeader
402
+ });
403
+ return null;
404
+ }
405
+ if (timeWindow !== false) {
406
+ const created = Temporal.Instant.fromEpochMilliseconds(createdSeconds * 1e3);
407
+ const tw = timeWindow ?? { minutes: 1 };
408
+ if (Temporal.Instant.compare(created, now.add(tw)) > 0) {
409
+ logger.debug("Failed to verify; created is too far in the future.", {
410
+ created: created.toString(),
411
+ now: now.toString()
412
+ });
413
+ return null;
414
+ } else if (Temporal.Instant.compare(created, now.subtract(tw)) < 0) {
415
+ logger.debug("Failed to verify; created is too far in the past.", {
416
+ created: created.toString(),
417
+ now: now.toString()
418
+ });
419
+ return null;
420
+ }
421
+ }
422
+ }
381
423
  const { keyId, headers, signature } = sigValues;
382
424
  span?.setAttribute("http_signatures.key_id", keyId);
383
425
  if ("algorithm" in sigValues) span?.setAttribute("http_signatures.algorithm", sigValues.algorithm);
@@ -397,11 +439,10 @@ async function verifyRequestDraft(request, span, { documentLoader, contextLoader
397
439
  logger.debug("Failed to verify; required headers missing in the Signature header: {headers}.", { headers });
398
440
  return null;
399
441
  }
400
- const message = headerNames.map((name) => `${name}: ` + (name == "(request-target)" ? `${request.method.toLowerCase()} ${new URL(request.url).pathname}` : name == "host" ? request.headers.get("host") ?? new URL(request.url).host : request.headers.get(name))).join("\n");
442
+ const message = headerNames.map((name) => `${name}: ` + (name === "(request-target)" ? `${request.method.toLowerCase()} ${new URL(request.url).pathname}` : name === "(created)" ? sigValues.created ?? "" : name === "(expires)" ? sigValues.expires ?? "" : name === "host" ? request.headers.get("host") ?? new URL(request.url).host : request.headers.get(name))).join("\n");
401
443
  const sig = decodeBase64(signature);
402
444
  span?.setAttribute("http_signatures.signature", encodeHex(sig));
403
- const verified = await crypto.subtle.verify("RSASSA-PKCS1-v1_5", key.publicKey, sig, new TextEncoder().encode(message));
404
- if (!verified) {
445
+ if (!await crypto.subtle.verify("RSASSA-PKCS1-v1_5", key.publicKey, sig, new TextEncoder().encode(message))) {
405
446
  if (cached) {
406
447
  logger.debug("Failed to verify with the cached key {keyId}; signature {signature} is invalid. Retrying with the freshly fetched key...", {
407
448
  keyId,
@@ -566,8 +607,7 @@ async function verifyRequestRfc9421(request, span, { documentLoader, contextLoad
566
607
  continue;
567
608
  }
568
609
  const body = await request.arrayBuffer();
569
- const digestValid = await verifyRfc9421ContentDigest(contentDigestHeader, body);
570
- if (!digestValid) {
610
+ if (!await verifyRfc9421ContentDigest(contentDigestHeader, body)) {
571
611
  logger.debug("Failed to verify; Content-Digest verification failed.", { contentDigest: contentDigestHeader });
572
612
  continue;
573
613
  }
@@ -613,8 +653,7 @@ async function verifyRequestRfc9421(request, span, { documentLoader, contextLoad
613
653
  const signatureBaseBytes = new TextEncoder().encode(signatureBase);
614
654
  span?.setAttribute("http_signatures.signature", encodeHex(sigBytes));
615
655
  try {
616
- const verified = await crypto.subtle.verify(algorithm, key.publicKey, sigBytes, signatureBaseBytes);
617
- if (verified) {
656
+ if (await crypto.subtle.verify(algorithm, key.publicKey, sigBytes, signatureBaseBytes)) {
618
657
  validKey = key;
619
658
  break;
620
659
  } else if (cached) {
@@ -2,9 +2,9 @@
2
2
  import { Temporal } from "@js-temporal/polyfill";
3
3
  import { URLPattern } from "urlpattern-polyfill";
4
4
 
5
- import { deno_default } from "./docloader-NmruKwBX.js";
6
- import { CryptographicKey } from "./actor-DY_ediuQ.js";
7
- import { fetchKey, validateCryptoKey } from "./key-B4UFmNvL.js";
5
+ import { deno_default } from "./docloader-DOrb0fRp.js";
6
+ import { CryptographicKey } from "./actor-CxnKxfwH.js";
7
+ import { fetchKey, validateCryptoKey } from "./key-Bg_PKGpe.js";
8
8
  import { getLogger } from "@logtape/logtape";
9
9
  import { SpanStatusCode, trace } from "@opentelemetry/api";
10
10
  import { decodeBase64, encodeBase64 } from "byte-encodings/base64";
@@ -24,9 +24,7 @@ import { Item, decodeDict, encodeItem } from "structured-field-values";
24
24
  */
25
25
  async function signRequest(request, privateKey, keyId, options = {}) {
26
26
  validateCryptoKey(privateKey, "private");
27
- const tracerProvider = options.tracerProvider ?? trace.getTracerProvider();
28
- const tracer = tracerProvider.getTracer(deno_default.name, deno_default.version);
29
- return await tracer.startActiveSpan("http_signatures.sign", async (span) => {
27
+ return await (options.tracerProvider ?? trace.getTracerProvider()).getTracer(deno_default.name, deno_default.version).startActiveSpan("http_signatures.sign", async (span) => {
30
28
  try {
31
29
  const spec = options.spec ?? "draft-cavage-http-signatures-12";
32
30
  let signed;
@@ -253,9 +251,7 @@ const supportedHashAlgorithms = {
253
251
  * could not be verified.
254
252
  */
255
253
  async function verifyRequest(request, options = {}) {
256
- const tracerProvider = options.tracerProvider ?? trace.getTracerProvider();
257
- const tracer = tracerProvider.getTracer(deno_default.name, deno_default.version);
258
- return await tracer.startActiveSpan("http_signatures.verify", async (span) => {
254
+ return await (options.tracerProvider ?? trace.getTracerProvider()).getTracer(deno_default.name, deno_default.version).startActiveSpan("http_signatures.verify", async (span) => {
259
255
  if (span.isRecording()) {
260
256
  span.setAttribute(ATTR_HTTP_REQUEST_METHOD, request.method);
261
257
  span.setAttribute(ATTR_URL_FULL, request.url);
@@ -366,7 +362,7 @@ async function verifyRequestDraft(request, span, { documentLoader, contextLoader
366
362
  return null;
367
363
  }
368
364
  }
369
- const sigValues = Object.fromEntries(sigHeader.split(",").map((pair) => pair.match(/^\s*([A-Za-z]+)="([^"]*)"\s*$/)).filter((m) => m != null).map((m) => m.slice(1, 3)));
365
+ const sigValues = Object.fromEntries(sigHeader.split(",").map((pair) => pair.match(/^\s*([A-Za-z]+)=(?:"([^"]*)"|(\d+))\s*$/)).filter((m) => m != null).map((m) => [m[1], m[2] ?? m[3]]));
370
366
  if (!("keyId" in sigValues)) {
371
367
  logger.debug("Failed to verify; no keyId field found in the Signature header.", { signature: sigHeader });
372
368
  return null;
@@ -377,6 +373,52 @@ async function verifyRequestDraft(request, span, { documentLoader, contextLoader
377
373
  logger.debug("Failed to verify; no signature field found in the Signature header.", { signature: sigHeader });
378
374
  return null;
379
375
  }
376
+ if ("expires" in sigValues) {
377
+ const expiresSeconds = parseInt(sigValues.expires);
378
+ if (!Number.isInteger(expiresSeconds)) {
379
+ logger.debug("Failed to verify; invalid expires field in the Signature header: {expires}.", {
380
+ expires: sigValues.expires,
381
+ signature: sigHeader
382
+ });
383
+ return null;
384
+ }
385
+ const expires = Temporal.Instant.fromEpochMilliseconds(expiresSeconds * 1e3);
386
+ if (Temporal.Instant.compare(now, expires) > 0) {
387
+ logger.debug("Failed to verify; signature expired at {expires} (now: {now}).", {
388
+ expires: expires.toString(),
389
+ now: now.toString(),
390
+ signature: sigHeader
391
+ });
392
+ return null;
393
+ }
394
+ }
395
+ if ("created" in sigValues) {
396
+ const createdSeconds = parseInt(sigValues.created);
397
+ if (!Number.isInteger(createdSeconds)) {
398
+ logger.debug("Failed to verify; invalid created field in the Signature header: {created}.", {
399
+ created: sigValues.created,
400
+ signature: sigHeader
401
+ });
402
+ return null;
403
+ }
404
+ if (timeWindow !== false) {
405
+ const created = Temporal.Instant.fromEpochMilliseconds(createdSeconds * 1e3);
406
+ const tw = timeWindow ?? { minutes: 1 };
407
+ if (Temporal.Instant.compare(created, now.add(tw)) > 0) {
408
+ logger.debug("Failed to verify; created is too far in the future.", {
409
+ created: created.toString(),
410
+ now: now.toString()
411
+ });
412
+ return null;
413
+ } else if (Temporal.Instant.compare(created, now.subtract(tw)) < 0) {
414
+ logger.debug("Failed to verify; created is too far in the past.", {
415
+ created: created.toString(),
416
+ now: now.toString()
417
+ });
418
+ return null;
419
+ }
420
+ }
421
+ }
380
422
  const { keyId, headers, signature } = sigValues;
381
423
  span?.setAttribute("http_signatures.key_id", keyId);
382
424
  if ("algorithm" in sigValues) span?.setAttribute("http_signatures.algorithm", sigValues.algorithm);
@@ -396,11 +438,10 @@ async function verifyRequestDraft(request, span, { documentLoader, contextLoader
396
438
  logger.debug("Failed to verify; required headers missing in the Signature header: {headers}.", { headers });
397
439
  return null;
398
440
  }
399
- const message = headerNames.map((name) => `${name}: ` + (name == "(request-target)" ? `${request.method.toLowerCase()} ${new URL(request.url).pathname}` : name == "host" ? request.headers.get("host") ?? new URL(request.url).host : request.headers.get(name))).join("\n");
441
+ const message = headerNames.map((name) => `${name}: ` + (name === "(request-target)" ? `${request.method.toLowerCase()} ${new URL(request.url).pathname}` : name === "(created)" ? sigValues.created ?? "" : name === "(expires)" ? sigValues.expires ?? "" : name === "host" ? request.headers.get("host") ?? new URL(request.url).host : request.headers.get(name))).join("\n");
400
442
  const sig = decodeBase64(signature);
401
443
  span?.setAttribute("http_signatures.signature", encodeHex(sig));
402
- const verified = await crypto.subtle.verify("RSASSA-PKCS1-v1_5", key.publicKey, sig, new TextEncoder().encode(message));
403
- if (!verified) {
444
+ if (!await crypto.subtle.verify("RSASSA-PKCS1-v1_5", key.publicKey, sig, new TextEncoder().encode(message))) {
404
445
  if (cached) {
405
446
  logger.debug("Failed to verify with the cached key {keyId}; signature {signature} is invalid. Retrying with the freshly fetched key...", {
406
447
  keyId,
@@ -565,8 +606,7 @@ async function verifyRequestRfc9421(request, span, { documentLoader, contextLoad
565
606
  continue;
566
607
  }
567
608
  const body = await request.arrayBuffer();
568
- const digestValid = await verifyRfc9421ContentDigest(contentDigestHeader, body);
569
- if (!digestValid) {
609
+ if (!await verifyRfc9421ContentDigest(contentDigestHeader, body)) {
570
610
  logger.debug("Failed to verify; Content-Digest verification failed.", { contentDigest: contentDigestHeader });
571
611
  continue;
572
612
  }
@@ -612,8 +652,7 @@ async function verifyRequestRfc9421(request, span, { documentLoader, contextLoad
612
652
  const signatureBaseBytes = new TextEncoder().encode(signatureBase);
613
653
  span?.setAttribute("http_signatures.signature", encodeHex(sigBytes));
614
654
  try {
615
- const verified = await crypto.subtle.verify(algorithm, key.publicKey, sigBytes, signatureBaseBytes);
616
- if (verified) {
655
+ if (await crypto.subtle.verify(algorithm, key.publicKey, sigBytes, signatureBaseBytes)) {
617
656
  validKey = key;
618
657
  break;
619
658
  } else if (cached) {
@@ -3,9 +3,9 @@
3
3
  import { URLPattern } from "urlpattern-polyfill";
4
4
  globalThis.addEventListener = () => {};
5
5
 
6
- import { deno_default } from "./docloader-UqcHJRjL.js";
7
- import { Activity } from "./vocab-Cs9KbJAx.js";
8
- import { getTypeId } from "./type-D2s5lmbZ.js";
6
+ import { deno_default } from "./docloader-BYgIBZvh.js";
7
+ import { Activity } from "./vocab-lzEJg2bH.js";
8
+ import { getTypeId } from "./type-CFuiGLz9.js";
9
9
  import { getLogger } from "@logtape/logtape";
10
10
  import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
11
11
 
@@ -55,8 +55,7 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
55
55
  activity.id.href
56
56
  ];
57
57
  if (cacheKey != null) {
58
- const cached = await kv.get(cacheKey);
59
- if (cached === true) {
58
+ if (await kv.get(cacheKey) === true) {
60
59
  logger.debug("Activity {activityId} has already been processed.", {
61
60
  activityId: activity.id?.href,
62
61
  activity: json,
@@ -113,8 +112,7 @@ async function routeActivity({ context: ctx, json, activity, recipient, inboxLis
113
112
  return "enqueued";
114
113
  }
115
114
  tracerProvider = tracerProvider ?? trace.getTracerProvider();
116
- const tracer = tracerProvider.getTracer(deno_default.name, deno_default.version);
117
- return await tracer.startActiveSpan("activitypub.dispatch_inbox_listener", { kind: SpanKind.INTERNAL }, async (span$1) => {
115
+ return await tracerProvider.getTracer(deno_default.name, deno_default.version).startActiveSpan("activitypub.dispatch_inbox_listener", { kind: SpanKind.INTERNAL }, async (span$1) => {
118
116
  const dispatched = inboxListeners?.dispatchWithClass(activity);
119
117
  if (dispatched == null) {
120
118
  logger.error("Unsupported activity type:\n{activity}", {
@@ -0,0 +1,16 @@
1
+
2
+ import { Temporal } from "@js-temporal/polyfill";
3
+ import { URLPattern } from "urlpattern-polyfill";
4
+ globalThis.addEventListener = () => {};
5
+
6
+ import "./docloader-BYgIBZvh.js";
7
+ import "./url-C2xuoQD1.js";
8
+ import "./multibase-CnLHszip.js";
9
+ import "./vocab-lzEJg2bH.js";
10
+ import "./langstr-DbWheeIS.js";
11
+ import "./lookup-Cutk-FXA.js";
12
+ import "./type-CFuiGLz9.js";
13
+ import "./actor-KrOvzFz1.js";
14
+ import { exportJwk, fetchKey, generateCryptoKeyPair, importJwk, validateCryptoKey } from "./key-BTcmhZp7.js";
15
+
16
+ export { validateCryptoKey };
@@ -3,9 +3,9 @@
3
3
  import { URLPattern } from "urlpattern-polyfill";
4
4
  globalThis.addEventListener = () => {};
5
5
 
6
- import { deno_default, getDocumentLoader } from "./docloader-UqcHJRjL.js";
7
- import { CryptographicKey, Object as Object$1 } from "./vocab-Cs9KbJAx.js";
8
- import { isActor } from "./actor-qNVQigMJ.js";
6
+ import { deno_default, getDocumentLoader } from "./docloader-BYgIBZvh.js";
7
+ import { CryptographicKey, Object as Object$1 } from "./vocab-lzEJg2bH.js";
8
+ import { isActor } from "./actor-KrOvzFz1.js";
9
9
  import { getLogger } from "@logtape/logtape";
10
10
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
11
11
 
@@ -23,8 +23,7 @@ function validateCryptoKey(key, type) {
23
23
  if (!key.extractable) throw new TypeError("The key is not extractable.");
24
24
  if (key.algorithm.name !== "RSASSA-PKCS1-v1_5" && key.algorithm.name !== "Ed25519") throw new TypeError("Currently only RSASSA-PKCS1-v1_5 and Ed25519 keys are supported. More algorithms will be added in the future!");
25
25
  if (key.algorithm.name === "RSASSA-PKCS1-v1_5") {
26
- const algorithm = key.algorithm;
27
- if (algorithm.hash.name !== "SHA-256") throw new TypeError("For compatibility with the existing Fediverse software (e.g., Mastodon), hash algorithm for RSASSA-PKCS1-v1_5 keys must be SHA-256.");
26
+ if (key.algorithm.hash.name !== "SHA-256") throw new TypeError("For compatibility with the existing Fediverse software (e.g., Mastodon), hash algorithm for RSASSA-PKCS1-v1_5 keys must be SHA-256.");
28
27
  }
29
28
  }
30
29
  /**
@@ -103,8 +102,7 @@ async function importJwk(jwk, type) {
103
102
  * @since 1.3.0
104
103
  */
105
104
  function fetchKey(keyId, cls, options = {}) {
106
- const tracerProvider = options.tracerProvider ?? trace.getTracerProvider();
107
- const tracer = tracerProvider.getTracer(deno_default.name, deno_default.version);
105
+ const tracer = (options.tracerProvider ?? trace.getTracerProvider()).getTracer(deno_default.name, deno_default.version);
108
106
  keyId = typeof keyId === "string" ? new URL(keyId) : keyId;
109
107
  return tracer.startActiveSpan("activitypub.fetch_key", {
110
108
  kind: SpanKind.CLIENT,
@@ -160,8 +158,7 @@ async function fetchKeyInternal(keyId, cls, { documentLoader, contextLoader, key
160
158
  logger.debug("Fetching key {keyId} to verify signature...", { keyId });
161
159
  let document;
162
160
  try {
163
- const remoteDocument = await (documentLoader ?? getDocumentLoader())(keyId);
164
- document = remoteDocument.document;
161
+ document = (await (documentLoader ?? getDocumentLoader())(keyId)).document;
165
162
  } catch (_) {
166
163
  logger.debug("Failed to fetch key {keyId}.", { keyId });
167
164
  await keyCache?.set(cacheKey, null);
@@ -2,8 +2,8 @@
2
2
  import { Temporal } from "@js-temporal/polyfill";
3
3
  import { URLPattern } from "urlpattern-polyfill";
4
4
 
5
- import { deno_default, getDocumentLoader } from "./docloader-NmruKwBX.js";
6
- import { CryptographicKey, Object as Object$1, isActor } from "./actor-DY_ediuQ.js";
5
+ import { deno_default, getDocumentLoader } from "./docloader-DOrb0fRp.js";
6
+ import { CryptographicKey, Object as Object$1, isActor } from "./actor-CxnKxfwH.js";
7
7
  import { getLogger } from "@logtape/logtape";
8
8
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
9
9
 
@@ -21,8 +21,7 @@ function validateCryptoKey(key, type) {
21
21
  if (!key.extractable) throw new TypeError("The key is not extractable.");
22
22
  if (key.algorithm.name !== "RSASSA-PKCS1-v1_5" && key.algorithm.name !== "Ed25519") throw new TypeError("Currently only RSASSA-PKCS1-v1_5 and Ed25519 keys are supported. More algorithms will be added in the future!");
23
23
  if (key.algorithm.name === "RSASSA-PKCS1-v1_5") {
24
- const algorithm = key.algorithm;
25
- if (algorithm.hash.name !== "SHA-256") throw new TypeError("For compatibility with the existing Fediverse software (e.g., Mastodon), hash algorithm for RSASSA-PKCS1-v1_5 keys must be SHA-256.");
24
+ if (key.algorithm.hash.name !== "SHA-256") throw new TypeError("For compatibility with the existing Fediverse software (e.g., Mastodon), hash algorithm for RSASSA-PKCS1-v1_5 keys must be SHA-256.");
26
25
  }
27
26
  }
28
27
  /**
@@ -101,8 +100,7 @@ async function importJwk(jwk, type) {
101
100
  * @since 1.3.0
102
101
  */
103
102
  function fetchKey(keyId, cls, options = {}) {
104
- const tracerProvider = options.tracerProvider ?? trace.getTracerProvider();
105
- const tracer = tracerProvider.getTracer(deno_default.name, deno_default.version);
103
+ const tracer = (options.tracerProvider ?? trace.getTracerProvider()).getTracer(deno_default.name, deno_default.version);
106
104
  keyId = typeof keyId === "string" ? new URL(keyId) : keyId;
107
105
  return tracer.startActiveSpan("activitypub.fetch_key", {
108
106
  kind: SpanKind.CLIENT,
@@ -158,8 +156,7 @@ async function fetchKeyInternal(keyId, cls, { documentLoader, contextLoader, key
158
156
  logger.debug("Fetching key {keyId} to verify signature...", { keyId });
159
157
  let document;
160
158
  try {
161
- const remoteDocument = await (documentLoader ?? getDocumentLoader())(keyId);
162
- document = remoteDocument.document;
159
+ document = (await (documentLoader ?? getDocumentLoader())(keyId)).document;
163
160
  } catch (_) {
164
161
  logger.debug("Failed to fetch key {keyId}.", { keyId });
165
162
  await keyCache?.set(cacheKey, null);
@@ -2,9 +2,9 @@
2
2
  import { Temporal } from "@js-temporal/polyfill";
3
3
  import { URLPattern } from "urlpattern-polyfill";
4
4
 
5
- import "./docloader-NmruKwBX.js";
6
- import "./actor-DY_ediuQ.js";
7
- import "./lookup-DHlMvC3Q.js";
8
- import { exportJwk, fetchKey, generateCryptoKeyPair, importJwk, validateCryptoKey } from "./key-B4UFmNvL.js";
5
+ import "./docloader-DOrb0fRp.js";
6
+ import "./actor-CxnKxfwH.js";
7
+ import "./lookup-DrAS097Q.js";
8
+ import { exportJwk, fetchKey, generateCryptoKeyPair, importJwk, validateCryptoKey } from "./key-Bg_PKGpe.js";
9
9
 
10
10
  export { validateCryptoKey };
@@ -3,7 +3,7 @@
3
3
  import { URLPattern } from "urlpattern-polyfill";
4
4
  globalThis.addEventListener = () => {};
5
5
 
6
- import { CryptographicKey, Multikey } from "./vocab-Cs9KbJAx.js";
6
+ import { CryptographicKey, Multikey } from "./vocab-lzEJg2bH.js";
7
7
 
8
8
  //#region federation/keycache.ts
9
9
  var KvKeyCache = class {
@@ -28,7 +28,7 @@ var KvKeyCache = class {
28
28
  return await Multikey.fromJsonLd(serialized, this.options);
29
29
  } catch {
30
30
  await this.kv.delete([...this.prefix, keyId.href]);
31
- return void 0;
31
+ return;
32
32
  }
33
33
  }
34
34
  }
@@ -3,7 +3,7 @@
3
3
  import { URLPattern } from "urlpattern-polyfill";
4
4
  globalThis.addEventListener = () => {};
5
5
 
6
- import { CryptographicKey, Multikey, importSpki } from "./vocab-Cs9KbJAx.js";
6
+ import { CryptographicKey, Multikey, importSpki } from "./vocab-lzEJg2bH.js";
7
7
 
8
8
  //#region testing/keys.ts
9
9
  const rsaPublicKey1 = new CryptographicKey({
@@ -3,10 +3,10 @@
3
3
  import { URLPattern } from "urlpattern-polyfill";
4
4
  globalThis.addEventListener = () => {};
5
5
 
6
- import { deno_default, getDocumentLoader } from "./docloader-UqcHJRjL.js";
7
- import { Activity, CryptographicKey, Object as Object$1 } from "./vocab-Cs9KbJAx.js";
8
- import { getTypeId } from "./type-D2s5lmbZ.js";
9
- import { fetchKey, validateCryptoKey } from "./key-CUEyp_yz.js";
6
+ import { deno_default, getDocumentLoader } from "./docloader-BYgIBZvh.js";
7
+ import { Activity, CryptographicKey, Object as Object$1 } from "./vocab-lzEJg2bH.js";
8
+ import { getTypeId } from "./type-CFuiGLz9.js";
9
+ import { fetchKey, validateCryptoKey } from "./key-BTcmhZp7.js";
10
10
  import { getLogger } from "@logtape/logtape";
11
11
  import { SpanStatusCode, trace } from "@opentelemetry/api";
12
12
  import { decodeBase64, encodeBase64 } from "byte-encodings/base64";
@@ -57,8 +57,7 @@ async function createSignature(jsonLd, privateKey, keyId, { contextLoader, creat
57
57
  const optionsHash = await hashJsonLd(options, contextLoader);
58
58
  const docHash = await hashJsonLd(jsonLd, contextLoader);
59
59
  const message = optionsHash + docHash;
60
- const encoder = new TextEncoder();
61
- const messageBytes = encoder.encode(message);
60
+ const messageBytes = new TextEncoder().encode(message);
62
61
  const signature = await crypto.subtle.sign("RSASSA-PKCS1-v1_5", privateKey, messageBytes);
63
62
  return {
64
63
  ...options,
@@ -80,9 +79,7 @@ async function createSignature(jsonLd, privateKey, keyId, { contextLoader, creat
80
79
  * @since 1.0.0
81
80
  */
82
81
  async function signJsonLd(jsonLd, privateKey, keyId, options) {
83
- const tracerProvider = options.tracerProvider ?? trace.getTracerProvider();
84
- const tracer = tracerProvider.getTracer(deno_default.name, deno_default.version);
85
- return await tracer.startActiveSpan("ld_signatures.sign", { attributes: { "ld_signatures.key_id": keyId.href } }, async (span) => {
82
+ return await (options.tracerProvider ?? trace.getTracerProvider()).getTracer(deno_default.name, deno_default.version).startActiveSpan("ld_signatures.sign", { attributes: { "ld_signatures.key_id": keyId.href } }, async (span) => {
86
83
  try {
87
84
  const signature = await createSignature(jsonLd, privateKey, keyId, options);
88
85
  if (span.isRecording()) {
@@ -184,8 +181,7 @@ async function verifySignature(jsonLd, options = {}) {
184
181
  const encoder = new TextEncoder();
185
182
  const message = sigOptsHash + docHash;
186
183
  const messageBytes = encoder.encode(message);
187
- const verified = await crypto.subtle.verify("RSASSA-PKCS1-v1_5", key.publicKey, signature, messageBytes);
188
- if (verified) return key;
184
+ if (await crypto.subtle.verify("RSASSA-PKCS1-v1_5", key.publicKey, signature.slice(), messageBytes)) return key;
189
185
  if (cached) {
190
186
  logger.debug("Failed to verify with the cached key {keyId}; signature {signatureValue} is invalid. Retrying with the freshly fetched key...", {
191
187
  keyId: sig.creator,
@@ -199,8 +195,7 @@ async function verifySignature(jsonLd, options = {}) {
199
195
  }
200
196
  });
201
197
  if (key$1 == null) return null;
202
- const verified$1 = await crypto.subtle.verify("RSASSA-PKCS1-v1_5", key$1.publicKey, signature, messageBytes);
203
- return verified$1 ? key$1 : null;
198
+ return await crypto.subtle.verify("RSASSA-PKCS1-v1_5", key$1.publicKey, signature.slice(), messageBytes) ? key$1 : null;
204
199
  }
205
200
  logger.debug("Failed to verify with the fetched key {keyId}; signature {signatureValue} is invalid. Check if the key is correct or if the signed message is correct. The message to sign is:\n{message}", {
206
201
  keyId: sig.creator,
@@ -219,9 +214,7 @@ async function verifySignature(jsonLd, options = {}) {
219
214
  * @returns `true` if the document is authentic; `false` otherwise.
220
215
  */
221
216
  async function verifyJsonLd(jsonLd, options = {}) {
222
- const tracerProvider = options.tracerProvider ?? trace.getTracerProvider();
223
- const tracer = tracerProvider.getTracer(deno_default.name, deno_default.version);
224
- return await tracer.startActiveSpan("ld_signatures.verify", async (span) => {
217
+ return await (options.tracerProvider ?? trace.getTracerProvider()).getTracer(deno_default.name, deno_default.version).startActiveSpan("ld_signatures.verify", async (span) => {
225
218
  try {
226
219
  const object = await Object$1.fromJsonLd(jsonLd, options);
227
220
  if (object.id != null) span.setAttribute("activitypub.object.id", object.id.href);