@@ -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