finish ssss tests
This commit is contained in:
parent
7803dc4b93
commit
ef0a567401
|
@ -93,7 +93,7 @@ class CrossSigning {
|
||||||
}
|
}
|
||||||
// master key is valid, set it to verified
|
// master key is valid, set it to verified
|
||||||
await masterKey.setVerified(true, false);
|
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([
|
await sign([
|
||||||
masterKey,
|
masterKey,
|
||||||
client.userDeviceKeys[client.userID].deviceKeys[client.deviceID]
|
client.userDeviceKeys[client.userID].deviceKeys[client.deviceID]
|
||||||
|
|
|
@ -405,7 +405,8 @@ class KeyManager {
|
||||||
try {
|
try {
|
||||||
await loadSingleKey(room.id, sessionId);
|
await loadSingleKey(room.id, sessionId);
|
||||||
} catch (err, stacktrace) {
|
} 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);
|
print(stacktrace);
|
||||||
}
|
}
|
||||||
if (!hadPreviously &&
|
if (!hadPreviously &&
|
||||||
|
|
|
@ -5850,6 +5850,15 @@ abstract class _$Database extends GeneratedDatabase {
|
||||||
readsFrom: {ssssCache}).map(_rowToDbSSSSCache);
|
readsFrom: {ssssCache}).map(_rowToDbSSSSCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<int> 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<int> insertClient(
|
Future<int> insertClient(
|
||||||
String name,
|
String name,
|
||||||
String homeserver_url,
|
String homeserver_url,
|
||||||
|
|
|
@ -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;
|
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);
|
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;
|
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);
|
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);
|
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;
|
setRoomPrevBatch: UPDATE rooms SET prev_batch = :prev_batch WHERE client_id = :client_id AND room_id = :room_id;
|
||||||
|
|
2
test.sh
2
test.sh
|
@ -2,5 +2,5 @@
|
||||||
pub run test -p vm
|
pub run test -p vm
|
||||||
pub run test_coverage
|
pub run test_coverage
|
||||||
pub global activate remove_from_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
|
genhtml -o coverage coverage/lcov.info || true
|
||||||
|
|
113
test/encryption/cross_signing_test.dart
Normal file
113
test/encryption/cross_signing_test.dart
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -32,176 +32,367 @@ import '../fake_matrix_api.dart';
|
||||||
void main() {
|
void main() {
|
||||||
group('SSSS', () {
|
group('SSSS', () {
|
||||||
var olmEnabled = true;
|
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 {
|
try {
|
||||||
olm.init();
|
handle.unlock(passphrase: 'invalid');
|
||||||
olm.Account();
|
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
olmEnabled = false;
|
failed = true;
|
||||||
print('[LibOlm] Failed to load LibOlm: ' + _.toString());
|
|
||||||
}
|
}
|
||||||
print('[LibOlm] Enabled: $olmEnabled');
|
expect(failed, true);
|
||||||
|
expect(handle.isUnlocked, false);
|
||||||
if (!olmEnabled) return;
|
failed = false;
|
||||||
|
try {
|
||||||
Client client;
|
handle.unlock(recoveryKey: 'invalid');
|
||||||
|
} catch (_) {
|
||||||
test('setupClient', () async {
|
failed = true;
|
||||||
client = await getClient();
|
}
|
||||||
|
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 {
|
test('cache', () async {
|
||||||
expect(client.encryption.ssss.defaultKeyId, '0FajDWYaM6wQ4O60OZnLvwZfsBNu4Bu3');
|
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', () {
|
test('make share requests', () async {
|
||||||
final signing = olm.PkSigning();
|
final key =
|
||||||
final key = Uint8List.fromList(SecureRandom(32).bytes);
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
final enc = SSSS.encryptAes('secret foxies', key, 'name');
|
test('answer to share requests', () async {
|
||||||
final dec = SSSS.decryptAes(enc, key, 'name');
|
var event = ToDeviceEvent(
|
||||||
expect(dec, 'secret foxies');
|
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('store', () async {
|
// now test some fail scenarios
|
||||||
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('cache', () async {
|
// not by us
|
||||||
final handle = client.encryption.ssss.open('m.cross_signing.self_signing');
|
event = ToDeviceEvent(
|
||||||
handle.unlock(recoveryKey: SSSS_KEY);
|
sender: '@someotheruser:example.org',
|
||||||
expect((await client.encryption.ssss.getCached('m.cross_signing.self_signing')) != null, false);
|
type: 'm.secret.request',
|
||||||
expect((await client.encryption.ssss.getCached('m.cross_signing.user_signing')) != null, false);
|
content: {
|
||||||
await handle.getStored('m.cross_signing.self_signing');
|
'action': 'request',
|
||||||
expect((await client.encryption.ssss.getCached('m.cross_signing.self_signing')) != null, true);
|
'requesting_device_id': 'OTHERDEVICE',
|
||||||
await handle.maybeCacheAll();
|
'name': 'm.cross_signing.self_signing',
|
||||||
expect((await client.encryption.ssss.getCached('m.cross_signing.user_signing')) != null, true);
|
'request_id': '1',
|
||||||
expect((await client.encryption.ssss.getCached('m.megolm_backup.v1')) != null, true);
|
},
|
||||||
});
|
);
|
||||||
|
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('make share requests', () async {
|
// secret not cached
|
||||||
final key = client.userDeviceKeys[client.userID].deviceKeys['OTHERDEVICE'];
|
event = ToDeviceEvent(
|
||||||
key.setDirectVerified(true);
|
sender: client.userID,
|
||||||
FakeMatrixApi.calledEndpoints.clear();
|
type: 'm.secret.request',
|
||||||
await client.encryption.ssss.request('some.type', [key]);
|
content: {
|
||||||
expect(FakeMatrixApi.calledEndpoints.keys.any((k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')), true);
|
'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);
|
||||||
|
|
||||||
test('answer to share requests', () async {
|
// is a cancelation
|
||||||
var event = ToDeviceEvent(
|
event = ToDeviceEvent(
|
||||||
sender: client.userID,
|
sender: client.userID,
|
||||||
type: 'm.secret.request',
|
type: 'm.secret.request',
|
||||||
content: {
|
content: {
|
||||||
'action': 'request',
|
'action': 'request_cancellation',
|
||||||
'requesting_device_id': 'OTHERDEVICE',
|
'requesting_device_id': 'OTHERDEVICE',
|
||||||
'name': 'm.cross_signing.self_signing',
|
'name': 'm.cross_signing.self_signing',
|
||||||
'request_id': '1',
|
'request_id': '1',
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
FakeMatrixApi.calledEndpoints.clear();
|
FakeMatrixApi.calledEndpoints.clear();
|
||||||
await client.encryption.ssss.handleToDeviceEvent(event);
|
await client.encryption.ssss.handleToDeviceEvent(event);
|
||||||
expect(FakeMatrixApi.calledEndpoints.keys.any((k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')), true);
|
expect(
|
||||||
|
FakeMatrixApi.calledEndpoints.keys.any(
|
||||||
|
(k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')),
|
||||||
|
false);
|
||||||
|
|
||||||
// now test some 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 by us
|
test('receive share requests', () async {
|
||||||
event = ToDeviceEvent(
|
final key =
|
||||||
sender: '@someotheruser:example.org',
|
client.userDeviceKeys[client.userID].deviceKeys['OTHERDEVICE'];
|
||||||
type: 'm.secret.request',
|
key.setDirectVerified(true);
|
||||||
content: {
|
final handle =
|
||||||
'action': 'request',
|
client.encryption.ssss.open('m.cross_signing.self_signing');
|
||||||
'requesting_device_id': 'OTHERDEVICE',
|
handle.unlock(recoveryKey: SSSS_KEY);
|
||||||
'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);
|
|
||||||
|
|
||||||
// secret not cached
|
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(
|
event = ToDeviceEvent(
|
||||||
sender: client.userID,
|
sender: client.userID,
|
||||||
type: 'm.secret.request',
|
type: 'm.secret.send',
|
||||||
content: {
|
content: {
|
||||||
'action': 'request',
|
'request_id':
|
||||||
'requesting_device_id': 'OTHERDEVICE',
|
client.encryption.ssss.pendingShareRequests.keys.first,
|
||||||
'name': 'm.unknown.secret',
|
'secret': secret,
|
||||||
'request_id': '1',
|
},
|
||||||
|
encryptedContent: {
|
||||||
|
'sender_key': key.curve25519Key,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
FakeMatrixApi.calledEndpoints.clear();
|
|
||||||
await client.encryption.ssss.handleToDeviceEvent(event);
|
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
|
// test different fail scenarios
|
||||||
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);
|
|
||||||
|
|
||||||
// device not verified
|
// not encrypted
|
||||||
final key = client.userDeviceKeys[client.userID].deviceKeys['OTHERDEVICE'];
|
await client.database.clearSSSSCache(client.id);
|
||||||
key.setDirectVerified(false);
|
client.encryption.ssss.pendingShareRequests.clear();
|
||||||
event = ToDeviceEvent(
|
await client.encryption.ssss.request('best animal', [key]);
|
||||||
sender: client.userID,
|
event = ToDeviceEvent(
|
||||||
type: 'm.secret.request',
|
sender: client.userID,
|
||||||
content: {
|
type: 'm.secret.send',
|
||||||
'action': 'request',
|
content: {
|
||||||
'requesting_device_id': 'OTHERDEVICE',
|
'request_id': client.encryption.ssss.pendingShareRequests.keys.first,
|
||||||
'name': 'm.cross_signing.self_signing',
|
'secret': 'foxies!',
|
||||||
'request_id': '1',
|
},
|
||||||
},
|
);
|
||||||
);
|
await client.encryption.ssss.handleToDeviceEvent(event);
|
||||||
FakeMatrixApi.calledEndpoints.clear();
|
expect(await client.encryption.ssss.getCached('best animal'), null);
|
||||||
await client.encryption.ssss.handleToDeviceEvent(event);
|
|
||||||
expect(FakeMatrixApi.calledEndpoints.keys.any((k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')), false);
|
|
||||||
key.setDirectVerified(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
// test('fail', () {
|
// unknown request id
|
||||||
// expect(true, false);
|
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 {
|
// not from a device we sent the request to
|
||||||
await client.dispose(closeDatabase: true);
|
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);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -539,7 +539,8 @@ class FakeMatrixApi extends MockClient {
|
||||||
'encrypted': {
|
'encrypted': {
|
||||||
'0FajDWYaM6wQ4O60OZnLvwZfsBNu4Bu3': {
|
'0FajDWYaM6wQ4O60OZnLvwZfsBNu4Bu3': {
|
||||||
'iv': 'eIb2IITxtmcq+1TrT8D5eQ==',
|
'iv': 'eIb2IITxtmcq+1TrT8D5eQ==',
|
||||||
'ciphertext': 'lWRTPo5qxf4LAVwVPzGHOyMcP181n7bb9/B0lvkLDC2Oy4DvAL0eLx2x3bY=',
|
'ciphertext':
|
||||||
|
'lWRTPo5qxf4LAVwVPzGHOyMcP181n7bb9/B0lvkLDC2Oy4DvAL0eLx2x3bY=',
|
||||||
'mac': 'Ynx89tIxPkx0o6ljMgxszww17JOgB4tg4etmNnMC9XI='
|
'mac': 'Ynx89tIxPkx0o6ljMgxszww17JOgB4tg4etmNnMC9XI='
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -551,7 +552,8 @@ class FakeMatrixApi extends MockClient {
|
||||||
'encrypted': {
|
'encrypted': {
|
||||||
'0FajDWYaM6wQ4O60OZnLvwZfsBNu4Bu3': {
|
'0FajDWYaM6wQ4O60OZnLvwZfsBNu4Bu3': {
|
||||||
'iv': 'YqU2XIjYulYZl+bkZtGgVw==',
|
'iv': 'YqU2XIjYulYZl+bkZtGgVw==',
|
||||||
'ciphertext': 'kM2TSoy/jR/4d357ZoRPbpPypxQl6XRLo3FsEXz+f7vIOp82GeRp28RYb3k=',
|
'ciphertext':
|
||||||
|
'kM2TSoy/jR/4d357ZoRPbpPypxQl6XRLo3FsEXz+f7vIOp82GeRp28RYb3k=',
|
||||||
'mac': 'F+DZa5tAFmWsYSryw5EuEpzTmmABRab4GETkM85bGGo='
|
'mac': 'F+DZa5tAFmWsYSryw5EuEpzTmmABRab4GETkM85bGGo='
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -563,7 +565,8 @@ class FakeMatrixApi extends MockClient {
|
||||||
'encrypted': {
|
'encrypted': {
|
||||||
'0FajDWYaM6wQ4O60OZnLvwZfsBNu4Bu3': {
|
'0FajDWYaM6wQ4O60OZnLvwZfsBNu4Bu3': {
|
||||||
'iv': 'D7AM3LXFu7ZlyGOkR+OeqQ==',
|
'iv': 'D7AM3LXFu7ZlyGOkR+OeqQ==',
|
||||||
'ciphertext': 'bYA2+OMgsO6QB1E31aY+ESAWrT0fUBTXqajy4qmL7bVDSZY4Uj64EXNbHuA=',
|
'ciphertext':
|
||||||
|
'bYA2+OMgsO6QB1E31aY+ESAWrT0fUBTXqajy4qmL7bVDSZY4Uj64EXNbHuA=',
|
||||||
'mac': 'j2UtyPo/UBSoiaQCWfzCiRZXp3IRt0ZZujuXgUMjnw4='
|
'mac': 'j2UtyPo/UBSoiaQCWfzCiRZXp3IRt0ZZujuXgUMjnw4='
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -575,7 +578,8 @@ class FakeMatrixApi extends MockClient {
|
||||||
'encrypted': {
|
'encrypted': {
|
||||||
'0FajDWYaM6wQ4O60OZnLvwZfsBNu4Bu3': {
|
'0FajDWYaM6wQ4O60OZnLvwZfsBNu4Bu3': {
|
||||||
'iv': 'cL/0MJZaiEd3fNU+I9oJrw==',
|
'iv': 'cL/0MJZaiEd3fNU+I9oJrw==',
|
||||||
'ciphertext': 'WL73Pzdk5wZdaaSpaeRH0uZYKcxkuV8IS6Qa2FEfA1+vMeRLuHcWlXbMX0w=',
|
'ciphertext':
|
||||||
|
'WL73Pzdk5wZdaaSpaeRH0uZYKcxkuV8IS6Qa2FEfA1+vMeRLuHcWlXbMX0w=',
|
||||||
'mac': '+xozp909S6oDX8KRV8D8ZFVRyh7eEYQpPP76f+DOsnw='
|
'mac': '+xozp909S6oDX8KRV8D8ZFVRyh7eEYQpPP76f+DOsnw='
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1527,12 +1531,14 @@ class FakeMatrixApi extends MockClient {
|
||||||
'event_fields': ['type', 'content', 'sender']
|
'event_fields': ['type', 'content', 'sender']
|
||||||
},
|
},
|
||||||
'/client/unstable/room_keys/version': (var req) => {
|
'/client/unstable/room_keys/version': (var req) => {
|
||||||
'algorithm': 'm.megolm_backup.v1.curve25519-aes-sha2',
|
'algorithm': 'm.megolm_backup.v1.curve25519-aes-sha2',
|
||||||
'auth_data': {'public_key': 'GXYaxqhNhUK28zUdxOmEsFRguz+PzBsDlTLlF0O0RkM'},
|
'auth_data': {
|
||||||
'count': 0,
|
'public_key': 'GXYaxqhNhUK28zUdxOmEsFRguz+PzBsDlTLlF0O0RkM'
|
||||||
'etag': '0',
|
},
|
||||||
'version': '5',
|
'count': 0,
|
||||||
},
|
'etag': '0',
|
||||||
|
'version': '5',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
'POST': {
|
'POST': {
|
||||||
'/client/r0/delete_devices': (var req) => {},
|
'/client/r0/delete_devices': (var req) => {},
|
||||||
|
@ -1782,7 +1788,16 @@ class FakeMatrixApi extends MockClient {
|
||||||
'user_id': '@test:fakeServer.notExisting',
|
'user_id': '@test:fakeServer.notExisting',
|
||||||
'usage': ['master'],
|
'usage': ['master'],
|
||||||
'keys': {
|
'keys': {
|
||||||
'ed25519:82mAXjsmbTbrE6zyShpR869jnrANO75H8nYY0nDLoJ8': '82mAXjsmbTbrE6zyShpR869jnrANO75H8nYY0nDLoJ8',
|
'ed25519:82mAXjsmbTbrE6zyShpR869jnrANO75H8nYY0nDLoJ8':
|
||||||
|
'82mAXjsmbTbrE6zyShpR869jnrANO75H8nYY0nDLoJ8',
|
||||||
|
},
|
||||||
|
'signatures': {},
|
||||||
|
},
|
||||||
|
'@othertest:fakeServer.notExisting': {
|
||||||
|
'user_id': '@othertest:fakeServer.notExisting',
|
||||||
|
'usage': ['master'],
|
||||||
|
'keys': {
|
||||||
|
'ed25519:master': 'master',
|
||||||
},
|
},
|
||||||
'signatures': {},
|
'signatures': {},
|
||||||
},
|
},
|
||||||
|
@ -1792,7 +1807,16 @@ class FakeMatrixApi extends MockClient {
|
||||||
'user_id': '@test:fakeServer.notExisting',
|
'user_id': '@test:fakeServer.notExisting',
|
||||||
'usage': ['self_signing'],
|
'usage': ['self_signing'],
|
||||||
'keys': {
|
'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': {},
|
'signatures': {},
|
||||||
},
|
},
|
||||||
|
@ -1802,7 +1826,16 @@ class FakeMatrixApi extends MockClient {
|
||||||
'user_id': '@test:fakeServer.notExisting',
|
'user_id': '@test:fakeServer.notExisting',
|
||||||
'usage': ['user_signing'],
|
'usage': ['user_signing'],
|
||||||
'keys': {
|
'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': {},
|
'signatures': {},
|
||||||
},
|
},
|
||||||
|
@ -1854,6 +1887,7 @@ class FakeMatrixApi extends MockClient {
|
||||||
'/client/r0/rooms/!localpart%3Aserver.abc/ban': (var reqI) => {},
|
'/client/r0/rooms/!localpart%3Aserver.abc/ban': (var reqI) => {},
|
||||||
'/client/r0/rooms/!localpart%3Aserver.abc/unban': (var reqI) => {},
|
'/client/r0/rooms/!localpart%3Aserver.abc/unban': (var reqI) => {},
|
||||||
'/client/r0/rooms/!localpart%3Aserver.abc/invite': (var reqI) => {},
|
'/client/r0/rooms/!localpart%3Aserver.abc/invite': (var reqI) => {},
|
||||||
|
'/client/r0/keys/signatures/upload': (var reqI) => {'failures': {}},
|
||||||
},
|
},
|
||||||
'PUT': {
|
'PUT': {
|
||||||
'/client/r0/presence/${Uri.encodeComponent('@alice:example.com')}/status':
|
'/client/r0/presence/${Uri.encodeComponent('@alice:example.com')}/status':
|
||||||
|
|
Loading…
Reference in a new issue