From 58b253885e53aec7ae89f4cd286e1e2a40fa8f45 Mon Sep 17 00:00:00 2001 From: Mike Date: Mon, 26 Oct 2026 20:21:27 -0700 Subject: [PATCH 1/2] Fix E2EEManager::frameCryptors() sending an empty E2EE request The request never selected manager_get_frame_cryptors, so the FFI rejected it and frameCryptors() always threw. Fixes #299 Co-Authored-By: Claude Opus 5.5 --- src/e2ee.cpp | 1 + src/tests/integration/test_e2ee.cpp | 67 +++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 src/tests/integration/test_e2ee.cpp diff --git a/src/e2ee.cpp b/src/e2ee.cpp index 8ce73742..403582df 100644 --- a/src/e2ee.cpp +++ b/src/e2ee.cpp @@ -155,6 +155,7 @@ std::weak_ptr E2EEManager::keyProvider() const { std::vector E2EEManager::frameCryptors() const { proto::FfiRequest req; req.mutable_e2ee()->set_room_handle(room_handle_); + req.mutable_e2ee()->mutable_manager_get_frame_cryptors(); auto resp = FfiClient::instance().sendRequest(req); std::vector out; const auto& list = resp.e2ee().manager_get_frame_cryptors().frame_cryptors(); diff --git a/src/tests/integration/test_e2ee.cpp b/src/tests/integration/test_e2ee.cpp new file mode 100644 index 00000000..0baabed0 --- /dev/null +++ b/src/tests/integration/test_e2ee.cpp @@ -0,0 +1,67 @@ +/* + * Copyright 2026 LiveKit + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "../common/audio_utils.h" +#include "../common/test_common.h" + +namespace livekit::test { + +using namespace std::chrono_literals; + +class E2EEIntegrationTest : public LiveKitTestBase {}; + +TEST_F(E2EEIntegrationTest, FrameCryptorsIncludePublishedTrack) { + failIfNotConfigured(); + + Room room; + RoomOptions room_options; + room_options.encryption = E2EEOptions{}; + room_options.encryption->key_provider_options.shared_key = std::vector{'k', 'e', 'y'}; + ASSERT_TRUE(room.connect(config_.url, config_.token_a, room_options)); + auto participant = lockLocalParticipant(room); + + auto source = std::make_shared(kDefaultAudioSampleRate, kDefaultAudioChannels, 0); + auto track = participant->publishAudioTrack("e2ee-frame-cryptors", source, TrackSource::SOURCE_MICROPHONE); + auto manager = room.e2eeManager().lock(); + ASSERT_NE(manager, nullptr); + + // The track's cryptor may be created after publishing returns. + bool found = false; + const auto deadline = std::chrono::steady_clock::now() + 5s; + while (!found && std::chrono::steady_clock::now() < deadline) { + std::vector cryptors; + ASSERT_NO_THROW(cryptors = manager->frameCryptors()); + for (const auto& cryptor : cryptors) { + found = found || cryptor.participantIdentity() == participant->identity(); + } + if (!found) { + std::this_thread::sleep_for(50ms); + } + } + EXPECT_TRUE(found) << "No frame cryptor for the local participant's track"; + participant->unpublishTrack(track->publication()->sid()); +} + +} // namespace livekit::test From 50f9e45fb12a042e6c57ca1c88d28921d7f3fcc4 Mon Sep 17 00:00:00 2001 From: Mike Date: Tue, 27 Oct 2026 20:27:34 -0700 Subject: [PATCH 2/2] Check frameCryptors() once instead of polling Publishing a track creates its frame cryptor before publishAudioTrack() returns, so the test doesn't need to wait for it. Co-Authored-By: Claude Opus 5.5 --- src/tests/integration/test_e2ee.cpp | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/src/tests/integration/test_e2ee.cpp b/src/tests/integration/test_e2ee.cpp index 0baabed0..68880c59 100644 --- a/src/tests/integration/test_e2ee.cpp +++ b/src/tests/integration/test_e2ee.cpp @@ -16,11 +16,10 @@ #include -#include +#include #include #include #include -#include #include #include "../common/audio_utils.h" @@ -28,8 +27,6 @@ namespace livekit::test { -using namespace std::chrono_literals; - class E2EEIntegrationTest : public LiveKitTestBase {}; TEST_F(E2EEIntegrationTest, FrameCryptorsIncludePublishedTrack) { @@ -47,19 +44,11 @@ TEST_F(E2EEIntegrationTest, FrameCryptorsIncludePublishedTrack) { auto manager = room.e2eeManager().lock(); ASSERT_NE(manager, nullptr); - // The track's cryptor may be created after publishing returns. - bool found = false; - const auto deadline = std::chrono::steady_clock::now() + 5s; - while (!found && std::chrono::steady_clock::now() < deadline) { - std::vector cryptors; - ASSERT_NO_THROW(cryptors = manager->frameCryptors()); - for (const auto& cryptor : cryptors) { - found = found || cryptor.participantIdentity() == participant->identity(); - } - if (!found) { - std::this_thread::sleep_for(50ms); - } - } + std::vector cryptors; + ASSERT_NO_THROW(cryptors = manager->frameCryptors()); + const bool found = std::any_of(cryptors.begin(), cryptors.end(), [&](const auto& cryptor) { + return cryptor.participantIdentity() == participant->identity(); + }); EXPECT_TRUE(found) << "No frame cryptor for the local participant's track"; participant->unpublishTrack(track->publication()->sid()); }