diff --git a/lib/encryption/cross_signing.dart b/lib/encryption/cross_signing.dart index 61540880..92cbb866 100644 --- a/lib/encryption/cross_signing.dart +++ b/lib/encryption/cross_signing.dart @@ -93,7 +93,7 @@ class CrossSigning { } // master key is valid, set it to verified await masterKey.setVerified(true, false); - // and now sign bout our own key and our master key + // and now sign both our own key and our master key await sign([ masterKey, client.userDeviceKeys[client.userID].deviceKeys[client.deviceID] diff --git a/lib/encryption/key_manager.dart b/lib/encryption/key_manager.dart index a284d190..eeeb08d0 100644 --- a/lib/encryption/key_manager.dart +++ b/lib/encryption/key_manager.dart @@ -405,7 +405,8 @@ class KeyManager { try { await loadSingleKey(room.id, sessionId); } catch (err, stacktrace) { - print('[KeyManager] Failed to access online key backup: ' + err.toString()); + print( + '[KeyManager] Failed to access online key backup: ' + err.toString()); print(stacktrace); } if (!hadPreviously && diff --git a/lib/src/database/database.g.dart b/lib/src/database/database.g.dart index 0c501182..52bf9884 100644 --- a/lib/src/database/database.g.dart +++ b/lib/src/database/database.g.dart @@ -5850,6 +5850,15 @@ abstract class _$Database extends GeneratedDatabase { readsFrom: {ssssCache}).map(_rowToDbSSSSCache); } + Future clearSSSSCache(int client_id) { + return customUpdate( + 'DELETE FROM ssss_cache WHERE client_id = :client_id', + variables: [Variable.withInt(client_id)], + updates: {ssssCache}, + updateKind: UpdateKind.delete, + ); + } + Future insertClient( String name, String homeserver_url, diff --git a/lib/src/database/database.moor b/lib/src/database/database.moor index 68a0ee51..abe513d4 100644 --- a/lib/src/database/database.moor +++ b/lib/src/database/database.moor @@ -198,6 +198,7 @@ storeUserCrossSigningKey: INSERT OR REPLACE INTO user_cross_signing_keys (client removeUserCrossSigningKey: DELETE FROM user_cross_signing_keys WHERE client_id = :client_id AND user_id = :user_id AND public_key = :public_key; storeSSSSCache: INSERT OR REPLACE INTO ssss_cache (client_id, type, key_id, ciphertext, content) VALUES (:client_id, :type, :key_id, :ciphertext, :content); dbGetSSSSCache: SELECT * FROM ssss_cache WHERE client_id = :client_id AND type = :type; +clearSSSSCache: DELETE FROM ssss_cache WHERE client_id = :client_id; insertClient: INSERT INTO clients (name, homeserver_url, token, user_id, device_id, device_name, prev_batch, olm_account) VALUES (:name, :homeserver_url, :token, :user_id, :device_id, :device_name, :prev_batch, :olm_account); ensureRoomExists: INSERT OR IGNORE INTO rooms (client_id, room_id, membership) VALUES (:client_id, :room_id, :membership); setRoomPrevBatch: UPDATE rooms SET prev_batch = :prev_batch WHERE client_id = :client_id AND room_id = :room_id; diff --git a/test.sh b/test.sh index 5efc1578..2496656b 100644 --- a/test.sh +++ b/test.sh @@ -2,5 +2,5 @@ pub run test -p vm pub run test_coverage pub global activate remove_from_coverage -pub global run remove_from_coverage:remove_from_coverage -f coverage/lcov.info -r '.g.dart$' +pub global run remove_from_coverage:remove_from_coverage -f coverage/lcov.info -r '\.g\.dart$' genhtml -o coverage coverage/lcov.info || true diff --git a/test/encryption/cross_signing_test.dart b/test/encryption/cross_signing_test.dart new file mode 100644 index 00000000..4ec212b1 --- /dev/null +++ b/test/encryption/cross_signing_test.dart @@ -0,0 +1,113 @@ +/* + * Ansible inventory script used at Famedly GmbH for managing many hosts + * Copyright (C) 2020 Famedly GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import 'dart:convert'; + +import 'package:famedlysdk/famedlysdk.dart'; +import 'package:test/test.dart'; +import 'package:olm/olm.dart' as olm; + +import '../fake_client.dart'; +import '../fake_matrix_api.dart'; + +void main() { + group('Cross Signing', () { + var olmEnabled = true; + try { + olm.init(); + olm.Account(); + } catch (_) { + olmEnabled = false; + print('[LibOlm] Failed to load LibOlm: ' + _.toString()); + } + print('[LibOlm] Enabled: $olmEnabled'); + + if (!olmEnabled) return; + + Client client; + + test('setupClient', () async { + client = await getClient(); + }); + + test('basic things', () async { + expect(client.encryption.crossSigning.enabled, true); + }); + + test('selfSign', () async { + final key = client.userDeviceKeys[client.userID].masterKey; + key.setDirectVerified(false); + FakeMatrixApi.calledEndpoints.clear(); + await client.encryption.crossSigning.selfSign(recoveryKey: SSSS_KEY); + expect(key.directVerified, true); + expect( + FakeMatrixApi.calledEndpoints + .containsKey('/client/r0/keys/signatures/upload'), + true); + expect(await client.encryption.crossSigning.isCached(), true); + }); + + test('signable', () async { + expect( + client.encryption.crossSigning + .signable([client.userDeviceKeys[client.userID].masterKey]), + true); + expect( + client.encryption.crossSigning.signable([ + client.userDeviceKeys[client.userID].deviceKeys[client.deviceID] + ]), + false); + expect( + client.encryption.crossSigning.signable( + [client.userDeviceKeys[client.userID].deviceKeys['OTHERDEVICE']]), + true); + expect( + client.encryption.crossSigning.signable([ + client.userDeviceKeys['@alice:example.com'].deviceKeys['JLAFKJWSCS'] + ]), + false); + }); + + test('sign', () async { + FakeMatrixApi.calledEndpoints.clear(); + await client.encryption.crossSigning.sign([ + client.userDeviceKeys[client.userID].masterKey, + client.userDeviceKeys[client.userID].deviceKeys['OTHERDEVICE'], + client.userDeviceKeys['@othertest:fakeServer.notExisting'].masterKey + ]); + var body = json.decode(FakeMatrixApi + .calledEndpoints['/client/r0/keys/signatures/upload'].first); + expect(body['@test:fakeServer.notExisting'].containsKey('OTHERDEVICE'), + true); + expect( + body['@test:fakeServer.notExisting'].containsKey( + client.userDeviceKeys[client.userID].masterKey.publicKey), + true); + expect( + body['@othertest:fakeServer.notExisting'].containsKey(client + .userDeviceKeys['@othertest:fakeServer.notExisting'] + .masterKey + .publicKey), + true); + }); + + test('dispose client', () async { + await client.dispose(closeDatabase: true); + }); + }); +} diff --git a/test/encryption/ssss_test.dart b/test/encryption/ssss_test.dart index 55d0d8f3..a0d5b948 100644 --- a/test/encryption/ssss_test.dart +++ b/test/encryption/ssss_test.dart @@ -32,176 +32,367 @@ import '../fake_matrix_api.dart'; void main() { group('SSSS', () { var olmEnabled = true; + try { + olm.init(); + olm.Account(); + } catch (_) { + olmEnabled = false; + print('[LibOlm] Failed to load LibOlm: ' + _.toString()); + } + print('[LibOlm] Enabled: $olmEnabled'); + + if (!olmEnabled) return; + + Client client; + + test('setupClient', () async { + client = await getClient(); + }); + + test('basic things', () async { + expect(client.encryption.ssss.defaultKeyId, + '0FajDWYaM6wQ4O60OZnLvwZfsBNu4Bu3'); + }); + + test('encrypt / decrypt', () { + final key = Uint8List.fromList(SecureRandom(32).bytes); + + final enc = SSSS.encryptAes('secret foxies', key, 'name'); + final dec = SSSS.decryptAes(enc, key, 'name'); + expect(dec, 'secret foxies'); + }); + + test('store', () async { + final handle = client.encryption.ssss.open(); + var failed = false; try { - olm.init(); - olm.Account(); + handle.unlock(passphrase: 'invalid'); } catch (_) { - olmEnabled = false; - print('[LibOlm] Failed to load LibOlm: ' + _.toString()); + failed = true; } - print('[LibOlm] Enabled: $olmEnabled'); - - if (!olmEnabled) return; - - Client client; - - test('setupClient', () async { - client = await getClient(); + expect(failed, true); + expect(handle.isUnlocked, false); + failed = false; + try { + handle.unlock(recoveryKey: 'invalid'); + } catch (_) { + failed = true; + } + expect(failed, true); + expect(handle.isUnlocked, false); + handle.unlock(passphrase: SSSS_PASSPHRASE); + handle.unlock(recoveryKey: SSSS_KEY); + expect(handle.isUnlocked, true); + FakeMatrixApi.calledEndpoints.clear(); + await handle.store('best animal', 'foxies'); + // alright, since we don't properly sync we will manually have to update + // account_data for this test + final content = FakeMatrixApi + .calledEndpoints[ + '/client/r0/user/%40test%3AfakeServer.notExisting/account_data/best+animal'] + .first; + client.accountData['best animal'] = BasicEvent.fromJson({ + 'type': 'best animal', + 'content': json.decode(content), }); + expect(await handle.getStored('best animal'), 'foxies'); + }); - test('basic things', () async { - expect(client.encryption.ssss.defaultKeyId, '0FajDWYaM6wQ4O60OZnLvwZfsBNu4Bu3'); - }); + test('cache', () async { + final handle = + client.encryption.ssss.open('m.cross_signing.self_signing'); + handle.unlock(recoveryKey: SSSS_KEY); + expect( + (await client.encryption.ssss + .getCached('m.cross_signing.self_signing')) != + null, + false); + expect( + (await client.encryption.ssss + .getCached('m.cross_signing.user_signing')) != + null, + false); + await handle.getStored('m.cross_signing.self_signing'); + expect( + (await client.encryption.ssss + .getCached('m.cross_signing.self_signing')) != + null, + true); + await handle.maybeCacheAll(); + expect( + (await client.encryption.ssss + .getCached('m.cross_signing.user_signing')) != + null, + true); + expect( + (await client.encryption.ssss.getCached('m.megolm_backup.v1')) != + null, + true); + }); - test('encrypt / decrypt', () { - final signing = olm.PkSigning(); - final key = Uint8List.fromList(SecureRandom(32).bytes); - - final enc = SSSS.encryptAes('secret foxies', key, 'name'); - final dec = SSSS.decryptAes(enc, key, 'name'); - expect(dec, 'secret foxies'); - }); + test('make share requests', () async { + final key = + client.userDeviceKeys[client.userID].deviceKeys['OTHERDEVICE']; + key.setDirectVerified(true); + FakeMatrixApi.calledEndpoints.clear(); + await client.encryption.ssss.request('some.type', [key]); + expect( + FakeMatrixApi.calledEndpoints.keys.any( + (k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')), + true); + }); - test('store', () async { - final handle = client.encryption.ssss.open(); - var failed = false; - try { - handle.unlock(passphrase: 'invalid'); - } catch (_) { - failed = true; - } - expect(failed, true); - expect(handle.isUnlocked, false); - failed = false; - try { - handle.unlock(recoveryKey: 'invalid'); - } catch (_) { - failed = true; - } - expect(failed, true); - expect(handle.isUnlocked, false); - handle.unlock(passphrase: SSSS_PASSPHRASE); - handle.unlock(recoveryKey: SSSS_KEY); - expect(handle.isUnlocked, true); - FakeMatrixApi.calledEndpoints.clear(); - await handle.store('best animal', 'foxies'); - // alright, since we don't properly sync we will manually have to update - // account_data for this test - final content = FakeMatrixApi.calledEndpoints['/client/r0/user/%40test%3AfakeServer.notExisting/account_data/best+animal'].first; - client.accountData['best animal'] = BasicEvent.fromJson({ - 'type': 'best animal', - 'content': json.decode(content), - }); - expect(await handle.getStored('best animal'), 'foxies'); - }); + test('answer to share requests', () async { + var event = ToDeviceEvent( + sender: client.userID, + type: 'm.secret.request', + content: { + 'action': 'request', + 'requesting_device_id': 'OTHERDEVICE', + 'name': 'm.cross_signing.self_signing', + 'request_id': '1', + }, + ); + FakeMatrixApi.calledEndpoints.clear(); + await client.encryption.ssss.handleToDeviceEvent(event); + expect( + FakeMatrixApi.calledEndpoints.keys.any( + (k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')), + true); - test('cache', () async { - final handle = client.encryption.ssss.open('m.cross_signing.self_signing'); - handle.unlock(recoveryKey: SSSS_KEY); - expect((await client.encryption.ssss.getCached('m.cross_signing.self_signing')) != null, false); - expect((await client.encryption.ssss.getCached('m.cross_signing.user_signing')) != null, false); - await handle.getStored('m.cross_signing.self_signing'); - expect((await client.encryption.ssss.getCached('m.cross_signing.self_signing')) != null, true); - await handle.maybeCacheAll(); - expect((await client.encryption.ssss.getCached('m.cross_signing.user_signing')) != null, true); - expect((await client.encryption.ssss.getCached('m.megolm_backup.v1')) != null, true); - }); + // now test some fail scenarios - test('make share requests', () async { - final key = client.userDeviceKeys[client.userID].deviceKeys['OTHERDEVICE']; - key.setDirectVerified(true); - FakeMatrixApi.calledEndpoints.clear(); - await client.encryption.ssss.request('some.type', [key]); - expect(FakeMatrixApi.calledEndpoints.keys.any((k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')), true); - }); + // not by us + event = ToDeviceEvent( + sender: '@someotheruser:example.org', + type: 'm.secret.request', + content: { + 'action': 'request', + 'requesting_device_id': 'OTHERDEVICE', + 'name': 'm.cross_signing.self_signing', + 'request_id': '1', + }, + ); + FakeMatrixApi.calledEndpoints.clear(); + await client.encryption.ssss.handleToDeviceEvent(event); + expect( + FakeMatrixApi.calledEndpoints.keys.any( + (k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')), + false); - test('answer to share requests', () async { - var event = ToDeviceEvent( - sender: client.userID, - type: 'm.secret.request', - content: { - 'action': 'request', - 'requesting_device_id': 'OTHERDEVICE', - 'name': 'm.cross_signing.self_signing', - 'request_id': '1', - }, - ); - FakeMatrixApi.calledEndpoints.clear(); - await client.encryption.ssss.handleToDeviceEvent(event); - expect(FakeMatrixApi.calledEndpoints.keys.any((k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')), true); + // secret not cached + event = ToDeviceEvent( + sender: client.userID, + type: 'm.secret.request', + content: { + 'action': 'request', + 'requesting_device_id': 'OTHERDEVICE', + 'name': 'm.unknown.secret', + 'request_id': '1', + }, + ); + FakeMatrixApi.calledEndpoints.clear(); + await client.encryption.ssss.handleToDeviceEvent(event); + expect( + FakeMatrixApi.calledEndpoints.keys.any( + (k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')), + false); - // now test some fail scenarios + // is a cancelation + event = ToDeviceEvent( + sender: client.userID, + type: 'm.secret.request', + content: { + 'action': 'request_cancellation', + 'requesting_device_id': 'OTHERDEVICE', + 'name': 'm.cross_signing.self_signing', + 'request_id': '1', + }, + ); + FakeMatrixApi.calledEndpoints.clear(); + await client.encryption.ssss.handleToDeviceEvent(event); + expect( + FakeMatrixApi.calledEndpoints.keys.any( + (k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')), + false); - // not by us - event = ToDeviceEvent( - sender: '@someotheruser:example.org', - type: 'm.secret.request', - content: { - 'action': 'request', - 'requesting_device_id': 'OTHERDEVICE', - 'name': 'm.cross_signing.self_signing', - 'request_id': '1', - }, - ); - FakeMatrixApi.calledEndpoints.clear(); - await client.encryption.ssss.handleToDeviceEvent(event); - expect(FakeMatrixApi.calledEndpoints.keys.any((k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')), false); + // device not verified + final key = + client.userDeviceKeys[client.userID].deviceKeys['OTHERDEVICE']; + key.setDirectVerified(false); + event = ToDeviceEvent( + sender: client.userID, + type: 'm.secret.request', + content: { + 'action': 'request', + 'requesting_device_id': 'OTHERDEVICE', + 'name': 'm.cross_signing.self_signing', + 'request_id': '1', + }, + ); + FakeMatrixApi.calledEndpoints.clear(); + await client.encryption.ssss.handleToDeviceEvent(event); + expect( + FakeMatrixApi.calledEndpoints.keys.any( + (k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')), + false); + key.setDirectVerified(true); + }); - // secret not cached + test('receive share requests', () async { + final key = + client.userDeviceKeys[client.userID].deviceKeys['OTHERDEVICE']; + key.setDirectVerified(true); + final handle = + client.encryption.ssss.open('m.cross_signing.self_signing'); + handle.unlock(recoveryKey: SSSS_KEY); + + await client.database.clearSSSSCache(client.id); + client.encryption.ssss.pendingShareRequests.clear(); + await client.encryption.ssss.request('best animal', [key]); + var event = ToDeviceEvent( + sender: client.userID, + type: 'm.secret.send', + content: { + 'request_id': client.encryption.ssss.pendingShareRequests.keys.first, + 'secret': 'foxies!', + }, + encryptedContent: { + 'sender_key': key.curve25519Key, + }, + ); + await client.encryption.ssss.handleToDeviceEvent(event); + expect(await client.encryption.ssss.getCached('best animal'), 'foxies!'); + + // test the different validators + for (final type in [ + 'm.cross_signing.self_signing', + 'm.cross_signing.user_signing', + 'm.megolm_backup.v1' + ]) { + final secret = await handle.getStored(type); + await client.database.clearSSSSCache(client.id); + client.encryption.ssss.pendingShareRequests.clear(); + await client.encryption.ssss.request(type, [key]); event = ToDeviceEvent( sender: client.userID, - type: 'm.secret.request', + type: 'm.secret.send', content: { - 'action': 'request', - 'requesting_device_id': 'OTHERDEVICE', - 'name': 'm.unknown.secret', - 'request_id': '1', + 'request_id': + client.encryption.ssss.pendingShareRequests.keys.first, + 'secret': secret, + }, + encryptedContent: { + 'sender_key': key.curve25519Key, }, ); - FakeMatrixApi.calledEndpoints.clear(); await client.encryption.ssss.handleToDeviceEvent(event); - expect(FakeMatrixApi.calledEndpoints.keys.any((k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')), false); + expect(await client.encryption.ssss.getCached(type), secret); + } - // is a cancelation - event = ToDeviceEvent( - sender: client.userID, - type: 'm.secret.request', - content: { - 'action': 'request_cancellation', - 'requesting_device_id': 'OTHERDEVICE', - 'name': 'm.cross_signing.self_signing', - 'request_id': '1', - }, - ); - FakeMatrixApi.calledEndpoints.clear(); - await client.encryption.ssss.handleToDeviceEvent(event); - expect(FakeMatrixApi.calledEndpoints.keys.any((k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')), false); + // test different fail scenarios - // device not verified - final key = client.userDeviceKeys[client.userID].deviceKeys['OTHERDEVICE']; - key.setDirectVerified(false); - event = ToDeviceEvent( - sender: client.userID, - type: 'm.secret.request', - content: { - 'action': 'request', - 'requesting_device_id': 'OTHERDEVICE', - 'name': 'm.cross_signing.self_signing', - 'request_id': '1', - }, - ); - FakeMatrixApi.calledEndpoints.clear(); - await client.encryption.ssss.handleToDeviceEvent(event); - expect(FakeMatrixApi.calledEndpoints.keys.any((k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')), false); - key.setDirectVerified(true); - }); + // not encrypted + await client.database.clearSSSSCache(client.id); + client.encryption.ssss.pendingShareRequests.clear(); + await client.encryption.ssss.request('best animal', [key]); + event = ToDeviceEvent( + sender: client.userID, + type: 'm.secret.send', + content: { + 'request_id': client.encryption.ssss.pendingShareRequests.keys.first, + 'secret': 'foxies!', + }, + ); + await client.encryption.ssss.handleToDeviceEvent(event); + expect(await client.encryption.ssss.getCached('best animal'), null); -// test('fail', () { -// expect(true, false); -// }); + // unknown request id + await client.database.clearSSSSCache(client.id); + client.encryption.ssss.pendingShareRequests.clear(); + await client.encryption.ssss.request('best animal', [key]); + event = ToDeviceEvent( + sender: client.userID, + type: 'm.secret.send', + content: { + 'request_id': 'invalid', + 'secret': 'foxies!', + }, + encryptedContent: { + 'sender_key': key.curve25519Key, + }, + ); + await client.encryption.ssss.handleToDeviceEvent(event); + expect(await client.encryption.ssss.getCached('best animal'), null); - test('dispose client', () async { - await client.dispose(closeDatabase: true); - }); + // not from a device we sent the request to + await client.database.clearSSSSCache(client.id); + client.encryption.ssss.pendingShareRequests.clear(); + await client.encryption.ssss.request('best animal', [key]); + event = ToDeviceEvent( + sender: client.userID, + type: 'm.secret.send', + content: { + 'request_id': client.encryption.ssss.pendingShareRequests.keys.first, + 'secret': 'foxies!', + }, + encryptedContent: { + 'sender_key': 'invalid', + }, + ); + await client.encryption.ssss.handleToDeviceEvent(event); + expect(await client.encryption.ssss.getCached('best animal'), null); + + // secret not a string + await client.database.clearSSSSCache(client.id); + client.encryption.ssss.pendingShareRequests.clear(); + await client.encryption.ssss.request('best animal', [key]); + event = ToDeviceEvent( + sender: client.userID, + type: 'm.secret.send', + content: { + 'request_id': client.encryption.ssss.pendingShareRequests.keys.first, + 'secret': 42, + }, + encryptedContent: { + 'sender_key': key.curve25519Key, + }, + ); + await client.encryption.ssss.handleToDeviceEvent(event); + expect(await client.encryption.ssss.getCached('best animal'), null); + + // validator doesn't check out + await client.database.clearSSSSCache(client.id); + client.encryption.ssss.pendingShareRequests.clear(); + await client.encryption.ssss.request('m.megolm_backup.v1', [key]); + event = ToDeviceEvent( + sender: client.userID, + type: 'm.secret.send', + content: { + 'request_id': client.encryption.ssss.pendingShareRequests.keys.first, + 'secret': 'foxies!', + }, + encryptedContent: { + 'sender_key': key.curve25519Key, + }, + ); + await client.encryption.ssss.handleToDeviceEvent(event); + expect( + await client.encryption.ssss.getCached('m.megolm_backup.v1'), null); + }); + + test('request all', () async { + final key = + client.userDeviceKeys[client.userID].deviceKeys['OTHERDEVICE']; + key.setDirectVerified(true); + await client.database.clearSSSSCache(client.id); + client.encryption.ssss.pendingShareRequests.clear(); + await client.encryption.ssss.maybeRequestAll([key]); + expect(client.encryption.ssss.pendingShareRequests.length, 3); + }); + + test('dispose client', () async { + await client.dispose(closeDatabase: true); + }); }); } diff --git a/test/fake_matrix_api.dart b/test/fake_matrix_api.dart index e53e7a87..e69538cb 100644 --- a/test/fake_matrix_api.dart +++ b/test/fake_matrix_api.dart @@ -539,7 +539,8 @@ class FakeMatrixApi extends MockClient { 'encrypted': { '0FajDWYaM6wQ4O60OZnLvwZfsBNu4Bu3': { 'iv': 'eIb2IITxtmcq+1TrT8D5eQ==', - 'ciphertext': 'lWRTPo5qxf4LAVwVPzGHOyMcP181n7bb9/B0lvkLDC2Oy4DvAL0eLx2x3bY=', + 'ciphertext': + 'lWRTPo5qxf4LAVwVPzGHOyMcP181n7bb9/B0lvkLDC2Oy4DvAL0eLx2x3bY=', 'mac': 'Ynx89tIxPkx0o6ljMgxszww17JOgB4tg4etmNnMC9XI=' } } @@ -551,7 +552,8 @@ class FakeMatrixApi extends MockClient { 'encrypted': { '0FajDWYaM6wQ4O60OZnLvwZfsBNu4Bu3': { 'iv': 'YqU2XIjYulYZl+bkZtGgVw==', - 'ciphertext': 'kM2TSoy/jR/4d357ZoRPbpPypxQl6XRLo3FsEXz+f7vIOp82GeRp28RYb3k=', + 'ciphertext': + 'kM2TSoy/jR/4d357ZoRPbpPypxQl6XRLo3FsEXz+f7vIOp82GeRp28RYb3k=', 'mac': 'F+DZa5tAFmWsYSryw5EuEpzTmmABRab4GETkM85bGGo=' } } @@ -563,7 +565,8 @@ class FakeMatrixApi extends MockClient { 'encrypted': { '0FajDWYaM6wQ4O60OZnLvwZfsBNu4Bu3': { 'iv': 'D7AM3LXFu7ZlyGOkR+OeqQ==', - 'ciphertext': 'bYA2+OMgsO6QB1E31aY+ESAWrT0fUBTXqajy4qmL7bVDSZY4Uj64EXNbHuA=', + 'ciphertext': + 'bYA2+OMgsO6QB1E31aY+ESAWrT0fUBTXqajy4qmL7bVDSZY4Uj64EXNbHuA=', 'mac': 'j2UtyPo/UBSoiaQCWfzCiRZXp3IRt0ZZujuXgUMjnw4=' } } @@ -575,7 +578,8 @@ class FakeMatrixApi extends MockClient { 'encrypted': { '0FajDWYaM6wQ4O60OZnLvwZfsBNu4Bu3': { 'iv': 'cL/0MJZaiEd3fNU+I9oJrw==', - 'ciphertext': 'WL73Pzdk5wZdaaSpaeRH0uZYKcxkuV8IS6Qa2FEfA1+vMeRLuHcWlXbMX0w=', + 'ciphertext': + 'WL73Pzdk5wZdaaSpaeRH0uZYKcxkuV8IS6Qa2FEfA1+vMeRLuHcWlXbMX0w=', 'mac': '+xozp909S6oDX8KRV8D8ZFVRyh7eEYQpPP76f+DOsnw=' } } @@ -1527,12 +1531,14 @@ class FakeMatrixApi extends MockClient { 'event_fields': ['type', 'content', 'sender'] }, '/client/unstable/room_keys/version': (var req) => { - 'algorithm': 'm.megolm_backup.v1.curve25519-aes-sha2', - 'auth_data': {'public_key': 'GXYaxqhNhUK28zUdxOmEsFRguz+PzBsDlTLlF0O0RkM'}, - 'count': 0, - 'etag': '0', - 'version': '5', - }, + 'algorithm': 'm.megolm_backup.v1.curve25519-aes-sha2', + 'auth_data': { + 'public_key': 'GXYaxqhNhUK28zUdxOmEsFRguz+PzBsDlTLlF0O0RkM' + }, + 'count': 0, + 'etag': '0', + 'version': '5', + }, }, 'POST': { '/client/r0/delete_devices': (var req) => {}, @@ -1782,7 +1788,16 @@ class FakeMatrixApi extends MockClient { 'user_id': '@test:fakeServer.notExisting', 'usage': ['master'], 'keys': { - 'ed25519:82mAXjsmbTbrE6zyShpR869jnrANO75H8nYY0nDLoJ8': '82mAXjsmbTbrE6zyShpR869jnrANO75H8nYY0nDLoJ8', + 'ed25519:82mAXjsmbTbrE6zyShpR869jnrANO75H8nYY0nDLoJ8': + '82mAXjsmbTbrE6zyShpR869jnrANO75H8nYY0nDLoJ8', + }, + 'signatures': {}, + }, + '@othertest:fakeServer.notExisting': { + 'user_id': '@othertest:fakeServer.notExisting', + 'usage': ['master'], + 'keys': { + 'ed25519:master': 'master', }, 'signatures': {}, }, @@ -1792,7 +1807,16 @@ class FakeMatrixApi extends MockClient { 'user_id': '@test:fakeServer.notExisting', 'usage': ['self_signing'], 'keys': { - 'ed25519:F9ypFzgbISXCzxQhhSnXMkc1vq12Luna3Nw5rqViOJY': 'F9ypFzgbISXCzxQhhSnXMkc1vq12Luna3Nw5rqViOJY', + 'ed25519:F9ypFzgbISXCzxQhhSnXMkc1vq12Luna3Nw5rqViOJY': + 'F9ypFzgbISXCzxQhhSnXMkc1vq12Luna3Nw5rqViOJY', + }, + 'signatures': {}, + }, + '@othertest:fakeServer.notExisting': { + 'user_id': '@othertest:fakeServer.notExisting', + 'usage': ['self_signing'], + 'keys': { + 'ed25519:self_signing': 'self_signing', }, 'signatures': {}, }, @@ -1802,7 +1826,16 @@ class FakeMatrixApi extends MockClient { 'user_id': '@test:fakeServer.notExisting', 'usage': ['user_signing'], 'keys': { - 'ed25519:0PiwulzJ/RU86LlzSSZ8St80HUMN3dqjKa/orIJoA0g': '0PiwulzJ/RU86LlzSSZ8St80HUMN3dqjKa/orIJoA0g', + 'ed25519:0PiwulzJ/RU86LlzSSZ8St80HUMN3dqjKa/orIJoA0g': + '0PiwulzJ/RU86LlzSSZ8St80HUMN3dqjKa/orIJoA0g', + }, + 'signatures': {}, + }, + '@othertest:fakeServer.notExisting': { + 'user_id': '@othertest:fakeServer.notExisting', + 'usage': ['user_signing'], + 'keys': { + 'ed25519:user_signing': 'user_signing', }, 'signatures': {}, }, @@ -1854,6 +1887,7 @@ class FakeMatrixApi extends MockClient { '/client/r0/rooms/!localpart%3Aserver.abc/ban': (var reqI) => {}, '/client/r0/rooms/!localpart%3Aserver.abc/unban': (var reqI) => {}, '/client/r0/rooms/!localpart%3Aserver.abc/invite': (var reqI) => {}, + '/client/r0/keys/signatures/upload': (var reqI) => {'failures': {}}, }, 'PUT': { '/client/r0/presence/${Uri.encodeComponent('@alice:example.com')}/status':