refactor: _updateUserDeviceKeys method
This commit is contained in:
parent
56af96c7ea
commit
00771fc209
|
|
@ -370,10 +370,12 @@ class KeyManager {
|
||||||
for (final device in devicesToReceive) {
|
for (final device in devicesToReceive) {
|
||||||
inboundSess.allowedAtIndex[device.userId] ??= <String, int>{};
|
inboundSess.allowedAtIndex[device.userId] ??= <String, int>{};
|
||||||
if (!inboundSess.allowedAtIndex[device.userId]
|
if (!inboundSess.allowedAtIndex[device.userId]
|
||||||
.containsKey(device.deviceId) ||
|
.containsKey(device.curve25519Key) ||
|
||||||
inboundSess.allowedAtIndex[device.userId][device.deviceId] >
|
inboundSess.allowedAtIndex[device.userId]
|
||||||
|
[device.curve25519Key] >
|
||||||
sess.outboundGroupSession.message_index()) {
|
sess.outboundGroupSession.message_index()) {
|
||||||
inboundSess.allowedAtIndex[device.userId][device.deviceId] =
|
inboundSess.allowedAtIndex[device.userId]
|
||||||
|
[device.curve25519Key] =
|
||||||
sess.outboundGroupSession.message_index();
|
sess.outboundGroupSession.message_index();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -471,7 +473,7 @@ class KeyManager {
|
||||||
final allowedAtIndex = <String, Map<String, int>>{};
|
final allowedAtIndex = <String, Map<String, int>>{};
|
||||||
for (final device in deviceKeys) {
|
for (final device in deviceKeys) {
|
||||||
allowedAtIndex[device.userId] ??= <String, int>{};
|
allowedAtIndex[device.userId] ??= <String, int>{};
|
||||||
allowedAtIndex[device.userId][device.deviceId] =
|
allowedAtIndex[device.userId][device.curve25519Key] =
|
||||||
outboundGroupSession.message_index();
|
outboundGroupSession.message_index();
|
||||||
}
|
}
|
||||||
setInboundGroupSession(
|
setInboundGroupSession(
|
||||||
|
|
@ -832,7 +834,8 @@ class KeyManager {
|
||||||
// if we know the user may see the message, then we can just forward the key.
|
// if we know the user may see the message, then we can just forward the key.
|
||||||
// we do not need to check if the device is verified, just if it is not blocked,
|
// we do not need to check if the device is verified, just if it is not blocked,
|
||||||
// as that is the logic we already initially try to send out the room keys.
|
// as that is the logic we already initially try to send out the room keys.
|
||||||
final index = session.allowedAtIndex[device.userId][device.deviceId];
|
final index =
|
||||||
|
session.allowedAtIndex[device.userId][device.curve25519Key];
|
||||||
Logs().i(
|
Logs().i(
|
||||||
'[KeyManager] Valid foreign request, forwarding key at index $index...');
|
'[KeyManager] Valid foreign request, forwarding key at index $index...');
|
||||||
await roomKeyRequest.forwardKey(index);
|
await roomKeyRequest.forwardKey(index);
|
||||||
|
|
|
||||||
|
|
@ -1138,7 +1138,7 @@ class Client extends MatrixApi {
|
||||||
// ignore: unawaited_futures
|
// ignore: unawaited_futures
|
||||||
database?.deleteOldFiles(
|
database?.deleteOldFiles(
|
||||||
DateTime.now().subtract(Duration(days: 30)).millisecondsSinceEpoch);
|
DateTime.now().subtract(Duration(days: 30)).millisecondsSinceEpoch);
|
||||||
await _updateUserDeviceKeys();
|
await updateUserDeviceKeys();
|
||||||
if (encryptionEnabled) {
|
if (encryptionEnabled) {
|
||||||
encryption.onSync();
|
encryption.onSync();
|
||||||
}
|
}
|
||||||
|
|
@ -1642,9 +1642,9 @@ class Client extends MatrixApi {
|
||||||
|
|
||||||
final Map<String, DateTime> _keyQueryFailures = {};
|
final Map<String, DateTime> _keyQueryFailures = {};
|
||||||
|
|
||||||
Future<void> _updateUserDeviceKeys() async {
|
Future<void> updateUserDeviceKeys() async {
|
||||||
try {
|
try {
|
||||||
if (!isLogged()) return;
|
if (!isLogged() || database == null) return;
|
||||||
final dbActions = <Future<dynamic> Function()>[];
|
final dbActions = <Future<dynamic> Function()>[];
|
||||||
final trackedUserIds = await _getUserIdsInEncryptedRooms();
|
final trackedUserIds = await _getUserIdsInEncryptedRooms();
|
||||||
if (!isLogged()) return;
|
if (!isLogged()) return;
|
||||||
|
|
@ -1691,11 +1691,45 @@ class Client extends MatrixApi {
|
||||||
// Set the new device key for this device
|
// Set the new device key for this device
|
||||||
final entry = DeviceKeys.fromMatrixDeviceKeys(
|
final entry = DeviceKeys.fromMatrixDeviceKeys(
|
||||||
rawDeviceKeyEntry.value, this, oldKeys[deviceId]?.lastActive);
|
rawDeviceKeyEntry.value, this, oldKeys[deviceId]?.lastActive);
|
||||||
if (entry.isValid) {
|
if (entry.isValid && deviceId == entry.deviceId) {
|
||||||
|
// Check if deviceId or deviceKeys are known
|
||||||
|
if (!oldKeys.containsKey(deviceId)) {
|
||||||
|
final oldPublicKeys =
|
||||||
|
await database.deviceIdSeen(id, userId, deviceId);
|
||||||
|
if (oldPublicKeys != null &&
|
||||||
|
oldPublicKeys != entry.curve25519Key + entry.ed25519Key) {
|
||||||
|
Logs().w(
|
||||||
|
'Already seen Device ID has been added again. This might be an attack!');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final oldDeviceId =
|
||||||
|
await database.publicKeySeen(id, entry.ed25519Key);
|
||||||
|
if (oldDeviceId != null && oldDeviceId != deviceId) {
|
||||||
|
Logs().w(
|
||||||
|
'Already seen ED25519 has been added again. This might be an attack!');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final oldDeviceId2 =
|
||||||
|
await database.publicKeySeen(id, entry.curve25519Key);
|
||||||
|
if (oldDeviceId2 != null && oldDeviceId2 != deviceId) {
|
||||||
|
Logs().w(
|
||||||
|
'Already seen Curve25519 has been added again. This might be an attack!');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
await database.addSeenDeviceId(id, userId, deviceId,
|
||||||
|
entry.curve25519Key + entry.ed25519Key);
|
||||||
|
await database.addSeenPublicKey(
|
||||||
|
id, entry.ed25519Key, deviceId);
|
||||||
|
await database.addSeenPublicKey(
|
||||||
|
id, entry.curve25519Key, deviceId);
|
||||||
|
}
|
||||||
|
|
||||||
// is this a new key or the same one as an old one?
|
// is this a new key or the same one as an old one?
|
||||||
// better store an update - the signatures might have changed!
|
// better store an update - the signatures might have changed!
|
||||||
if (!oldKeys.containsKey(deviceId) ||
|
if (!oldKeys.containsKey(deviceId) ||
|
||||||
oldKeys[deviceId].ed25519Key == entry.ed25519Key) {
|
(oldKeys[deviceId].ed25519Key == entry.ed25519Key &&
|
||||||
|
oldKeys[deviceId].curve25519Key ==
|
||||||
|
entry.curve25519Key)) {
|
||||||
if (oldKeys.containsKey(deviceId)) {
|
if (oldKeys.containsKey(deviceId)) {
|
||||||
// be sure to save the verified status
|
// be sure to save the verified status
|
||||||
entry.setDirectVerified(oldKeys[deviceId].directVerified);
|
entry.setDirectVerified(oldKeys[deviceId].directVerified);
|
||||||
|
|
@ -1708,7 +1742,6 @@ class Client extends MatrixApi {
|
||||||
// Always trust the own device
|
// Always trust the own device
|
||||||
entry.setDirectVerified(true);
|
entry.setDirectVerified(true);
|
||||||
}
|
}
|
||||||
if (database != null) {
|
|
||||||
dbActions.add(() => database.storeUserDeviceKey(
|
dbActions.add(() => database.storeUserDeviceKey(
|
||||||
id,
|
id,
|
||||||
userId,
|
userId,
|
||||||
|
|
@ -1718,7 +1751,6 @@ class Client extends MatrixApi {
|
||||||
entry.blocked,
|
entry.blocked,
|
||||||
entry.lastActive.millisecondsSinceEpoch,
|
entry.lastActive.millisecondsSinceEpoch,
|
||||||
));
|
));
|
||||||
}
|
|
||||||
} else if (oldKeys.containsKey(deviceId)) {
|
} else if (oldKeys.containsKey(deviceId)) {
|
||||||
// This shouldn't ever happen. The same device ID has gotten
|
// This shouldn't ever happen. The same device ID has gotten
|
||||||
// a new public key. So we ignore the update. TODO: ask krille
|
// a new public key. So we ignore the update. TODO: ask krille
|
||||||
|
|
@ -1731,7 +1763,6 @@ class Client extends MatrixApi {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// delete old/unused entries
|
// delete old/unused entries
|
||||||
if (database != null) {
|
|
||||||
for (final oldDeviceKeyEntry in oldKeys.entries) {
|
for (final oldDeviceKeyEntry in oldKeys.entries) {
|
||||||
final deviceId = oldDeviceKeyEntry.key;
|
final deviceId = oldDeviceKeyEntry.key;
|
||||||
if (!_userDeviceKeys[userId].deviceKeys.containsKey(deviceId)) {
|
if (!_userDeviceKeys[userId].deviceKeys.containsKey(deviceId)) {
|
||||||
|
|
@ -1740,7 +1771,6 @@ class Client extends MatrixApi {
|
||||||
() => database.removeUserDeviceKey(id, userId, deviceId));
|
() => database.removeUserDeviceKey(id, userId, deviceId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
_userDeviceKeys[userId].outdated = false;
|
_userDeviceKeys[userId].outdated = false;
|
||||||
if (database != null) {
|
if (database != null) {
|
||||||
dbActions.add(
|
dbActions.add(
|
||||||
|
|
|
||||||
|
|
@ -318,6 +318,16 @@ abstract class DatabaseApi {
|
||||||
|
|
||||||
Future<List<StoredInboundGroupSession>> getInboundGroupSessionsToUpload();
|
Future<List<StoredInboundGroupSession>> getInboundGroupSessionsToUpload();
|
||||||
|
|
||||||
|
Future<void> addSeenDeviceId(
|
||||||
|
int clientId, String userId, String deviceId, String publicKeys);
|
||||||
|
|
||||||
|
Future<void> addSeenPublicKey(
|
||||||
|
int clientId, String publicKey, String deviceId);
|
||||||
|
|
||||||
|
Future<String?> deviceIdSeen(int clientId, userId, deviceId);
|
||||||
|
|
||||||
|
Future<String?> publicKeySeen(int clientId, String publicKey);
|
||||||
|
|
||||||
Future<dynamic> close();
|
Future<dynamic> close();
|
||||||
|
|
||||||
Future<T> transaction<T>(Future<T> Function() action);
|
Future<T> transaction<T>(Future<T> Function() action);
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ import 'package:hive/hive.dart';
|
||||||
///
|
///
|
||||||
/// This database does not support file caching!
|
/// This database does not support file caching!
|
||||||
class FamedlySdkHiveDatabase extends DatabaseApi {
|
class FamedlySdkHiveDatabase extends DatabaseApi {
|
||||||
static const int version = 4;
|
static const int version = 5;
|
||||||
final String name;
|
final String name;
|
||||||
late Box _clientBox;
|
late Box _clientBox;
|
||||||
late Box _accountDataBox;
|
late Box _accountDataBox;
|
||||||
|
|
@ -74,6 +74,11 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
|
||||||
/// Key is a tuple as MultiKey(roomId, eventId)
|
/// Key is a tuple as MultiKey(roomId, eventId)
|
||||||
late LazyBox _eventsBox;
|
late LazyBox _eventsBox;
|
||||||
|
|
||||||
|
/// Key is a tuple as MultiKey(userId, deviceId)
|
||||||
|
late LazyBox _seenDeviceIdsBox;
|
||||||
|
|
||||||
|
late LazyBox _seenDeviceKeysBox;
|
||||||
|
|
||||||
String get _clientBoxName => '$name.box.client';
|
String get _clientBoxName => '$name.box.client';
|
||||||
String get _accountDataBoxName => '$name.box.account_data';
|
String get _accountDataBoxName => '$name.box.account_data';
|
||||||
String get _roomsBoxName => '$name.box.rooms';
|
String get _roomsBoxName => '$name.box.rooms';
|
||||||
|
|
@ -93,6 +98,8 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
|
||||||
String get _presencesBoxName => '$name.box.presences';
|
String get _presencesBoxName => '$name.box.presences';
|
||||||
String get _timelineFragmentsBoxName => '$name.box.timeline_fragments';
|
String get _timelineFragmentsBoxName => '$name.box.timeline_fragments';
|
||||||
String get _eventsBoxName => '$name.box.events';
|
String get _eventsBoxName => '$name.box.events';
|
||||||
|
String get _seenDeviceIdsBoxName => '$name.box.seen_device_ids';
|
||||||
|
String get _seenDeviceKeysBoxName => '$name.box.seen_device_keys';
|
||||||
|
|
||||||
final HiveCipher? encryptionCipher;
|
final HiveCipher? encryptionCipher;
|
||||||
|
|
||||||
|
|
@ -120,6 +127,8 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
|
||||||
action(_presencesBox),
|
action(_presencesBox),
|
||||||
action(_timelineFragmentsBox),
|
action(_timelineFragmentsBox),
|
||||||
action(_eventsBox),
|
action(_eventsBox),
|
||||||
|
action(_seenDeviceIdsBox),
|
||||||
|
action(_seenDeviceKeysBox),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Future<void> open() async {
|
Future<void> open() async {
|
||||||
|
|
@ -191,6 +200,14 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
|
||||||
_eventsBoxName,
|
_eventsBoxName,
|
||||||
encryptionCipher: encryptionCipher,
|
encryptionCipher: encryptionCipher,
|
||||||
);
|
);
|
||||||
|
_seenDeviceIdsBox = await Hive.openLazyBox(
|
||||||
|
_seenDeviceIdsBoxName,
|
||||||
|
encryptionCipher: encryptionCipher,
|
||||||
|
);
|
||||||
|
_seenDeviceKeysBox = await Hive.openLazyBox(
|
||||||
|
_seenDeviceKeysBoxName,
|
||||||
|
encryptionCipher: encryptionCipher,
|
||||||
|
);
|
||||||
|
|
||||||
// Check version and check if we need a migration
|
// Check version and check if we need a migration
|
||||||
final currentVersion = (await _clientBox.get('version') as int?);
|
final currentVersion = (await _clientBox.get('version') as int?);
|
||||||
|
|
@ -205,6 +222,25 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
|
||||||
|
|
||||||
Future<void> _migrateFromVersion(int currentVersion) async {
|
Future<void> _migrateFromVersion(int currentVersion) async {
|
||||||
Logs().i('Migrate Hive database from version $currentVersion to $version');
|
Logs().i('Migrate Hive database from version $currentVersion to $version');
|
||||||
|
if (version == 5) {
|
||||||
|
for (final key in _userDeviceKeysBox.keys) {
|
||||||
|
try {
|
||||||
|
final raw = await _userDeviceKeysBox.get(key) as Map;
|
||||||
|
if (!raw.containsKey('keys')) continue;
|
||||||
|
final deviceKeys = DeviceKeys.fromJson(
|
||||||
|
convertToJson(raw),
|
||||||
|
Client(''),
|
||||||
|
);
|
||||||
|
await addSeenDeviceId(0, deviceKeys.userId, deviceKeys.deviceId,
|
||||||
|
deviceKeys.curve25519Key + deviceKeys.ed25519Key);
|
||||||
|
await addSeenPublicKey(0, deviceKeys.ed25519Key, deviceKeys.deviceId);
|
||||||
|
await addSeenPublicKey(
|
||||||
|
0, deviceKeys.curve25519Key, deviceKeys.deviceId);
|
||||||
|
} catch (e) {
|
||||||
|
Logs().w('Can not migrate device $key', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
await clearCache(0);
|
await clearCache(0);
|
||||||
await _clientBox.put('version', version);
|
await _clientBox.put('version', version);
|
||||||
}
|
}
|
||||||
|
|
@ -1232,6 +1268,39 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
|
||||||
.map((raw) => StoredInboundGroupSession.fromJson(convertToJson(raw)))
|
.map((raw) => StoredInboundGroupSession.fromJson(convertToJson(raw)))
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> addSeenDeviceId(
|
||||||
|
int clientId,
|
||||||
|
String userId,
|
||||||
|
String deviceId,
|
||||||
|
String publicKeysHash,
|
||||||
|
) =>
|
||||||
|
_seenDeviceIdsBox.put(
|
||||||
|
MultiKey(userId, deviceId).toString(), publicKeysHash);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> addSeenPublicKey(
|
||||||
|
int clientId,
|
||||||
|
String publicKey,
|
||||||
|
String deviceId,
|
||||||
|
) =>
|
||||||
|
_seenDeviceKeysBox.put(publicKey.toHiveKey, deviceId);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String?> deviceIdSeen(int clientId, userId, deviceId) async {
|
||||||
|
final raw =
|
||||||
|
await _seenDeviceIdsBox.get(MultiKey(userId, deviceId).toString());
|
||||||
|
if (raw == null) return null;
|
||||||
|
return raw as String;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String?> publicKeySeen(int clientId, String publicKey) async {
|
||||||
|
final raw = await _seenDeviceKeysBox.get(publicKey.toHiveKey);
|
||||||
|
if (raw == null) return null;
|
||||||
|
return raw as String;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dynamic _castValue(dynamic value) {
|
dynamic _castValue(dynamic value) {
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,11 @@ void main() {
|
||||||
|
|
||||||
/// Check if all Elements get created
|
/// Check if all Elements get created
|
||||||
|
|
||||||
matrix = Client('testclient', httpClient: FakeMatrixApi());
|
matrix = Client(
|
||||||
|
'testclient',
|
||||||
|
httpClient: FakeMatrixApi(),
|
||||||
|
databaseBuilder: getDatabase,
|
||||||
|
);
|
||||||
|
|
||||||
eventUpdateListFuture = matrix.onEvent.stream.toList();
|
eventUpdateListFuture = matrix.onEvent.stream.toList();
|
||||||
toDeviceUpdateListFuture = matrix.onToDeviceEvent.stream.toList();
|
toDeviceUpdateListFuture = matrix.onToDeviceEvent.stream.toList();
|
||||||
|
|
@ -291,7 +295,11 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Login', () async {
|
test('Login', () async {
|
||||||
matrix = Client('testclient', httpClient: FakeMatrixApi());
|
matrix = Client(
|
||||||
|
'testclient',
|
||||||
|
httpClient: FakeMatrixApi(),
|
||||||
|
databaseBuilder: getDatabase,
|
||||||
|
);
|
||||||
|
|
||||||
eventUpdateListFuture = matrix.onEvent.stream.toList();
|
eventUpdateListFuture = matrix.onEvent.stream.toList();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import 'package:test/test.dart';
|
||||||
import 'package:olm/olm.dart' as olm;
|
import 'package:olm/olm.dart' as olm;
|
||||||
|
|
||||||
import '../fake_client.dart';
|
import '../fake_client.dart';
|
||||||
|
import '../fake_matrix_api.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('Key Manager', () {
|
group('Key Manager', () {
|
||||||
|
|
@ -113,8 +114,14 @@ void main() {
|
||||||
var inbound = client.encryption.keyManager.getInboundGroupSession(
|
var inbound = client.encryption.keyManager.getInboundGroupSession(
|
||||||
roomId, sess.outboundGroupSession.session_id(), client.identityKey);
|
roomId, sess.outboundGroupSession.session_id(), client.identityKey);
|
||||||
expect(inbound != null, true);
|
expect(inbound != null, true);
|
||||||
expect(inbound.allowedAtIndex['@alice:example.com']['JLAFKJWSCS'], 0);
|
expect(
|
||||||
expect(inbound.allowedAtIndex['@alice:example.com']['OTHERDEVICE'], 0);
|
inbound.allowedAtIndex['@alice:example.com']
|
||||||
|
['L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8'],
|
||||||
|
0);
|
||||||
|
expect(
|
||||||
|
inbound.allowedAtIndex['@alice:example.com']
|
||||||
|
['wMIDhiQl5jEXQrTB03ePOSQfR8sA/KMrW0CIfFfXKEE'],
|
||||||
|
0);
|
||||||
|
|
||||||
// rotate after too many messages
|
// rotate after too many messages
|
||||||
sess.sentMessages = 300;
|
sess.sentMessages = 300;
|
||||||
|
|
@ -208,9 +215,18 @@ void main() {
|
||||||
true);
|
true);
|
||||||
inbound = client.encryption.keyManager.getInboundGroupSession(
|
inbound = client.encryption.keyManager.getInboundGroupSession(
|
||||||
roomId, sess.outboundGroupSession.session_id(), client.identityKey);
|
roomId, sess.outboundGroupSession.session_id(), client.identityKey);
|
||||||
expect(inbound.allowedAtIndex['@alice:example.com']['JLAFKJWSCS'], 0);
|
expect(
|
||||||
expect(inbound.allowedAtIndex['@alice:example.com']['OTHERDEVICE'], 0);
|
inbound.allowedAtIndex['@alice:example.com']
|
||||||
expect(inbound.allowedAtIndex['@alice:example.com']['NEWDEVICE'], 1);
|
['L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8'],
|
||||||
|
0);
|
||||||
|
expect(
|
||||||
|
inbound.allowedAtIndex['@alice:example.com']
|
||||||
|
['wMIDhiQl5jEXQrTB03ePOSQfR8sA/KMrW0CIfFfXKEE'],
|
||||||
|
0);
|
||||||
|
expect(
|
||||||
|
inbound.allowedAtIndex['@alice:example.com']
|
||||||
|
['bnKQp6pPW0l9cGoIgHpBoK5OUi4h0gylJ7upc4asFV8'],
|
||||||
|
1);
|
||||||
|
|
||||||
// do not rotate if new user is added
|
// do not rotate if new user is added
|
||||||
member.content['membership'] = 'leave';
|
member.content['membership'] = 'leave';
|
||||||
|
|
@ -500,9 +516,56 @@ void main() {
|
||||||
session.free();
|
session.free();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Reused deviceID attack', () async {
|
||||||
|
if (!olmEnabled) return;
|
||||||
|
Logs().level = Level.warning;
|
||||||
|
client ??= await getClient();
|
||||||
|
|
||||||
|
// Ensure the device came from sync
|
||||||
|
expect(
|
||||||
|
client.userDeviceKeys['@alice:example.com']
|
||||||
|
.deviceKeys['JLAFKJWSCS'] !=
|
||||||
|
null,
|
||||||
|
true);
|
||||||
|
|
||||||
|
// Alice removes her device
|
||||||
|
client.userDeviceKeys['@alice:example.com'].deviceKeys
|
||||||
|
.remove('JLAFKJWSCS');
|
||||||
|
|
||||||
|
// Alice adds her device with same device ID but different keys
|
||||||
|
final oldResp = FakeMatrixApi.api['POST']['/client/r0/keys/query'](null);
|
||||||
|
FakeMatrixApi.api['POST']['/client/r0/keys/query'] = (_) {
|
||||||
|
oldResp['device_keys']['@alice:example.com']['JLAFKJWSCS'] = {
|
||||||
|
'user_id': '@alice:example.com',
|
||||||
|
'device_id': 'JLAFKJWSCS',
|
||||||
|
'algorithms': [
|
||||||
|
'm.olm.v1.curve25519-aes-sha2',
|
||||||
|
'm.megolm.v1.aes-sha2'
|
||||||
|
],
|
||||||
|
'keys': {
|
||||||
|
'curve25519:JLAFKJWSCS':
|
||||||
|
'WbwrNyD7nvtmcLQ0TTuVPFGJq6JznfjrVsjIpmBqvDw',
|
||||||
|
'ed25519:JLAFKJWSCS': 'vl0d54pTVRcvBgUzoQFa8e6TldHWG9O8bh0iuIvgd/I'
|
||||||
|
},
|
||||||
|
'signatures': {
|
||||||
|
'@alice:example.com': {
|
||||||
|
'ed25519:JLAFKJWSCS':
|
||||||
|
's/L86jLa8BTroL8GsBeqO0gRLC3ZrSA7Gch6UoLI2SefC1+1ycmnP9UGbLPh3qBJOmlhczMpBLZwelg87qNNDA'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return oldResp;
|
||||||
|
};
|
||||||
|
client.userDeviceKeys['@alice:example.com'].outdated = true;
|
||||||
|
await client.updateUserDeviceKeys();
|
||||||
|
expect(
|
||||||
|
client.userDeviceKeys['@alice:example.com'].deviceKeys['JLAFKJWSCS'],
|
||||||
|
null);
|
||||||
|
});
|
||||||
|
|
||||||
test('dispose client', () async {
|
test('dispose client', () async {
|
||||||
if (!olmEnabled) return;
|
if (!olmEnabled) return;
|
||||||
await client.dispose(closeDatabase: true);
|
await client.dispose(closeDatabase: false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import 'package:test/test.dart';
|
||||||
import 'package:olm/olm.dart' as olm;
|
import 'package:olm/olm.dart' as olm;
|
||||||
|
|
||||||
import '../fake_client.dart';
|
import '../fake_client.dart';
|
||||||
|
import '../fake_database.dart';
|
||||||
import '../fake_matrix_api.dart';
|
import '../fake_matrix_api.dart';
|
||||||
|
|
||||||
class MockSSSS extends SSSS {
|
class MockSSSS extends SSSS {
|
||||||
|
|
@ -84,9 +85,11 @@ void main() {
|
||||||
if (!olmEnabled) return;
|
if (!olmEnabled) return;
|
||||||
|
|
||||||
client1 = await getClient();
|
client1 = await getClient();
|
||||||
client2 = Client('othertestclient',
|
client2 = Client(
|
||||||
|
'othertestclient',
|
||||||
httpClient: FakeMatrixApi(),
|
httpClient: FakeMatrixApi(),
|
||||||
databaseBuilder: (_) => client1.database);
|
databaseBuilder: getDatabase,
|
||||||
|
);
|
||||||
await client2.checkHomeserver('https://fakeserver.notexisting',
|
await client2.checkHomeserver('https://fakeserver.notexisting',
|
||||||
checkWellKnown: false);
|
checkWellKnown: false);
|
||||||
await client2.init(
|
await client2.init(
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ void main() {
|
||||||
|
|
||||||
test('dispose client', () async {
|
test('dispose client', () async {
|
||||||
if (!olmEnabled) return;
|
if (!olmEnabled) return;
|
||||||
await client.dispose(closeDatabase: true);
|
await client.dispose(closeDatabase: false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue