|
27 | 27 |
|
28 | 28 |
|
29 | 29 | SHARED_KEY = b"12345678" |
| 30 | +WRONG_KEY = b"wrongkey" |
30 | 31 | WIDTH, HEIGHT = 320, 180 |
31 | 32 | FRAME_RATE = 15 |
32 | 33 |
|
@@ -80,6 +81,9 @@ def make_e2ee_options() -> rtc.E2EEOptions: |
80 | 81 | options = rtc.E2EEOptions() |
81 | 82 | options.key_provider_options.shared_key = SHARED_KEY |
82 | 83 | 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 |
83 | 87 | return options |
84 | 88 |
|
85 | 89 |
|
@@ -112,8 +116,11 @@ async def test_e2ee_shared_key(): |
112 | 116 | 3. All participants must use GCM encryption. |
113 | 117 | 4. Publisher ratchets the shared key; receivers must observe |
114 | 118 | 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. |
117 | 124 | """ |
118 | 125 | room_name = unique_room_name("test-e2ee-shared-key") |
119 | 126 | url = os.getenv("LIVEKIT_URL") |
@@ -272,54 +279,61 @@ def all_remote_video_pubs_gcm(room: rtc.Room) -> bool: |
272 | 279 | ), |
273 | 280 | ) |
274 | 281 |
|
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. |
276 | 285 | seen_e2ee_states["receiver1"].clear() |
277 | 286 | seen_e2ee_states["receiver2"].clear() |
278 | 287 | e2ee_state_log["receiver1"].clear() |
279 | 288 | e2ee_state_log["receiver2"].clear() |
280 | 289 | await asyncio.sleep(1.0) |
281 | 290 |
|
282 | | - publisher_room.e2ee_manager.set_enabled(False) |
| 291 | + key_provider.set_shared_key(WRONG_KEY, key_index=0) |
283 | 292 |
|
284 | 293 | await assert_eventually( |
285 | 294 | lambda: rtc.EncryptionState.DECRYPTION_FAILED in seen_e2ee_states["receiver1"], |
286 | 295 | timeout=15.0, |
287 | 296 | message=( |
288 | | - "receiver1 did not observe DECRYPTION_FAILED after publisher disabled e2ee " |
| 297 | + "receiver1 did not observe DECRYPTION_FAILED after publisher swapped key " |
289 | 298 | f"(saw {e2ee_state_log['receiver1']})" |
290 | 299 | ), |
291 | 300 | ) |
292 | 301 | await assert_eventually( |
293 | 302 | lambda: rtc.EncryptionState.DECRYPTION_FAILED in seen_e2ee_states["receiver2"], |
294 | 303 | timeout=15.0, |
295 | 304 | message=( |
296 | | - "receiver2 did not observe DECRYPTION_FAILED after publisher disabled e2ee " |
| 305 | + "receiver2 did not observe DECRYPTION_FAILED after publisher swapped key " |
297 | 306 | f"(saw {e2ee_state_log['receiver2']})" |
298 | 307 | ), |
299 | 308 | ) |
300 | 309 |
|
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. |
302 | 314 | seen_e2ee_states["receiver1"].clear() |
303 | 315 | seen_e2ee_states["receiver2"].clear() |
304 | 316 | e2ee_state_log["receiver1"].clear() |
305 | 317 | e2ee_state_log["receiver2"].clear() |
306 | 318 | await asyncio.sleep(1.0) |
307 | 319 |
|
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) |
309 | 323 |
|
310 | 324 | await assert_eventually( |
311 | 325 | lambda: rtc.EncryptionState.OK in seen_e2ee_states["receiver1"], |
312 | 326 | timeout=15.0, |
313 | 327 | message=( |
314 | | - "receiver1 did not return to EncryptionState.OK after re-enable " |
| 328 | + "receiver1 did not return to EncryptionState.OK after key restore " |
315 | 329 | f"(saw {e2ee_state_log['receiver1']})" |
316 | 330 | ), |
317 | 331 | ) |
318 | 332 | await assert_eventually( |
319 | 333 | lambda: rtc.EncryptionState.OK in seen_e2ee_states["receiver2"], |
320 | 334 | timeout=15.0, |
321 | 335 | message=( |
322 | | - "receiver2 did not return to EncryptionState.OK after re-enable " |
| 336 | + "receiver2 did not return to EncryptionState.OK after key restore " |
323 | 337 | f"(saw {e2ee_state_log['receiver2']})" |
324 | 338 | ), |
325 | 339 | ) |
|
0 commit comments