diff --git a/packages/relay/src/encrypted-channel.ts b/packages/relay/src/encrypted-channel.ts index e81e0e7e5..9b298ba12 100644 --- a/packages/relay/src/encrypted-channel.ts +++ b/packages/relay/src/encrypted-channel.ts @@ -37,23 +37,23 @@ type ChannelState = "connecting" | "handshaking" | "open" | "closed"; type EncryptedChannelOptions = { /** - * If set, the channel can validate repeated plaintext `{type:"hello"}` + * If set, the channel can validate repeated plaintext `{type:"e2ee_hello"}` * messages even after it is open. * * This is useful for robustness when the client retries the handshake - * (e.g., it didn't observe the daemon's `{type:"ready"}` yet). In that case, - * the daemon should re-send `{type:"ready"}` without changing keys. + * (e.g., it didn't observe the daemon's `{type:"e2ee_ready"}` yet). In that case, + * the daemon should re-send `{type:"e2ee_ready"}` without changing keys. */ daemonKeyPair?: KeyPair; }; -interface HelloMessage { - type: "hello"; +interface E2EEHelloMessage { + type: "e2ee_hello"; key: string; } -interface ReadyMessage { - type: "ready"; +interface E2EEReadyMessage { + type: "e2ee_ready"; } const HANDSHAKE_RETRY_MS = 1000; @@ -65,7 +65,7 @@ const MAX_PENDING_SENDS = 200; * The client: * 1. Receives daemon's public key via QR code * 2. Generates own keypair - * 3. Sends hello with own public key + * 3. Sends e2ee_hello with own public key * 4. Derives shared key and starts encrypted communication */ export async function createClientChannel( @@ -79,9 +79,9 @@ export async function createClientChannel( const channel = new EncryptedChannel(transport, sharedKey, events); - // Send hello with our public key + // Send e2ee_hello with our public key const ourPublicKeyB64 = exportPublicKey(keyPair.publicKey); - const hello: HelloMessage = { type: "hello", key: ourPublicKeyB64 }; + const hello: E2EEHelloMessage = { type: "e2ee_hello", key: ourPublicKeyB64 }; const helloText = JSON.stringify(hello); let retry: ReturnType | null = null; @@ -129,7 +129,7 @@ export async function createClientChannel( * * The daemon: * 1. Has pre-generated keypair (public key was in QR) - * 2. Waits for client's hello with their public key + * 2. Waits for client's e2ee_hello with their public key * 3. Derives shared key and starts encrypted communication */ export async function createDaemonChannel( @@ -142,8 +142,8 @@ export async function createDaemonChannel( const shouldIgnorePostHelloPlaintext = (data: string | ArrayBuffer): boolean => { try { const text = typeof data === "string" ? data : new TextDecoder().decode(data); - const parsed = JSON.parse(text) as Partial; - return parsed.type === "hello" || parsed.type === "ready"; + const parsed = JSON.parse(text) as Partial; + return parsed.type === "e2ee_hello" || parsed.type === "e2ee_ready"; } catch { return false; } @@ -154,8 +154,8 @@ export async function createDaemonChannel( const helloText = typeof data === "string" ? data : new TextDecoder().decode(data); - const msg = JSON.parse(helloText) as HelloMessage; - if (msg.type !== "hello" || !msg.key) { + const msg = JSON.parse(helloText) as E2EEHelloMessage; + if (msg.type !== "e2ee_hello" || !msg.key) { throw new Error("Invalid hello message"); } @@ -171,7 +171,9 @@ export async function createDaemonChannel( const sharedKey = deriveSharedKey(daemonKeyPair.secretKey, clientPublicKey); const channel = new EncryptedChannel(transport, sharedKey, events, { daemonKeyPair }); - transport.send(JSON.stringify({ type: "ready" } satisfies ReadyMessage)); + transport.send( + JSON.stringify({ type: "e2ee_ready" } satisfies E2EEReadyMessage) + ); channel.setState("open"); events.onopen?.(); @@ -240,8 +242,8 @@ export class EncryptedChannel { if (this.state === "handshaking") { try { const text = typeof data === "string" ? data : new TextDecoder().decode(data); - const msg = JSON.parse(text) as Partial; - if (msg.type === "ready") { + const msg = JSON.parse(text) as Partial; + if (msg.type === "e2ee_ready") { this.state = "open"; this.events.onopen?.(); for (const cb of this.onOpenCallbacks) cb(); @@ -261,9 +263,11 @@ export class EncryptedChannel { try { const text = typeof data === "string" ? data : new TextDecoder().decode(data); if (text.trim().startsWith("{")) { - const parsed = JSON.parse(text) as Partial; + const parsed = JSON.parse(text) as Partial< + E2EEHelloMessage | E2EEReadyMessage + >; - if (parsed.type === "hello" && typeof parsed.key === "string") { + if (parsed.type === "e2ee_hello" && typeof parsed.key === "string") { if (this.options.daemonKeyPair) { try { const clientPublicKey = importPublicKey(parsed.key); @@ -277,7 +281,7 @@ export class EncryptedChannel { // the channel and cause decrypt failures. if (keysEqual(nextSharedKey, this.sharedKey)) { this.transport.send( - JSON.stringify({ type: "ready" } satisfies ReadyMessage) + JSON.stringify({ type: "e2ee_ready" } satisfies E2EEReadyMessage) ); return null; } @@ -290,7 +294,7 @@ export class EncryptedChannel { this.sharedKey = nextSharedKey; this.pendingSends = []; this.transport.send( - JSON.stringify({ type: "ready" } satisfies ReadyMessage) + JSON.stringify({ type: "e2ee_ready" } satisfies E2EEReadyMessage) ); this.state = "open"; await this.flushPendingSends(); @@ -302,7 +306,7 @@ export class EncryptedChannel { return null; } - if (parsed.type === "ready") { + if (parsed.type === "e2ee_ready") { return null; }