Skip to content

Commit 25f4353

Browse files
committed
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 9019dfd commit 25f4353

File tree

3 files changed

+84
-65
lines changed

3 files changed

+84
-65
lines changed

lib/encryption/encryption.dart

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -232,9 +232,12 @@ class Encryption {
232232
// the entry should always exist. In the case it doesn't, the following
233233
// line *could* throw an error. As that is a future, though, and we call
234234
// it un-awaited here, nothing happens, which is exactly the result we want
235-
// ignore: discarded_futures
236-
client.database?.updateInboundGroupSessionIndexes(
237-
json.encode(inboundGroupSession.indexes), roomId, sessionId);
235+
client.database
236+
// ignore: discarded_futures
237+
?.updateInboundGroupSessionIndexes(
238+
json.encode(inboundGroupSession.indexes), roomId, sessionId)
239+
// ignore: discarded_futures
240+
.onError((e, _) => Logs().e('Ignoring error for updating indexes'));
238241
}
239242
decryptedPayload = json.decode(decryptResult.plaintext);
240243
} catch (exception) {

lib/encryption/key_manager.dart

Lines changed: 73 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -795,74 +795,86 @@ class KeyManager {
795795
// Make sure to not run in parallel
796796
if (_uploadingFuture != null) {
797797
if (skipIfInProgress) return;
798-
await _uploadingFuture;
798+
try {
799+
await _uploadingFuture;
800+
} finally {
801+
// 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
802+
_uploadingFuture = null;
803+
}
799804
}
800-
final completer = Completer<void>();
801-
_uploadingFuture = completer.future;
802805

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

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

@@ -1187,12 +1199,12 @@ RoomKeys generateUploadKeysImplementation(GenerateUploadKeysArgs args) {
11871199
},
11881200
);
11891201
}
1202+
enc.free();
11901203
return roomKeys;
11911204
} catch (e, s) {
11921205
Logs().e('[Key Manager] Error generating payload', e, s);
1193-
rethrow;
1194-
} finally {
11951206
enc.free();
1207+
rethrow;
11961208
}
11971209
}
11981210

lib/src/utils/native_implementations.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ abstract class NativeImplementations {
5252

5353
/// this implementation will catch any non-implemented method
5454
@override
55-
dynamic noSuchMethod(Invocation invocation) async {
55+
dynamic noSuchMethod(Invocation invocation) {
5656
final dynamic argument = invocation.positionalArguments.single;
5757
final memberName = invocation.memberName.toString().split('"')[1];
5858

@@ -62,11 +62,15 @@ abstract class NativeImplementations {
6262
'Fallback from NativeImplementations.dummy used.',
6363
);
6464
switch (memberName) {
65+
// we need to pass the futures right through or we will run into type errors later!
6566
case 'generateUploadKeys':
67+
// ignore: discarded_futures
6668
return dummy.generateUploadKeys(argument);
6769
case 'keyFromPassphrase':
70+
// ignore: discarded_futures
6871
return dummy.keyFromPassphrase(argument);
6972
case 'decryptFile':
73+
// ignore: discarded_futures
7074
return dummy.decryptFile(argument);
7175
case 'shrinkImage':
7276
return dummy.shrinkImage(argument);

0 commit comments

Comments
 (0)