@fedify/fedify 1.7.12 → 1.7.14
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/{actor-CPpvuBKU.d.ts → actor-Dx5YX74F.d.ts} +3 -3
- package/dist/{actor-GZRoTRqg.js → actor-OYVgkiZG.js} +329 -465
- package/dist/{actor-TYqJGdVq.js → actor-y7shp0CR.js} +6 -6
- package/dist/{assert-LOEeCUK5.js → assert-C-mZuSQl.js} +2 -2
- package/dist/{assert_equals-B44MxcIj.js → assert_equals-Dy0MG_Zw.js} +16 -16
- package/dist/{assert_instance_of-XtuFevV_.js → assert_instance_of-lS0Jr2iu.js} +2 -2
- package/dist/{assert_is_error-BTlryvT0.js → assert_is_error-CIYFACrT.js} +2 -2
- package/dist/{assert_not_equals-C685gKx6.js → assert_not_equals-C1azCAB0.js} +3 -6
- package/dist/{assert_rejects-DWQ4jaf9.js → assert_rejects-Bkh5lA1a.js} +3 -3
- package/dist/{assert_throws-YetpVSc-.js → assert_throws-CmpfkWEM.js} +3 -3
- package/dist/{authdocloader-hiFSyM7V.js → authdocloader-C0JcfxUs.js} +7 -9
- package/dist/{authdocloader-BopfyipD.js → authdocloader-C2krvRo1.js} +6 -8
- package/dist/{builder-mi6RdzNE.js → builder-DaECqbbT.js} +7 -7
- package/dist/{chunk-Cx8LTkjm.js → chunk-DvTpRkcT.js} +22 -15
- package/dist/{client-DuUKk4pk.js → client-dfu57WLy.js} +3 -3
- package/dist/{client-DvtwXO7t.d.ts → client-wKLuY12i.d.ts} +2 -2
- package/dist/{collection-Dfb0TPno.js → collection-XNLQhehO.js} +2 -3
- package/dist/{mod-DFncUgcE.d.ts → compat/mod-Bwqav33K.d.ts} +3 -3
- package/dist/compat/mod.js +1 -2
- package/dist/compat/transformers.test.js +34 -38
- package/dist/{context-OBWjptjU.d.ts → context-CW_8R4BH.d.ts} +11 -11
- package/dist/{context-RMU32mk4.js → context-nxDPfAiA.js} +5 -5
- package/dist/{docloader-09nVWLAZ.js → docloader-BDSHZfTJ.js} +1 -1
- package/dist/{docloader-DJxET2fN.js → docloader-BU25UQLB.js} +31 -151
- package/dist/{docloader-Q42SMRIB.d.ts → docloader-D_MGP37Q.d.ts} +2 -2
- package/dist/{docloader-bgBm1Hd1.js → docloader-sGz4vcrK.js} +32 -152
- package/dist/{esm-DO9PrujO.js → esm-BRXvTSrx.js} +36 -22
- package/dist/federation/builder.test.js +20 -26
- package/dist/federation/collection.test.js +11 -16
- package/dist/federation/handler.test.js +41 -48
- package/dist/federation/inbox.test.js +11 -11
- package/dist/federation/keycache.test.js +12 -11
- package/dist/federation/kv.test.js +8 -10
- package/dist/federation/middleware.test.js +105 -134
- package/dist/federation/mod-DvlLc8Ru.d.ts +6 -0
- package/dist/federation/mod.js +206 -12
- package/dist/federation/mq.test.js +12 -17
- package/dist/federation/retry.test.js +5 -4
- package/dist/federation/router.test.js +9 -10
- package/dist/federation/send.test.js +23 -24
- package/dist/{http-D2DkwsjA.js → http-BhES0Sg9.js} +23 -30
- package/dist/{http-DMTrO3Ye.d.ts → http-DFZRNfDP.d.ts} +3 -3
- package/dist/{http-vqMAvOVs.js → http-DRePVWfE.js} +23 -30
- package/dist/{inbox-DAAZZl2k.js → inbox-Z_QlSGYN.js} +5 -5
- package/dist/key-CJuc1GE1.js +10 -0
- package/dist/{key-DmqJj57e.js → key-D2HZrCxh.js} +4 -4
- package/dist/{key-KVhaUM92.js → key-bMrDyA2T.js} +5 -5
- package/dist/key-eP5Yk7hl.js +16 -0
- package/dist/{keycache-C1pEuRyQ.js → keycache-B5Rr8Lbc.js} +2 -2
- package/dist/{keys-C4XQHW5_.js → keys-DK4k1R8e.js} +2 -2
- package/dist/{kv-DRaeSXco.d.ts → kv-CKqSUxHd.d.ts} +1 -1
- package/dist/{kv-BMY6Qf_A.js → kv-QeuZ51go.js} +1 -1
- package/dist/{langstr-DbWheeIS.js → langstr-pFHBDU4y.js} +1 -1
- package/dist/{ld-DzlJ_IpT.js → ld--auFISy7.js} +9 -12
- package/dist/{lookup-CSngxuWm.js → lookup-4uc2jh52.js} +3 -3
- package/dist/{lookup-DL62q3Xh.js → lookup-Bq8vhIG4.js} +4 -4
- package/dist/{lookup-Bf-K85bV.d.ts → lookup-CqI9jhXo.d.ts} +2 -2
- package/dist/{lookup-D0je8AqR.js → lookup-cHV4n1IB.js} +6 -6
- package/dist/middleware-72Tuh6Iw.js +17 -0
- package/dist/{middleware-3wVT6S9E.js → middleware-B7p-onxQ.js} +35 -39
- package/dist/middleware-DB3BUysJ.js +32 -0
- package/dist/{middleware-DlDXugwZ.js → middleware-QJjRfjjc.js} +38 -43
- package/dist/{mod-CCL2cvnI.d.ts → mod-BEZy4WHZ.d.ts} +4 -4
- package/dist/mod-DMn4Bxyg.d.ts +20 -0
- package/dist/{mod-CDzlVCUF.d.ts → mod-DjGYGrBd.d.ts} +3 -3
- package/dist/mod.js +17 -17
- package/dist/{mq-DYKDDJmp.d.ts → mq-Cgfbl44M.d.ts} +1 -1
- package/dist/{multibase-CnLHszip.js → multibase-DBcKTV2a.js} +1 -1
- package/dist/nodeinfo/client.test.js +19 -28
- package/dist/nodeinfo/handler.test.js +37 -44
- package/dist/nodeinfo/mod--upgPcaX.d.ts +4 -0
- package/dist/nodeinfo/mod.js +2 -3
- package/dist/nodeinfo/semver.test.js +13 -21
- package/dist/nodeinfo/types.test.js +9 -11
- package/dist/{owner-D0cOz8R5.d.ts → owner-CewLNqWO.d.ts} +4 -4
- package/dist/{owner-81ey_76X.js → owner-DkTRLQAV.js} +4 -4
- package/dist/{proof-DlI7QNdn.js → proof-D4HXHDPz.js} +14 -19
- package/dist/{proof-BlULDH4H.js → proof-DT_xxRPF.js} +11 -13
- package/dist/{retry-BiIhZWgD.js → retry-BQet39_l.js} +1 -1
- package/dist/{router-D_aVZZUc.js → router-BuDkN4RQ.js} +1 -1
- package/dist/runtime/authdocloader.test.js +22 -26
- package/dist/runtime/docloader.test.js +56 -56
- package/dist/runtime/key.test.js +31 -53
- package/dist/runtime/langstr.test.js +8 -10
- package/dist/{mod-qFMzZ3iF.d.ts → runtime/mod-BaH1vtHp.d.ts} +4 -3
- package/dist/runtime/mod.js +6 -7
- package/dist/runtime/multibase/multibase.test.js +12 -18
- package/dist/runtime/url.test.js +7 -6
- package/dist/{semver-BNrOOAs9.js → semver-D9d-VO-_.js} +7 -17
- package/dist/{send-DdxfTHo6.js → send-axIyQX2p.js} +4 -4
- package/dist/sig/http.test.js +104 -168
- package/dist/sig/key.test.js +17 -20
- package/dist/sig/ld.test.js +28 -35
- package/dist/sig/mod-BmJQTnPT.d.ts +6 -0
- package/dist/sig/mod.js +6 -7
- package/dist/sig/owner.test.js +24 -30
- package/dist/sig/proof.test.js +24 -29
- package/dist/{std__assert-o_r9vqm1.js → std__assert-BdP_WkD-.js} +7 -13
- package/dist/testing/docloader.test.js +9 -12
- package/dist/testing/mod.js +2 -1
- package/dist/{testing-DLyvtiiW.js → testing-qaAD4B0t.js} +2 -2
- package/dist/{transformers-ghwJuzGY.js → transformers-CFSWUhNi.js} +1 -1
- package/dist/{type-CFuiGLz9.js → type-DFsmi-p1.js} +1 -1
- package/dist/{types-CmVV9LT1.js → types-Bk4b1bGc.js} +8 -18
- package/dist/{types-CJHS5pXl.js → types-CB_2uuCA.js} +2 -2
- package/dist/{url-C2xuoQD1.js → url-BdNvnK9P.js} +1 -1
- package/dist/vocab/actor.test.js +76 -128
- package/dist/vocab/lookup.test.js +19 -23
- package/dist/vocab/mod-BkXGAYkx.d.ts +6 -0
- package/dist/vocab/mod.js +4 -4
- package/dist/vocab/type.test.js +10 -10
- package/dist/vocab/vocab.test.js +109 -105
- package/dist/{vocab-DFlq4Wa6.js → vocab-DCHRuBGF.js} +328 -464
- package/dist/{vocab-CzEfWQk2.d.ts → vocab-DoBcp8ow.d.ts} +2 -2
- package/dist/{vocab-BebR0kAy.js → vocab-sMuiKIWm.js} +5 -5
- package/dist/webfinger/handler.test.js +40 -48
- package/dist/webfinger/lookup.test.js +12 -15
- package/dist/webfinger/mod-B1UhCvlL.d.ts +4 -0
- package/dist/webfinger/mod.js +2 -3
- package/dist/x/{cfworkers.d.ts → cfworkers-BIcR64Sf.d.ts} +2 -2
- package/dist/x/cfworkers.test.js +7 -9
- package/dist/x/{hono.d.ts → hono-DmQmp8wi.d.ts} +2 -11
- package/dist/x/{sveltekit.d.ts → sveltekit-Cvh0XnN4.d.ts} +2 -11
- package/package.json +1 -1
- package/dist/compat/mod.d.ts +0 -15
- package/dist/compat-Bb5myD13.js +0 -4
- package/dist/federation/mod.d.ts +0 -15
- package/dist/federation-B0aljx0V.js +0 -203
- package/dist/key-5ssHQ67E.js +0 -16
- package/dist/key-DSJGnD10.js +0 -10
- package/dist/middleware-CxFoFBD8.js +0 -33
- package/dist/middleware-w7_U3fFi.js +0 -17
- package/dist/mod-1pDWKvUL.d.ts +0 -2
- package/dist/mod-GIh5OYxW.d.ts +0 -2
- package/dist/mod-g0xFzAP9.d.ts +0 -2
- package/dist/mod.d.ts +0 -20
- package/dist/nodeinfo/mod.d.ts +0 -7
- package/dist/nodeinfo-CyEbLjHs.js +0 -4
- package/dist/runtime/mod.d.ts +0 -8
- package/dist/runtime-BSkOVUWM.js +0 -4
- package/dist/sig/mod.d.ts +0 -10
- package/dist/sig-BXJO--F9.js +0 -4
- package/dist/vocab/mod.d.ts +0 -8
- package/dist/webfinger/mod.d.ts +0 -7
- package/dist/webfinger-C3GIyXIg.js +0 -4
- /package/dist/compat/{transformers.test.d.ts → transformers.test-DnJbd34u.d.ts} +0 -0
- /package/dist/{denokv-CvROlGYX.js → denokv-NcJeZ6rP.js} +0 -0
- /package/dist/federation/{builder.test.d.ts → builder.test-Bpt6NOZ6.d.ts} +0 -0
- /package/dist/federation/{collection.test.d.ts → collection.test-DKJ6JOZz.d.ts} +0 -0
- /package/dist/federation/{handler.test.d.ts → handler.test-BMT7uLC0.d.ts} +0 -0
- /package/dist/federation/{inbox.test.d.ts → inbox.test-Do6i02Qp.d.ts} +0 -0
- /package/dist/federation/{keycache.test.d.ts → keycache.test-BT83IPZY.d.ts} +0 -0
- /package/dist/federation/{kv.test.d.ts → kv.test-kFzzF2VN.d.ts} +0 -0
- /package/dist/federation/{middleware.test.d.ts → middleware.test-B1R4_e3-.d.ts} +0 -0
- /package/dist/federation/{mq.test.d.ts → mq.test-l79EQQOe.d.ts} +0 -0
- /package/dist/federation/{retry.test.d.ts → retry.test-BqS50VCX.d.ts} +0 -0
- /package/dist/federation/{router.test.d.ts → router.test-CYQl4po-.d.ts} +0 -0
- /package/dist/federation/{send.test.d.ts → send.test-COUnNUzv.d.ts} +0 -0
- /package/dist/nodeinfo/{client.test.d.ts → client.test-CZLe79hL.d.ts} +0 -0
- /package/dist/nodeinfo/{handler.test.d.ts → handler.test-B-EDZ_hK.d.ts} +0 -0
- /package/dist/nodeinfo/{semver.test.d.ts → semver.test-BEuuQSEM.d.ts} +0 -0
- /package/dist/nodeinfo/{types.test.d.ts → types.test-B5AT89WV.d.ts} +0 -0
- /package/dist/runtime/{authdocloader.test.d.ts → authdocloader.test-hCRKzn9v.d.ts} +0 -0
- /package/dist/runtime/{docloader.test.d.ts → docloader.test-CVd7i_5h.d.ts} +0 -0
- /package/dist/runtime/{key.test.d.ts → key.test-DBsILYSD.d.ts} +0 -0
- /package/dist/runtime/{langstr.test.d.ts → langstr.test-CiKxuuRY.d.ts} +0 -0
- /package/dist/runtime/multibase/{multibase.test.d.ts → multibase.test-Brh6gPBP.d.ts} +0 -0
- /package/dist/runtime/{url.test.d.ts → url.test-DlRqkU2j.d.ts} +0 -0
- /package/dist/sig/{http.test.d.ts → http.test-BpXNAWNI.d.ts} +0 -0
- /package/dist/sig/{key.test.d.ts → key.test-B2iLIugy.d.ts} +0 -0
- /package/dist/sig/{ld.test.d.ts → ld.test-D-cI70Gw.d.ts} +0 -0
- /package/dist/sig/{owner.test.d.ts → owner.test-B_YRjMPj.d.ts} +0 -0
- /package/dist/sig/{proof.test.d.ts → proof.test-BagEM_-4.d.ts} +0 -0
- /package/dist/testing/{docloader.test.d.ts → docloader.test-lrzf6sDZ.d.ts} +0 -0
- /package/dist/testing/{mod.d.ts → mod-3uM8ZvS7.d.ts} +0 -0
- /package/dist/vocab/{actor.test.d.ts → actor.test-ClC-iVWk.d.ts} +0 -0
- /package/dist/vocab/{lookup.test.d.ts → lookup.test-Cq1I-27w.d.ts} +0 -0
- /package/dist/vocab/{type.test.d.ts → type.test-bfFiYGcs.d.ts} +0 -0
- /package/dist/vocab/{vocab.test.d.ts → vocab.test-h-ZTisfu.d.ts} +0 -0
- /package/dist/webfinger/{handler.test.d.ts → handler.test-DiUeEDDD.d.ts} +0 -0
- /package/dist/webfinger/{lookup.test.d.ts → lookup.test-D9onm3U3.d.ts} +0 -0
- /package/dist/x/{cfworkers.test.d.ts → cfworkers.test-KXHlJ29z.d.ts} +0 -0
package/dist/sig/http.test.js
CHANGED
|
@@ -3,42 +3,38 @@
|
|
|
3
3
|
import { URLPattern } from "urlpattern-polyfill";
|
|
4
4
|
globalThis.addEventListener = () => {};
|
|
5
5
|
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
8
|
-
import "../
|
|
9
|
-
import "../docloader-
|
|
10
|
-
import "../url-
|
|
11
|
-
import "../multibase-
|
|
12
|
-
import { exportSpki } from "../vocab-
|
|
13
|
-
import "../langstr-
|
|
14
|
-
import "../lookup-
|
|
15
|
-
import "../
|
|
16
|
-
import "../
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import
|
|
22
|
-
import "../
|
|
23
|
-
import "../
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import { rsaPrivateKey2, rsaPublicKey1, rsaPublicKey2, rsaPublicKey5 } from "../keys-C4XQHW5_.js";
|
|
27
|
-
import { esm_default } from "../esm-DO9PrujO.js";
|
|
6
|
+
import "../chunk-DvTpRkcT.js";
|
|
7
|
+
import { t as assertEquals } from "../assert_equals-Dy0MG_Zw.js";
|
|
8
|
+
import { t as assert } from "../assert-C-mZuSQl.js";
|
|
9
|
+
import "../docloader-sGz4vcrK.js";
|
|
10
|
+
import "../url-BdNvnK9P.js";
|
|
11
|
+
import "../multibase-DBcKTV2a.js";
|
|
12
|
+
import { A as exportSpki } from "../vocab-DCHRuBGF.js";
|
|
13
|
+
import "../langstr-pFHBDU4y.js";
|
|
14
|
+
import "../lookup-Bq8vhIG4.js";
|
|
15
|
+
import "../actor-y7shp0CR.js";
|
|
16
|
+
import { t as exportJwk } from "../key-bMrDyA2T.js";
|
|
17
|
+
import { a as parseRfc9421Signature, c as timingSafeEqual, i as formatRfc9421SignatureParameters, l as verifyRequest, n as doubleKnock, o as parseRfc9421SignatureInput, r as formatRfc9421Signature, s as signRequest, t as createRfc9421SignatureBase } from "../http-BhES0Sg9.js";
|
|
18
|
+
import { t as test } from "../testing-qaAD4B0t.js";
|
|
19
|
+
import { a as assertExists, t as assertStringIncludes } from "../std__assert-BdP_WkD-.js";
|
|
20
|
+
import { n as assertFalse } from "../assert_rejects-Bkh5lA1a.js";
|
|
21
|
+
import "../assert_is_error-CIYFACrT.js";
|
|
22
|
+
import { t as assertThrows } from "../assert_throws-CmpfkWEM.js";
|
|
23
|
+
import { t as mockDocumentLoader } from "../docloader-BDSHZfTJ.js";
|
|
24
|
+
import { i as rsaPrivateKey2, l as rsaPublicKey5, o as rsaPublicKey1, s as rsaPublicKey2 } from "../keys-DK4k1R8e.js";
|
|
25
|
+
import { t as esm_default } from "../esm-BRXvTSrx.js";
|
|
28
26
|
import { encodeBase64 } from "byte-encodings/base64";
|
|
29
27
|
|
|
30
28
|
//#region sig/http.test.ts
|
|
31
29
|
test("signRequest() [draft-cavage]", async () => {
|
|
32
|
-
|
|
30
|
+
assertEquals(await verifyRequest(await signRequest(new Request("https://example.com/", {
|
|
33
31
|
method: "POST",
|
|
34
32
|
body: "Hello, world!",
|
|
35
33
|
headers: {
|
|
36
34
|
"Content-Type": "text/plain; charset=utf-8",
|
|
37
35
|
Accept: "text/plain"
|
|
38
36
|
}
|
|
39
|
-
})
|
|
40
|
-
const signed = await signRequest(request, rsaPrivateKey2, new URL("https://example.com/key2"));
|
|
41
|
-
assertEquals(await verifyRequest(signed, {
|
|
37
|
+
}), rsaPrivateKey2, new URL("https://example.com/key2")), {
|
|
42
38
|
contextLoader: mockDocumentLoader,
|
|
43
39
|
documentLoader: mockDocumentLoader
|
|
44
40
|
}), rsaPublicKey2);
|
|
@@ -153,7 +149,7 @@ test("verifyRequest() [draft-cavage]", async () => {
|
|
|
153
149
|
currentTime: Temporal.Instant.from("2025-01-01T00:00:00.0000Z"),
|
|
154
150
|
timeWindow: false
|
|
155
151
|
}), rsaPublicKey1);
|
|
156
|
-
|
|
152
|
+
assert(await verifyRequest(new Request("https://c27a97f98d5f.ngrok.app/i/inbox", {
|
|
157
153
|
method: "POST",
|
|
158
154
|
body: "{\"@context\":[\"https://www.w3.org/ns/activitystreams\",\"https://w3id.org/security/v1\"],\"actor\":\"https://oeee.cafe/ap/users/3609fd4e-d51d-4db8-9f04-4189815864dd\",\"object\":{\"actor\":\"https://c27a97f98d5f.ngrok.app/i\",\"object\":\"https://oeee.cafe/ap/users/3609fd4e-d51d-4db8-9f04-4189815864dd\",\"type\":\"Follow\",\"id\":\"https://c27a97f98d5f.ngrok.app/i#follows/https://oeee.cafe/ap/users/3609fd4e-d51d-4db8-9f04-4189815864dd\"},\"type\":\"Accept\",\"id\":\"https://oeee.cafe/objects/0fc2608f-5660-4b91-b8c7-63c0c2ac2e20\"}",
|
|
159
155
|
headers: {
|
|
@@ -163,12 +159,10 @@ test("verifyRequest() [draft-cavage]", async () => {
|
|
|
163
159
|
Digest: "SHA-256=YZyjeVQW5GwliJowASkteBJhFBTq3eQk/AMqRETc//A=",
|
|
164
160
|
Signature: "keyId=\"https://oeee.cafe/ap/users/3609fd4e-d51d-4db8-9f04-4189815864dd#main-key\",algorithm=\"hs2019\",created=\"1756126694\",expires=\"1756130294\",headers=\"(request-target) (created) (expires) content-type date digest host\",signature=\"XFb0jl2uMhE7RhbneE9sK9Zls2qZec8iy6+9O8UgDQeBGJThORFLjXKlps4QO1WAf1YSVB/i5aV6yF+h73Lm3ZiuAJDx1h+00iLsxoYuIw1CZvF0V2jELoo3sQ2/ZzqeoO6H5TbK7tKnU+ulFAPTuJgjIvPwYl11OMRouVS34NiaHP9Yx9pU813TLv37thG/hUKanyq8kk0IJWtDWteY/zxDvzoe7VOkBXVBHslMyrNAI/5JGulVQAQp/E61dJAhTHHIyGxkc/7iutWFZuqFXIiPJ9KR2OuKDj/B32hEzlsf5xH/CjqOJPIg1qMK8FzDiALCq6zjiKIBEnW8HQc/hQ==\""
|
|
165
161
|
}
|
|
166
|
-
})
|
|
167
|
-
const options2 = {
|
|
162
|
+
}), {
|
|
168
163
|
...options,
|
|
169
164
|
currentTime: Temporal.Instant.from("2025-08-25T12:58:14Z")
|
|
170
|
-
};
|
|
171
|
-
assert(await verifyRequest(request2, options2) != null);
|
|
165
|
+
}) != null);
|
|
172
166
|
});
|
|
173
167
|
test("signRequest() and verifyRequest() [rfc9421] implementation", async () => {
|
|
174
168
|
const currentTimestamp = 1709626184;
|
|
@@ -209,9 +203,7 @@ test("signRequest() and verifyRequest() [rfc9421] implementation", async () => {
|
|
|
209
203
|
const contentDigest = signed.headers.get("Content-Digest");
|
|
210
204
|
assertExists(contentDigest);
|
|
211
205
|
assert(contentDigest.startsWith("sha-256=:"), "Content-Digest should use RFC 9421 format");
|
|
212
|
-
|
|
213
|
-
const expectedDigestBase64 = encodeBase64(expectedDigest);
|
|
214
|
-
assertEquals(contentDigest, `sha-256=:${expectedDigestBase64}:`, "Content-Digest should have correct value");
|
|
206
|
+
assertEquals(contentDigest, `sha-256=:${encodeBase64(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(requestBody)))}:`, "Content-Digest should have correct value");
|
|
215
207
|
const signature = signed.headers.get("Signature");
|
|
216
208
|
assertExists(signature);
|
|
217
209
|
const sigFormat = /^sig1=:([A-Za-z0-9+/]+=*):/;
|
|
@@ -232,25 +224,22 @@ test("signRequest() and verifyRequest() [rfc9421] implementation", async () => {
|
|
|
232
224
|
assertEquals(parsedSig.sig1.byteLength > 0, true, "Signature value should be a non-empty Uint8Array");
|
|
233
225
|
const verifyHeaders = new Headers();
|
|
234
226
|
for (const [name, value] of signed.headers.entries()) if (name !== "Signature" && name !== "Signature-Input") verifyHeaders.set(name, value);
|
|
235
|
-
const
|
|
227
|
+
const reconstructedBase = createRfc9421SignatureBase(new Request(request.url, {
|
|
236
228
|
method: request.method,
|
|
237
229
|
headers: verifyHeaders
|
|
238
|
-
});
|
|
239
|
-
const reconstructedBase = createRfc9421SignatureBase(reconstructedRequest, parsedInput.sig1.components, parsedInput.sig1.parameters);
|
|
230
|
+
}), parsedInput.sig1.components, parsedInput.sig1.parameters);
|
|
240
231
|
const signatureBytes = new Uint8Array(parsedSig.sig1);
|
|
241
|
-
|
|
242
|
-
assert(signatureVerifies, "Manual verification of signature should succeed");
|
|
232
|
+
assert(await crypto.subtle.verify("RSASSA-PKCS1-v1_5", rsaPublicKey2.publicKey, signatureBytes, new TextEncoder().encode(reconstructedBase)), "Manual verification of signature should succeed");
|
|
243
233
|
});
|
|
244
234
|
test("createRfc9421SignatureBase()", () => {
|
|
245
|
-
|
|
235
|
+
assertEquals(createRfc9421SignatureBase(new Request("https://example.com/path?query=value", {
|
|
246
236
|
method: "POST",
|
|
247
237
|
headers: {
|
|
248
238
|
Host: "example.com",
|
|
249
239
|
Date: "Tue, 05 Mar 2024 07:49:44 GMT",
|
|
250
240
|
"Content-Type": "text/plain"
|
|
251
241
|
}
|
|
252
|
-
})
|
|
253
|
-
const signatureBase = createRfc9421SignatureBase(request, [
|
|
242
|
+
}), [
|
|
254
243
|
"@method",
|
|
255
244
|
"@target-uri",
|
|
256
245
|
"host",
|
|
@@ -259,31 +248,27 @@ test("createRfc9421SignatureBase()", () => {
|
|
|
259
248
|
algorithm: "rsa-v1_5-sha256",
|
|
260
249
|
keyId: new URL("https://example.com/key"),
|
|
261
250
|
created: 1709626184
|
|
262
|
-
}))
|
|
263
|
-
const expected = [
|
|
251
|
+
})), [
|
|
264
252
|
`"@method": POST`,
|
|
265
253
|
`"@target-uri": https://example.com/path?query=value`,
|
|
266
254
|
`"host": example.com`,
|
|
267
255
|
`"date": Tue, 05 Mar 2024 07:49:44 GMT`,
|
|
268
256
|
`"@signature-params": ("@method" "@target-uri" "host" "date");alg="rsa-v1_5-sha256";keyid="https://example.com/key";created=1709626184`
|
|
269
|
-
].join("\n");
|
|
270
|
-
assertEquals(signatureBase, expected);
|
|
257
|
+
].join("\n"));
|
|
271
258
|
});
|
|
272
259
|
test("formatRfc9421Signature()", () => {
|
|
273
|
-
const
|
|
260
|
+
const [signatureInput, signatureHeader] = formatRfc9421Signature(new Uint8Array([
|
|
274
261
|
1,
|
|
275
262
|
2,
|
|
276
263
|
3,
|
|
277
264
|
4
|
|
278
|
-
])
|
|
279
|
-
const keyId = new URL("https://example.com/key");
|
|
280
|
-
const [signatureInput, signatureHeader] = formatRfc9421Signature(signature, [
|
|
265
|
+
]), [
|
|
281
266
|
"@method",
|
|
282
267
|
"@target-uri",
|
|
283
268
|
"host"
|
|
284
269
|
], formatRfc9421SignatureParameters({
|
|
285
270
|
algorithm: "rsa-v1_5-sha256",
|
|
286
|
-
keyId,
|
|
271
|
+
keyId: new URL("https://example.com/key"),
|
|
287
272
|
created: 1709626184
|
|
288
273
|
}));
|
|
289
274
|
assertEquals(signatureInput, `sig1=("@method" "@target-uri" "host");alg="rsa-v1_5-sha256";keyid="https://example.com/key";created=1709626184`);
|
|
@@ -312,37 +297,33 @@ test("parseRfc9421Signature()", () => {
|
|
|
312
297
|
assertEquals(sig1Bytes[1], 2);
|
|
313
298
|
assertEquals(sig1Bytes[2], 3);
|
|
314
299
|
assertEquals(sig1Bytes[3], 4);
|
|
315
|
-
|
|
316
|
-
assertEquals(sig2Text, "foobar");
|
|
300
|
+
assertEquals(new TextDecoder().decode(parsed.sig2), "foobar");
|
|
317
301
|
});
|
|
318
302
|
test("verifyRequest() [rfc9421] successful GET verification", async () => {
|
|
319
303
|
const currentTimestamp = 1709626184;
|
|
320
304
|
const currentTime = Temporal.Instant.from("2024-03-05T08:09:44Z");
|
|
321
|
-
|
|
305
|
+
assertEquals(await verifyRequest(await signRequest(new Request("https://example.com/api/resource", {
|
|
322
306
|
method: "GET",
|
|
323
307
|
headers: {
|
|
324
308
|
"Accept": "application/json",
|
|
325
309
|
"Host": "example.com",
|
|
326
310
|
"Date": "Tue, 05 Mar 2024 07:49:44 GMT"
|
|
327
311
|
}
|
|
328
|
-
})
|
|
329
|
-
const signedRequest = await signRequest(validRequest, rsaPrivateKey2, new URL("https://example.com/key2"), {
|
|
312
|
+
}), rsaPrivateKey2, new URL("https://example.com/key2"), {
|
|
330
313
|
spec: "rfc9421",
|
|
331
314
|
currentTime
|
|
332
|
-
})
|
|
333
|
-
const verifiedKey = await verifyRequest(signedRequest, {
|
|
315
|
+
}), {
|
|
334
316
|
contextLoader: mockDocumentLoader,
|
|
335
317
|
documentLoader: mockDocumentLoader,
|
|
336
318
|
spec: "rfc9421",
|
|
337
319
|
currentTime: Temporal.Instant.from(`${(/* @__PURE__ */ new Date(currentTimestamp * 1e3)).toISOString()}`)
|
|
338
|
-
});
|
|
339
|
-
assertEquals(verifiedKey, rsaPublicKey2, "Valid signature should verify to the correct public key");
|
|
320
|
+
}), rsaPublicKey2, "Valid signature should verify to the correct public key");
|
|
340
321
|
});
|
|
341
322
|
test("verifyRequest() [rfc9421] manual POST verification", async () => {
|
|
342
323
|
const currentTimestamp = 1709626184;
|
|
343
324
|
const currentTime = Temporal.Instant.from("2024-03-05T08:09:44Z");
|
|
344
325
|
const postBody = "Test content for signature verification";
|
|
345
|
-
const
|
|
326
|
+
const signedPostRequest = await signRequest(new Request("https://example.com/api/resource", {
|
|
346
327
|
method: "POST",
|
|
347
328
|
body: postBody,
|
|
348
329
|
headers: {
|
|
@@ -351,8 +332,7 @@ test("verifyRequest() [rfc9421] manual POST verification", async () => {
|
|
|
351
332
|
"Host": "example.com",
|
|
352
333
|
"Date": "Tue, 05 Mar 2024 07:49:44 GMT"
|
|
353
334
|
}
|
|
354
|
-
})
|
|
355
|
-
const signedPostRequest = await signRequest(postRequest, rsaPrivateKey2, new URL("https://example.com/key2"), {
|
|
335
|
+
}), rsaPrivateKey2, new URL("https://example.com/key2"), {
|
|
356
336
|
spec: "rfc9421",
|
|
357
337
|
currentTime
|
|
358
338
|
});
|
|
@@ -374,61 +354,54 @@ test("verifyRequest() [rfc9421] manual POST verification", async () => {
|
|
|
374
354
|
assertExists(parsedSignature.sig1, "Should have a valid signature value");
|
|
375
355
|
assertEquals(parsedInput.sig1.keyId, "https://example.com/key2", "Signature should have the correct key ID");
|
|
376
356
|
assertEquals(parsedInput.sig1.created, currentTimestamp, "Signature should have the correct timestamp");
|
|
377
|
-
const
|
|
357
|
+
const signatureBase = createRfc9421SignatureBase(new Request("https://example.com/api/resource", {
|
|
378
358
|
method: "POST",
|
|
379
359
|
body: postBody,
|
|
380
360
|
headers: new Headers(signedPostRequest.headers)
|
|
381
|
-
});
|
|
382
|
-
|
|
383
|
-
const signatureVerified = await crypto.subtle.verify("RSASSA-PKCS1-v1_5", rsaPublicKey2.publicKey, parsedSignature.sig1.slice(), new TextEncoder().encode(signatureBase));
|
|
384
|
-
assert(signatureVerified, "Manual verification of POST signature should succeed");
|
|
361
|
+
}), parsedInput.sig1.components, parsedInput.sig1.parameters);
|
|
362
|
+
assert(await crypto.subtle.verify("RSASSA-PKCS1-v1_5", rsaPublicKey2.publicKey, parsedSignature.sig1.slice(), new TextEncoder().encode(signatureBase)), "Manual verification of POST signature should succeed");
|
|
385
363
|
});
|
|
386
364
|
test("verifyRequest() [rfc9421] error cases and edge cases", async () => {
|
|
387
365
|
const currentTimestamp = 1709626184;
|
|
388
366
|
const currentTime = Temporal.Instant.from("2024-03-05T08:09:44Z");
|
|
389
|
-
const
|
|
367
|
+
const signedRequest = await signRequest(new Request("https://example.com/api/resource", {
|
|
390
368
|
method: "GET",
|
|
391
369
|
headers: {
|
|
392
370
|
"Accept": "application/json",
|
|
393
371
|
"Host": "example.com",
|
|
394
372
|
"Date": "Tue, 05 Mar 2024 07:49:44 GMT"
|
|
395
373
|
}
|
|
396
|
-
})
|
|
397
|
-
const signedRequest = await signRequest(validRequest, rsaPrivateKey2, new URL("https://example.com/key2"), {
|
|
374
|
+
}), rsaPrivateKey2, new URL("https://example.com/key2"), {
|
|
398
375
|
spec: "rfc9421",
|
|
399
376
|
currentTime
|
|
400
377
|
});
|
|
401
378
|
const validSignatureInput = signedRequest.headers.get("Signature-Input") || "";
|
|
402
379
|
const validSignature = signedRequest.headers.get("Signature") || "";
|
|
403
|
-
|
|
380
|
+
assertEquals(await verifyRequest(new Request("https://example.com/api/resource", {
|
|
404
381
|
method: "GET",
|
|
405
382
|
headers: new Headers({
|
|
406
383
|
"Accept": "application/json",
|
|
407
384
|
"Host": "example.com",
|
|
408
385
|
"Signature": validSignature
|
|
409
386
|
})
|
|
410
|
-
})
|
|
411
|
-
const missingInputResult = await verifyRequest(missingInputHeader, {
|
|
387
|
+
}), {
|
|
412
388
|
contextLoader: mockDocumentLoader,
|
|
413
389
|
documentLoader: mockDocumentLoader,
|
|
414
390
|
spec: "rfc9421"
|
|
415
|
-
});
|
|
416
|
-
assertEquals(
|
|
417
|
-
const missingSignatureHeader = new Request("https://example.com/api/resource", {
|
|
391
|
+
}), null, "Should fail verification when Signature-Input header is missing");
|
|
392
|
+
assertEquals(await verifyRequest(new Request("https://example.com/api/resource", {
|
|
418
393
|
method: "GET",
|
|
419
394
|
headers: new Headers({
|
|
420
395
|
"Accept": "application/json",
|
|
421
396
|
"Host": "example.com",
|
|
422
397
|
"Signature-Input": validSignatureInput
|
|
423
398
|
})
|
|
424
|
-
})
|
|
425
|
-
const missingSignatureResult = await verifyRequest(missingSignatureHeader, {
|
|
399
|
+
}), {
|
|
426
400
|
contextLoader: mockDocumentLoader,
|
|
427
401
|
documentLoader: mockDocumentLoader,
|
|
428
402
|
spec: "rfc9421"
|
|
429
|
-
});
|
|
430
|
-
assertEquals(
|
|
431
|
-
const tamperedRequest = new Request("https://example.com/api/resource", {
|
|
403
|
+
}), null, "Should fail verification when Signature header is missing");
|
|
404
|
+
assertEquals(await verifyRequest(new Request("https://example.com/api/resource", {
|
|
432
405
|
method: "GET",
|
|
433
406
|
headers: new Headers({
|
|
434
407
|
"Accept": "application/json",
|
|
@@ -437,14 +410,12 @@ test("verifyRequest() [rfc9421] error cases and edge cases", async () => {
|
|
|
437
410
|
"Signature-Input": validSignatureInput,
|
|
438
411
|
"Signature": "sig1=:AAAAAA==:"
|
|
439
412
|
})
|
|
440
|
-
})
|
|
441
|
-
const tamperedResult = await verifyRequest(tamperedRequest, {
|
|
413
|
+
}), {
|
|
442
414
|
contextLoader: mockDocumentLoader,
|
|
443
415
|
documentLoader: mockDocumentLoader,
|
|
444
416
|
spec: "rfc9421"
|
|
445
|
-
});
|
|
446
|
-
assertEquals(
|
|
447
|
-
const expiredRequest = new Request("https://example.com/api/resource", {
|
|
417
|
+
}), null, "Should fail verification when signature is tampered");
|
|
418
|
+
assertEquals(await verifyRequest(new Request("https://example.com/api/resource", {
|
|
448
419
|
method: "GET",
|
|
449
420
|
headers: new Headers({
|
|
450
421
|
"Accept": "application/json",
|
|
@@ -453,16 +424,14 @@ test("verifyRequest() [rfc9421] error cases and edge cases", async () => {
|
|
|
453
424
|
"Signature-Input": validSignatureInput,
|
|
454
425
|
"Signature": validSignature
|
|
455
426
|
})
|
|
456
|
-
})
|
|
457
|
-
const expiredResult = await verifyRequest(expiredRequest, {
|
|
427
|
+
}), {
|
|
458
428
|
contextLoader: mockDocumentLoader,
|
|
459
429
|
documentLoader: mockDocumentLoader,
|
|
460
430
|
spec: "rfc9421",
|
|
461
431
|
currentTime: Temporal.Instant.from(`${(/* @__PURE__ */ new Date((currentTimestamp + 2592e3) * 1e3)).toISOString()}`),
|
|
462
432
|
timeWindow: { hours: 1 }
|
|
463
|
-
});
|
|
464
|
-
assertEquals(
|
|
465
|
-
const futureRequest = new Request("https://example.com/api/resource", {
|
|
433
|
+
}), null, "Should fail verification when signature timestamp is too old");
|
|
434
|
+
assertEquals(await verifyRequest(new Request("https://example.com/api/resource", {
|
|
466
435
|
method: "GET",
|
|
467
436
|
headers: new Headers({
|
|
468
437
|
"Accept": "application/json",
|
|
@@ -471,16 +440,14 @@ test("verifyRequest() [rfc9421] error cases and edge cases", async () => {
|
|
|
471
440
|
"Signature-Input": validSignatureInput,
|
|
472
441
|
"Signature": validSignature
|
|
473
442
|
})
|
|
474
|
-
})
|
|
475
|
-
const futureResult = await verifyRequest(futureRequest, {
|
|
443
|
+
}), {
|
|
476
444
|
contextLoader: mockDocumentLoader,
|
|
477
445
|
documentLoader: mockDocumentLoader,
|
|
478
446
|
spec: "rfc9421",
|
|
479
447
|
currentTime: Temporal.Instant.from(`${(/* @__PURE__ */ new Date((currentTimestamp - 2592e3) * 1e3)).toISOString()}`),
|
|
480
448
|
timeWindow: { hours: 1 }
|
|
481
|
-
});
|
|
482
|
-
assertEquals(
|
|
483
|
-
const timeCheckRequest = new Request("https://example.com/api/resource", {
|
|
449
|
+
}), null, "Should fail verification when signature timestamp is in the future");
|
|
450
|
+
assertEquals(await verifyRequest(new Request("https://example.com/api/resource", {
|
|
484
451
|
method: "GET",
|
|
485
452
|
headers: new Headers({
|
|
486
453
|
"Accept": "application/json",
|
|
@@ -489,16 +456,14 @@ test("verifyRequest() [rfc9421] error cases and edge cases", async () => {
|
|
|
489
456
|
"Signature-Input": validSignatureInput,
|
|
490
457
|
"Signature": validSignature
|
|
491
458
|
})
|
|
492
|
-
})
|
|
493
|
-
const timeDisabledResult = await verifyRequest(timeCheckRequest, {
|
|
459
|
+
}), {
|
|
494
460
|
contextLoader: mockDocumentLoader,
|
|
495
461
|
documentLoader: mockDocumentLoader,
|
|
496
462
|
spec: "rfc9421",
|
|
497
463
|
currentTime: Temporal.Instant.from(`${(/* @__PURE__ */ new Date((currentTimestamp + 31536e3) * 1e3)).toISOString()}`),
|
|
498
464
|
timeWindow: false
|
|
499
|
-
});
|
|
500
|
-
|
|
501
|
-
const postRequest = new Request("https://example.com/api/resource", {
|
|
465
|
+
}), rsaPublicKey2, "Should verify signature when time checking is disabled");
|
|
466
|
+
const freshSignedPostRequest = await signRequest(new Request("https://example.com/api/resource", {
|
|
502
467
|
method: "POST",
|
|
503
468
|
body: "Test content for signature verification",
|
|
504
469
|
headers: {
|
|
@@ -507,15 +472,14 @@ test("verifyRequest() [rfc9421] error cases and edge cases", async () => {
|
|
|
507
472
|
"Host": "example.com",
|
|
508
473
|
"Date": "Tue, 05 Mar 2024 07:49:44 GMT"
|
|
509
474
|
}
|
|
510
|
-
})
|
|
511
|
-
const freshSignedPostRequest = await signRequest(postRequest, rsaPrivateKey2, new URL("https://example.com/key2"), {
|
|
475
|
+
}), rsaPrivateKey2, new URL("https://example.com/key2"), {
|
|
512
476
|
spec: "rfc9421",
|
|
513
477
|
currentTime
|
|
514
478
|
});
|
|
515
479
|
const postSignatureInput = freshSignedPostRequest.headers.get("Signature-Input") || "";
|
|
516
480
|
const postSignature = freshSignedPostRequest.headers.get("Signature") || "";
|
|
517
481
|
const postContentDigest = freshSignedPostRequest.headers.get("Content-Digest") || "";
|
|
518
|
-
|
|
482
|
+
assertEquals(await verifyRequest(new Request("https://example.com/api/resource", {
|
|
519
483
|
method: "POST",
|
|
520
484
|
body: "This content won't match the digest",
|
|
521
485
|
headers: new Headers({
|
|
@@ -526,22 +490,19 @@ test("verifyRequest() [rfc9421] error cases and edge cases", async () => {
|
|
|
526
490
|
"Signature": postSignature,
|
|
527
491
|
"Content-Digest": postContentDigest
|
|
528
492
|
})
|
|
529
|
-
})
|
|
530
|
-
const tamperDigestResult = await verifyRequest(tamperDigestRequest, {
|
|
493
|
+
}), {
|
|
531
494
|
contextLoader: mockDocumentLoader,
|
|
532
495
|
documentLoader: mockDocumentLoader,
|
|
533
496
|
spec: "rfc9421",
|
|
534
497
|
currentTime: Temporal.Instant.from(`${(/* @__PURE__ */ new Date(currentTimestamp * 1e3)).toISOString()}`)
|
|
535
|
-
});
|
|
536
|
-
assertEquals(tamperDigestResult, null, "Should fail verification with invalid Content-Digest");
|
|
498
|
+
}), null, "Should fail verification with invalid Content-Digest");
|
|
537
499
|
const testRequest = new Request("https://example.com/", { headers: new Headers({
|
|
538
500
|
"Date": "Tue, 05 Mar 2024 07:49:44 GMT",
|
|
539
501
|
"Host": "example.com",
|
|
540
502
|
"Signature-Input": `sig1=("@method" "@target-uri" "host" "date");keyid="https://example.com/key";alg="rsa-v1_5-sha256";created=1709626184`,
|
|
541
503
|
"Signature": `sig1=:YXNkZmprc2RmaGprc2RoZmprc2hkZmtqaHNkZg==:`
|
|
542
504
|
}) });
|
|
543
|
-
const
|
|
544
|
-
const parsedInput = parseRfc9421SignatureInput(signatureInput);
|
|
505
|
+
const parsedInput = parseRfc9421SignatureInput(testRequest.headers.get("Signature-Input") || "");
|
|
545
506
|
assertExists(parsedInput.sig1);
|
|
546
507
|
assertEquals(parsedInput.sig1.keyId, "https://example.com/key");
|
|
547
508
|
assertEquals(parsedInput.sig1.alg, "rsa-v1_5-sha256");
|
|
@@ -552,8 +513,7 @@ test("verifyRequest() [rfc9421] error cases and edge cases", async () => {
|
|
|
552
513
|
"host",
|
|
553
514
|
"date"
|
|
554
515
|
]);
|
|
555
|
-
const
|
|
556
|
-
const parsedSig = parseRfc9421Signature(signature);
|
|
516
|
+
const parsedSig = parseRfc9421Signature(testRequest.headers.get("Signature") || "");
|
|
557
517
|
assertExists(parsedSig.sig1);
|
|
558
518
|
assert(new TextDecoder().decode(parsedSig.sig1).length > 0, "Signature base64 should decode to non-empty string");
|
|
559
519
|
const complexParsedInput = parseRfc9421SignatureInput("sig1=(\"@method\" \"@target-uri\" \"host\" \"content-type\" \"value with \\\"quotes\\\" and spaces\");keyid=\"https://example.com/key with spaces\";alg=\"rsa-v1_5-sha256\";created=1709626184");
|
|
@@ -581,7 +541,7 @@ test("verifyRequest() [rfc9421] error cases and edge cases", async () => {
|
|
|
581
541
|
assertEquals(Object.keys(parsedInvalidSig).length, 0, "Should handle invalid Signature format");
|
|
582
542
|
const parsedInvalidBase64 = parseRfc9421Signature("sig1=:!@#$%%^&*():");
|
|
583
543
|
assertEquals(Object.keys(parsedInvalidBase64).length, 0, "Should handle invalid base64 in signature");
|
|
584
|
-
|
|
544
|
+
assertEquals(await verifyRequest(new Request("https://example.com/api/resource", {
|
|
585
545
|
method: "GET",
|
|
586
546
|
headers: new Headers({
|
|
587
547
|
"Accept": "application/json",
|
|
@@ -590,14 +550,12 @@ test("verifyRequest() [rfc9421] error cases and edge cases", async () => {
|
|
|
590
550
|
"Signature-Input": `${validSignatureInput},sig2=("@method" "@target-uri" "host" "date");keyid="https://example.com/invalid-key";alg="rsa-v1_5-sha256";created=${currentTimestamp}`,
|
|
591
551
|
"Signature": `${validSignature},sig2=:AAAAAA==:`
|
|
592
552
|
})
|
|
593
|
-
})
|
|
594
|
-
const mixedResult = await verifyRequest(mixedRequest, {
|
|
553
|
+
}), {
|
|
595
554
|
contextLoader: mockDocumentLoader,
|
|
596
555
|
documentLoader: mockDocumentLoader,
|
|
597
556
|
spec: "rfc9421",
|
|
598
557
|
currentTime: Temporal.Instant.from(`${(/* @__PURE__ */ new Date(currentTimestamp * 1e3)).toISOString()}`)
|
|
599
|
-
});
|
|
600
|
-
assertEquals(mixedResult, rsaPublicKey2, "Should verify when at least one signature is valid");
|
|
558
|
+
}), rsaPublicKey2, "Should verify when at least one signature is valid");
|
|
601
559
|
});
|
|
602
560
|
test("verifyRequest() [rfc9421] test vector from Mastodon", async () => {
|
|
603
561
|
const signedRequest = new Request("https://www.example.com/activitypub/success", {
|
|
@@ -660,14 +618,13 @@ test("doubleKnock() function with successful first attempt", async () => {
|
|
|
660
618
|
const logFunction = (req) => {
|
|
661
619
|
loggedRequest = req;
|
|
662
620
|
};
|
|
663
|
-
|
|
621
|
+
assertEquals((await doubleKnock(request, {
|
|
664
622
|
keyId: rsaPublicKey2.id,
|
|
665
623
|
privateKey: rsaPrivateKey2
|
|
666
624
|
}, {
|
|
667
625
|
specDeterminer,
|
|
668
626
|
log: logFunction
|
|
669
|
-
});
|
|
670
|
-
assertEquals(response.status, 202, "Response status should be 202 Accepted");
|
|
627
|
+
})).status, 202, "Response status should be 202 Accepted");
|
|
671
628
|
assertEquals(requestCount, 1, "Only one request should have been made");
|
|
672
629
|
assertEquals(firstRequestSpec, "rfc9421", "First attempt should use RFC 9421");
|
|
673
630
|
assertEquals(specDeterminer.usedSpec, "rfc9421", "Spec should be remembered");
|
|
@@ -708,11 +665,10 @@ test("doubleKnock() function with fallback to draft-cavage", async () => {
|
|
|
708
665
|
this.rememberedSpec = spec;
|
|
709
666
|
}
|
|
710
667
|
};
|
|
711
|
-
|
|
668
|
+
assertEquals((await doubleKnock(request, {
|
|
712
669
|
keyId: rsaPublicKey2.id,
|
|
713
670
|
privateKey: rsaPrivateKey2
|
|
714
|
-
}, { specDeterminer });
|
|
715
|
-
assertEquals(response.status, 202, "Response status should be 202 Accepted");
|
|
671
|
+
}, { specDeterminer })).status, 202, "Response status should be 202 Accepted");
|
|
716
672
|
assertEquals(requestCount, 2, "Two requests should have been made");
|
|
717
673
|
assertEquals(firstSpec, "rfc9421", "First attempt should use RFC 9421");
|
|
718
674
|
assertEquals(secondSpec, "draft-cavage-http-signatures-12", "Second attempt should use draft-cavage");
|
|
@@ -734,16 +690,14 @@ test("doubleKnock() function with redirect handling", async () => {
|
|
|
734
690
|
responseCodes.push(202);
|
|
735
691
|
return new Response("", { status: 202 });
|
|
736
692
|
});
|
|
737
|
-
|
|
693
|
+
assertEquals((await doubleKnock(new Request("https://example.com/redirect-endpoint", {
|
|
738
694
|
method: "POST",
|
|
739
695
|
body: "Test message that will be redirected",
|
|
740
696
|
headers: { "Content-Type": "text/plain" }
|
|
741
|
-
})
|
|
742
|
-
const response = await doubleKnock(request, {
|
|
697
|
+
}), {
|
|
743
698
|
keyId: rsaPublicKey2.id,
|
|
744
699
|
privateKey: rsaPrivateKey2
|
|
745
|
-
});
|
|
746
|
-
assertEquals(response.status, 202, "Final response status should be 202 Accepted");
|
|
700
|
+
})).status, 202, "Final response status should be 202 Accepted");
|
|
747
701
|
assertEquals(requestedUrls.length, 2, "Two URLs should have been requested");
|
|
748
702
|
assertEquals(requestedUrls[0], "https://example.com/redirect-endpoint", "First request should be to redirect-endpoint");
|
|
749
703
|
assertEquals(requestedUrls[1], "https://example.com/final-endpoint", "Second request should be to final-endpoint");
|
|
@@ -762,16 +716,14 @@ test("doubleKnock() function with both specs rejected", async () => {
|
|
|
762
716
|
else attempts.push("unknown");
|
|
763
717
|
return new Response("Unauthorized", { status: 401 });
|
|
764
718
|
});
|
|
765
|
-
|
|
719
|
+
assertEquals((await doubleKnock(new Request("https://example.com/inbox-rejects-all", {
|
|
766
720
|
method: "POST",
|
|
767
721
|
body: "Test message that will be rejected regardless of signature format",
|
|
768
722
|
headers: { "Content-Type": "text/plain" }
|
|
769
|
-
})
|
|
770
|
-
const response = await doubleKnock(request, {
|
|
723
|
+
}), {
|
|
771
724
|
keyId: rsaPublicKey2.id,
|
|
772
725
|
privateKey: rsaPrivateKey2
|
|
773
|
-
});
|
|
774
|
-
assertEquals(response.status, 401, "Final response status should be 401 Unauthorized");
|
|
726
|
+
})).status, 401, "Final response status should be 401 Unauthorized");
|
|
775
727
|
assertEquals(requestCount, 2, "Two requests should have been made");
|
|
776
728
|
assertEquals(attempts.length, 2, "Two signature attempts should have been made");
|
|
777
729
|
assertEquals(attempts[0], "rfc9421", "First attempt should use RFC 9421");
|
|
@@ -795,16 +747,14 @@ test("doubleKnock() function with specDeterminer choosing draft-cavage first", a
|
|
|
795
747
|
},
|
|
796
748
|
rememberSpec(_origin, _spec) {}
|
|
797
749
|
};
|
|
798
|
-
|
|
750
|
+
assertEquals((await doubleKnock(new Request("https://example.com/inbox-accepts-any", {
|
|
799
751
|
method: "POST",
|
|
800
752
|
body: "Test message with draft-cavage preference",
|
|
801
753
|
headers: { "Content-Type": "text/plain" }
|
|
802
|
-
})
|
|
803
|
-
const response = await doubleKnock(request, {
|
|
754
|
+
}), {
|
|
804
755
|
keyId: rsaPublicKey2.id,
|
|
805
756
|
privateKey: rsaPrivateKey2
|
|
806
|
-
}, { specDeterminer });
|
|
807
|
-
assertEquals(response.status, 202, "Response status should be 202 Accepted");
|
|
757
|
+
}, { specDeterminer })).status, 202, "Response status should be 202 Accepted");
|
|
808
758
|
assertEquals(requestCount, 1, "Only one request should have been made");
|
|
809
759
|
assertEquals(firstSpec, "draft-cavage", "First attempt should use draft-cavage");
|
|
810
760
|
esm_default.hardReset();
|
|
@@ -872,25 +822,21 @@ test("doubleKnock() async specDeterminer test", async () => {
|
|
|
872
822
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
873
823
|
}
|
|
874
824
|
};
|
|
875
|
-
|
|
825
|
+
assertEquals((await doubleKnock(new Request("https://example.com/inbox-async-determiner", {
|
|
876
826
|
method: "POST",
|
|
877
827
|
body: "Test message with async spec determiner",
|
|
878
828
|
headers: { "Content-Type": "text/plain" }
|
|
879
|
-
})
|
|
880
|
-
const response = await doubleKnock(request, {
|
|
829
|
+
}), {
|
|
881
830
|
keyId: rsaPublicKey2.id,
|
|
882
831
|
privateKey: rsaPrivateKey2
|
|
883
|
-
}, { specDeterminer });
|
|
884
|
-
assertEquals(response.status, 202, "Response status should be 202 Accepted");
|
|
832
|
+
}, { specDeterminer })).status, 202, "Response status should be 202 Accepted");
|
|
885
833
|
assertEquals(requestCount, 1, "Only one request should have been made");
|
|
886
834
|
assertEquals(specUsed, "draft-cavage-http-signatures-12", "Should use spec from async determiner");
|
|
887
835
|
esm_default.hardReset();
|
|
888
836
|
});
|
|
889
837
|
test("timingSafeEqual()", async (t) => {
|
|
890
838
|
await t.step("should return true for equal empty arrays", () => {
|
|
891
|
-
|
|
892
|
-
const b = new Uint8Array([]);
|
|
893
|
-
assert(timingSafeEqual(a, b));
|
|
839
|
+
assert(timingSafeEqual(new Uint8Array([]), new Uint8Array([])));
|
|
894
840
|
});
|
|
895
841
|
await t.step("should return true for equal non-empty arrays", async (t2) => {
|
|
896
842
|
const testCases = [
|
|
@@ -1097,14 +1043,10 @@ test("timingSafeEqual()", async (t) => {
|
|
|
1097
1043
|
});
|
|
1098
1044
|
await t.step("should correctly handle comparisons involving padding bytes", async (t2) => {
|
|
1099
1045
|
await t2.step("a=[1], b=[1,0] (b longer with trailing zero)", () => {
|
|
1100
|
-
|
|
1101
|
-
const b1 = new Uint8Array([1, 0]);
|
|
1102
|
-
assertFalse(timingSafeEqual(a1, b1));
|
|
1046
|
+
assertFalse(timingSafeEqual(new Uint8Array([1]), new Uint8Array([1, 0])));
|
|
1103
1047
|
});
|
|
1104
1048
|
await t2.step("a=[1,0], b=[1] (a longer with trailing zero)", () => {
|
|
1105
|
-
|
|
1106
|
-
const b2 = new Uint8Array([1]);
|
|
1107
|
-
assertFalse(timingSafeEqual(a2, b2));
|
|
1049
|
+
assertFalse(timingSafeEqual(new Uint8Array([1, 0]), new Uint8Array([1])));
|
|
1108
1050
|
});
|
|
1109
1051
|
});
|
|
1110
1052
|
});
|
|
@@ -1121,20 +1063,18 @@ test("signRequest() [rfc9421] error handling for invalid signature base creation
|
|
|
1121
1063
|
assertExists(signedRequest.headers.get("Signature"));
|
|
1122
1064
|
});
|
|
1123
1065
|
test("verifyRequest() [rfc9421] error handling for invalid signature base creation", async () => {
|
|
1124
|
-
|
|
1066
|
+
assertEquals(await verifyRequest(new Request("https://example.com/test", {
|
|
1125
1067
|
method: "GET",
|
|
1126
1068
|
headers: {
|
|
1127
1069
|
"Accept": "application/json",
|
|
1128
1070
|
"Signature-Input": "sig1=(\"@unsupported\");alg=\"rsa-pss-sha256\";keyid=\"https://example.com/key2\";created=1234567890",
|
|
1129
1071
|
"Signature": "sig1=:invalid_signature_data:"
|
|
1130
1072
|
}
|
|
1131
|
-
})
|
|
1132
|
-
const result = await verifyRequest(request, {
|
|
1073
|
+
}), {
|
|
1133
1074
|
spec: "rfc9421",
|
|
1134
1075
|
documentLoader: mockDocumentLoader,
|
|
1135
1076
|
contextLoader: mockDocumentLoader
|
|
1136
|
-
});
|
|
1137
|
-
assertEquals(result, null, "Verification should fail gracefully for malformed signature inputs");
|
|
1077
|
+
}), null, "Verification should fail gracefully for malformed signature inputs");
|
|
1138
1078
|
});
|
|
1139
1079
|
test("doubleKnock() regression test for TypeError: unusable bug #294", async () => {
|
|
1140
1080
|
esm_default.spyGlobal();
|
|
@@ -1148,16 +1088,14 @@ test("doubleKnock() regression test for TypeError: unusable bug #294", async ()
|
|
|
1148
1088
|
esm_default.post("https://example.com/final-destination", () => {
|
|
1149
1089
|
return new Response("Success", { status: 200 });
|
|
1150
1090
|
});
|
|
1151
|
-
|
|
1091
|
+
assertEquals((await doubleKnock(new Request("https://example.com/inbox-retry-redirect", {
|
|
1152
1092
|
method: "POST",
|
|
1153
1093
|
body: "Test activity content",
|
|
1154
1094
|
headers: { "Content-Type": "application/activity+json" }
|
|
1155
|
-
})
|
|
1156
|
-
const response = await doubleKnock(request, {
|
|
1095
|
+
}), {
|
|
1157
1096
|
keyId: rsaPublicKey2.id,
|
|
1158
1097
|
privateKey: rsaPrivateKey2
|
|
1159
|
-
});
|
|
1160
|
-
assertEquals(response.status, 200);
|
|
1098
|
+
})).status, 200);
|
|
1161
1099
|
assertEquals(requestCount, 2, "Should make 2 requests before redirect");
|
|
1162
1100
|
esm_default.hardReset();
|
|
1163
1101
|
});
|
|
@@ -1172,16 +1110,14 @@ test("doubleKnock() regression test for redirect handling bug", async () => {
|
|
|
1172
1110
|
esm_default.post("https://example.com/final-destination", () => {
|
|
1173
1111
|
return new Response("Success", { status: 200 });
|
|
1174
1112
|
});
|
|
1175
|
-
|
|
1113
|
+
assertEquals((await doubleKnock(new Request("https://example.com/inbox-retry-redirect", {
|
|
1176
1114
|
method: "POST",
|
|
1177
1115
|
body: "Test activity content",
|
|
1178
1116
|
headers: { "Content-Type": "application/activity+json" }
|
|
1179
|
-
})
|
|
1180
|
-
const response = await doubleKnock(request, {
|
|
1117
|
+
}), {
|
|
1181
1118
|
keyId: rsaPublicKey2.id,
|
|
1182
1119
|
privateKey: rsaPrivateKey2
|
|
1183
|
-
});
|
|
1184
|
-
assertEquals(response.status, 200);
|
|
1120
|
+
})).status, 200);
|
|
1185
1121
|
esm_default.hardReset();
|
|
1186
1122
|
});
|
|
1187
1123
|
|