Skip to content

Commit ff068ee

Browse files
committed
feat: debounce ratcheting voip keys
1 parent 0ac0ef6 commit ff068ee

File tree

1 file changed

+42
-19
lines changed

1 file changed

+42
-19
lines changed

lib/src/voip/backend/livekit_backend.dart

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class LiveKitBackend extends CallBackend {
3737
/// participant:keyIndex:keyBin
3838
final Map<CallParticipant, Map<int, Uint8List>> _encryptionKeysMap = {};
3939

40-
final List<Future> _setNewKeyTimeouts = [];
40+
final List<Future<void>> _setNewKeyTimeouts = [];
4141

4242
int _indexCounter = 0;
4343

@@ -90,6 +90,8 @@ class LiveKitBackend extends CallBackend {
9090
);
9191
}
9292

93+
DateTime lastRatchetAt = DateTime(1980);
94+
9395
/// also does the sending for you
9496
Future<void> _ratchetLocalParticipantKey(
9597
GroupCallSession groupCall,
@@ -109,28 +111,49 @@ class LiveKitBackend extends CallBackend {
109111
return;
110112
}
111113

112-
Uint8List? ratchetedKey;
114+
if (_currentLocalKeyIndex != _latestLocalKeyIndex) {
115+
/// Leave causes rotate, new user joins after making new key but
116+
/// before using new key, this then causes a ratchet of the latestLocalKey
117+
/// returns null until that key is set when useKeyDelay is done.
118+
///
119+
/// You will see some onRatchetKey sending empty responses here
120+
/// therefore the below while loop.
121+
Logs().w(
122+
'[VOIP E2EE] Leave and join / rotate and ratchet scenario detected, expect ${useKeyDelay.inSeconds} seconds disruption. latest: $latestLocalKeyIndex, current: $currentLocalKeyIndex');
123+
}
113124

114-
while (ratchetedKey == null || ratchetedKey.isEmpty) {
115-
Logs().i('[VOIP E2EE] Ignoring empty ratcheted key');
116-
ratchetedKey = await keyProvider.onRatchetKey(
125+
if (lastRatchetAt.isBefore(DateTime.now().subtract(makeKeyDelay))) {
126+
Uint8List? ratchedKey;
127+
while (ratchedKey == null || ratchedKey.isEmpty) {
128+
Logs().d(
129+
'[VOIP E2EE] Ignoring empty ratcheted key, probably waiting for useKeyDelay to finish, expect around ${useKeyDelay.inSeconds} seconds of disruption. latest: $latestLocalKeyIndex, current: $currentLocalKeyIndex');
130+
ratchedKey = await keyProvider.onRatchetKey(
131+
groupCall.localParticipant!,
132+
latestLocalKeyIndex,
133+
);
134+
}
135+
lastRatchetAt = DateTime.now();
136+
Logs().i(
137+
'[VOIP E2EE] Ratched latest key to $ratchedKey at idx $latestLocalKeyIndex');
138+
await _setEncryptionKey(
139+
groupCall,
117140
groupCall.localParticipant!,
118141
latestLocalKeyIndex,
142+
ratchedKey,
143+
delayBeforeUsingKeyOurself: false,
144+
send: true,
145+
sendTo: sendTo,
146+
);
147+
} else {
148+
Logs().d(
149+
'[VOIP E2EE] Skipped ratcheting because lastRatchet run was at ${lastRatchetAt.millisecondsSinceEpoch}');
150+
// send without setting because it is already set
151+
await _sendEncryptionKeysEvent(
152+
groupCall,
153+
latestLocalKeyIndex,
154+
sendTo: sendTo,
119155
);
120156
}
121-
122-
Logs().i(
123-
'[VOIP E2EE] Ratched latest key to $ratchetedKey at idx $latestLocalKeyIndex');
124-
125-
await _setEncryptionKey(
126-
groupCall,
127-
groupCall.localParticipant!,
128-
latestLocalKeyIndex,
129-
ratchetedKey,
130-
delayBeforeUsingKeyOurself: false,
131-
send: true,
132-
sendTo: sendTo,
133-
);
134157
}
135158

136159
Future<void> _changeEncryptionKey(
@@ -177,7 +200,7 @@ class LiveKitBackend extends CallBackend {
177200
if (delayBeforeUsingKeyOurself) {
178201
// now wait for the key to propogate and then set it, hopefully users can
179202
// stil decrypt everything
180-
final useKeyTimeout = Future.delayed(useKeyDelay, () async {
203+
final useKeyTimeout = Future<void>.delayed(useKeyDelay, () async {
181204
Logs().i(
182205
'[VOIP E2EE] setting key changed event for ${participant.id} idx $encryptionKeyIndex key $encryptionKeyBin');
183206
await groupCall.voip.delegate.keyProvider?.onSetEncryptionKey(

0 commit comments

Comments
 (0)