Skip to content

Commit

Permalink
feat: end-to-end encryption (#161)
Browse files Browse the repository at this point in the history
Co-authored-by: Théo Monnom <[email protected]>
  • Loading branch information
cloudwebrtc and theomonnom committed Sep 4, 2023
1 parent a934d50 commit 0edb535
Show file tree
Hide file tree
Showing 41 changed files with 2,150 additions and 250 deletions.
342 changes: 164 additions & 178 deletions Cargo.lock

Large diffs are not rendered by default.

23 changes: 6 additions & 17 deletions examples/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ members = [
"basic_room",
"mobile",
"save_to_disk",
"play_from_disk",
"wgpu_room",
"webhooks",
]
3 changes: 2 additions & 1 deletion examples/save_to_disk/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ async fn main() {
println!("Connected to room: {} - {}", room.name(), room.sid());

while let Some(msg) = rx.recv().await {
#[allow(clippy::single_match)]
match msg {
RoomEvent::TrackSubscribed {
track,
Expand Down Expand Up @@ -127,7 +128,7 @@ async fn record_track(audio_track: RemoteAudioTrack) -> Result<(), std::io::Erro
let mut wav_writer = WavWriter::create(FILE_PATH, header).await?;
let mut audio_stream = NativeAudioStream::new(rtc_track);

let max_record = 5 * header.sample_rate * header.num_channels as u32;
let max_record = 5 * header.sample_rate * header.num_channels;
let mut sample_count = 0;
'recv_loop: while let Some(frame) = audio_stream.next().await {
let data = resampler.remix_and_resample(
Expand Down
57 changes: 45 additions & 12 deletions examples/wgpu_room/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{
video_renderer::VideoRenderer,
};
use egui::{Rounding, Stroke};
use livekit::{prelude::*, SimulateScenario};
use livekit::{e2ee::EncryptionType, prelude::*, SimulateScenario};
use std::collections::HashMap;

/// The state of the application are saved on app exit and restored on app start.
Expand All @@ -13,7 +13,9 @@ use std::collections::HashMap;
struct AppState {
url: String,
token: String,
key: String,
auto_subscribe: bool,
enable_e2ee: bool,
}

pub struct LkApp {
Expand All @@ -32,6 +34,8 @@ impl Default for AppState {
url: "ws://localhost:7880".to_string(),
token: "".to_string(),
auto_subscribe: true,
enable_e2ee: false,
key: "".to_string(),
}
}
}
Expand Down Expand Up @@ -122,6 +126,10 @@ impl LkApp {
RoomEvent::Disconnected { reason: _ } => {
self.video_renderers.clear();
}
RoomEvent::E2eeStateChanged { participant, state } => {
let identity = participant.identity();
log::info!("e2ee state changed {} - {:?}", identity, state)
}
_ => {}
}
}
Expand Down Expand Up @@ -179,6 +187,17 @@ impl LkApp {
ui.text_edit_singleline(&mut self.state.token);
});

ui.horizontal(|ui| {
ui.label("E2EE Key: ");
ui.text_edit_singleline(&mut self.state.key);
});

ui.horizontal(|ui| {
ui.add_enabled_ui(true, |ui| {
ui.checkbox(&mut self.state.enable_e2ee, "Enable E2EE");
});
});

ui.horizontal(|ui| {
ui.add_enabled_ui(!connected && !self.connecting, |ui| {
if ui.button("Connect").clicked() {
Expand All @@ -188,19 +207,23 @@ impl LkApp {
url: self.state.url.clone(),
token: self.state.token.clone(),
auto_subscribe: self.state.auto_subscribe,
enable_e2ee: self.state.enable_e2ee,
key: self.state.key.clone(),
});
}
});

if self.connecting {
ui.spinner();
} else if connected {
if ui.button("Disconnect").clicked() {
let _ = self.service.send(AsyncCmd::RoomDisconnect);
}
} else if connected && ui.button("Disconnect").clicked() {
let _ = self.service.send(AsyncCmd::RoomDisconnect);
}
});

if ui.button("E2eeKeyRatchet").clicked() {
let _ = self.service.send(AsyncCmd::E2eeKeyRatchet);
}

ui.horizontal(|ui| {
ui.add_enabled_ui(true, |ui| {
ui.checkbox(&mut self.state.auto_subscribe, "Auto Subscribe");
Expand Down Expand Up @@ -230,7 +253,9 @@ impl LkApp {
ui.label("Participants");
ui.separator();

let Some(room) = self.service.room() else { return; };
let Some(room) = self.service.room() else {
return;
};

egui::ScrollArea::vertical().show(ui, |ui| {
// Iterate with sorted keys to avoid flickers (Because this is a immediate mode UI)
Expand All @@ -239,18 +264,28 @@ impl LkApp {
.keys()
.cloned()
.collect::<Vec<ParticipantSid>>();
sorted_participants.sort_by(|a, b| a.as_str().cmp(&b.as_str()));
sorted_participants.sort_by(|a, b| a.as_str().cmp(b.as_str()));

for psid in sorted_participants {
let participant = participants.get(&psid).unwrap();
let tracks = participant.tracks();
let mut sorted_tracks = tracks.keys().cloned().collect::<Vec<TrackSid>>();
sorted_tracks.sort_by(|a, b| a.as_str().cmp(&b.as_str()));
sorted_tracks.sort_by(|a, b| a.as_str().cmp(b.as_str()));

ui.monospace(&participant.identity().0);
for tsid in sorted_tracks {
let publication = tracks.get(&tsid).unwrap().clone();

ui.horizontal(|ui| {
ui.label("Encrypted - ");
let enc_type = publication.encryption_type();
if enc_type == EncryptionType::None {
ui.colored_label(egui::Color32::RED, format!("{:?}", enc_type));
} else {
ui.colored_label(egui::Color32::GREEN, format!("{:?}", enc_type));
}
});

ui.label(format!(
"{} - {:?}",
publication.name(),
Expand All @@ -274,10 +309,8 @@ impl LkApp {
.service
.send(AsyncCmd::UnsubscribeTrack { publication });
}
} else {
if ui.button("Subscribe").clicked() {
let _ = self.service.send(AsyncCmd::SubscribeTrack { publication });
}
} else if ui.button("Subscribe").clicked() {
let _ = self.service.send(AsyncCmd::SubscribeTrack { publication });
}
});
}
Expand Down
27 changes: 26 additions & 1 deletion examples/wgpu_room/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ use crate::{
logo_track::LogoTrack,
sine_track::{SineParameters, SineTrack},
};
use livekit::{prelude::*, SimulateScenario};
use livekit::{
e2ee::{key_provider::*, E2eeOptions, EncryptionType},
prelude::*,
SimulateScenario,
};
use parking_lot::Mutex;
use std::sync::Arc;
use tokio::sync::mpsc::{self, error::SendError};
Expand All @@ -13,6 +17,8 @@ pub enum AsyncCmd {
url: String,
token: String,
auto_subscribe: bool,
enable_e2ee: bool,
key: String,
},
RoomDisconnect,
SimulateScenario {
Expand All @@ -26,6 +32,7 @@ pub enum AsyncCmd {
UnsubscribeTrack {
publication: RemoteTrackPublication,
},
E2eeKeyRatchet,
}

#[derive(Debug)]
Expand Down Expand Up @@ -102,14 +109,24 @@ async fn service_task(inner: Arc<ServiceInner>, mut cmd_rx: mpsc::UnboundedRecei
url,
token,
auto_subscribe,
enable_e2ee,
key,
} => {
log::info!("connecting to room: {}", url);

let key_provider =
KeyProvider::with_shared_key(KeyProviderOptions::default(), key.into_bytes());
let e2ee = enable_e2ee.then_some(E2eeOptions {
encryption_type: EncryptionType::Gcm,
key_provider,
});

let res = Room::connect(
&url,
&token,
RoomOptions {
auto_subscribe,
e2ee,
..Default::default()
},
)
Expand Down Expand Up @@ -174,6 +191,14 @@ async fn service_task(inner: Arc<ServiceInner>, mut cmd_rx: mpsc::UnboundedRecei
AsyncCmd::UnsubscribeTrack { publication } => {
publication.set_subscribed(false);
}
AsyncCmd::E2eeKeyRatchet => {
if let Some(state) = running_state.as_ref() {
let e2ee_manager = state.room.e2ee_manager();
if let Some(key_provider) = e2ee_manager.key_provider() {
key_provider.ratchet_shared_key(0);
}
}
}
}
}
}
Expand Down
10 changes: 5 additions & 5 deletions examples/wgpu_room/src/video_renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ impl VideoRenderer {
let mut internal = internal.lock();
let buffer = frame.buffer.to_i420();

let width: u32 = buffer.width().try_into().unwrap();
let height: u32 = buffer.height().try_into().unwrap();
let width: u32 = buffer.width();
let height: u32 = buffer.height();

internal.ensure_texture_size(width, height);

Expand Down Expand Up @@ -110,7 +110,7 @@ impl VideoRenderer {

// Returns the texture id, can be used to draw the texture on the UI
pub fn texture_id(&self) -> Option<egui::TextureId> {
self.internal.lock().egui_texture.clone()
self.internal.lock().egui_texture
}
}

Expand Down Expand Up @@ -160,14 +160,14 @@ impl RendererInternal {
.renderer
.write()
.update_egui_texture_from_wgpu_texture(
&*self.render_state.device,
&self.render_state.device,
self.texture_view.as_ref().unwrap(),
wgpu::FilterMode::Linear,
texture_id,
);
} else {
self.egui_texture = Some(self.render_state.renderer.write().register_native_texture(
&*self.render_state.device,
&self.render_state.device,
self.texture_view.as_ref().unwrap(),
wgpu::FilterMode::Linear,
));
Expand Down
3 changes: 2 additions & 1 deletion livekit-ffi/generate_proto.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@ protoc \
$PROTOCOL/track.proto \
$PROTOCOL/participant.proto \
$PROTOCOL/video_frame.proto \
$PROTOCOL/audio_frame.proto
$PROTOCOL/audio_frame.proto \
$PROTOCOL/e2ee.proto
Loading

0 comments on commit 0edb535

Please sign in to comment.