Skip to content

Commit f1e0268

Browse files
authored
Merge pull request #1623 from famedly/nico/fix-key-backup
fix: key uploads only running once
2 parents 9019dfd + 25f4353 commit f1e0268

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)