From 498c7825a558f7fe89307503922fb3e81330b501 Mon Sep 17 00:00:00 2001 From: Sorunome Date: Mon, 15 Feb 2021 16:26:49 +0100 Subject: [PATCH] feat: Add fallback keys support --- lib/encryption/encryption.dart | 19 ++- lib/encryption/olm_manager.dart | 108 ++++++++++++------ lib/src/client.dart | 7 +- pubspec.yaml | 4 +- test/client_test.dart | 17 +-- test/device_keys_list_test.dart | 25 ++-- test/encryption/bootstrap_test.dart | 25 ++-- test/encryption/cross_signing_test.dart | 25 ++-- .../encrypt_decrypt_room_message_test.dart | 24 ++-- .../encrypt_decrypt_to_device_test.dart | 24 ++-- test/encryption/key_manager_test.dart | 25 ++-- test/encryption/key_request_test.dart | 22 ++-- test/encryption/key_verification_test.dart | 27 +++-- test/encryption/olm_manager_test.dart | 53 ++++++--- test/encryption/online_key_backup_test.dart | 24 ++-- test/encryption/ssss_test.dart | 33 ++++-- test/event_test.dart | 19 +-- test/timeline_test.dart | 16 +-- 18 files changed, 319 insertions(+), 178 deletions(-) diff --git a/lib/encryption/encryption.dart b/lib/encryption/encryption.dart index 4a75f1dd..5d2c61b7 100644 --- a/lib/encryption/encryption.dart +++ b/lib/encryption/encryption.dart @@ -20,6 +20,7 @@ import 'dart:convert'; import 'dart:async'; import 'package:pedantic/pedantic.dart'; +import 'package:olm/olm.dart' as olm; import '../famedlysdk.dart'; import '../src/utils/run_in_root.dart'; @@ -68,13 +69,27 @@ class Encryption { _backgroundTasks(); // start the background tasks } + bool isMinOlmVersion(int major, int minor, int patch) { + try { + final version = olm.get_library_version(); + return version[0] > major || + (version[0] == major && + (version[1] > minor || + (version[1] == minor && version[2] >= patch))); + } catch (_) { + return false; + } + } + Bootstrap bootstrap({void Function() onUpdate}) => Bootstrap( encryption: this, onUpdate: onUpdate, ); - void handleDeviceOneTimeKeysCount(Map countJson) { - runInRoot(() => olmManager.handleDeviceOneTimeKeysCount(countJson)); + void handleDeviceOneTimeKeysCount( + Map countJson, List unusedFallbackKeyTypes) { + runInRoot(() => olmManager.handleDeviceOneTimeKeysCount( + countJson, unusedFallbackKeyTypes)); } void onSync() { diff --git a/lib/encryption/olm_manager.dart b/lib/encryption/olm_manager.dart index 4153e1d0..11ee8e85 100644 --- a/lib/encryption/olm_manager.dart +++ b/lib/encryption/olm_manager.dart @@ -138,10 +138,12 @@ class OlmManager { bool _uploadKeysLock = false; /// Generates new one time keys, signs everything and upload it to the server. - Future uploadKeys( - {bool uploadDeviceKeys = false, - int oldKeyCount = 0, - bool updateDatabase = true}) async { + Future uploadKeys({ + bool uploadDeviceKeys = false, + int oldKeyCount = 0, + bool updateDatabase = true, + bool unusedFallbackKey = false, + }) async { if (!enabled) { return true; } @@ -152,32 +154,53 @@ class OlmManager { _uploadKeysLock = true; try { - // check if we have OTKs that still need uploading. If we do, we don't try to generate new ones, - // instead we try to upload the old ones first - final oldOTKsNeedingUpload = - json.decode(_olmAccount.one_time_keys())['curve25519'].entries.length; - // generate one-time keys - // we generate 2/3rds of max, so that other keys people may still have can - // still be used - final oneTimeKeysCount = - (_olmAccount.max_number_of_one_time_keys() * 2 / 3).floor() - - oldKeyCount - - oldOTKsNeedingUpload; - if (oneTimeKeysCount > 0) { - _olmAccount.generate_one_time_keys(oneTimeKeysCount); - } - final Map oneTimeKeys = - json.decode(_olmAccount.one_time_keys()); - - // now sign all the one-time keys final signedOneTimeKeys = {}; - for (final entry in oneTimeKeys['curve25519'].entries) { - final key = entry.key; - final value = entry.value; - signedOneTimeKeys['signed_curve25519:$key'] = {}; - signedOneTimeKeys['signed_curve25519:$key'] = signJson({ - 'key': value, - }); + int uploadedOneTimeKeysCount; + if (oldKeyCount != null) { + // check if we have OTKs that still need uploading. If we do, we don't try to generate new ones, + // instead we try to upload the old ones first + final oldOTKsNeedingUpload = json + .decode(_olmAccount.one_time_keys())['curve25519'] + .entries + .length; + // generate one-time keys + // we generate 2/3rds of max, so that other keys people may still have can + // still be used + final oneTimeKeysCount = + (_olmAccount.max_number_of_one_time_keys() * 2 / 3).floor() - + oldKeyCount - + oldOTKsNeedingUpload; + if (oneTimeKeysCount > 0) { + _olmAccount.generate_one_time_keys(oneTimeKeysCount); + } + uploadedOneTimeKeysCount = oneTimeKeysCount + oldOTKsNeedingUpload; + final Map oneTimeKeys = + json.decode(_olmAccount.one_time_keys()); + + // now sign all the one-time keys + for (final entry in oneTimeKeys['curve25519'].entries) { + final key = entry.key; + final value = entry.value; + signedOneTimeKeys['signed_curve25519:$key'] = signJson({ + 'key': value, + }); + } + } + + final signedFallbackKeys = {}; + if (encryption.isMinOlmVersion(3, 2, 0) && unusedFallbackKey == false) { + // we don't have an unused fallback key uploaded....so let's change that! + _olmAccount.generate_fallback_key(); + final fallbackKey = json.decode(_olmAccount.fallback_key()); + // now sign all the fallback keys + for (final entry in fallbackKey['curve25519'].entries) { + final key = entry.key; + final value = entry.value; + signedFallbackKeys['signed_curve25519:$key'] = signJson({ + 'key': value, + 'fallback': true, + }); + } } // and now generate the payload to upload @@ -217,28 +240,43 @@ class OlmManager { ? MatrixDeviceKeys.fromJson(keysContent['device_keys']) : null, oneTimeKeys: signedOneTimeKeys, + fallbackKeys: signedFallbackKeys, ); // mark the OTKs as published and save that to datbase _olmAccount.mark_keys_as_published(); if (updateDatabase) { await client.database?.updateClientKeys(pickledOlmAccount, client.id); } - return response['signed_curve25519'] == oneTimeKeysCount; + return (uploadedOneTimeKeysCount != null && + response['signed_curve25519'] == uploadedOneTimeKeysCount) || + uploadedOneTimeKeysCount == null; } finally { _uploadKeysLock = false; } } - void handleDeviceOneTimeKeysCount(Map countJson) { + void handleDeviceOneTimeKeysCount( + Map countJson, List unusedFallbackKeyTypes) { if (!enabled) { return; } + final haveFallbackKeys = encryption.isMinOlmVersion(3, 2, 0); // Check if there are at least half of max_number_of_one_time_keys left on the server // and generate and upload more if not. - if (countJson.containsKey('signed_curve25519') && - countJson['signed_curve25519'] < - (_olmAccount.max_number_of_one_time_keys() / 2)) { - uploadKeys(oldKeyCount: countJson['signed_curve25519']); + if ((countJson != null && + ((countJson.containsKey('signed_curve25519') && + countJson['signed_curve25519'] < + (_olmAccount.max_number_of_one_time_keys() / 2)) || + !countJson.containsKey('signed_curve25519'))) || + (haveFallbackKeys && + unusedFallbackKeyTypes?.contains('signed_curve25519') == false)) { + uploadKeys( + oldKeyCount: + countJson != null ? (countJson['signed_curve25519'] ?? 0) : null, + unusedFallbackKey: haveFallbackKeys + ? unusedFallbackKeyTypes?.contains('signed_curve25519') + : null, + ); } } diff --git a/lib/src/client.dart b/lib/src/client.dart index 7dfc3014..c05dc95a 100644 --- a/lib/src/client.dart +++ b/lib/src/client.dart @@ -1073,8 +1073,11 @@ class Client extends MatrixApi { if (sync.deviceLists != null) { await _handleDeviceListsEvents(sync.deviceLists); } - if (sync.deviceOneTimeKeysCount != null && encryptionEnabled) { - encryption.handleDeviceOneTimeKeysCount(sync.deviceOneTimeKeysCount); + if ((sync.deviceUnusedFallbackKeyTypes != null || + sync.deviceOneTimeKeysCount != null) && + encryptionEnabled) { + encryption.handleDeviceOneTimeKeysCount( + sync.deviceOneTimeKeysCount, sync.deviceUnusedFallbackKeyTypes); } onSync.add(sync); } diff --git a/pubspec.yaml b/pubspec.yaml index 3a7d48a5..90428883 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -19,11 +19,11 @@ dependencies: crypto: ^2.1.5 base58check: ^1.0.1 password_hash: ^2.0.0 - olm: ^1.2.1 + olm: ^1.3.0 matrix_file_e2ee: ^1.0.5 isolate: ^2.0.3 logger: ^0.9.4 - matrix_api_lite: ^0.1.8 + matrix_api_lite: ^0.1.9 dev_dependencies: test: ^1.15.7 diff --git a/test/client_test.dart b/test/client_test.dart index 65ab1199..69160044 100644 --- a/test/client_test.dart +++ b/test/client_test.dart @@ -61,16 +61,17 @@ void main() { toDeviceUpdateListFuture = matrix.onToDeviceEvent.stream.toList(); var olmEnabled = true; - try { - olm.init(); - olm.Account(); - } catch (e) { - olmEnabled = false; - Logs().w('[LibOlm] Failed to load LibOlm', e); - } - Logs().w('[LibOlm] Enabled: $olmEnabled'); test('Login', () async { + try { + await olm.init(); + olm.get_library_version(); + } catch (e) { + olmEnabled = false; + Logs().w('[LibOlm] Failed to load LibOlm', e); + } + Logs().w('[LibOlm] Enabled: $olmEnabled'); + var presenceCounter = 0; var accountDataCounter = 0; matrix.onPresence.stream.listen((Presence data) { diff --git a/test/device_keys_list_test.dart b/test/device_keys_list_test.dart index 0eec64a1..6d1cb52f 100644 --- a/test/device_keys_list_test.dart +++ b/test/device_keys_list_test.dart @@ -74,24 +74,25 @@ void main() { }); var olmEnabled = true; - try { - olm.init(); - olm.Account(); - } catch (e) { - olmEnabled = false; - Logs().w('[LibOlm] Failed to load LibOlm', e); - } - Logs().i('[LibOlm] Enabled: $olmEnabled'); - - if (!olmEnabled) return; Client client; test('setupClient', () async { + try { + await olm.init(); + olm.get_library_version(); + } catch (e) { + olmEnabled = false; + Logs().w('[LibOlm] Failed to load LibOlm', e); + } + Logs().i('[LibOlm] Enabled: $olmEnabled'); + if (!olmEnabled) return; + client = await getClient(); }); test('reject devices without self-signature', () async { + if (!olmEnabled) return; var key = DeviceKeys.fromJson({ 'user_id': '@test:fakeServer.notExisting', 'device_id': 'BADDEVICE', @@ -128,6 +129,7 @@ void main() { }); test('set blocked / verified', () async { + if (!olmEnabled) return; final key = client.userDeviceKeys[client.userID].deviceKeys['OTHERDEVICE']; client.userDeviceKeys[client.userID].deviceKeys['UNSIGNEDDEVICE'] = @@ -203,6 +205,7 @@ void main() { }); test('verification based on signatures', () async { + if (!olmEnabled) return; final user = client.userDeviceKeys[client.userID]; user.masterKey.setDirectVerified(true); expect(user.deviceKeys['GHTYAJCE'].crossVerified, true); @@ -238,6 +241,7 @@ void main() { }); test('start verification', () async { + if (!olmEnabled) return; var req = client .userDeviceKeys['@alice:example.com'].deviceKeys['JLAFKJWSCS'] .startVerification(); @@ -251,6 +255,7 @@ void main() { }); test('dispose client', () async { + if (!olmEnabled) return; await client.dispose(closeDatabase: true); }); }); diff --git a/test/encryption/bootstrap_test.dart b/test/encryption/bootstrap_test.dart index 0f4ed021..c5a5c068 100644 --- a/test/encryption/bootstrap_test.dart +++ b/test/encryption/bootstrap_test.dart @@ -31,16 +31,6 @@ void main() { group('Bootstrap', () { Logs().level = Level.error; var olmEnabled = true; - try { - olm.init(); - olm.Account(); - } catch (e) { - olmEnabled = false; - Logs().w('[LibOlm] Failed to load LibOlm', e); - } - Logs().i('[LibOlm] Enabled: $olmEnabled'); - - if (!olmEnabled) return; Client client; Map oldSecret; @@ -52,6 +42,16 @@ void main() { }); test('setup', () async { + try { + await olm.init(); + olm.get_library_version(); + } catch (e) { + olmEnabled = false; + Logs().w('[LibOlm] Failed to load LibOlm', e); + } + Logs().i('[LibOlm] Enabled: $olmEnabled'); + if (!olmEnabled) return; + Bootstrap bootstrap; bootstrap = client.encryption.bootstrap( onUpdate: () async { @@ -108,6 +108,7 @@ void main() { }, timeout: Timeout(Duration(minutes: 2))); test('change recovery passphrase', () async { + if (!olmEnabled) return; Bootstrap bootstrap; bootstrap = client.encryption.bootstrap( onUpdate: () async { @@ -158,6 +159,7 @@ void main() { }, timeout: Timeout(Duration(minutes: 2))); test('change passphrase with multiple keys', () async { + if (!olmEnabled) return; await client.setAccountData(client.userID, 'foxes', oldSecret); await Future.delayed(Duration(milliseconds: 50)); @@ -212,6 +214,7 @@ void main() { }, timeout: Timeout(Duration(minutes: 2))); test('setup new ssss', () async { + if (!olmEnabled) return; client.accountData.clear(); Bootstrap bootstrap; bootstrap = client.encryption.bootstrap( @@ -237,6 +240,7 @@ void main() { }, timeout: Timeout(Duration(minutes: 2))); test('bad ssss', () async { + if (!olmEnabled) return; client.accountData.clear(); await client.setAccountData(client.userID, 'foxes', oldSecret); await Future.delayed(Duration(milliseconds: 50)); @@ -262,6 +266,7 @@ void main() { }); test('dispose client', () async { + if (!olmEnabled) return; await client.dispose(closeDatabase: true); }); }); diff --git a/test/encryption/cross_signing_test.dart b/test/encryption/cross_signing_test.dart index 835314c7..3c1cc5a0 100644 --- a/test/encryption/cross_signing_test.dart +++ b/test/encryption/cross_signing_test.dart @@ -30,28 +30,30 @@ void main() { group('Cross Signing', () { Logs().level = Level.error; var olmEnabled = true; - try { - olm.init(); - olm.Account(); - } catch (e) { - olmEnabled = false; - Logs().w('[LibOlm] Failed to load LibOlm', e); - } - Logs().i('[LibOlm] Enabled: $olmEnabled'); - - if (!olmEnabled) return; Client client; test('setupClient', () async { + try { + await olm.init(); + olm.get_library_version(); + } catch (e) { + olmEnabled = false; + Logs().w('[LibOlm] Failed to load LibOlm', e); + } + Logs().i('[LibOlm] Enabled: $olmEnabled'); + if (!olmEnabled) return; + client = await getClient(); }); test('basic things', () async { + if (!olmEnabled) return; expect(client.encryption.crossSigning.enabled, true); }); test('selfSign', () async { + if (!olmEnabled) return; final key = client.userDeviceKeys[client.userID].masterKey; key.setDirectVerified(false); FakeMatrixApi.calledEndpoints.clear(); @@ -65,6 +67,7 @@ void main() { }); test('signable', () async { + if (!olmEnabled) return; expect( client.encryption.crossSigning .signable([client.userDeviceKeys[client.userID].masterKey]), @@ -86,6 +89,7 @@ void main() { }); test('sign', () async { + if (!olmEnabled) return; FakeMatrixApi.calledEndpoints.clear(); await client.encryption.crossSigning.sign([ client.userDeviceKeys[client.userID].masterKey, @@ -109,6 +113,7 @@ void main() { }); test('dispose client', () async { + if (!olmEnabled) return; await client.dispose(closeDatabase: true); }); }); diff --git a/test/encryption/encrypt_decrypt_room_message_test.dart b/test/encryption/encrypt_decrypt_room_message_test.dart index 9b3c90fa..03a98a32 100644 --- a/test/encryption/encrypt_decrypt_room_message_test.dart +++ b/test/encryption/encrypt_decrypt_room_message_test.dart @@ -27,16 +27,6 @@ void main() { group('Encrypt/Decrypt room message', () { Logs().level = Level.error; var olmEnabled = true; - try { - olm.init(); - olm.Account(); - } catch (e) { - olmEnabled = false; - Logs().w('[LibOlm] Failed to load LibOlm', e); - } - Logs().i('[LibOlm] Enabled: $olmEnabled'); - - if (!olmEnabled) return; Client client; final roomId = '!726s6s6q:example.com'; @@ -45,11 +35,22 @@ void main() { final now = DateTime.now(); test('setupClient', () async { + try { + await olm.init(); + olm.get_library_version(); + } catch (e) { + olmEnabled = false; + Logs().w('[LibOlm] Failed to load LibOlm', e); + } + Logs().i('[LibOlm] Enabled: $olmEnabled'); + if (!olmEnabled) return; + client = await getClient(); room = client.getRoomById(roomId); }); test('encrypt payload', () async { + if (!olmEnabled) return; payload = await client.encryption.encryptGroupMessagePayload(roomId, { 'msgtype': 'm.text', 'text': 'Hello foxies!', @@ -62,6 +63,7 @@ void main() { }); test('decrypt payload', () async { + if (!olmEnabled) return; final encryptedEvent = Event( type: EventTypes.Encrypted, content: payload, @@ -78,6 +80,7 @@ void main() { }); test('decrypt payload nocache', () async { + if (!olmEnabled) return; client.encryption.keyManager.clearInboundGroupSessions(); final encryptedEvent = Event( type: EventTypes.Encrypted, @@ -98,6 +101,7 @@ void main() { }); test('dispose client', () async { + if (!olmEnabled) return; await client.dispose(closeDatabase: true); }); }); diff --git a/test/encryption/encrypt_decrypt_to_device_test.dart b/test/encryption/encrypt_decrypt_to_device_test.dart index bd6ddc69..4b72b6c1 100644 --- a/test/encryption/encrypt_decrypt_to_device_test.dart +++ b/test/encryption/encrypt_decrypt_to_device_test.dart @@ -33,16 +33,6 @@ void main() { group('Encrypt/Decrypt to-device messages', () { Logs().level = Level.error; var olmEnabled = true; - try { - olm.init(); - olm.Account(); - } catch (e) { - olmEnabled = false; - Logs().w('[LibOlm] Failed to load LibOlm', e); - } - Logs().i('[LibOlm] Enabled: $olmEnabled'); - - if (!olmEnabled) return; Client client; var otherClient = Client('othertestclient', @@ -51,6 +41,16 @@ void main() { Map payload; test('setupClient', () async { + try { + await olm.init(); + olm.get_library_version(); + } catch (e) { + olmEnabled = false; + Logs().w('[LibOlm] Failed to load LibOlm', e); + } + Logs().i('[LibOlm] Enabled: $olmEnabled'); + if (!olmEnabled) return; + client = await getClient(); await client.abortSync(); await otherClient.checkHomeserver('https://fakeserver.notexisting', @@ -81,11 +81,13 @@ void main() { }); test('encryptToDeviceMessage', () async { + if (!olmEnabled) return; payload = await otherClient.encryption .encryptToDeviceMessage([device], 'm.to_device', {'hello': 'foxies'}); }); test('decryptToDeviceEvent', () async { + if (!olmEnabled) return; final encryptedEvent = ToDeviceEvent( sender: '@othertest:fakeServer.notExisting', type: EventTypes.Encrypted, @@ -98,6 +100,7 @@ void main() { }); test('decryptToDeviceEvent nocache', () async { + if (!olmEnabled) return; client.encryption.olmManager.olmSessions.clear(); payload = await otherClient.encryption.encryptToDeviceMessage( [device], 'm.to_device', {'hello': 'superfoxies'}); @@ -113,6 +116,7 @@ void main() { }); test('dispose client', () async { + if (!olmEnabled) return; await client.dispose(closeDatabase: true); await otherClient.dispose(closeDatabase: true); }); diff --git a/test/encryption/key_manager_test.dart b/test/encryption/key_manager_test.dart index b596687e..32fe345e 100644 --- a/test/encryption/key_manager_test.dart +++ b/test/encryption/key_manager_test.dart @@ -29,24 +29,25 @@ void main() { group('Key Manager', () { Logs().level = Level.error; var olmEnabled = true; - try { - olm.init(); - olm.Account(); - } catch (e) { - olmEnabled = false; - Logs().w('[LibOlm] Failed to load LibOlm', e); - } - Logs().i('[LibOlm] Enabled: $olmEnabled'); - - if (!olmEnabled) return; Client client; test('setupClient', () async { + try { + await olm.init(); + olm.get_library_version(); + } catch (e) { + olmEnabled = false; + Logs().w('[LibOlm] Failed to load LibOlm', e); + } + Logs().i('[LibOlm] Enabled: $olmEnabled'); + if (!olmEnabled) return; + client = await getClient(); }); test('handle new m.room_key', () async { + if (!olmEnabled) return; final validSessionId = 'ciM/JWTPrmiWPPZNkRLDPQYf9AW/I46bxyLSr+Bx5oU'; final validSenderKey = 'JBG7ZaPn54OBC7TuIEiylW3BZ+7WcGQhFBPB9pogbAg'; final sessionKey = @@ -94,6 +95,7 @@ void main() { }); test('outbound group session', () async { + if (!olmEnabled) return; final roomId = '!726s6s6q:example.com'; expect( client.encryption.keyManager.getOutboundGroupSession(roomId) != null, @@ -244,6 +246,7 @@ void main() { }); test('inbound group session', () async { + if (!olmEnabled) return; final roomId = '!726s6s6q:example.com'; final sessionId = 'ciM/JWTPrmiWPPZNkRLDPQYf9AW/I46bxyLSr+Bx5oU'; final senderKey = 'JBG7ZaPn54OBC7TuIEiylW3BZ+7WcGQhFBPB9pogbAg'; @@ -325,6 +328,7 @@ void main() { }); test('setInboundGroupSession', () async { + if (!olmEnabled) return; final session = olm.OutboundGroupSession(); session.create(); final inbound = olm.InboundGroupSession(); @@ -495,6 +499,7 @@ void main() { }); test('dispose client', () async { + if (!olmEnabled) return; await client.dispose(closeDatabase: true); }); }); diff --git a/test/encryption/key_request_test.dart b/test/encryption/key_request_test.dart index 953d7f16..0e687daf 100644 --- a/test/encryption/key_request_test.dart +++ b/test/encryption/key_request_test.dart @@ -42,20 +42,20 @@ void main() { group('Key Request', () { Logs().level = Level.error; var olmEnabled = true; - try { - olm.init(); - olm.Account(); - } catch (e) { - olmEnabled = false; - Logs().w('[LibOlm] Failed to load LibOlm', e); - } - Logs().i('[LibOlm] Enabled: $olmEnabled'); - - if (!olmEnabled) return; final validSessionId = 'ciM/JWTPrmiWPPZNkRLDPQYf9AW/I46bxyLSr+Bx5oU'; final validSenderKey = 'JBG7ZaPn54OBC7TuIEiylW3BZ+7WcGQhFBPB9pogbAg'; test('Create Request', () async { + try { + await olm.init(); + olm.get_library_version(); + } catch (e) { + olmEnabled = false; + Logs().w('[LibOlm] Failed to load LibOlm', e); + } + Logs().i('[LibOlm] Enabled: $olmEnabled'); + if (!olmEnabled) return; + var matrix = await getClient(); final requestRoom = matrix.getRoomById('!726s6s6q:example.com'); await matrix.encryption.keyManager.request( @@ -83,6 +83,7 @@ void main() { await matrix.dispose(closeDatabase: true); }); test('Reply To Request', () async { + if (!olmEnabled) return; var matrix = await getClient(); matrix.setUserId('@alice:example.com'); // we need to pretend to be alice FakeMatrixApi.calledEndpoints.clear(); @@ -277,6 +278,7 @@ void main() { await matrix.dispose(closeDatabase: true); }); test('Receive shared keys', () async { + if (!olmEnabled) return; var matrix = await getClient(); final requestRoom = matrix.getRoomById('!726s6s6q:example.com'); await matrix.encryption.keyManager.request( diff --git a/test/encryption/key_verification_test.dart b/test/encryption/key_verification_test.dart index 8bbf3e8e..a704081a 100644 --- a/test/encryption/key_verification_test.dart +++ b/test/encryption/key_verification_test.dart @@ -64,16 +64,6 @@ void main() { group('Key Verification', () { Logs().level = Level.error; var olmEnabled = true; - try { - olm.init(); - olm.Account(); - } catch (e) { - olmEnabled = false; - Logs().w('[LibOlm] Failed to load LibOlm', e); - } - Logs().i('[LibOlm] Enabled: $olmEnabled'); - - if (!olmEnabled) return; // key @othertest:fakeServer.notExisting const otherPickledOlmAccount = @@ -83,6 +73,16 @@ void main() { Client client2; test('setupClient', () async { + try { + await olm.init(); + olm.get_library_version(); + } catch (e) { + olmEnabled = false; + Logs().w('[LibOlm] Failed to load LibOlm', e); + } + Logs().i('[LibOlm] Enabled: $olmEnabled'); + if (!olmEnabled) return; + client1 = await getClient(); client2 = Client('othertestclient', httpClient: FakeMatrixApi(), @@ -109,6 +109,7 @@ void main() { }); test('Run emoji / number verification', () async { + if (!olmEnabled) return; // for a full run we test in-room verification in a cleartext room // because then we can easily intercept the payloads and inject in the other client FakeMatrixApi.calledEndpoints.clear(); @@ -209,6 +210,7 @@ void main() { }); test('ask SSSS start', () async { + if (!olmEnabled) return; client1.userDeviceKeys[client1.userID].masterKey.setDirectVerified(true); await client1.encryption.ssss.clearCache(); final req1 = @@ -223,6 +225,7 @@ void main() { }); test('ask SSSS end', () async { + if (!olmEnabled) return; FakeMatrixApi.calledEndpoints.clear(); // make sure our master key is *not* verified to not triger SSSS for now client1.userDeviceKeys[client1.userID].masterKey.setDirectVerified(false); @@ -327,6 +330,7 @@ void main() { }); test('reject verification', () async { + if (!olmEnabled) return; FakeMatrixApi.calledEndpoints.clear(); // make sure our master key is *not* verified to not triger SSSS for now client1.userDeviceKeys[client1.userID].masterKey.setDirectVerified(false); @@ -355,6 +359,7 @@ void main() { }); test('reject sas', () async { + if (!olmEnabled) return; FakeMatrixApi.calledEndpoints.clear(); // make sure our master key is *not* verified to not triger SSSS for now client1.userDeviceKeys[client1.userID].masterKey.setDirectVerified(false); @@ -415,6 +420,7 @@ void main() { }); test('other device accepted', () async { + if (!olmEnabled) return; FakeMatrixApi.calledEndpoints.clear(); // make sure our master key is *not* verified to not triger SSSS for now client1.userDeviceKeys[client1.userID].masterKey.setDirectVerified(false); @@ -460,6 +466,7 @@ void main() { }); test('dispose client', () async { + if (!olmEnabled) return; await client1.dispose(closeDatabase: true); await client2.dispose(closeDatabase: true); }); diff --git a/test/encryption/olm_manager_test.dart b/test/encryption/olm_manager_test.dart index 6b32c0ff..39c866f6 100644 --- a/test/encryption/olm_manager_test.dart +++ b/test/encryption/olm_manager_test.dart @@ -30,24 +30,25 @@ void main() { group('Olm Manager', () { Logs().level = Level.error; var olmEnabled = true; - try { - olm.init(); - olm.Account(); - } catch (e) { - olmEnabled = false; - Logs().w('[LibOlm] Failed to load LibOlm', e); - } - Logs().i('[LibOlm] Enabled: $olmEnabled'); - - if (!olmEnabled) return; Client client; test('setupClient', () async { + try { + await olm.init(); + olm.get_library_version(); + } catch (e) { + olmEnabled = false; + Logs().w('[LibOlm] Failed to load LibOlm', e); + } + Logs().i('[LibOlm] Enabled: $olmEnabled'); + if (!olmEnabled) return; + client = await getClient(); }); test('signatures', () async { + if (!olmEnabled) return; final payload = { 'fox': 'floof', }; @@ -59,6 +60,7 @@ void main() { }); test('uploadKeys', () async { + if (!olmEnabled) return; FakeMatrixApi.calledEndpoints.clear(); final res = await client.encryption.olmManager.uploadKeys(uploadDeviceKeys: true); @@ -68,22 +70,28 @@ void main() { expect(sent['device_keys'] != null, true); expect(sent['one_time_keys'] != null, true); expect(sent['one_time_keys'].keys.length, 66); + expect(sent['fallback_keys'] != null, true); + expect(sent['fallback_keys'].keys.length, 1); FakeMatrixApi.calledEndpoints.clear(); await client.encryption.olmManager.uploadKeys(); sent = json.decode( FakeMatrixApi.calledEndpoints['/client/r0/keys/upload'].first); expect(sent['device_keys'] != null, false); + expect(sent['fallback_keys'].keys.length, 1); FakeMatrixApi.calledEndpoints.clear(); - await client.encryption.olmManager.uploadKeys(oldKeyCount: 20); + await client.encryption.olmManager + .uploadKeys(oldKeyCount: 20, unusedFallbackKey: true); sent = json.decode( FakeMatrixApi.calledEndpoints['/client/r0/keys/upload'].first); expect(sent['one_time_keys'].keys.length, 46); + expect(sent['fallback_keys'].keys.length, 0); }); test('handleDeviceOneTimeKeysCount', () async { + if (!olmEnabled) return; FakeMatrixApi.calledEndpoints.clear(); client.encryption.olmManager - .handleDeviceOneTimeKeysCount({'signed_curve25519': 20}); + .handleDeviceOneTimeKeysCount({'signed_curve25519': 20}, null); await Future.delayed(Duration(milliseconds: 50)); expect( FakeMatrixApi.calledEndpoints.containsKey('/client/r0/keys/upload'), @@ -91,7 +99,22 @@ void main() { FakeMatrixApi.calledEndpoints.clear(); client.encryption.olmManager - .handleDeviceOneTimeKeysCount({'signed_curve25519': 70}); + .handleDeviceOneTimeKeysCount({'signed_curve25519': 70}, null); + await Future.delayed(Duration(milliseconds: 50)); + expect( + FakeMatrixApi.calledEndpoints.containsKey('/client/r0/keys/upload'), + false); + + FakeMatrixApi.calledEndpoints.clear(); + client.encryption.olmManager.handleDeviceOneTimeKeysCount(null, []); + await Future.delayed(Duration(milliseconds: 50)); + expect( + FakeMatrixApi.calledEndpoints.containsKey('/client/r0/keys/upload'), + true); + + FakeMatrixApi.calledEndpoints.clear(); + client.encryption.olmManager + .handleDeviceOneTimeKeysCount(null, ['signed_curve25519']); await Future.delayed(Duration(milliseconds: 50)); expect( FakeMatrixApi.calledEndpoints.containsKey('/client/r0/keys/upload'), @@ -99,6 +122,7 @@ void main() { }); test('restoreOlmSession', () async { + if (!olmEnabled) return; client.encryption.olmManager.olmSessions.clear(); await client.encryption.olmManager .restoreOlmSession(client.userID, client.identityKey); @@ -116,6 +140,7 @@ void main() { }); test('startOutgoingOlmSessions', () async { + if (!olmEnabled) return; // start an olm session.....with ourself! client.encryption.olmManager.olmSessions.clear(); await client.encryption.olmManager.startOutgoingOlmSessions( @@ -127,6 +152,7 @@ void main() { }); test('replay to_device events', () async { + if (!olmEnabled) return; final userId = '@alice:example.com'; final deviceId = 'JLAFKJWSCS'; final senderKey = 'L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8'; @@ -209,6 +235,7 @@ void main() { }); test('dispose client', () async { + if (!olmEnabled) return; await client.dispose(closeDatabase: true); }); }); diff --git a/test/encryption/online_key_backup_test.dart b/test/encryption/online_key_backup_test.dart index c224435d..0f427a29 100644 --- a/test/encryption/online_key_backup_test.dart +++ b/test/encryption/online_key_backup_test.dart @@ -30,16 +30,6 @@ void main() { group('Online Key Backup', () { Logs().level = Level.error; var olmEnabled = true; - try { - olm.init(); - olm.Account(); - } catch (e) { - olmEnabled = false; - Logs().w('[LibOlm] Failed to load LibOlm', e); - } - Logs().i('[LibOlm] Enabled: $olmEnabled'); - - if (!olmEnabled) return; Client client; @@ -48,10 +38,21 @@ void main() { final senderKey = 'JBG7ZaPn54OBC7TuIEiylW3BZ+7WcGQhFBPB9pogbAg'; test('setupClient', () async { + try { + await olm.init(); + olm.get_library_version(); + } catch (e) { + olmEnabled = false; + Logs().w('[LibOlm] Failed to load LibOlm', e); + } + Logs().i('[LibOlm] Enabled: $olmEnabled'); + if (!olmEnabled) return; + client = await getClient(); }); test('basic things', () async { + if (!olmEnabled) return; expect(client.encryption.keyManager.enabled, true); expect(await client.encryption.keyManager.isCached(), false); final handle = client.encryption.ssss.open(); @@ -61,6 +62,7 @@ void main() { }); test('load key', () async { + if (!olmEnabled) return; client.encryption.keyManager.clearInboundGroupSessions(); await client.encryption.keyManager .request(client.getRoomById(roomId), sessionId, senderKey); @@ -72,6 +74,7 @@ void main() { }); test('upload key', () async { + if (!olmEnabled) return; final session = olm.OutboundGroupSession(); session.create(); final inbound = olm.InboundGroupSession(); @@ -115,6 +118,7 @@ void main() { }); test('dispose client', () async { + if (!olmEnabled) return; await client.dispose(closeDatabase: true); }); }); diff --git a/test/encryption/ssss_test.dart b/test/encryption/ssss_test.dart index efb8cd7f..ce9da26c 100644 --- a/test/encryption/ssss_test.dart +++ b/test/encryption/ssss_test.dart @@ -46,29 +46,31 @@ void main() { group('SSSS', () { Logs().level = Level.error; var olmEnabled = true; - try { - olm.init(); - olm.Account(); - } catch (e) { - olmEnabled = false; - Logs().w('[LibOlm] Failed to load LibOlm', e); - } - Logs().i('[LibOlm] Enabled: $olmEnabled'); - - if (!olmEnabled) return; Client client; test('setupClient', () async { + try { + await olm.init(); + olm.get_library_version(); + } catch (e) { + olmEnabled = false; + Logs().w('[LibOlm] Failed to load LibOlm', e); + } + Logs().i('[LibOlm] Enabled: $olmEnabled'); + if (!olmEnabled) return; + client = await getClient(); }); test('basic things', () async { + if (!olmEnabled) return; expect(client.encryption.ssss.defaultKeyId, '0FajDWYaM6wQ4O60OZnLvwZfsBNu4Bu3'); }); test('encrypt / decrypt', () { + if (!olmEnabled) return; final key = Uint8List.fromList(SecureRandom(32).bytes); final enc = SSSS.encryptAes('secret foxies', key, 'name'); @@ -77,6 +79,7 @@ void main() { }); test('store', () async { + if (!olmEnabled) return; final handle = client.encryption.ssss.open(); var failed = false; try { @@ -113,6 +116,7 @@ void main() { }); test('encode / decode recovery key', () async { + if (!olmEnabled) return; final key = Uint8List.fromList(SecureRandom(32).bytes); final encoded = SSSS.encodeRecoveryKey(key); final decoded = SSSS.decodeRecoveryKey(encoded); @@ -124,6 +128,7 @@ void main() { }); test('cache', () async { + if (!olmEnabled) return; await client.encryption.ssss.clearCache(); final handle = client.encryption.ssss.open(EventTypes.CrossSigningSelfSigning); @@ -157,6 +162,7 @@ void main() { }); test('postUnlock', () async { + if (!olmEnabled) return; await client.encryption.ssss.clearCache(); client.userDeviceKeys[client.userID].masterKey.setDirectVerified(false); final handle = @@ -181,6 +187,7 @@ void main() { }); test('make share requests', () async { + if (!olmEnabled) return; final key = client.userDeviceKeys[client.userID].deviceKeys['OTHERDEVICE']; key.setDirectVerified(true); @@ -193,6 +200,7 @@ void main() { }); test('answer to share requests', () async { + if (!olmEnabled) return; var event = ToDeviceEvent( sender: client.userID, type: 'm.secret.request', @@ -291,6 +299,7 @@ void main() { }); test('receive share requests', () async { + if (!olmEnabled) return; final key = client.userDeviceKeys[client.userID].deviceKeys['OTHERDEVICE']; key.setDirectVerified(true); @@ -433,6 +442,7 @@ void main() { }); test('request all', () async { + if (!olmEnabled) return; final key = client.userDeviceKeys[client.userID].deviceKeys['OTHERDEVICE']; key.setDirectVerified(true); @@ -443,6 +453,7 @@ void main() { }); test('periodicallyRequestMissingCache', () async { + if (!olmEnabled) return; client.userDeviceKeys[client.userID].masterKey.setDirectVerified(true); client.encryption.ssss = MockSSSS(client.encryption); (client.encryption.ssss as MockSSSS).requestedSecrets = false; @@ -455,6 +466,7 @@ void main() { }); test('createKey', () async { + if (!olmEnabled) return; // with passphrase var newKey = await client.encryption.ssss.createKey('test'); expect(client.encryption.ssss.isKeyValid(newKey.keyId), true); @@ -470,6 +482,7 @@ void main() { }); test('dispose client', () async { + if (!olmEnabled) return; await client.dispose(closeDatabase: true); }); }); diff --git a/test/event_test.dart b/test/event_test.dart index 8089d675..4d4d4615 100644 --- a/test/event_test.dart +++ b/test/event_test.dart @@ -35,14 +35,6 @@ void main() { group('Event', () { Logs().level = Level.error; var olmEnabled = true; - try { - olm.init(); - olm.Account(); - } catch (e) { - olmEnabled = false; - Logs().w('[LibOlm] Failed to load LibOlm', e); - } - Logs().i('[LibOlm] Enabled: $olmEnabled'); final timestamp = DateTime.now().millisecondsSinceEpoch; final id = '!4fsdfjisjf:server.abc'; @@ -68,6 +60,17 @@ void main() { var event = Event.fromJson( jsonObj, Room(id: '!localpart:server.abc', client: client)); + test('setup', () async { + try { + await olm.init(); + olm.get_library_version(); + } catch (e) { + olmEnabled = false; + Logs().w('[LibOlm] Failed to load LibOlm', e); + } + Logs().i('[LibOlm] Enabled: $olmEnabled'); + }); + test('Create from json', () async { jsonObj.remove('status'); jsonObj['content'] = json.decode(contentJson); diff --git a/test/timeline_test.dart b/test/timeline_test.dart index 2a67644e..361f39f0 100644 --- a/test/timeline_test.dart +++ b/test/timeline_test.dart @@ -35,19 +35,19 @@ void main() { var updateCount = 0; var insertList = []; var olmEnabled = true; - try { - olm.init(); - olm.Account(); - } catch (e) { - olmEnabled = false; - Logs().w('[LibOlm] Failed to load LibOlm', e); - } - Logs().i('[LibOlm] Enabled: $olmEnabled'); Client client; Room room; Timeline timeline; test('create stuff', () async { + try { + await olm.init(); + olm.get_library_version(); + } catch (e) { + olmEnabled = false; + Logs().w('[LibOlm] Failed to load LibOlm', e); + } + Logs().i('[LibOlm] Enabled: $olmEnabled'); client = await getClient(); client.sendMessageTimeoutSeconds = 5;