Skip to content

Commit 26947e9

Browse files
e2ee. (#226)
* chore: e2ee. * update. * update. * update. * update. * chore: Use feat/frame-encryption branch of flutter-webrtc. * chore: Add E2EEKEY defines for dart environment, and e2ee switch. * Add encodedInsertableStreams to RTCConfiguration. * update. * feat: Add e2ee indicator for Participant. * feat: add e2ee worker js for flutter web. * dart format. * remove unused file. * fix flutter analyze . * update. * update. * add: indicate for decryption failure, and string key. * remove .lock files. * update. * update. * update e2ee.worker for web. * feat: support setCodecPreferences. * state TrackE2EEStateEvent. * fix wrong import interface from dart_webrtc. * update. * update. * update. * update. * update pubspec.lock. * chore: update protocol and add EncryptionType for Participant. * Update lib/src/e2ee/options.dart Co-authored-by: Théo Monnom <[email protected]> * fix typo. * revert changes for internal import. * Add _cleanUp() for previous room. * Add e2ee supports detection method for native/web. * Remove redundant overriding methods. * dart format. * Add e2ee.worker code and deployment docs. * chore: remove duplicate words. * chore: using Pbkdf2 derive the key. * Update pubspec.yaml * fix e2ee for safari. * fix key length. * update e2ee.worker.dart.js. * fix. * update proto. * update. * chore: add simulate for rachetKey. * update. * chore: key ratchet for flutter web. * update. * update. * update. * chore: key ratchet export for web. * bump version for xframeworks. * update. * chore: some changes for key safety ratcheting. * update. * fix typo. * update. * rename. * magic bytes for web. * bump version for flutter-webrtc. * fix analyzer. --------- Co-authored-by: Théo Monnom <[email protected]>
1 parent 3407221 commit 26947e9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+12523
-39
lines changed

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,6 @@ proto:
1313
format:
1414
flutter format --set-exit-if-changed -l 100 .
1515

16+
e2ee: dart compile js .\web\e2ee.worker.dart -o .\example\web\e2ee.worker.dart.js
17+
1618
.PHONY: proto format

README.md

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ More Docs and guides are available at [https://docs.livekit.io](https://docs.liv
2020

2121
## Current supported features
2222

23-
| Feature | Subscribe/Publish | Simulcast | Background audio | Screen sharing |
24-
| :-----: | :---------------: | :-------: | :--------------: | :------------: |
25-
| Web | 🟢 | 🟢 | 🟢 | 🟢 |
26-
| iOS | 🟢 | 🟢 | 🟢 | 🟢 |
27-
| Android | 🟢 | 🟢 | 🟢 | 🟢 |
28-
| Mac | 🟢 | 🟢 | 🟢 | 🟢 |
29-
| Windows | 🟢 | 🟢 | 🟢 | 🟢 |
23+
| Feature | Subscribe/Publish | Simulcast | Background audio | Screen sharing | End to End Encryption |
24+
| :-----: | :---------------: | :-------: | :--------------: | :------------: | :------------: |
25+
| Web | 🟢 | 🟢 | 🟢 | 🟢 | 🟢 |
26+
| iOS | 🟢 | 🟢 | 🟢 | 🟢 | 🟢 |
27+
| Android | 🟢 | 🟢 | 🟢 | 🟢 | 🟢 |
28+
| Mac | 🟢 | 🟢 | 🟢 | 🟢 | 🟢 |
29+
| Windows | 🟢 | 🟢 | 🟢 | 🟢 | 🟢 |
3030

3131
🟢 = Available
3232

@@ -215,6 +215,21 @@ try {
215215
}
216216
```
217217

218+
### End to End Encryption
219+
220+
LiveKit supports end-to-end encryption for audio/video data sent over the network.
221+
By default, the native platform can support E2EE without any settings, but for flutter web, you need to use the following steps to create `e2ee.worker.dart.js` file.
222+
223+
```bash
224+
# for example app
225+
dart compile js .\web\e2ee.worker.dart -o .\example\web\e2ee.worker.dart.js
226+
# for your project
227+
export YOU_PROJECT_DIR=your_project_dir
228+
git clone https://github.com/livekit/client-sdk-flutter.git
229+
cd client-sdk-flutter && flutter pub get
230+
dart compile js .\web\e2ee.worker.dart -o ${YOU_PROJECT_DIR}\web\e2ee.worker.dart.js
231+
```
232+
218233
### Advanced track manipulation
219234

220235
The setCameraEnabled/setMicrophoneEnabled helpers are wrappers around the Track API.

analysis_options.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ analyzer:
3030
- '**/*.pbenum.dart'
3131
- '**/*.pbjson.dart'
3232
- '**/*.pbserver.dart'
33+
- 'web/*.dart'
3334

3435
linter:
3536
rules:
@@ -39,3 +40,4 @@ linter:
3940
prefer_single_quotes: true
4041
unnecessary_brace_in_string_interps: false
4142
unawaited_futures: true
43+
depend_on_referenced_packages: false

example/lib/exts.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,4 +194,5 @@ enum SimulateScenarioResult {
194194
serverLeave,
195195
switchCandidate,
196196
clear,
197+
e2eeKeyRatchet,
197198
}

example/lib/pages/connect.dart

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,18 @@ class _ConnectPageState extends State<ConnectPage> {
2525
static const _storeKeyAdaptiveStream = 'adaptive-stream';
2626
static const _storeKeyDynacast = 'dynacast';
2727
static const _storeKeyFastConnect = 'fast-connect';
28+
static const _storeKeyE2EE = 'e2ee';
29+
static const _storeKeySharedKey = 'shared-key';
2830

2931
final _uriCtrl = TextEditingController();
3032
final _tokenCtrl = TextEditingController();
33+
final _sharedKeyCtrl = TextEditingController();
3134
bool _simulcast = true;
3235
bool _adaptiveStream = true;
3336
bool _dynacast = true;
3437
bool _busy = false;
3538
bool _fastConnect = false;
39+
bool _e2ee = false;
3640

3741
@override
3842
void initState() {
@@ -56,11 +60,15 @@ class _ConnectPageState extends State<ConnectPage> {
5660
_tokenCtrl.text = const bool.hasEnvironment('TOKEN')
5761
? const String.fromEnvironment('TOKEN')
5862
: prefs.getString(_storeKeyToken) ?? '';
63+
_sharedKeyCtrl.text = const bool.hasEnvironment('E2EEKEY')
64+
? const String.fromEnvironment('E2EEKEY')
65+
: prefs.getString(_storeKeySharedKey) ?? '';
5966
setState(() {
6067
_simulcast = prefs.getBool(_storeKeySimulcast) ?? true;
6168
_adaptiveStream = prefs.getBool(_storeKeyAdaptiveStream) ?? true;
6269
_dynacast = prefs.getBool(_storeKeyDynacast) ?? true;
6370
_fastConnect = prefs.getBool(_storeKeyFastConnect) ?? false;
71+
_e2ee = prefs.getBool(_storeKeyE2EE) ?? false;
6472
});
6573
}
6674

@@ -69,10 +77,12 @@ class _ConnectPageState extends State<ConnectPage> {
6977
final prefs = await SharedPreferences.getInstance();
7078
await prefs.setString(_storeKeyUri, _uriCtrl.text);
7179
await prefs.setString(_storeKeyToken, _tokenCtrl.text);
80+
await prefs.setString(_storeKeySharedKey, _sharedKeyCtrl.text);
7281
await prefs.setBool(_storeKeySimulcast, _simulcast);
7382
await prefs.setBool(_storeKeyAdaptiveStream, _adaptiveStream);
7483
await prefs.setBool(_storeKeyDynacast, _dynacast);
7584
await prefs.setBool(_storeKeyFastConnect, _fastConnect);
85+
await prefs.setBool(_storeKeyE2EE, _e2ee);
7686
}
7787

7888
Future<void> _connect(BuildContext ctx) async {
@@ -93,6 +103,13 @@ class _ConnectPageState extends State<ConnectPage> {
93103

94104
// Create a Listener before connecting
95105
final listener = room.createListener();
106+
E2EEOptions? e2eeOptions;
107+
if (_e2ee) {
108+
final keyProvider = await BaseKeyProvider.create();
109+
e2eeOptions = E2EEOptions(keyProvider: keyProvider);
110+
var sharedKey = _sharedKeyCtrl.text;
111+
await keyProvider.setKey(sharedKey);
112+
}
96113

97114
// Try to connect to the room
98115
// This will throw an Exception if it fails for any reason.
@@ -107,6 +124,7 @@ class _ConnectPageState extends State<ConnectPage> {
107124
),
108125
defaultScreenShareCaptureOptions:
109126
const ScreenShareCaptureOptions(useiOSBroadcastExtension: true),
127+
e2eeOptions: e2eeOptions,
110128
),
111129
fastConnectOptions: _fastConnect
112130
? FastConnectOptions(
@@ -115,6 +133,7 @@ class _ConnectPageState extends State<ConnectPage> {
115133
)
116134
: null,
117135
);
136+
118137
await Navigator.push<void>(
119138
ctx,
120139
MaterialPageRoute(builder: (_) => RoomPage(room, listener)),
@@ -136,6 +155,13 @@ class _ConnectPageState extends State<ConnectPage> {
136155
});
137156
}
138157

158+
void _setE2EE(bool? value) async {
159+
if (value == null || _e2ee == value) return;
160+
setState(() {
161+
_e2ee = value;
162+
});
163+
}
164+
139165
void _setAdaptiveStream(bool? value) async {
140166
if (value == null || _adaptiveStream == value) return;
141167
setState(() {
@@ -192,6 +218,26 @@ class _ConnectPageState extends State<ConnectPage> {
192218
ctrl: _tokenCtrl,
193219
),
194220
),
221+
Padding(
222+
padding: const EdgeInsets.only(bottom: 25),
223+
child: LKTextField(
224+
label: 'Shared Key',
225+
ctrl: _sharedKeyCtrl,
226+
),
227+
),
228+
Padding(
229+
padding: const EdgeInsets.only(bottom: 5),
230+
child: Row(
231+
mainAxisAlignment: MainAxisAlignment.spaceBetween,
232+
children: [
233+
const Text('E2EE'),
234+
Switch(
235+
value: _e2ee,
236+
onChanged: (value) => _setE2EE(value),
237+
),
238+
],
239+
),
240+
),
195241
Padding(
196242
padding: const EdgeInsets.only(bottom: 5),
197243
child: Row(

example/lib/pages/room.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ class _RoomPageState extends State<RoomPage> {
6666
})
6767
..on<LocalTrackPublishedEvent>((_) => _sortParticipants())
6868
..on<LocalTrackUnpublishedEvent>((_) => _sortParticipants())
69+
..on<TrackE2EEStateEvent>(_onE2EEStateEvent)
6970
..on<ParticipantNameUpdatedEvent>((event) {
7071
print(
7172
'Participant name updated: ${event.participant.identity}, name => ${event.name}');
@@ -102,6 +103,10 @@ class _RoomPageState extends State<RoomPage> {
102103
_sortParticipants();
103104
}
104105

106+
void _onE2EEStateEvent(TrackE2EEStateEvent e2eeState) {
107+
print('e2ee state: $e2eeState');
108+
}
109+
105110
void _sortParticipants() {
106111
List<ParticipantTrack> userMediaTracks = [];
107112
List<ParticipantTrack> screenTracks = [];

example/lib/widgets/controls.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,11 @@ class _ControlsWidgetState extends State<ControlsWidget> {
226226
final result = await context.showSimulateScenarioDialog();
227227
if (result != null) {
228228
print('${result}');
229+
230+
if (SimulateScenarioResult.e2eeKeyRatchet == result) {
231+
await widget.room.e2eeManager?.ratchetKey();
232+
}
233+
229234
await widget.room.sendSimulateScenario(
230235
signalReconnect:
231236
result == SimulateScenarioResult.signalReconnect ? true : null,

example/lib/widgets/participant.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,8 @@ abstract class _ParticipantWidgetState<T extends ParticipantWidget>
153153
firstAudioPublication?.subscribed == true,
154154
connectionQuality: widget.participant.connectionQuality,
155155
isScreenShare: widget.isScreenShare,
156+
enabledE2EE: widget.participant.firstTrackEncryptionType !=
157+
EncryptionType.kNone,
156158
),
157159
],
158160
),

example/lib/widgets/participant_info.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@ class ParticipantInfoWidget extends StatelessWidget {
1818
final bool audioAvailable;
1919
final ConnectionQuality connectionQuality;
2020
final bool isScreenShare;
21+
final bool enabledE2EE;
2122

2223
const ParticipantInfoWidget({
2324
this.title,
2425
this.audioAvailable = true,
2526
this.connectionQuality = ConnectionQuality.unknown,
2627
this.isScreenShare = false,
28+
this.enabledE2EE = false,
2729
Key? key,
2830
}) : super(key: key);
2931

@@ -77,6 +79,14 @@ class ParticipantInfoWidget extends StatelessWidget {
7779
size: 16,
7880
),
7981
),
82+
Padding(
83+
padding: const EdgeInsets.only(left: 5),
84+
child: Icon(
85+
enabledE2EE ? EvaIcons.lock : EvaIcons.unlock,
86+
color: enabledE2EE ? Colors.green : Colors.red,
87+
size: 16,
88+
),
89+
),
8090
],
8191
),
8292
);

0 commit comments

Comments
 (0)