@sideband/secure-relay 0.0.1 → 0.2.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 (46) hide show
  1. package/README.md +33 -11
  2. package/{LICENSE → dist/LICENSE} +3 -2
  3. package/package.json +1 -2
  4. package/src/constants.ts +27 -2
  5. package/src/crypto.test.ts +644 -0
  6. package/src/crypto.ts +1 -2
  7. package/src/frame.test.ts +820 -0
  8. package/src/frame.ts +771 -0
  9. package/src/handshake.test.ts +325 -0
  10. package/src/handshake.ts +1 -2
  11. package/src/index.ts +44 -2
  12. package/src/integration.test.ts +1025 -0
  13. package/src/replay.test.ts +306 -0
  14. package/src/replay.ts +1 -2
  15. package/src/session.test.ts +767 -0
  16. package/src/session.ts +1 -2
  17. package/src/types.ts +85 -18
  18. package/dist/.tsbuildinfo +0 -1
  19. package/dist/constants.d.ts +0 -33
  20. package/dist/constants.d.ts.map +0 -1
  21. package/dist/constants.js +0 -35
  22. package/dist/constants.js.map +0 -1
  23. package/dist/crypto.d.ts +0 -70
  24. package/dist/crypto.d.ts.map +0 -1
  25. package/dist/crypto.js +0 -145
  26. package/dist/crypto.js.map +0 -1
  27. package/dist/handshake.d.ts +0 -42
  28. package/dist/handshake.d.ts.map +0 -1
  29. package/dist/handshake.js +0 -83
  30. package/dist/handshake.js.map +0 -1
  31. package/dist/index.d.ts +0 -45
  32. package/dist/index.d.ts.map +0 -1
  33. package/dist/index.js +0 -11
  34. package/dist/index.js.map +0 -1
  35. package/dist/replay.d.ts +0 -32
  36. package/dist/replay.d.ts.map +0 -1
  37. package/dist/replay.js +0 -89
  38. package/dist/replay.js.map +0 -1
  39. package/dist/session.d.ts +0 -67
  40. package/dist/session.d.ts.map +0 -1
  41. package/dist/session.js +0 -123
  42. package/dist/session.js.map +0 -1
  43. package/dist/types.d.ts +0 -79
  44. package/dist/types.d.ts.map +0 -1
  45. package/dist/types.js +0 -39
  46. package/dist/types.js.map +0 -1
package/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # @sideband/secure-relay
2
2
 
3
- End-to-end encrypted communication between browsers and daemons via untrusted relay servers.
3
+ Low-level E2EE primitives for the Sideband Relay Protocol (SBRP).
4
+
5
+ Implements authenticated handshake, key derivation, and message encryption for secure browser ↔ daemon communication via untrusted relay servers. Most applications should use `@sideband/peer` instead of this package directly.
4
6
 
5
7
  ## Features
6
8
 
@@ -10,6 +12,15 @@ End-to-end encrypted communication between browsers and daemons via untrusted re
10
12
  - **TOFU identity pinning** — Trust-on-first-use with key change detection
11
13
  - **Replay protection** — Bitmap-based sequence window
12
14
 
15
+ ## Non-Goals
16
+
17
+ This package intentionally does NOT:
18
+
19
+ - Handle network transport or WebSockets
20
+ - Manage session lifecycle or reconnection
21
+ - Persist identity keys or TOFU pins
22
+ - Implement relay authentication or tokens
23
+
13
24
  ## Install
14
25
 
15
26
  ```bash
@@ -18,7 +29,7 @@ bun add @sideband/secure-relay
18
29
 
19
30
  ## Usage
20
31
 
21
- ```typescript
32
+ ```ts
22
33
  import {
23
34
  generateIdentityKeyPair,
24
35
  createHandshakeInit,
@@ -34,7 +45,8 @@ import {
34
45
  asClientId,
35
46
  } from "@sideband/secure-relay";
36
47
 
37
- // Daemon: generate identity keypair (persist this!)
48
+ // Daemon: generate identity keypair ONCE and persist securely.
49
+ // Regenerating causes TOFU mismatch warnings for all clients.
38
50
  const identity = generateIdentityKeyPair();
39
51
  const daemonId = asDaemonId("my-daemon");
40
52
 
@@ -61,25 +73,35 @@ const { sessionKeys } = processHandshakeAccept(
61
73
  );
62
74
  const daemonSession = createDaemonSession(sessionKeys);
63
75
 
64
- // Encrypt/decrypt messages
76
+ // Encrypt/decrypt messages (sessions are stateful — do not clone)
65
77
  const encrypted = encryptClientToDaemon(daemonSession, plaintext);
66
78
  const decrypted = decryptClientToDaemon(clientSession, encrypted);
67
79
  ```
68
80
 
81
+ ## TOFU Security
82
+
83
+ Identity keys use trust-on-first-use (TOFU) pinning:
84
+
85
+ - Pin daemon identity keys on first successful handshake
86
+ - Never accept key changes silently — `identity_key_changed` indicates potential MITM
87
+ - On mismatch, present both fingerprints and require explicit user approval
88
+
69
89
  ## Error Handling
70
90
 
71
91
  All errors throw `SbrpError` with a specific `code`:
72
92
 
73
- | Code | Meaning |
74
- | ---------------------- | ----------------------------------------- |
75
- | `identity_key_changed` | Pinned key doesn't match (potential MITM) |
76
- | `handshake_failed` | Signature verification failed |
77
- | `decrypt_failed` | Message authentication failed |
78
- | `sequence_error` | Replay detected or sequence out of window |
93
+ | Code | Meaning | Recovery |
94
+ | ---------------------- | ----------------------------------------- | ------------------------- |
95
+ | `identity_key_changed` | Pinned key doesn't match (potential MITM) | Close session, alert user |
96
+ | `handshake_failed` | Signature verification failed | Close session |
97
+ | `decrypt_failed` | Message authentication failed | Close session |
98
+ | `sequence_error` | Replay detected or sequence out of window | Close session |
99
+
100
+ All errors are fatal — close the session and re-handshake.
79
101
 
80
102
  ## Specification
81
103
 
82
- See [Secure Relay Protocol](https://sideband.tech/specs/secure-relay-protocol) for the full protocol specification.
104
+ See the [SBRP protocol specification](https://sideband.tech/protocols/sbrp/) for implementation details.
83
105
 
84
106
  ## License
85
107
 
@@ -1,3 +1,4 @@
1
+
1
2
  Apache License
2
3
  Version 2.0, January 2004
3
4
  http://www.apache.org/licenses/
@@ -48,7 +49,7 @@
48
49
  "Contribution" shall mean any work of authorship, including
49
50
  the original version of the Work and any modifications or additions
50
51
  to that Work or Derivative Works thereof, that is intentionally
51
- submitted to the Licensor for inclusion in the Work by the copyright owner
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
53
  or by an individual or Legal Entity authorized to submit on behalf of
53
54
  the copyright owner. For the purposes of this definition, "submitted"
54
55
  means any form of electronic, verbal, or written communication sent
@@ -175,7 +176,7 @@
175
176
 
176
177
  END OF TERMS AND CONDITIONS
177
178
 
178
- Copyright 2025-present Sideband <https://sideband.tech>
179
+ Copyright 2025 Sideband Project
179
180
 
180
181
  Licensed under the Apache License, Version 2.0 (the "License");
181
182
  you may not use this file except in compliance with the License.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sideband/secure-relay",
3
- "version": "0.0.1",
3
+ "version": "0.2.0",
4
4
  "description": "Secure Relay Protocol (SBRP): E2EE handshake, session encryption, and TOFU identity pinning for relay-mediated communication.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -40,7 +40,6 @@
40
40
  "exports": {
41
41
  ".": {
42
42
  "types": "./dist/index.d.ts",
43
- "bun": "./src/index.ts",
44
43
  "default": "./dist/index.js"
45
44
  },
46
45
  "./package.json": "./package.json"
package/src/constants.ts CHANGED
@@ -1,5 +1,4 @@
1
- // SPDX-FileCopyrightText: 2025-present Sideband
2
- // SPDX-License-Identifier: AGPL-3.0-or-later
1
+ // SPDX-License-Identifier: Apache-2.0
3
2
 
4
3
  /**
5
4
  * Protocol constants for Sideband Relay Protocol (SBRP).
@@ -47,3 +46,29 @@ export const DEFAULT_REPLAY_WINDOW_SIZE = 64n;
47
46
  /** Direction bytes in nonce (4 bytes, big-endian) */
48
47
  export const DIRECTION_CLIENT_TO_DAEMON = new Uint8Array([0, 0, 0, 1]);
49
48
  export const DIRECTION_DAEMON_TO_CLIENT = new Uint8Array([0, 0, 0, 2]);
49
+
50
+ // Wire format constants (§13)
51
+
52
+ /** Frame header size: type (1) + length (4) + sessionId (8) */
53
+ export const FRAME_HEADER_SIZE = 13;
54
+
55
+ /** Maximum payload size in bytes (64 KB) */
56
+ export const MAX_PAYLOAD_SIZE = 65536;
57
+
58
+ /** HandshakeInit payload: X25519 ephemeral public key */
59
+ export const HANDSHAKE_INIT_PAYLOAD_SIZE = 32;
60
+
61
+ /** HandshakeAccept payload: X25519 ephemeral (32) + Ed25519 signature (64) */
62
+ export const HANDSHAKE_ACCEPT_PAYLOAD_SIZE = 96;
63
+
64
+ /** Minimum encrypted payload: nonce (12) + authTag (16), no plaintext */
65
+ export const MIN_ENCRYPTED_PAYLOAD_SIZE = NONCE_LENGTH + AUTH_TAG_LENGTH;
66
+
67
+ /** Control payload minimum: code (2 bytes) */
68
+ export const MIN_CONTROL_PAYLOAD_SIZE = 2;
69
+
70
+ /** Signal payload size: signal (1) + reason (1) */
71
+ export const SIGNAL_PAYLOAD_SIZE = 2;
72
+
73
+ /** Maximum Ping/Pong payload size (0-8 bytes for RTT nonce) */
74
+ export const MAX_PING_PAYLOAD_SIZE = 8;