End-to-end encryption for the LumenJS Communication module, inspired by the Signal Protocol's X3DH key agreement pattern.
The communication module supports end-to-end encryption inspired by the Signal Protocol's X3DH (Extended Triple Diffie-Hellman) key agreement pattern. The server never sees plaintext, it stores and relays opaque encrypted envelopes.
How it works:
KeyBundle to the server'prekey' envelope containing her ephemeral public key'message' envelopesUploading keys:
// Client generates keys locally (using Web Crypto or libsignal) this.emit('encryption:upload-keys', { userId: myUserId, identityKey: identityPublicKeyBase64, signedPreKey: { keyId: 1, publicKey: signedPreKeyBase64 }, signedPreKeySignature: signatureBase64, oneTimePreKeys: [ { keyId: 1, publicKey: otk1Base64 }, { keyId: 2, publicKey: otk2Base64 }, // ... up to 100 one-time pre-keys ], uploadedAt: new Date().toISOString(), });
Requesting a recipient's keys for session setup:
// Request Bob's key bundle this.emit('encryption:request-keys', { recipientId: 'bob-user-id' }); // Server responds by setting event + data properties: // event = 'encryption:keys-response', data = KeyExchangeResponse // KeyExchangeResponse: { // recipientId, identityKey, signedPreKey, // signedPreKeySignature, oneTimePreKey? // }
Initializing an encrypted session:
// After performing X3DH and deriving the shared secret, // send the first (prekey) message: this.emit('encryption:session-init', { recipientId: 'bob-user-id', sessionId: 'session-abc', envelope: { senderId: myUserId, recipientId: 'bob-user-id', sessionId: 'session-abc', ciphertext: encryptedBase64, messageType: 'prekey', senderIdentityKey: myIdentityKeyBase64, senderEphemeralKey: ephemeralKeyBase64, usedOneTimePreKeyId: 3, }, });
encryption:keys-depleted event. Clients should listen for this event and upload a fresh batch of pre-keys to maintain forward secrecy for new sessions.
Encryption event flow:
| Client Event | Server Response | Purpose |
|---|---|---|
encryption:upload-keys | encryption:keys-uploaded | Store public key bundle on server |
encryption:request-keys | encryption:keys-response | Fetch recipient's keys for X3DH |
encryption:session-init | encryption:session-init (to recipient) | Relay first prekey message to establish session |
| -- | encryption:keys-depleted | Notify user to upload more one-time pre-keys |