Skip to content

Commit 4d343bc

Browse files
committed
Using wrongkey key to trigger decryption failure.
1 parent c607f9b commit 4d343bc

2 files changed

Lines changed: 41 additions & 18 deletions

File tree

livekit-rtc/tests/test_e2ee_per_participant.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ def make_per_participant_e2ee_options() -> rtc.E2EEOptions:
8888
# No shared key — keys are installed per participant after connect.
8989
options.key_provider_options.shared_key = None
9090
options.key_provider_options.ratchet_window_size = 16
91+
# failure_tolerance must be >= 0 for the cryptor to surface DECRYPTION_FAILED;
92+
# the default -1 means "infinite retries via auto-ratchet" and never emits.
93+
options.key_provider_options.failure_tolerance = 3
9194
return options
9295

9396

@@ -134,9 +137,10 @@ async def test_e2ee_per_participant():
134137
track_published and an OK e2ee_state.
135138
3. All participants must use GCM encryption.
136139
4. Publisher ratchets key index 2 → receivers observe KEY_RATCHETED.
137-
5. Publisher disables E2EE → receivers observe DECRYPTION_FAILED.
138-
6. Publisher switches to key index 1 and re-enables E2EE → receivers
139-
observe OK (since they already have key index 1).
140+
5. Publisher overwrites key index 0 with a wrong key → receivers
141+
observe DECRYPTION_FAILED.
142+
6. Publisher switches to key index 1 → receivers observe OK
143+
(since they already have key index 1).
140144
7. Publisher switches to key index 2 and ratchets it; receivers
141145
switch to key index 2 → receivers observe KEY_RATCHETED.
142146
"""
@@ -301,14 +305,15 @@ def all_remote_video_pubs_gcm(room: rtc.Room) -> bool:
301305
),
302306
)
303307

304-
# 7) disable e2ee on publisher → receivers observe DECRYPTION_FAILED
308+
# 7) publisher overwrites key index 0 with a wrong key (receivers
309+
# still hold the original key 0) → receivers observe DECRYPTION_FAILED
305310
seen_e2ee_states["receiver1"].clear()
306311
seen_e2ee_states["receiver2"].clear()
307312
e2ee_state_log["receiver1"].clear()
308313
e2ee_state_log["receiver2"].clear()
309314
await asyncio.sleep(1.0)
310315

311-
publisher_room.e2ee_manager.set_enabled(False)
316+
publisher_key_provider.set_key(PUBLISHER_IDENTITY, b"wrongkey", 0)
312317

313318
await assert_eventually(
314319
lambda: rtc.EncryptionState.DECRYPTION_FAILED in seen_e2ee_states["receiver1"],
@@ -323,16 +328,20 @@ def all_remote_video_pubs_gcm(room: rtc.Room) -> bool:
323328
),
324329
)
325330

326-
# 8) publisher switches to key index 1 and re-enables e2ee
327-
# receivers already have key index 1 → should reach OK
331+
# 8) publisher switches to key index 1 (still correct on both sides).
332+
# Once failure_tolerance is exhausted the receiver cryptor stops
333+
# retrying, so we also re-install key index 1 on the receivers to
334+
# deliver a fresh key event that wakes the cryptor back up.
328335
seen_e2ee_states["receiver1"].clear()
329336
seen_e2ee_states["receiver2"].clear()
330337
e2ee_state_log["receiver1"].clear()
331338
e2ee_state_log["receiver2"].clear()
332339
await asyncio.sleep(1.0)
333340

334341
set_key_index_on_all_cryptors(publisher_room, 1)
335-
publisher_room.e2ee_manager.set_enabled(True)
342+
key1_bytes, _ = PUBLISHER_KEYS[1]
343+
receiver1_room.e2ee_manager.key_provider.set_key(PUBLISHER_IDENTITY, key1_bytes, 1)
344+
receiver2_room.e2ee_manager.key_provider.set_key(PUBLISHER_IDENTITY, key1_bytes, 1)
336345

337346
await assert_eventually(
338347
lambda: rtc.EncryptionState.OK in seen_e2ee_states["receiver1"],

livekit-rtc/tests/test_e2ee_shared_key.py

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828

2929
SHARED_KEY = b"12345678"
30+
WRONG_KEY = b"wrongkey"
3031
WIDTH, HEIGHT = 320, 180
3132
FRAME_RATE = 15
3233

@@ -80,6 +81,9 @@ def make_e2ee_options() -> rtc.E2EEOptions:
8081
options = rtc.E2EEOptions()
8182
options.key_provider_options.shared_key = SHARED_KEY
8283
options.key_provider_options.ratchet_window_size = 16
84+
# failure_tolerance must be >= 0 for the cryptor to surface DECRYPTION_FAILED;
85+
# the default -1 means "infinite retries via auto-ratchet" and never emits.
86+
options.key_provider_options.failure_tolerance = 3
8387
return options
8488

8589

@@ -112,8 +116,11 @@ async def test_e2ee_shared_key():
112116
3. All participants must use GCM encryption.
113117
4. Publisher ratchets the shared key; receivers must observe
114118
KEY_RATCHETED state.
115-
5. Publisher disables E2EE; receivers must observe DECRYPTION_FAILED.
116-
6. Publisher re-enables E2EE; receivers must observe OK again.
119+
5. Publisher swaps its shared key for a wrong one; receivers must observe
120+
DECRYPTION_FAILED.
121+
6. After exhausting failure_tolerance, the receiver cryptor stops
122+
retrying; restoring the correct key on the publisher and re-installing
123+
it on the receivers must bring them back to OK.
117124
"""
118125
room_name = unique_room_name("test-e2ee-shared-key")
119126
url = os.getenv("LIVEKIT_URL")
@@ -272,54 +279,61 @@ def all_remote_video_pubs_gcm(room: rtc.Room) -> bool:
272279
),
273280
)
274281

275-
# 8) disable e2ee on the publisher; receivers should fail to decrypt
282+
# 8) swap the publisher's shared key for a wrong one; receivers should
283+
# fail to decrypt and surface DECRYPTION_FAILED once failure_tolerance
284+
# is exhausted.
276285
seen_e2ee_states["receiver1"].clear()
277286
seen_e2ee_states["receiver2"].clear()
278287
e2ee_state_log["receiver1"].clear()
279288
e2ee_state_log["receiver2"].clear()
280289
await asyncio.sleep(1.0)
281290

282-
publisher_room.e2ee_manager.set_enabled(False)
291+
key_provider.set_shared_key(WRONG_KEY, key_index=0)
283292

284293
await assert_eventually(
285294
lambda: rtc.EncryptionState.DECRYPTION_FAILED in seen_e2ee_states["receiver1"],
286295
timeout=15.0,
287296
message=(
288-
"receiver1 did not observe DECRYPTION_FAILED after publisher disabled e2ee "
297+
"receiver1 did not observe DECRYPTION_FAILED after publisher swapped key "
289298
f"(saw {e2ee_state_log['receiver1']})"
290299
),
291300
)
292301
await assert_eventually(
293302
lambda: rtc.EncryptionState.DECRYPTION_FAILED in seen_e2ee_states["receiver2"],
294303
timeout=15.0,
295304
message=(
296-
"receiver2 did not observe DECRYPTION_FAILED after publisher disabled e2ee "
305+
"receiver2 did not observe DECRYPTION_FAILED after publisher swapped key "
297306
f"(saw {e2ee_state_log['receiver2']})"
298307
),
299308
)
300309

301-
# 9) re-enable e2ee on the publisher; receivers should reach OK again
310+
# 9) restore the correct shared key on the publisher, and re-install it
311+
# on the receivers too: once failure_tolerance is exhausted the
312+
# receiver cryptor stops retrying, so a fresh key event is required to
313+
# bring it back to OK.
302314
seen_e2ee_states["receiver1"].clear()
303315
seen_e2ee_states["receiver2"].clear()
304316
e2ee_state_log["receiver1"].clear()
305317
e2ee_state_log["receiver2"].clear()
306318
await asyncio.sleep(1.0)
307319

308-
publisher_room.e2ee_manager.set_enabled(True)
320+
key_provider.set_shared_key(SHARED_KEY, key_index=0)
321+
receiver1_room.e2ee_manager.key_provider.set_shared_key(SHARED_KEY, key_index=0)
322+
receiver2_room.e2ee_manager.key_provider.set_shared_key(SHARED_KEY, key_index=0)
309323

310324
await assert_eventually(
311325
lambda: rtc.EncryptionState.OK in seen_e2ee_states["receiver1"],
312326
timeout=15.0,
313327
message=(
314-
"receiver1 did not return to EncryptionState.OK after re-enable "
328+
"receiver1 did not return to EncryptionState.OK after key restore "
315329
f"(saw {e2ee_state_log['receiver1']})"
316330
),
317331
)
318332
await assert_eventually(
319333
lambda: rtc.EncryptionState.OK in seen_e2ee_states["receiver2"],
320334
timeout=15.0,
321335
message=(
322-
"receiver2 did not return to EncryptionState.OK after re-enable "
336+
"receiver2 did not return to EncryptionState.OK after key restore "
323337
f"(saw {e2ee_state_log['receiver2']})"
324338
),
325339
)

0 commit comments

Comments
 (0)