fix: Properly rotate megolm session on device changes
This commit is contained in:
parent
9f4257c5fd
commit
412da6ae0c
|
@ -159,17 +159,7 @@ class Encryption {
|
||||||
var haveIndex = inboundGroupSession.indexes.containsKey(messageIndexKey);
|
var haveIndex = inboundGroupSession.indexes.containsKey(messageIndexKey);
|
||||||
if (haveIndex &&
|
if (haveIndex &&
|
||||||
inboundGroupSession.indexes[messageIndexKey] != messageIndexValue) {
|
inboundGroupSession.indexes[messageIndexKey] != messageIndexValue) {
|
||||||
// TODO: maybe clear outbound session, if it is ours
|
|
||||||
// TODO: Make it so that we can't re-request the session keys, this is just for debugging
|
|
||||||
Logs.error('[Decrypt] Could not decrypt due to a corrupted session.');
|
Logs.error('[Decrypt] Could not decrypt due to a corrupted session.');
|
||||||
Logs.error('[Decrypt] Want session: $roomId $sessionId $senderKey');
|
|
||||||
Logs.error(
|
|
||||||
'[Decrypt] Have sessoin: ${inboundGroupSession.roomId} ${inboundGroupSession.sessionId} ${inboundGroupSession.senderKey}');
|
|
||||||
Logs.error(
|
|
||||||
'[Decrypt] Want indexes: $messageIndexKey $messageIndexValue');
|
|
||||||
Logs.error(
|
|
||||||
'[Decrypt] Have indexes: $messageIndexKey ${inboundGroupSession.indexes[messageIndexKey]}');
|
|
||||||
canRequestSession = true;
|
|
||||||
throw (DecryptError.CHANNEL_CORRUPTED);
|
throw (DecryptError.CHANNEL_CORRUPTED);
|
||||||
}
|
}
|
||||||
inboundGroupSession.indexes[messageIndexKey] = messageIndexValue;
|
inboundGroupSession.indexes[messageIndexKey] = messageIndexValue;
|
||||||
|
@ -188,13 +178,15 @@ class Encryption {
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
// alright, if this was actually by our own outbound group session, we might as well clear it
|
// alright, if this was actually by our own outbound group session, we might as well clear it
|
||||||
if (client.enableE2eeRecovery &&
|
if (client.enableE2eeRecovery &&
|
||||||
|
exception != DecryptError.UNKNOWN_SESSION &&
|
||||||
(keyManager
|
(keyManager
|
||||||
.getOutboundGroupSession(roomId)
|
.getOutboundGroupSession(roomId)
|
||||||
?.outboundGroupSession
|
?.outboundGroupSession
|
||||||
?.session_id() ??
|
?.session_id() ??
|
||||||
'') ==
|
'') ==
|
||||||
event.content['session_id']) {
|
event.content['session_id']) {
|
||||||
keyManager.clearOutboundGroupSession(roomId, wipe: true);
|
runInRoot(() =>
|
||||||
|
keyManager.clearOrUseOutboundGroupSession(roomId, wipe: true));
|
||||||
}
|
}
|
||||||
if (canRequestSession) {
|
if (canRequestSession) {
|
||||||
decryptedPayload = {
|
decryptedPayload = {
|
||||||
|
@ -237,7 +229,18 @@ class Encryption {
|
||||||
Future<Event> decryptRoomEvent(String roomId, Event event,
|
Future<Event> decryptRoomEvent(String roomId, Event event,
|
||||||
{bool store = false,
|
{bool store = false,
|
||||||
EventUpdateType updateType = EventUpdateType.timeline}) async {
|
EventUpdateType updateType = EventUpdateType.timeline}) async {
|
||||||
final doStore = () async {
|
if (event.type != EventTypes.Encrypted) {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
if (client.database != null &&
|
||||||
|
keyManager.getInboundGroupSession(roomId, event.content['session_id'],
|
||||||
|
event.content['sender_key']) ==
|
||||||
|
null) {
|
||||||
|
await keyManager.loadInboundGroupSession(
|
||||||
|
roomId, event.content['session_id'], event.content['sender_key']);
|
||||||
|
}
|
||||||
|
event = decryptRoomEventSync(roomId, event);
|
||||||
|
if (event.type != EventTypes.Encrypted && store) {
|
||||||
if (updateType != EventUpdateType.history) {
|
if (updateType != EventUpdateType.history) {
|
||||||
event.room?.setState(event);
|
event.room?.setState(event);
|
||||||
}
|
}
|
||||||
|
@ -251,25 +254,6 @@ class Encryption {
|
||||||
sortOrder: event.sortOrder,
|
sortOrder: event.sortOrder,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
};
|
|
||||||
if (event.type != EventTypes.Encrypted) {
|
|
||||||
return event;
|
|
||||||
}
|
|
||||||
event = decryptRoomEventSync(roomId, event);
|
|
||||||
if (event.type != EventTypes.Encrypted) {
|
|
||||||
if (store) {
|
|
||||||
await doStore();
|
|
||||||
}
|
|
||||||
return event;
|
|
||||||
}
|
|
||||||
if (client.database == null) {
|
|
||||||
return event;
|
|
||||||
}
|
|
||||||
await keyManager.loadInboundGroupSession(
|
|
||||||
roomId, event.content['session_id'], event.content['sender_key']);
|
|
||||||
event = decryptRoomEventSync(roomId, event);
|
|
||||||
if (event.type != EventTypes.Encrypted && store) {
|
|
||||||
await doStore();
|
|
||||||
}
|
}
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
@ -289,7 +273,7 @@ class Encryption {
|
||||||
if (keyManager.getOutboundGroupSession(roomId) == null) {
|
if (keyManager.getOutboundGroupSession(roomId) == null) {
|
||||||
await keyManager.loadOutboundGroupSession(roomId);
|
await keyManager.loadOutboundGroupSession(roomId);
|
||||||
}
|
}
|
||||||
await keyManager.clearOutboundGroupSession(roomId);
|
await keyManager.clearOrUseOutboundGroupSession(roomId);
|
||||||
if (keyManager.getOutboundGroupSession(roomId) == null) {
|
if (keyManager.getOutboundGroupSession(roomId) == null) {
|
||||||
await keyManager.createOutboundGroupSession(roomId);
|
await keyManager.createOutboundGroupSession(roomId);
|
||||||
}
|
}
|
||||||
|
@ -315,7 +299,6 @@ class Encryption {
|
||||||
'session_id': sess.outboundGroupSession.session_id(),
|
'session_id': sess.outboundGroupSession.session_id(),
|
||||||
if (mRelatesTo != null) 'm.relates_to': mRelatesTo,
|
if (mRelatesTo != null) 'm.relates_to': mRelatesTo,
|
||||||
};
|
};
|
||||||
sess.sentMessages++;
|
|
||||||
await keyManager.storeOutboundGroupSession(roomId, sess);
|
await keyManager.storeOutboundGroupSession(roomId, sess);
|
||||||
return encryptedPayload;
|
return encryptedPayload;
|
||||||
}
|
}
|
||||||
|
|
|
@ -231,6 +231,18 @@ class KeyManager {
|
||||||
return sess;
|
return sess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<String, Map<String, bool>> _getDeviceKeyIdMap(
|
||||||
|
List<DeviceKeys> deviceKeys) {
|
||||||
|
final deviceKeyIds = <String, Map<String, bool>>{};
|
||||||
|
for (final device in deviceKeys) {
|
||||||
|
if (!deviceKeyIds.containsKey(device.userId)) {
|
||||||
|
deviceKeyIds[device.userId] = <String, bool>{};
|
||||||
|
}
|
||||||
|
deviceKeyIds[device.userId][device.deviceId] = device.blocked;
|
||||||
|
}
|
||||||
|
return deviceKeyIds;
|
||||||
|
}
|
||||||
|
|
||||||
/// clear all cached inbound group sessions. useful for testing
|
/// clear all cached inbound group sessions. useful for testing
|
||||||
void clearOutboundGroupSessions() {
|
void clearOutboundGroupSessions() {
|
||||||
_outboundGroupSessions.clear();
|
_outboundGroupSessions.clear();
|
||||||
|
@ -238,8 +250,8 @@ class KeyManager {
|
||||||
|
|
||||||
/// Clears the existing outboundGroupSession but first checks if the participating
|
/// Clears the existing outboundGroupSession but first checks if the participating
|
||||||
/// devices have been changed. Returns false if the session has not been cleared because
|
/// devices have been changed. Returns false if the session has not been cleared because
|
||||||
/// it wasn't necessary.
|
/// it wasn't necessary. Otherwise returns true.
|
||||||
Future<bool> clearOutboundGroupSession(String roomId,
|
Future<bool> clearOrUseOutboundGroupSession(String roomId,
|
||||||
{bool wipe = false}) async {
|
{bool wipe = false}) async {
|
||||||
final room = client.getRoomById(roomId);
|
final room = client.getRoomById(roomId);
|
||||||
final sess = getOutboundGroupSession(roomId);
|
final sess = getOutboundGroupSession(roomId);
|
||||||
|
@ -247,15 +259,7 @@ class KeyManager {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!wipe) {
|
if (!wipe) {
|
||||||
// first check if the devices in the room changed
|
// first check if it needs to be rotated
|
||||||
final deviceKeys = await room.getUserDeviceKeys();
|
|
||||||
deviceKeys.removeWhere((k) => k.blocked);
|
|
||||||
final deviceKeyIds = deviceKeys.map((k) => k.deviceId).toList();
|
|
||||||
deviceKeyIds.sort();
|
|
||||||
if (deviceKeyIds.toString() != sess.devices.toString()) {
|
|
||||||
wipe = true;
|
|
||||||
}
|
|
||||||
// next check if it needs to be rotated
|
|
||||||
final encryptionContent = room.getState(EventTypes.Encryption)?.content;
|
final encryptionContent = room.getState(EventTypes.Encryption)?.content;
|
||||||
final maxMessages = encryptionContent != null &&
|
final maxMessages = encryptionContent != null &&
|
||||||
encryptionContent['rotation_period_msgs'] is int
|
encryptionContent['rotation_period_msgs'] is int
|
||||||
|
@ -271,7 +275,76 @@ class KeyManager {
|
||||||
.isBefore(DateTime.now())) {
|
.isBefore(DateTime.now())) {
|
||||||
wipe = true;
|
wipe = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (!wipe) {
|
if (!wipe) {
|
||||||
|
// next check if the devices in the room changed
|
||||||
|
final devicesToReceive = <DeviceKeys>[];
|
||||||
|
final newDeviceKeys = await room.getUserDeviceKeys();
|
||||||
|
final newDeviceKeyIds = _getDeviceKeyIdMap(newDeviceKeys);
|
||||||
|
// first check for user differences
|
||||||
|
final oldUserIds = Set.from(sess.devices.keys);
|
||||||
|
final newUserIds = Set.from(newDeviceKeyIds.keys);
|
||||||
|
if (oldUserIds.difference(newUserIds).isNotEmpty) {
|
||||||
|
// a user left the room, we must wipe the session
|
||||||
|
wipe = true;
|
||||||
|
} else {
|
||||||
|
final newUsers = newUserIds.difference(oldUserIds);
|
||||||
|
if (newUsers.isNotEmpty) {
|
||||||
|
// new user! Gotta send the megolm session to them
|
||||||
|
devicesToReceive
|
||||||
|
.addAll(newDeviceKeys.where((d) => newUsers.contains(d.userId)));
|
||||||
|
}
|
||||||
|
// okay, now we must test all the individual user devices, if anything new got blocked
|
||||||
|
// or if we need to send to any new devices.
|
||||||
|
// for this it is enough if we iterate over the old user Ids, as the new ones already have the needed keys in the list.
|
||||||
|
// we also know that all the old user IDs appear in the old one, else we have already wiped the session
|
||||||
|
for (final userId in oldUserIds) {
|
||||||
|
final oldBlockedDevices = Set.from(sess.devices[userId].entries
|
||||||
|
.where((e) => e.value)
|
||||||
|
.map((e) => e.key));
|
||||||
|
final newBlockedDevices = Set.from(newDeviceKeyIds[userId]
|
||||||
|
.entries
|
||||||
|
.where((e) => e.value)
|
||||||
|
.map((e) => e.key));
|
||||||
|
// we don't really care about old devices that got dropped (deleted), we only care if new ones got added and if new ones got blocked
|
||||||
|
// check if new devices got blocked
|
||||||
|
if (newBlockedDevices.difference(oldBlockedDevices).isNotEmpty) {
|
||||||
|
wipe = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// and now add all the new devices!
|
||||||
|
final oldDeviceIds = Set.from(sess.devices[userId].keys);
|
||||||
|
final newDeviceIds = Set.from(newDeviceKeyIds[userId].keys);
|
||||||
|
final newDevices = newDeviceIds.difference(oldDeviceIds);
|
||||||
|
if (newDeviceIds.isNotEmpty) {
|
||||||
|
devicesToReceive.addAll(newDeviceKeys.where(
|
||||||
|
(d) => d.userId == userId && newDevices.contains(d.deviceId)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wipe) {
|
||||||
|
// okay, we use the outbound group session!
|
||||||
|
sess.sentMessages++;
|
||||||
|
sess.devices = newDeviceKeyIds;
|
||||||
|
final rawSession = <String, dynamic>{
|
||||||
|
'algorithm': 'm.megolm.v1.aes-sha2',
|
||||||
|
'room_id': room.id,
|
||||||
|
'session_id': sess.outboundGroupSession.session_id(),
|
||||||
|
'session_key': sess.outboundGroupSession.session_key(),
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
devicesToReceive.removeWhere((k) => k.blocked);
|
||||||
|
if (devicesToReceive.isNotEmpty) {
|
||||||
|
await client.sendToDeviceEncrypted(
|
||||||
|
devicesToReceive, 'm.room_key', rawSession);
|
||||||
|
}
|
||||||
|
} catch (e, s) {
|
||||||
|
Logs.error(
|
||||||
|
'[LibOlm] Unable to re-send the session key at later index to new devices: ' +
|
||||||
|
e.toString(),
|
||||||
|
s);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -296,15 +369,13 @@ class KeyManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<OutboundGroupSession> createOutboundGroupSession(String roomId) async {
|
Future<OutboundGroupSession> createOutboundGroupSession(String roomId) async {
|
||||||
await clearOutboundGroupSession(roomId, wipe: true);
|
await clearOrUseOutboundGroupSession(roomId, wipe: true);
|
||||||
final room = client.getRoomById(roomId);
|
final room = client.getRoomById(roomId);
|
||||||
if (room == null) {
|
if (room == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final deviceKeys = await room.getUserDeviceKeys();
|
final deviceKeys = await room.getUserDeviceKeys();
|
||||||
deviceKeys.removeWhere((k) => k.blocked);
|
final deviceKeyIds = _getDeviceKeyIdMap(deviceKeys);
|
||||||
final deviceKeyIds = deviceKeys.map((k) => k.deviceId).toList();
|
|
||||||
deviceKeyIds.sort();
|
|
||||||
final outboundGroupSession = olm.OutboundGroupSession();
|
final outboundGroupSession = olm.OutboundGroupSession();
|
||||||
try {
|
try {
|
||||||
outboundGroupSession.create();
|
outboundGroupSession.create();
|
||||||
|
@ -331,6 +402,7 @@ class KeyManager {
|
||||||
key: client.userID,
|
key: client.userID,
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
|
deviceKeys.removeWhere((k) => k.blocked);
|
||||||
await client.sendToDeviceEncrypted(deviceKeys, 'm.room_key', rawSession);
|
await client.sendToDeviceEncrypted(deviceKeys, 'm.room_key', rawSession);
|
||||||
await storeOutboundGroupSession(roomId, sess);
|
await storeOutboundGroupSession(roomId, sess);
|
||||||
_outboundGroupSessions[roomId] = sess;
|
_outboundGroupSessions[roomId] = sess;
|
||||||
|
|
|
@ -24,7 +24,11 @@ import '../../src/database/database.dart' show DbOutboundGroupSession;
|
||||||
import '../../src/utils/logs.dart';
|
import '../../src/utils/logs.dart';
|
||||||
|
|
||||||
class OutboundGroupSession {
|
class OutboundGroupSession {
|
||||||
List<String> devices;
|
/// The devices is a map from user id to device id to if the device is blocked.
|
||||||
|
/// This way we can easily know if a new user is added, leaves, a new devices is added, and,
|
||||||
|
/// very importantly, if we block a device. These are all important for determining if/when
|
||||||
|
/// an outbound session needs to be rotated.
|
||||||
|
Map<String, Map<String, bool>> devices;
|
||||||
DateTime creationTime;
|
DateTime creationTime;
|
||||||
olm.OutboundGroupSession outboundGroupSession;
|
olm.OutboundGroupSession outboundGroupSession;
|
||||||
int sentMessages;
|
int sentMessages;
|
||||||
|
@ -40,10 +44,21 @@ class OutboundGroupSession {
|
||||||
|
|
||||||
OutboundGroupSession.fromDb(DbOutboundGroupSession dbEntry, String key)
|
OutboundGroupSession.fromDb(DbOutboundGroupSession dbEntry, String key)
|
||||||
: key = key {
|
: key = key {
|
||||||
|
try {
|
||||||
|
devices = {};
|
||||||
|
for (final entry in json.decode(dbEntry.deviceIds).entries) {
|
||||||
|
devices[entry.key] = Map<String, bool>.from(entry.value);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// devices is bad (old data), so just not use this session
|
||||||
|
Logs.info(
|
||||||
|
'[OutboundGroupSession] Session in database is old, not using it. ' +
|
||||||
|
e.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
outboundGroupSession = olm.OutboundGroupSession();
|
outboundGroupSession = olm.OutboundGroupSession();
|
||||||
try {
|
try {
|
||||||
outboundGroupSession.unpickle(key, dbEntry.pickle);
|
outboundGroupSession.unpickle(key, dbEntry.pickle);
|
||||||
devices = List<String>.from(json.decode(dbEntry.deviceIds));
|
|
||||||
creationTime = DateTime.fromMillisecondsSinceEpoch(dbEntry.creationTime);
|
creationTime = DateTime.fromMillisecondsSinceEpoch(dbEntry.creationTime);
|
||||||
sentMessages = dbEntry.sentMessages;
|
sentMessages = dbEntry.sentMessages;
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
|
|
|
@ -102,7 +102,7 @@ void main() {
|
||||||
expect(
|
expect(
|
||||||
client.encryption.keyManager.getOutboundGroupSession(roomId) != null,
|
client.encryption.keyManager.getOutboundGroupSession(roomId) != null,
|
||||||
true);
|
true);
|
||||||
await client.encryption.keyManager.clearOutboundGroupSession(roomId);
|
await client.encryption.keyManager.clearOrUseOutboundGroupSession(roomId);
|
||||||
expect(
|
expect(
|
||||||
client.encryption.keyManager.getOutboundGroupSession(roomId) != null,
|
client.encryption.keyManager.getOutboundGroupSession(roomId) != null,
|
||||||
true);
|
true);
|
||||||
|
@ -114,17 +114,17 @@ void main() {
|
||||||
|
|
||||||
// rotate after too many messages
|
// rotate after too many messages
|
||||||
sess.sentMessages = 300;
|
sess.sentMessages = 300;
|
||||||
await client.encryption.keyManager.clearOutboundGroupSession(roomId);
|
await client.encryption.keyManager.clearOrUseOutboundGroupSession(roomId);
|
||||||
expect(
|
expect(
|
||||||
client.encryption.keyManager.getOutboundGroupSession(roomId) != null,
|
client.encryption.keyManager.getOutboundGroupSession(roomId) != null,
|
||||||
false);
|
false);
|
||||||
|
|
||||||
// rotate if devices in room change
|
// rotate if device is blocked
|
||||||
sess =
|
sess =
|
||||||
await client.encryption.keyManager.createOutboundGroupSession(roomId);
|
await client.encryption.keyManager.createOutboundGroupSession(roomId);
|
||||||
client.userDeviceKeys['@alice:example.com'].deviceKeys['JLAFKJWSCS']
|
client.userDeviceKeys['@alice:example.com'].deviceKeys['JLAFKJWSCS']
|
||||||
.blocked = true;
|
.blocked = true;
|
||||||
await client.encryption.keyManager.clearOutboundGroupSession(roomId);
|
await client.encryption.keyManager.clearOrUseOutboundGroupSession(roomId);
|
||||||
expect(
|
expect(
|
||||||
client.encryption.keyManager.getOutboundGroupSession(roomId) != null,
|
client.encryption.keyManager.getOutboundGroupSession(roomId) != null,
|
||||||
false);
|
false);
|
||||||
|
@ -135,16 +135,61 @@ void main() {
|
||||||
sess =
|
sess =
|
||||||
await client.encryption.keyManager.createOutboundGroupSession(roomId);
|
await client.encryption.keyManager.createOutboundGroupSession(roomId);
|
||||||
sess.creationTime = DateTime.now().subtract(Duration(days: 30));
|
sess.creationTime = DateTime.now().subtract(Duration(days: 30));
|
||||||
await client.encryption.keyManager.clearOutboundGroupSession(roomId);
|
await client.encryption.keyManager.clearOrUseOutboundGroupSession(roomId);
|
||||||
expect(
|
expect(
|
||||||
client.encryption.keyManager.getOutboundGroupSession(roomId) != null,
|
client.encryption.keyManager.getOutboundGroupSession(roomId) != null,
|
||||||
false);
|
false);
|
||||||
|
|
||||||
|
// rotate if user leaves
|
||||||
|
sess =
|
||||||
|
await client.encryption.keyManager.createOutboundGroupSession(roomId);
|
||||||
|
final room = client.getRoomById(roomId);
|
||||||
|
final member = room.getState('m.room.member', '@alice:example.com');
|
||||||
|
member.content['membership'] = 'leave';
|
||||||
|
room.mJoinedMemberCount--;
|
||||||
|
await client.encryption.keyManager.clearOrUseOutboundGroupSession(roomId);
|
||||||
|
expect(
|
||||||
|
client.encryption.keyManager.getOutboundGroupSession(roomId) != null,
|
||||||
|
false);
|
||||||
|
member.content['membership'] = 'join';
|
||||||
|
room.mJoinedMemberCount++;
|
||||||
|
|
||||||
|
// do not rotate if new device is added
|
||||||
|
sess =
|
||||||
|
await client.encryption.keyManager.createOutboundGroupSession(roomId);
|
||||||
|
client.userDeviceKeys['@alice:example.com'].deviceKeys['NEWDEVICE'] =
|
||||||
|
DeviceKeys.fromJson({
|
||||||
|
'user_id': '@alice:example.com',
|
||||||
|
'device_id': 'NEWDEVICE',
|
||||||
|
'algorithms': ['m.olm.v1.curve25519-aes-sha2', 'm.megolm.v1.aes-sha2'],
|
||||||
|
'keys': {
|
||||||
|
'curve25519:JLAFKJWSCS':
|
||||||
|
'3C5BFWi2Y8MaVvjM8M22DBmh24PmgR0nPvJOIArzgyI',
|
||||||
|
'ed25519:JLAFKJWSCS': 'lEuiRJBit0IG6nUf5pUzWTUEsRVVe/HJkoKuEww9ULI'
|
||||||
|
},
|
||||||
|
}, client);
|
||||||
|
await client.encryption.keyManager.clearOrUseOutboundGroupSession(roomId);
|
||||||
|
expect(
|
||||||
|
client.encryption.keyManager.getOutboundGroupSession(roomId) != null,
|
||||||
|
true);
|
||||||
|
|
||||||
|
// do not rotate if new user is added
|
||||||
|
member.content['membership'] = 'leave';
|
||||||
|
room.mJoinedMemberCount--;
|
||||||
|
sess =
|
||||||
|
await client.encryption.keyManager.createOutboundGroupSession(roomId);
|
||||||
|
member.content['membership'] = 'join';
|
||||||
|
room.mJoinedMemberCount++;
|
||||||
|
await client.encryption.keyManager.clearOrUseOutboundGroupSession(roomId);
|
||||||
|
expect(
|
||||||
|
client.encryption.keyManager.getOutboundGroupSession(roomId) != null,
|
||||||
|
true);
|
||||||
|
|
||||||
// force wipe
|
// force wipe
|
||||||
sess =
|
sess =
|
||||||
await client.encryption.keyManager.createOutboundGroupSession(roomId);
|
await client.encryption.keyManager.createOutboundGroupSession(roomId);
|
||||||
await client.encryption.keyManager
|
await client.encryption.keyManager
|
||||||
.clearOutboundGroupSession(roomId, wipe: true);
|
.clearOrUseOutboundGroupSession(roomId, wipe: true);
|
||||||
expect(
|
expect(
|
||||||
client.encryption.keyManager.getOutboundGroupSession(roomId) != null,
|
client.encryption.keyManager.getOutboundGroupSession(roomId) != null,
|
||||||
false);
|
false);
|
||||||
|
|
Loading…
Reference in a new issue