Skip to content

Commit

Permalink
feat: debounce ratcheting voip keys
Browse files Browse the repository at this point in the history
  • Loading branch information
td-famedly committed Jul 12, 2024
1 parent 0ac0ef6 commit ff068ee
Showing 1 changed file with 42 additions and 19 deletions.
61 changes: 42 additions & 19 deletions lib/src/voip/backend/livekit_backend.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class LiveKitBackend extends CallBackend {
/// participant:keyIndex:keyBin
final Map<CallParticipant, Map<int, Uint8List>> _encryptionKeysMap = {};

final List<Future> _setNewKeyTimeouts = [];
final List<Future<void>> _setNewKeyTimeouts = [];

int _indexCounter = 0;

Expand Down Expand Up @@ -90,6 +90,8 @@ class LiveKitBackend extends CallBackend {
);
}

DateTime lastRatchetAt = DateTime(1980);

/// also does the sending for you
Future<void> _ratchetLocalParticipantKey(
GroupCallSession groupCall,
Expand All @@ -109,28 +111,49 @@ class LiveKitBackend extends CallBackend {
return;
}

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

while (ratchetedKey == null || ratchetedKey.isEmpty) {
Logs().i('[VOIP E2EE] Ignoring empty ratcheted key');
ratchetedKey = await keyProvider.onRatchetKey(
if (lastRatchetAt.isBefore(DateTime.now().subtract(makeKeyDelay))) {
Uint8List? ratchedKey;
while (ratchedKey == null || ratchedKey.isEmpty) {
Logs().d(
'[VOIP E2EE] Ignoring empty ratcheted key, probably waiting for useKeyDelay to finish, expect around ${useKeyDelay.inSeconds} seconds of disruption. latest: $latestLocalKeyIndex, current: $currentLocalKeyIndex');
ratchedKey = await keyProvider.onRatchetKey(
groupCall.localParticipant!,
latestLocalKeyIndex,
);
}
lastRatchetAt = DateTime.now();
Logs().i(
'[VOIP E2EE] Ratched latest key to $ratchedKey at idx $latestLocalKeyIndex');
await _setEncryptionKey(
groupCall,
groupCall.localParticipant!,
latestLocalKeyIndex,
ratchedKey,
delayBeforeUsingKeyOurself: false,
send: true,
sendTo: sendTo,
);
} else {
Logs().d(
'[VOIP E2EE] Skipped ratcheting because lastRatchet run was at ${lastRatchetAt.millisecondsSinceEpoch}');
// send without setting because it is already set
await _sendEncryptionKeysEvent(
groupCall,
latestLocalKeyIndex,
sendTo: sendTo,
);
}

Logs().i(
'[VOIP E2EE] Ratched latest key to $ratchetedKey at idx $latestLocalKeyIndex');

await _setEncryptionKey(
groupCall,
groupCall.localParticipant!,
latestLocalKeyIndex,
ratchetedKey,
delayBeforeUsingKeyOurself: false,
send: true,
sendTo: sendTo,
);
}

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

0 comments on commit ff068ee

Please sign in to comment.