Skip to content

Commit 4849a47

Browse files
nico-famedlyhoangdat
authored andcommitted
fix: key uploads only running once
There were several issues here. Key uploads in general failed, because the await caused a Future<dynamic> to be returned, which failed type checking. But also we never unset our future, which was used for the online key backup uploads, which meant we would very quickly stop uploading any keys at all.
1 parent 2a32e86 commit 4849a47

File tree

3 files changed

+84
-65
lines changed

3 files changed

+84
-65
lines changed

lib/encryption/encryption.dart

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,8 +239,12 @@ class Encryption {
239239
// the entry should always exist. In the case it doesn't, the following
240240
// line *could* throw an error. As that is a future, though, and we call
241241
// it un-awaited here, nothing happens, which is exactly the result we want
242-
client.database?.updateInboundGroupSessionIndexes(
243-
json.encode(inboundGroupSession.indexes), roomId, sessionId);
242+
client.database
243+
// ignore: discarded_futures
244+
?.updateInboundGroupSessionIndexes(
245+
json.encode(inboundGroupSession.indexes), roomId, sessionId)
246+
// ignore: discarded_futures
247+
.onError((e, _) => Logs().e('Ignoring error for updating indexes'));
244248
}
245249
decryptedPayload = json.decode(decryptResult.plaintext);
246250
} catch (exception) {

lib/encryption/key_manager.dart

Lines changed: 73 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -794,74 +794,86 @@ class KeyManager {
794794
// Make sure to not run in parallel
795795
if (_uploadingFuture != null) {
796796
if (skipIfInProgress) return;
797-
await _uploadingFuture;
797+
try {
798+
await _uploadingFuture;
799+
} finally {
800+
// shouldn't be necessary, since it will be unset already by the other process that started it, but just to be safe, also unset the future here
801+
_uploadingFuture = null;
802+
}
798803
}
799-
final completer = Completer<void>();
800-
_uploadingFuture = completer.future;
801804

802-
await client.userDeviceKeysLoading;
803-
try {
804-
if (!(await isCached())) {
805-
return; // we can't backup anyways
806-
}
807-
final dbSessions = await database.getInboundGroupSessionsToUpload();
808-
if (dbSessions.isEmpty) {
809-
return; // nothing to do
810-
}
811-
final privateKey =
812-
base64decodeUnpadded((await encryption.ssss.getCached(megolmKey))!);
813-
// decryption is needed to calculate the public key and thus see if the claimed information is in fact valid
814-
final decryption = olm.PkDecryption();
815-
final info = await getRoomKeysBackupInfo(false);
816-
String backupPubKey;
805+
Future<void> uploadInternal() async {
817806
try {
818-
backupPubKey = decryption.init_with_private_key(privateKey);
807+
await client.userDeviceKeysLoading;
819808

820-
if (info.algorithm !=
821-
BackupAlgorithm.mMegolmBackupV1Curve25519AesSha2 ||
822-
info.authData['public_key'] != backupPubKey) {
823-
return;
809+
if (!(await isCached())) {
810+
return; // we can't backup anyways
824811
}
825-
final args = GenerateUploadKeysArgs(
826-
pubkey: backupPubKey,
827-
dbSessions: <DbInboundGroupSessionBundle>[],
828-
userId: userID,
829-
);
830-
// we need to calculate verified beforehand, as else we pass a closure to an isolate
831-
// with 500 keys they do, however, noticably block the UI, which is why we give brief async suspentions in here
832-
// so that the event loop can progress
833-
var i = 0;
834-
for (final dbSession in dbSessions) {
835-
final device =
836-
client.getUserDeviceKeysByCurve25519Key(dbSession.senderKey);
837-
args.dbSessions.add(DbInboundGroupSessionBundle(
838-
dbSession: dbSession,
839-
verified: device?.verified ?? false,
840-
));
841-
i++;
842-
if (i > 10) {
843-
await Future.delayed(Duration(milliseconds: 1));
844-
i = 0;
845-
}
812+
final dbSessions = await database.getInboundGroupSessionsToUpload();
813+
if (dbSessions.isEmpty) {
814+
return; // nothing to do
846815
}
847-
final roomKeys =
848-
await client.nativeImplementations.generateUploadKeys(args);
849-
Logs().i('[Key Manager] Uploading ${dbSessions.length} room keys...');
850-
// upload the payload...
851-
await client.putRoomKeys(info.version, roomKeys);
852-
// and now finally mark all the keys as uploaded
853-
// no need to optimze this, as we only run it so seldomly and almost never with many keys at once
854-
for (final dbSession in dbSessions) {
855-
await database.markInboundGroupSessionAsUploaded(
856-
dbSession.roomId, dbSession.sessionId);
816+
final privateKey =
817+
base64decodeUnpadded((await encryption.ssss.getCached(megolmKey))!);
818+
// decryption is needed to calculate the public key and thus see if the claimed information is in fact valid
819+
final decryption = olm.PkDecryption();
820+
final info = await getRoomKeysBackupInfo(false);
821+
String backupPubKey;
822+
try {
823+
backupPubKey = decryption.init_with_private_key(privateKey);
824+
825+
if (info.algorithm !=
826+
BackupAlgorithm.mMegolmBackupV1Curve25519AesSha2 ||
827+
info.authData['public_key'] != backupPubKey) {
828+
decryption.free();
829+
return;
830+
}
831+
final args = GenerateUploadKeysArgs(
832+
pubkey: backupPubKey,
833+
dbSessions: <DbInboundGroupSessionBundle>[],
834+
userId: userID,
835+
);
836+
// we need to calculate verified beforehand, as else we pass a closure to an isolate
837+
// with 500 keys they do, however, noticably block the UI, which is why we give brief async suspentions in here
838+
// so that the event loop can progress
839+
var i = 0;
840+
for (final dbSession in dbSessions) {
841+
final device =
842+
client.getUserDeviceKeysByCurve25519Key(dbSession.senderKey);
843+
args.dbSessions.add(DbInboundGroupSessionBundle(
844+
dbSession: dbSession,
845+
verified: device?.verified ?? false,
846+
));
847+
i++;
848+
if (i > 10) {
849+
await Future.delayed(Duration(milliseconds: 1));
850+
i = 0;
851+
}
852+
}
853+
final roomKeys =
854+
await client.nativeImplementations.generateUploadKeys(args);
855+
Logs().i('[Key Manager] Uploading ${dbSessions.length} room keys...');
856+
// upload the payload...
857+
await client.putRoomKeys(info.version, roomKeys);
858+
// and now finally mark all the keys as uploaded
859+
// no need to optimze this, as we only run it so seldomly and almost never with many keys at once
860+
for (final dbSession in dbSessions) {
861+
await database.markInboundGroupSessionAsUploaded(
862+
dbSession.roomId, dbSession.sessionId);
863+
}
864+
} finally {
865+
decryption.free();
857866
}
858-
} finally {
859-
decryption.free();
867+
} catch (e, s) {
868+
Logs().e('[Key Manager] Error uploading room keys', e, s);
860869
}
861-
} catch (e, s) {
862-
Logs().e('[Key Manager] Error uploading room keys', e, s);
870+
}
871+
872+
_uploadingFuture = uploadInternal();
873+
try {
874+
await _uploadingFuture;
863875
} finally {
864-
completer.complete();
876+
_uploadingFuture = null;
865877
}
866878
}
867879

@@ -1185,12 +1197,12 @@ RoomKeys generateUploadKeysImplementation(GenerateUploadKeysArgs args) {
11851197
},
11861198
);
11871199
}
1200+
enc.free();
11881201
return roomKeys;
11891202
} catch (e, s) {
11901203
Logs().e('[Key Manager] Error generating payload', e, s);
1191-
rethrow;
1192-
} finally {
11931204
enc.free();
1205+
rethrow;
11941206
}
11951207
}
11961208

lib/src/utils/native_implementations.dart

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,8 @@ abstract class NativeImplementations {
5050
bool retryInDummy = false,
5151
});
5252

53-
@override
54-
5553
/// this implementation will catch any non-implemented method
54+
@override
5655
dynamic noSuchMethod(Invocation invocation) {
5756
final dynamic argument = invocation.positionalArguments.single;
5857
final memberName = invocation.memberName.toString().split('"')[1];
@@ -63,11 +62,15 @@ abstract class NativeImplementations {
6362
'Fallback from NativeImplementations.dummy used.',
6463
);
6564
switch (memberName) {
65+
// we need to pass the futures right through or we will run into type errors later!
6666
case 'generateUploadKeys':
67+
// ignore: discarded_futures
6768
return dummy.generateUploadKeys(argument);
6869
case 'keyFromPassphrase':
70+
// ignore: discarded_futures
6971
return dummy.keyFromPassphrase(argument);
7072
case 'decryptFile':
73+
// ignore: discarded_futures
7174
return dummy.decryptFile(argument);
7275
case 'shrinkImage':
7376
return dummy.shrinkImage(argument);

0 commit comments

Comments
 (0)