Skip to content

Commit 05a3b5e

Browse files
authored
update e2ee defaults and protocol (#708)
* update e2ee defaults * fix imports * Create three-eagles-carry.md * add e2e e2ee test * prettier
1 parent 23b8652 commit 05a3b5e

9 files changed

Lines changed: 100 additions & 27 deletions

File tree

.changeset/three-eagles-carry.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@livekit/rtc-node": patch
3+
"livekit-server-sdk": patch
4+
---
5+
6+
update e2ee defaults to confirm to required FFI fields and update protocol

examples/agent-dispatch/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"dependencies": {
1515
"dotenv": "^16.4.5",
1616
"livekit-server-sdk": "workspace:*",
17-
"@livekit/protocol": "^1.46.3"
17+
"@livekit/protocol": "catalog:"
1818
},
1919
"devDependencies": {
2020
"@types/node": "^20.10.4",

examples/data-streams/index.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const greetParticipant = async (room: Room, recipient: RemoteParticipant) => {
2121
const greeting = 'Hi this is just a text sample';
2222
const streamWriter = await room.localParticipant?.streamText({
2323
destinationIdentities: [recipient.identity],
24-
topic: 'chat',
24+
topic: 'lk.chat',
2525
});
2626

2727
for (const c of greeting) {
@@ -53,7 +53,7 @@ const main = async () => {
5353
room.on(RoomEvent.ParticipantDisconnected, resolve);
5454
});
5555

56-
room.registerTextStreamHandler('chat', async (reader: TextStreamReader, { identity }) => {
56+
room.registerTextStreamHandler('lk.chat', async (reader: TextStreamReader, { identity }) => {
5757
console.log(`chat message from ${identity}: ${await reader.readAll()}`);
5858
// for await (const { collected } of reader) {
5959
// console.log(collected);
@@ -62,14 +62,16 @@ const main = async () => {
6262

6363
room.registerByteStreamHandler('files', async (reader: ByteStreamReader, { identity }) => {
6464
console.log(`welcome image received from ${identity}: ${reader.info.name}`);
65-
65+
console.time('receiving file');
6666
// create write stream and write received file to disk, make sure ./temp folder exists
6767
const writer = fs.createWriteStream(`./temp/${reader.info.name}`, {});
6868

6969
for await (const chunk of reader) {
7070
writer.write(chunk);
7171
}
7272
writer.close();
73+
console.timeEnd('receiving file');
74+
console.log('received a total of', writer.bytesWritten);
7375
});
7476

7577
room.on(RoomEvent.ParticipantConnected, async (participant) => {

packages/livekit-rtc/src/e2ee.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
FrameCryptorSetKeyIndexRequest,
1818
GetKeyRequest,
1919
GetSharedKeyRequest,
20+
KeyDerivationFunction,
2021
RatchetKeyRequest,
2122
RatchetSharedKeyRequest,
2223
SetKeyRequest,
@@ -27,18 +28,24 @@ import { FfiClient } from './ffi_client.js';
2728
const DEFAULT_RATCHET_SALT = new TextEncoder().encode('LKFrameEncryptionKey');
2829
const DEFAULT_RATCHET_WINDOW_SIZE = 16;
2930
const DEFAULT_FAILURE_TOLERANCE = -1;
31+
const DEFAULT_KEY_RING_SIZE = 16;
32+
const DEFAULT_KEY_DERIVATION_FUNCTION = KeyDerivationFunction.PBKDF2;
3033

3134
export interface KeyProviderOptions {
3235
sharedKey?: Uint8Array;
3336
ratchetSalt?: Uint8Array;
3437
ratchetWindowSize?: number;
3538
failureTolerance?: number;
39+
keyRingSize?: number;
40+
keyDerivationFunction?: KeyDerivationFunction;
3641
}
3742

3843
export const defaultKeyProviderOptions: KeyProviderOptions = {
3944
ratchetSalt: DEFAULT_RATCHET_SALT,
4045
ratchetWindowSize: DEFAULT_RATCHET_WINDOW_SIZE,
4146
failureTolerance: DEFAULT_FAILURE_TOLERANCE,
47+
keyRingSize: DEFAULT_KEY_RING_SIZE,
48+
keyDerivationFunction: DEFAULT_KEY_DERIVATION_FUNCTION,
4249
};
4350

4451
export interface E2EEOptions {

packages/livekit-rtc/src/room.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -284,14 +284,28 @@ export class Room extends (EventEmitter as new () => TypedEmitter<RoomCallbacks>
284284
useLegacyClientImplementation: true,
285285
});
286286
const e2eeEnabled = options.encryption || options.e2ee;
287-
const e2eeOptions = options.encryption
288-
? { ...defaultE2EEOptions, ...options.encryption }
289-
: { ...defaultE2EEOptions, ...options.e2ee };
287+
const userE2EEOptions = options.encryption ?? options.e2ee;
288+
const e2eeOptions: E2EEOptions = {
289+
...defaultE2EEOptions,
290+
...userE2EEOptions,
291+
keyProviderOptions: {
292+
...defaultE2EEOptions.keyProviderOptions,
293+
...userE2EEOptions?.keyProviderOptions,
294+
},
295+
};
296+
297+
const mergedOptions = { ...options };
298+
299+
if (options.encryption) {
300+
mergedOptions.encryption = e2eeOptions;
301+
} else if (options.e2ee) {
302+
mergedOptions.e2ee = e2eeOptions;
303+
}
290304

291305
const req = new ConnectRequest({
292306
url: url,
293307
token: token,
294-
options,
308+
options: mergedOptions,
295309
});
296310

297311
FfiClient.instance.on(FfiClientEvent.FfiEvent, this.onFfiEvent);

packages/livekit-rtc/src/tests/e2e.test.ts

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
ParticipantKind,
1515
Room,
1616
RoomEvent,
17+
type RoomOptions,
1718
RpcError,
1819
SimulateScenarioKind,
1920
TrackKind,
@@ -99,7 +100,10 @@ async function createJoinToken(params: {
99100
return await token.toJwt();
100101
}
101102

102-
async function connectTestRooms(count: number): Promise<{ roomName: string; rooms: Room[] }> {
103+
async function connectTestRooms(
104+
count: number,
105+
extraOptions?: Partial<RoomOptions>,
106+
): Promise<{ roomName: string; rooms: Room[] }> {
103107
const env = getTestEnv();
104108
const roomName = `test_room_${randomUUID()}`;
105109
const rooms = await Promise.all(
@@ -111,7 +115,11 @@ async function connectTestRooms(count: number): Promise<{ roomName: string; room
111115
name: `Participant ${i}`,
112116
});
113117
const room = new Room();
114-
await room.connect(env.url, token, { autoSubscribe: true, dynacast: false });
118+
await room.connect(env.url, token, {
119+
autoSubscribe: true,
120+
dynacast: false,
121+
...extraOptions,
122+
});
115123
return room;
116124
}),
117125
);
@@ -296,6 +304,46 @@ describeE2E('livekit-rtc e2e', () => {
296304
testTimeoutMs,
297305
);
298306

307+
it(
308+
'sends and receives a data message between participants sharing a key',
309+
async () => {
310+
// A shared-key `keyProviderOptions` with only `sharedKey` set relies on
311+
// the SDK filling in the remaining (proto-required) provider defaults;
312+
// regression guard for connect failing to encode KeyProviderOptions.
313+
const encryption = {
314+
keyProviderOptions: { sharedKey: new TextEncoder().encode('test-shared-key') },
315+
};
316+
const { rooms } = await connectTestRooms(2, { encryption });
317+
const [receivingRoom, sendingRoom] = rooms;
318+
const senderIdentity = sendingRoom!.localParticipant!.identity;
319+
const receiverIdentity = receivingRoom!.localParticipant!.identity;
320+
321+
const payload = new TextEncoder().encode('e2ee-hello');
322+
const received = withTimeout(
323+
new Promise<Uint8Array>((resolve) => {
324+
receivingRoom!.on(RoomEvent.DataReceived, (data, participant) => {
325+
if (participant?.identity !== senderIdentity) return;
326+
resolve(data);
327+
});
328+
}),
329+
testTimeoutMs,
330+
'Timed out waiting for encrypted data message',
331+
);
332+
333+
await sendingRoom!.localParticipant!.publishData(payload, {
334+
reliable: true,
335+
destination_identities: [receiverIdentity],
336+
});
337+
338+
// The message round-trips intact only if both sides brought a working
339+
// shared-key provider through connect and encrypt/decrypt lined up.
340+
expect(await received).toEqual(payload);
341+
342+
await Promise.all(rooms.map((r) => r.disconnect()));
343+
},
344+
testTimeoutMs,
345+
);
346+
299347
it(
300348
'emits participantDisconnected when a participant leaves',
301349
async () => {

packages/livekit-server-sdk/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
},
4646
"dependencies": {
4747
"@bufbuild/protobuf": "^1.10.1",
48-
"@livekit/protocol": "^1.48.0",
48+
"@livekit/protocol": "catalog:",
4949
"jose": "^5.1.2"
5050
},
5151
"devDependencies": {

pnpm-lock.yaml

Lines changed: 9 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pnpm-workspace.yaml

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@ packages:
66
minimumReleaseAge: 2880
77
minimumReleaseAgeExclude:
88
- '@livekit/*'
9-
# Renovate security update: turbo@2.9.14
10-
- turbo@2.9.14
11-
# Renovate security update: vite@6.4.2
12-
- vite@6.4.2
13-
# Renovate security update: vitest@4.1.0
14-
- vitest@4.1.0
9+
10+
catalog:
11+
'@livekit/protocol': 1.48.0

0 commit comments

Comments
 (0)