@dwk/http-signatures 0.1.0-beta.0

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 (58) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +141 -0
  3. package/dist/algorithms.d.ts +31 -0
  4. package/dist/algorithms.d.ts.map +1 -0
  5. package/dist/algorithms.js +106 -0
  6. package/dist/algorithms.js.map +1 -0
  7. package/dist/base64.d.ts +8 -0
  8. package/dist/base64.d.ts.map +1 -0
  9. package/dist/base64.js +23 -0
  10. package/dist/base64.js.map +1 -0
  11. package/dist/cavage.d.ts +38 -0
  12. package/dist/cavage.d.ts.map +1 -0
  13. package/dist/cavage.js +249 -0
  14. package/dist/cavage.js.map +1 -0
  15. package/dist/components.d.ts +26 -0
  16. package/dist/components.d.ts.map +1 -0
  17. package/dist/components.js +65 -0
  18. package/dist/components.js.map +1 -0
  19. package/dist/digest.d.ts +45 -0
  20. package/dist/digest.d.ts.map +1 -0
  21. package/dist/digest.js +128 -0
  22. package/dist/digest.js.map +1 -0
  23. package/dist/index.d.ts +28 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +26 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/rfc9421.d.ts +49 -0
  28. package/dist/rfc9421.d.ts.map +1 -0
  29. package/dist/rfc9421.js +216 -0
  30. package/dist/rfc9421.js.map +1 -0
  31. package/dist/sf.d.ts +49 -0
  32. package/dist/sf.d.ts.map +1 -0
  33. package/dist/sf.js +234 -0
  34. package/dist/sf.js.map +1 -0
  35. package/dist/sign.d.ts +46 -0
  36. package/dist/sign.d.ts.map +1 -0
  37. package/dist/sign.js +39 -0
  38. package/dist/sign.js.map +1 -0
  39. package/dist/types.d.ts +62 -0
  40. package/dist/types.d.ts.map +1 -0
  41. package/dist/types.js +9 -0
  42. package/dist/types.js.map +1 -0
  43. package/dist/verify.d.ts +37 -0
  44. package/dist/verify.d.ts.map +1 -0
  45. package/dist/verify.js +60 -0
  46. package/dist/verify.js.map +1 -0
  47. package/package.json +44 -0
  48. package/src/algorithms.ts +168 -0
  49. package/src/base64.ts +25 -0
  50. package/src/cavage.ts +292 -0
  51. package/src/components.ts +79 -0
  52. package/src/digest.ts +156 -0
  53. package/src/index.ts +45 -0
  54. package/src/rfc9421.ts +280 -0
  55. package/src/sf.ts +246 -0
  56. package/src/sign.ts +79 -0
  57. package/src/types.ts +94 -0
  58. package/src/verify.ts +102 -0
package/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2026 David W. Keith
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,141 @@
1
+ # `@dwk/http-signatures`
2
+
3
+ > HTTP Message Signatures (RFC 9421) sign/verify, with the legacy
4
+ > `draft-cavage-http-signatures` profile for fediverse interop. Cross-standard
5
+ > reusable.
6
+
7
+ Part of the [`@dwk` IndieWeb + Solid cohort](../../README.md). See the
8
+ [package specification](../../spec/packages/http-signatures.md) for the full
9
+ requirements.
10
+
11
+ This package is **cross-standard reusable**: it takes plain-data inputs only
12
+ (method, URL, headers) plus a resolved `CryptoKey`, has no Workers-runtime
13
+ dependency (only Web Crypto), and unit-tests in isolation (Node, no `workerd`).
14
+ It is **protocol-agnostic** — it knows nothing about ActivityPub, IndieWeb, or
15
+ Solid. Key resolution (fetching an actor's public key, caching it) is the
16
+ caller's responsibility, supplied as a `KeyResolver`.
17
+
18
+ It mirrors the [`@dwk/dpop`](../dpop/README.md) hardening posture: only
19
+ asymmetric algorithms from an explicit allow-list are accepted — never `none`
20
+ or HMAC — and a resolved key is validated against the claimed algorithm (RSA
21
+ modulus floor of 2048 bits, EC curve) before any signature is checked.
22
+
23
+ ## API
24
+
25
+ ### Signing (RFC 9421)
26
+
27
+ ```ts
28
+ import { signMessage, createContentDigest } from "@dwk/http-signatures";
29
+
30
+ const body = JSON.stringify(activity);
31
+
32
+ // Cover the body by digesting it and listing the digest among the components.
33
+ const headers = {
34
+ host: "remote.example",
35
+ date: new Date().toUTCString(),
36
+ "content-type": "application/activity+json",
37
+ "content-digest": await createContentDigest(body),
38
+ };
39
+
40
+ const sig = await signMessage(
41
+ { method: "POST", url: "https://remote.example/inbox", headers },
42
+ {
43
+ key: privateKey, // a private CryptoKey you imported for `alg`
44
+ keyId: "https://me.example/actor#main-key",
45
+ alg: "ed25519",
46
+ components: ["@method", "@target-uri", "@authority", "date", "content-digest"],
47
+ // created defaults to now; expires, nonce, tag, label are optional
48
+ },
49
+ );
50
+
51
+ // Merge sig (Signature-Input + Signature) into the outbound request headers.
52
+ await fetch("https://remote.example/inbox", {
53
+ method: "POST",
54
+ headers: { ...headers, ...sig },
55
+ body,
56
+ });
57
+ ```
58
+
59
+ ### Verifying
60
+
61
+ ```ts
62
+ import { verifyMessage } from "@dwk/http-signatures";
63
+
64
+ const result = await verifyMessage(
65
+ { method: request.method, url: request.url, headers },
66
+ {
67
+ // Resolve the public key for the claimed keyid (fetch the actor, cache it).
68
+ resolveKey: async ({ keyId, alg }) => fetchActorKey(keyId, alg),
69
+ requiredComponents: ["@method", "@target-uri", "date", "content-digest"],
70
+ body, // optional: also recompute & check any covered content-digest/digest
71
+ },
72
+ );
73
+
74
+ if (!result.valid) {
75
+ // result.reason is a stable code, e.g. "signature_invalid", "key_unresolved"
76
+ return new Response("bad signature", { status: 401 });
77
+ }
78
+ // result.keyId is the verified principal; result.coveredComponents the audit trail.
79
+ ```
80
+
81
+ The profile is auto-detected from the headers (a `Signature-Input` header means
82
+ RFC 9421, otherwise the legacy single `Signature` header is parsed as
83
+ `draft-cavage`); pass `profile` to force one.
84
+
85
+ ### `draft-cavage` (fediverse)
86
+
87
+ ```ts
88
+ import { signMessage, createDigest } from "@dwk/http-signatures";
89
+
90
+ const headers = {
91
+ host: "remote.example",
92
+ date: new Date().toUTCString(),
93
+ digest: await createDigest(body), // legacy "SHA-256=<base64>" header
94
+ };
95
+
96
+ const sig = await signMessage(
97
+ { method: "POST", url: "https://remote.example/inbox", headers },
98
+ {
99
+ profile: "cavage",
100
+ key: privateKey,
101
+ keyId: "https://me.example/actor#main-key",
102
+ alg: "rsa-v1_5-sha256", // serialized as algorithm="rsa-sha256"
103
+ components: ["(request-target)", "host", "date", "digest"],
104
+ },
105
+ );
106
+ ```
107
+
108
+ ## What is verified
109
+
110
+ - **Profile** — `Signature-Input` / `Signature` (RFC 9421) or the single
111
+ `Signature` header (`draft-cavage`), auto-detected unless forced.
112
+ - **Algorithm** — from the allow-list (`rsa-pss-sha512`, `rsa-v1_5-sha256`,
113
+ `ecdsa-p256-sha256`, `ecdsa-p384-sha384`, `ed25519`); never `none` or HMAC.
114
+ - **Key** — the resolved `CryptoKey`'s algorithm and EC curve must match the
115
+ claimed algorithm, and RSA moduli must be at least 2048 bits.
116
+ - **Signature** — over the reconstructed signature base / signing string.
117
+ - **Window** — `created` not in the future and `expires` not in the past,
118
+ within `toleranceSeconds` (default 300).
119
+ - **Coverage** — every component named in the signature is reconstructed from
120
+ the message; `requiredComponents` lets the caller insist specific components
121
+ were covered.
122
+ - **Body integrity** (optional) — when a `body` is supplied and a
123
+ `content-digest` (RFC 9530) or legacy `Digest` component is covered, the
124
+ header is recomputed over the body and must match.
125
+
126
+ `verifyMessage` never throws — failures return `{ valid: false, reason }` with a
127
+ stable `SignatureFailureReason` code. On success it returns
128
+ `{ valid: true, profile, keyId, coveredComponents, created?, expires? }`.
129
+
130
+ ## Out of scope
131
+
132
+ - **Key resolution and caching** — the caller supplies a `KeyResolver` and owns
133
+ fetching/importing the public key (PEM/JWK → `CryptoKey`) and any caching.
134
+ - **Replay/nonce storage** — the caller owns nonce bookkeeping.
135
+ - **Component parameters** — `;sf`, `;key`, `;bs`, `@query-param`, and response
136
+ components (`@status`) are not derived; a signature covering them is reported
137
+ as `components_malformed` rather than guessed at.
138
+
139
+ ## License
140
+
141
+ [ISC](../../LICENSE)
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Algorithm allow-list and the Web Crypto primitives that back it.
3
+ *
4
+ * Mirrors the `@dwk/dpop` hardening posture: asymmetric algorithms only, an
5
+ * explicit allow-list (no `none`, no HMAC), and the resolved key is validated
6
+ * against the claimed algorithm — RSA moduli below 2048 bits and curve
7
+ * mismatches are rejected before any signature is checked.
8
+ */
9
+ import type { SignatureAlgorithm } from "./types";
10
+ /** Whether `alg` is in the allow-list. */
11
+ export declare function isSupportedAlgorithm(alg: string): alg is SignatureAlgorithm;
12
+ /** The reasons {@link validateKey} can reject a key, or `null` when it is sound. */
13
+ export type KeyRejection = "key_alg_mismatch" | "key_too_small";
14
+ /**
15
+ * Validate that a resolved {@link CryptoKey} matches the claimed algorithm:
16
+ * the key's algorithm name and EC curve must line up, and RSA moduli must meet
17
+ * the {@link MIN_RSA_KEY_BITS} floor. Returns `null` when the key is acceptable.
18
+ */
19
+ export declare function validateKey(key: CryptoKey, alg: SignatureAlgorithm): KeyRejection | null;
20
+ /**
21
+ * Derive the signature algorithm implied by a resolved key's type. Used when a
22
+ * signature omits its algorithm (RFC 9421 `alg` is OPTIONAL; the legacy
23
+ * `hs2019` token is intentionally key-derived). Returns `null` for a key type
24
+ * outside the allow-list.
25
+ */
26
+ export declare function deriveAlgFromKey(key: CryptoKey): SignatureAlgorithm | null;
27
+ /** Sign `data` with `key` under `alg`. */
28
+ export declare function signBytes(key: CryptoKey, alg: SignatureAlgorithm, data: Uint8Array): Promise<ArrayBuffer>;
29
+ /** Verify `signature` over `data` with `key` under `alg`. Never throws. */
30
+ export declare function verifyBytes(key: CryptoKey, alg: SignatureAlgorithm, signature: Uint8Array, data: Uint8Array): Promise<boolean>;
31
+ //# sourceMappingURL=algorithms.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"algorithms.d.ts","sourceRoot":"","sources":["../src/algorithms.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAiElD,0CAA0C;AAC1C,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,IAAI,kBAAkB,CAE3E;AAED,oFAAoF;AACpF,MAAM,MAAM,YAAY,GAAG,kBAAkB,GAAG,eAAe,CAAC;AAUhE;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,SAAS,EACd,GAAG,EAAE,kBAAkB,GACtB,YAAY,GAAG,IAAI,CAiBrB;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,SAAS,GAAG,kBAAkB,GAAG,IAAI,CAgB1E;AAED,0CAA0C;AAC1C,wBAAgB,SAAS,CACvB,GAAG,EAAE,SAAS,EACd,GAAG,EAAE,kBAAkB,EACvB,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,WAAW,CAAC,CAEtB;AAED,2EAA2E;AAC3E,wBAAsB,WAAW,CAC/B,GAAG,EAAE,SAAS,EACd,GAAG,EAAE,kBAAkB,EACvB,SAAS,EAAE,UAAU,EACrB,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,OAAO,CAAC,CAWlB"}
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Algorithm allow-list and the Web Crypto primitives that back it.
3
+ *
4
+ * Mirrors the `@dwk/dpop` hardening posture: asymmetric algorithms only, an
5
+ * explicit allow-list (no `none`, no HMAC), and the resolved key is validated
6
+ * against the claimed algorithm — RSA moduli below 2048 bits and curve
7
+ * mismatches are rejected before any signature is checked.
8
+ */
9
+ const ALGS = {
10
+ "rsa-pss-sha512": {
11
+ keyName: "RSA-PSS",
12
+ rsa: true,
13
+ importParams: { name: "RSA-PSS", hash: "SHA-512" },
14
+ signParams: { name: "RSA-PSS", saltLength: 64 },
15
+ },
16
+ "rsa-v1_5-sha256": {
17
+ keyName: "RSASSA-PKCS1-v1_5",
18
+ rsa: true,
19
+ importParams: { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
20
+ signParams: { name: "RSASSA-PKCS1-v1_5" },
21
+ },
22
+ "ecdsa-p256-sha256": {
23
+ keyName: "ECDSA",
24
+ expectedCurve: "P-256",
25
+ rsa: false,
26
+ importParams: { name: "ECDSA", namedCurve: "P-256" },
27
+ signParams: { name: "ECDSA", hash: "SHA-256" },
28
+ },
29
+ "ecdsa-p384-sha384": {
30
+ keyName: "ECDSA",
31
+ expectedCurve: "P-384",
32
+ rsa: false,
33
+ importParams: { name: "ECDSA", namedCurve: "P-384" },
34
+ signParams: { name: "ECDSA", hash: "SHA-384" },
35
+ },
36
+ ed25519: {
37
+ keyName: "Ed25519",
38
+ rsa: false,
39
+ importParams: { name: "Ed25519" },
40
+ signParams: { name: "Ed25519" },
41
+ },
42
+ };
43
+ /** Minimum accepted RSA modulus size, in bits. */
44
+ const MIN_RSA_KEY_BITS = 2048;
45
+ /** Whether `alg` is in the allow-list. */
46
+ export function isSupportedAlgorithm(alg) {
47
+ return Object.prototype.hasOwnProperty.call(ALGS, alg);
48
+ }
49
+ /**
50
+ * Validate that a resolved {@link CryptoKey} matches the claimed algorithm:
51
+ * the key's algorithm name and EC curve must line up, and RSA moduli must meet
52
+ * the {@link MIN_RSA_KEY_BITS} floor. Returns `null` when the key is acceptable.
53
+ */
54
+ export function validateKey(key, alg) {
55
+ const spec = ALGS[alg];
56
+ const keyAlg = key.algorithm;
57
+ if (keyAlg.name !== spec.keyName) {
58
+ return "key_alg_mismatch";
59
+ }
60
+ if (spec.expectedCurve && keyAlg.namedCurve !== spec.expectedCurve) {
61
+ return "key_alg_mismatch";
62
+ }
63
+ if (spec.rsa &&
64
+ typeof keyAlg.modulusLength === "number" &&
65
+ keyAlg.modulusLength < MIN_RSA_KEY_BITS) {
66
+ return "key_too_small";
67
+ }
68
+ return null;
69
+ }
70
+ /**
71
+ * Derive the signature algorithm implied by a resolved key's type. Used when a
72
+ * signature omits its algorithm (RFC 9421 `alg` is OPTIONAL; the legacy
73
+ * `hs2019` token is intentionally key-derived). Returns `null` for a key type
74
+ * outside the allow-list.
75
+ */
76
+ export function deriveAlgFromKey(key) {
77
+ const a = key.algorithm;
78
+ switch (a.name) {
79
+ case "RSA-PSS":
80
+ return "rsa-pss-sha512";
81
+ case "RSASSA-PKCS1-v1_5":
82
+ return "rsa-v1_5-sha256";
83
+ case "ECDSA":
84
+ return a.namedCurve === "P-384"
85
+ ? "ecdsa-p384-sha384"
86
+ : "ecdsa-p256-sha256";
87
+ case "Ed25519":
88
+ return "ed25519";
89
+ default:
90
+ return null;
91
+ }
92
+ }
93
+ /** Sign `data` with `key` under `alg`. */
94
+ export function signBytes(key, alg, data) {
95
+ return crypto.subtle.sign(ALGS[alg].signParams, key, data);
96
+ }
97
+ /** Verify `signature` over `data` with `key` under `alg`. Never throws. */
98
+ export async function verifyBytes(key, alg, signature, data) {
99
+ try {
100
+ return await crypto.subtle.verify(ALGS[alg].signParams, key, signature, data);
101
+ }
102
+ catch {
103
+ return false;
104
+ }
105
+ }
106
+ //# sourceMappingURL=algorithms.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"algorithms.js","sourceRoot":"","sources":["../src/algorithms.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /** Base64 (standard, padded) <-> bytes, plus base64url helpers. */
2
+ /** Decode standard padded base64 to bytes. Throws on invalid input. */
3
+ export declare function base64ToBytes(b64: string): Uint8Array;
4
+ /** Encode bytes as standard padded base64. */
5
+ export declare function bytesToBase64(bytes: Uint8Array): string;
6
+ /** Encode a UTF-8 string to bytes. */
7
+ export declare function utf8(input: string): Uint8Array;
8
+ //# sourceMappingURL=base64.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base64.d.ts","sourceRoot":"","sources":["../src/base64.ts"],"names":[],"mappings":"AAAA,mEAAmE;AAEnE,uEAAuE;AACvE,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAOrD;AAED,8CAA8C;AAC9C,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAMvD;AAED,sCAAsC;AACtC,wBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAE9C"}
package/dist/base64.js ADDED
@@ -0,0 +1,23 @@
1
+ /** Base64 (standard, padded) <-> bytes, plus base64url helpers. */
2
+ /** Decode standard padded base64 to bytes. Throws on invalid input. */
3
+ export function base64ToBytes(b64) {
4
+ const binary = atob(b64);
5
+ const bytes = new Uint8Array(binary.length);
6
+ for (let i = 0; i < binary.length; i++) {
7
+ bytes[i] = binary.charCodeAt(i);
8
+ }
9
+ return bytes;
10
+ }
11
+ /** Encode bytes as standard padded base64. */
12
+ export function bytesToBase64(bytes) {
13
+ let binary = "";
14
+ for (const byte of bytes) {
15
+ binary += String.fromCharCode(byte);
16
+ }
17
+ return btoa(binary);
18
+ }
19
+ /** Encode a UTF-8 string to bytes. */
20
+ export function utf8(input) {
21
+ return new TextEncoder().encode(input);
22
+ }
23
+ //# sourceMappingURL=base64.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base64.js","sourceRoot":"","sources":["../src/base64.ts"],"names":[],"mappings":"AAAA,mEAAmE;AAEnE,uEAAuE;AACvE,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;IACzB,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC5C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,KAAK,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAClC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,aAAa,CAAC,KAAiB;IAC7C,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC;AACtB,CAAC;AAED,sCAAsC;AACtC,MAAM,UAAU,IAAI,CAAC,KAAa;IAChC,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACzC,CAAC"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * The legacy `draft-cavage-http-signatures` "Signature" profile that much of
3
+ * the fediverse still emits. A single `Signature` header carries the `keyId`,
4
+ * `algorithm`, the space-separated `headers` list, and the base64 `signature`;
5
+ * the signing string is the `headers` components joined by newlines, where
6
+ * `(request-target)`, `(created)`, and `(expires)` are pseudo-headers.
7
+ */
8
+ import type { KeyResolver } from "./rfc9421";
9
+ import type { HttpMessage, SignatureAlgorithm, VerifyResult } from "./types";
10
+ /** Resolved signing inputs for the `draft-cavage` profile. */
11
+ export interface CavageSignParams {
12
+ key: CryptoKey;
13
+ keyId: string;
14
+ alg: SignatureAlgorithm;
15
+ /** Cavage-style component names, e.g. `["(request-target)", "host", "date", "digest"]`. */
16
+ components: string[];
17
+ created?: number;
18
+ expires?: number;
19
+ }
20
+ /**
21
+ * Sign `message` under the `draft-cavage` profile. Returns the single
22
+ * `Signature` header to merge into the request. Throws if a covered component
23
+ * is absent — a signer bug.
24
+ */
25
+ export declare function signCavage(message: HttpMessage, params: CavageSignParams): Promise<Record<string, string>>;
26
+ /** Parameters for verifying a `draft-cavage` signature. */
27
+ export interface CavageVerifyParams {
28
+ resolveKey: KeyResolver;
29
+ requiredComponents?: string[];
30
+ now?: number;
31
+ toleranceSeconds?: number;
32
+ }
33
+ /**
34
+ * Verify a `draft-cavage` signature on `message`. Never throws — failures are
35
+ * returned as `{ valid: false, reason }`.
36
+ */
37
+ export declare function verifyCavage(message: HttpMessage, params: CavageVerifyParams): Promise<VerifyResult>;
38
+ //# sourceMappingURL=cavage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cavage.d.ts","sourceRoot":"","sources":["../src/cavage.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAWH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,EACV,WAAW,EACX,kBAAkB,EAElB,YAAY,EACb,MAAM,SAAS,CAAC;AAsDjB,8DAA8D;AAC9D,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,SAAS,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,kBAAkB,CAAC;IACxB,2FAA2F;IAC3F,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAuBjC;AAsCD,2DAA2D;AAC3D,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,WAAW,CAAC;IACxB,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAsBD;;;GAGG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,YAAY,CAAC,CAmGvB"}
package/dist/cavage.js ADDED
@@ -0,0 +1,249 @@
1
+ /**
2
+ * The legacy `draft-cavage-http-signatures` "Signature" profile that much of
3
+ * the fediverse still emits. A single `Signature` header carries the `keyId`,
4
+ * `algorithm`, the space-separated `headers` list, and the base64 `signature`;
5
+ * the signing string is the `headers` components joined by newlines, where
6
+ * `(request-target)`, `(created)`, and `(expires)` are pseudo-headers.
7
+ */
8
+ import { deriveAlgFromKey, isSupportedAlgorithm, signBytes, validateKey, verifyBytes, } from "./algorithms";
9
+ import { base64ToBytes, bytesToBase64, utf8 } from "./base64";
10
+ import { derivationContext } from "./components";
11
+ /** Map our algorithm identifiers to a `draft-cavage` `algorithm` token. */
12
+ const CAVAGE_TOKEN = {
13
+ "rsa-v1_5-sha256": "rsa-sha256",
14
+ "rsa-pss-sha512": "hs2019",
15
+ "ecdsa-p256-sha256": "ecdsa-sha256",
16
+ "ecdsa-p384-sha384": "ecdsa-sha384",
17
+ ed25519: "ed25519",
18
+ };
19
+ /** Map a `draft-cavage` `algorithm` token to our identifier, or `null` for `hs2019`. */
20
+ function tokenToAlg(token) {
21
+ switch (token.toLowerCase()) {
22
+ case "rsa-sha256":
23
+ return "rsa-v1_5-sha256";
24
+ case "ecdsa-sha256":
25
+ return "ecdsa-p256-sha256";
26
+ case "ecdsa-sha384":
27
+ return "ecdsa-p384-sha384";
28
+ case "ed25519":
29
+ return "ed25519";
30
+ case "hs2019":
31
+ return "derive";
32
+ default:
33
+ return null;
34
+ }
35
+ }
36
+ /** Build the `draft-cavage` signing string, or report the first missing component. */
37
+ function buildSigningString(ctx, components, times) {
38
+ const lines = [];
39
+ for (const raw of components) {
40
+ const name = raw.toLowerCase();
41
+ let value;
42
+ if (name === "(request-target)") {
43
+ value = `${ctx.message.method.toLowerCase()} ${ctx.url.pathname}${ctx.url.search}`;
44
+ }
45
+ else if (name === "(created)") {
46
+ value = times.created === undefined ? null : String(times.created);
47
+ }
48
+ else if (name === "(expires)") {
49
+ value = times.expires === undefined ? null : String(times.expires);
50
+ }
51
+ else {
52
+ value = ctx.headers.get(name) ?? null;
53
+ }
54
+ if (value === null)
55
+ return { missing: name };
56
+ lines.push(`${name}: ${value}`);
57
+ }
58
+ return { string: lines.join("\n") };
59
+ }
60
+ /**
61
+ * Sign `message` under the `draft-cavage` profile. Returns the single
62
+ * `Signature` header to merge into the request. Throws if a covered component
63
+ * is absent — a signer bug.
64
+ */
65
+ export async function signCavage(message, params) {
66
+ const ctx = derivationContext(message);
67
+ if (ctx === null)
68
+ throw new Error("http-signatures: message.url is not a valid URL");
69
+ const built = buildSigningString(ctx, params.components, {
70
+ created: params.created,
71
+ expires: params.expires,
72
+ });
73
+ if ("missing" in built) {
74
+ throw new Error(`http-signatures: covered component "${built.missing}" is missing from the message`);
75
+ }
76
+ const signature = await signBytes(params.key, params.alg, utf8(built.string));
77
+ const parts = [
78
+ `keyId="${params.keyId}"`,
79
+ `algorithm="${CAVAGE_TOKEN[params.alg]}"`,
80
+ `headers="${params.components.map((c) => c.toLowerCase()).join(" ")}"`,
81
+ `signature="${bytesToBase64(new Uint8Array(signature))}"`,
82
+ ];
83
+ if (params.created !== undefined)
84
+ parts.push(`created=${params.created}`);
85
+ if (params.expires !== undefined)
86
+ parts.push(`expires=${params.expires}`);
87
+ return { Signature: parts.join(",") };
88
+ }
89
+ /** Parse a `draft-cavage` `Signature` header into its named members. */
90
+ function parseCavageHeader(value) {
91
+ const out = new Map();
92
+ let i = 0;
93
+ while (i < value.length) {
94
+ while (i < value.length &&
95
+ (value[i] === " " || value[i] === "," || value[i] === "\t"))
96
+ i++;
97
+ const eq = value.indexOf("=", i);
98
+ if (eq < 0)
99
+ break;
100
+ const key = value.slice(i, eq).trim();
101
+ i = eq + 1;
102
+ let raw;
103
+ if (value[i] === '"') {
104
+ i++;
105
+ let s = "";
106
+ while (i < value.length && value[i] !== '"') {
107
+ if (value[i] === "\\" && i + 1 < value.length)
108
+ i++;
109
+ s += value[i];
110
+ i++;
111
+ }
112
+ i++; // closing quote
113
+ raw = s;
114
+ }
115
+ else {
116
+ let j = i;
117
+ while (j < value.length && value[j] !== ",")
118
+ j++;
119
+ raw = value.slice(i, j).trim();
120
+ i = j;
121
+ }
122
+ if (key)
123
+ out.set(key.toLowerCase(), raw);
124
+ }
125
+ return out;
126
+ }
127
+ const DEFAULT_TOLERANCE_SECONDS = 300;
128
+ function getHeader(message, name) {
129
+ const lower = name.toLowerCase();
130
+ for (const [k, v] of Object.entries(message.headers)) {
131
+ if (k.toLowerCase() === lower)
132
+ return Array.isArray(v) ? v.join(", ") : v;
133
+ }
134
+ return null;
135
+ }
136
+ function fail(reason) {
137
+ return { valid: false, profile: "cavage", reason };
138
+ }
139
+ function parseIntegerParam(raw) {
140
+ if (raw === undefined)
141
+ return undefined;
142
+ const n = Number(raw);
143
+ return Number.isInteger(n) ? n : null;
144
+ }
145
+ /**
146
+ * Verify a `draft-cavage` signature on `message`. Never throws — failures are
147
+ * returned as `{ valid: false, reason }`.
148
+ */
149
+ export async function verifyCavage(message, params) {
150
+ const header = getHeader(message, "signature");
151
+ if (header === null)
152
+ return fail("signature_missing");
153
+ const fields = parseCavageHeader(header);
154
+ const sigB64 = fields.get("signature");
155
+ if (sigB64 === undefined)
156
+ return fail("signature_malformed");
157
+ let signature;
158
+ try {
159
+ signature = base64ToBytes(sigB64);
160
+ }
161
+ catch {
162
+ return fail("signature_malformed");
163
+ }
164
+ const keyId = fields.get("keyid") ?? null;
165
+ if (keyId === null)
166
+ return fail("keyid_missing");
167
+ const token = fields.get("algorithm");
168
+ const createdRaw = parseIntegerParam(fields.get("created"));
169
+ if (createdRaw === null)
170
+ return fail("created_invalid");
171
+ const expiresRaw = parseIntegerParam(fields.get("expires"));
172
+ if (expiresRaw === null)
173
+ return fail("expires_invalid");
174
+ // Default covered-component list when no explicit `headers` is sent.
175
+ // draft-cavage-12 §2.1.6 specifies the default is `(created)` unconditionally.
176
+ // We deliberately diverge: when `created` is absent we fall back to `date`,
177
+ // matching the older "Signing HTTP Messages" rule that essentially every
178
+ // fediverse peer implements. In practice senders always send an explicit
179
+ // `headers`, so this branch is rarely reached; the divergence only affects
180
+ // the (non-conforming) case of a signature with neither `headers` nor
181
+ // `created`, where interop with real-world peers beats spec-literalism.
182
+ const headersList = fields.get("headers");
183
+ const components = headersList
184
+ ? headersList.split(/\s+/).filter(Boolean)
185
+ : createdRaw !== undefined
186
+ ? ["(created)"]
187
+ : ["date"];
188
+ // Resolve the algorithm. `hs2019` (and a missing token) defer to the key type.
189
+ let alg = null;
190
+ if (token !== undefined) {
191
+ const mapped = tokenToAlg(token);
192
+ if (mapped === null)
193
+ return fail("alg_unsupported");
194
+ if (mapped !== "derive")
195
+ alg = mapped;
196
+ }
197
+ const key = await params.resolveKey({ keyId, alg });
198
+ if (key === null)
199
+ return fail("key_unresolved");
200
+ if (alg === null) {
201
+ alg = deriveAlgFromKey(key);
202
+ if (alg === null || !isSupportedAlgorithm(alg))
203
+ return fail("alg_unsupported");
204
+ }
205
+ const rejection = validateKey(key, alg);
206
+ if (rejection !== null)
207
+ return fail(rejection);
208
+ // created / expires window.
209
+ const now = params.now ?? Math.floor(Date.now() / 1000);
210
+ const tolerance = params.toleranceSeconds ?? DEFAULT_TOLERANCE_SECONDS;
211
+ if (createdRaw !== undefined && createdRaw > now + tolerance)
212
+ return fail("signature_future");
213
+ if (expiresRaw !== undefined && now > expiresRaw + tolerance)
214
+ return fail("signature_expired");
215
+ // RFC 9421 §2.3 / draft-cavage: `expires` MUST NOT precede `created`.
216
+ if (createdRaw !== undefined &&
217
+ expiresRaw !== undefined &&
218
+ expiresRaw < createdRaw)
219
+ return fail("expires_invalid");
220
+ // Required-component policy.
221
+ if (params.requiredComponents) {
222
+ const covered = new Set(components.map((c) => c.toLowerCase()));
223
+ for (const required of params.requiredComponents) {
224
+ if (!covered.has(required.toLowerCase()))
225
+ return fail("required_component_missing");
226
+ }
227
+ }
228
+ const ctx = derivationContext(message);
229
+ if (ctx === null)
230
+ return fail("covered_component_missing");
231
+ const built = buildSigningString(ctx, components, {
232
+ created: createdRaw,
233
+ expires: expiresRaw,
234
+ });
235
+ if ("missing" in built)
236
+ return fail("covered_component_missing");
237
+ const ok = await verifyBytes(key, alg, signature, utf8(built.string));
238
+ if (!ok)
239
+ return fail("signature_invalid");
240
+ return {
241
+ valid: true,
242
+ profile: "cavage",
243
+ keyId,
244
+ coveredComponents: components,
245
+ created: createdRaw,
246
+ expires: expiresRaw,
247
+ };
248
+ }
249
+ //# sourceMappingURL=cavage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cavage.js","sourceRoot":"","sources":["../src/cavage.ts"],"names":[],"mappings":"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"}