Merge branch 'soru/fix-lazy-session-keys' into 'master'
lazy-load group session keys See merge request famedly/famedlysdk!293
This commit is contained in:
commit
81b9d79518
|
@ -1315,7 +1315,6 @@ class Client {
|
||||||
roomAccountData: {},
|
roomAccountData: {},
|
||||||
client: this,
|
client: this,
|
||||||
);
|
);
|
||||||
newRoom.restoreGroupSessionKeys();
|
|
||||||
rooms.insert(position, newRoom);
|
rooms.insert(position, newRoom);
|
||||||
}
|
}
|
||||||
// If the membership is "leave" then remove the item and stop here
|
// If the membership is "leave" then remove the item and stop here
|
||||||
|
@ -1450,6 +1449,7 @@ class Client {
|
||||||
.containsKey(toDeviceEvent.content['requesting_device_id'])) {
|
.containsKey(toDeviceEvent.content['requesting_device_id'])) {
|
||||||
deviceKeys = userDeviceKeys[toDeviceEvent.sender]
|
deviceKeys = userDeviceKeys[toDeviceEvent.sender]
|
||||||
.deviceKeys[toDeviceEvent.content['requesting_device_id']];
|
.deviceKeys[toDeviceEvent.content['requesting_device_id']];
|
||||||
|
await room.loadInboundGroupSessionKey(sessionId);
|
||||||
if (room.inboundGroupSessions.containsKey(sessionId)) {
|
if (room.inboundGroupSessions.containsKey(sessionId)) {
|
||||||
final roomKeyRequest =
|
final roomKeyRequest =
|
||||||
RoomKeyRequest.fromToDeviceEvent(toDeviceEvent, this);
|
RoomKeyRequest.fromToDeviceEvent(toDeviceEvent, this);
|
||||||
|
@ -1600,11 +1600,6 @@ class Client {
|
||||||
await f();
|
await f();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
rooms.forEach((Room room) {
|
|
||||||
if (room.encrypted) {
|
|
||||||
room.clearOutboundGroupSession();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('[LibOlm] Unable to update user device keys: ' + e.toString());
|
print('[LibOlm] Unable to update user device keys: ' + e.toString());
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,24 +92,27 @@ class Database extends _$Database {
|
||||||
return await dbGetInboundGroupSessionKeys(clientId, roomId).get();
|
return await dbGetInboundGroupSessionKeys(clientId, roomId).get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<DbInboundGroupSession> getDbInboundGroupSession(int clientId, String roomId, String sessionId) async {
|
||||||
|
final res = await dbGetInboundGroupSessionKey(clientId, roomId, sessionId).get();
|
||||||
|
if (res.isEmpty) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return res.first;
|
||||||
|
}
|
||||||
|
|
||||||
Future<List<sdk.Room>> getRoomList(sdk.Client client, {bool onlyLeft = false}) async {
|
Future<List<sdk.Room>> getRoomList(sdk.Client client, {bool onlyLeft = false}) async {
|
||||||
final res = await (select(rooms)..where((t) => onlyLeft
|
final res = await (select(rooms)..where((t) => onlyLeft
|
||||||
? t.membership.equals('leave')
|
? t.membership.equals('leave')
|
||||||
: t.membership.equals('leave').not())).get();
|
: t.membership.equals('leave').not())).get();
|
||||||
final resStates = await getAllRoomStates(client.id).get();
|
final resStates = await getAllRoomStates(client.id).get();
|
||||||
final resAccountData = await getAllRoomAccountData(client.id).get();
|
final resAccountData = await getAllRoomAccountData(client.id).get();
|
||||||
final resOutboundGroupSessions = await getAllOutboundGroupSessions(client.id).get();
|
|
||||||
final resInboundGroupSessions = await getAllInboundGroupSessions(client.id).get();
|
|
||||||
final roomList = <sdk.Room>[];
|
final roomList = <sdk.Room>[];
|
||||||
for (final r in res) {
|
for (final r in res) {
|
||||||
final outboundGroupSession = resOutboundGroupSessions.where((rs) => rs.roomId == r.roomId);
|
|
||||||
final room = await sdk.Room.getRoomFromTableRow(
|
final room = await sdk.Room.getRoomFromTableRow(
|
||||||
r,
|
r,
|
||||||
client,
|
client,
|
||||||
states: resStates.where((rs) => rs.roomId == r.roomId),
|
states: resStates.where((rs) => rs.roomId == r.roomId),
|
||||||
roomAccountData: resAccountData.where((rs) => rs.roomId == r.roomId),
|
roomAccountData: resAccountData.where((rs) => rs.roomId == r.roomId),
|
||||||
outboundGroupSession: outboundGroupSession.isEmpty ? false : outboundGroupSession.first,
|
|
||||||
inboundGroupSessions: resInboundGroupSessions.where((rs) => rs.roomId == r.roomId),
|
|
||||||
);
|
);
|
||||||
roomList.add(room);
|
roomList.add(room);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4831,6 +4831,20 @@ abstract class _$Database extends GeneratedDatabase {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Selectable<DbInboundGroupSession> dbGetInboundGroupSessionKey(
|
||||||
|
int client_id, String room_id, String session_id) {
|
||||||
|
return customSelect(
|
||||||
|
'SELECT * FROM inbound_group_sessions WHERE client_id = :client_id AND room_id = :room_id AND session_id = :session_id',
|
||||||
|
variables: [
|
||||||
|
Variable.withInt(client_id),
|
||||||
|
Variable.withString(room_id),
|
||||||
|
Variable.withString(session_id)
|
||||||
|
],
|
||||||
|
readsFrom: {
|
||||||
|
inboundGroupSessions
|
||||||
|
}).map(_rowToDbInboundGroupSession);
|
||||||
|
}
|
||||||
|
|
||||||
Selectable<DbInboundGroupSession> dbGetInboundGroupSessionKeys(
|
Selectable<DbInboundGroupSession> dbGetInboundGroupSessionKeys(
|
||||||
int client_id, String room_id) {
|
int client_id, String room_id) {
|
||||||
return customSelect(
|
return customSelect(
|
||||||
|
|
|
@ -158,6 +158,7 @@ getAllOutboundGroupSessions: SELECT * FROM outbound_group_sessions WHERE client_
|
||||||
dbGetOutboundGroupSession: SELECT * FROM outbound_group_sessions WHERE client_id = :client_id AND room_id = :room_id;
|
dbGetOutboundGroupSession: SELECT * FROM outbound_group_sessions WHERE client_id = :client_id AND room_id = :room_id;
|
||||||
storeOutboundGroupSession: INSERT OR REPLACE INTO outbound_group_sessions (client_id, room_id, pickle, device_ids) VALUES (:client_id, :room_id, :pickle, :device_ids);
|
storeOutboundGroupSession: INSERT OR REPLACE INTO outbound_group_sessions (client_id, room_id, pickle, device_ids) VALUES (:client_id, :room_id, :pickle, :device_ids);
|
||||||
removeOutboundGroupSession: DELETE FROM outbound_group_sessions WHERE client_id = :client_id AND room_id = :room_id;
|
removeOutboundGroupSession: DELETE FROM outbound_group_sessions WHERE client_id = :client_id AND room_id = :room_id;
|
||||||
|
dbGetInboundGroupSessionKey: SELECT * FROM inbound_group_sessions WHERE client_id = :client_id AND room_id = :room_id AND session_id = :session_id;
|
||||||
dbGetInboundGroupSessionKeys: SELECT * FROM inbound_group_sessions WHERE client_id = :client_id AND room_id = :room_id;
|
dbGetInboundGroupSessionKeys: SELECT * FROM inbound_group_sessions WHERE client_id = :client_id AND room_id = :room_id;
|
||||||
getAllInboundGroupSessions: SELECT * FROM inbound_group_sessions WHERE client_id = :client_id;
|
getAllInboundGroupSessions: SELECT * FROM inbound_group_sessions WHERE client_id = :client_id;
|
||||||
storeInboundGroupSession: INSERT OR REPLACE INTO inbound_group_sessions (client_id, room_id, session_id, pickle, content, indexes) VALUES (:client_id, :room_id, :session_id, :pickle, :content, :indexes);
|
storeInboundGroupSession: INSERT OR REPLACE INTO inbound_group_sessions (client_id, room_id, session_id, pickle, content, indexes) VALUES (:client_id, :room_id, :session_id, :pickle, :content, :indexes);
|
||||||
|
|
|
@ -417,6 +417,10 @@ class Event {
|
||||||
return await timeline.getEventById(replyEventId);
|
return await timeline.getEventById(replyEventId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> loadSession() {
|
||||||
|
return room.loadInboundGroupSessionKeyForEvent(this);
|
||||||
|
}
|
||||||
|
|
||||||
/// Trys to decrypt this event. Returns a m.bad.encrypted event
|
/// Trys to decrypt this event. Returns a m.bad.encrypted event
|
||||||
/// if it fails and does nothing if the event was not encrypted.
|
/// if it fails and does nothing if the event was not encrypted.
|
||||||
Event get decrypted => room.decryptGroupMessage(this);
|
Event get decrypted => room.decryptGroupMessage(this);
|
||||||
|
|
|
@ -201,7 +201,7 @@ class Room {
|
||||||
/// "session_key": "AgAAAADxKHa9uFxcXzwYoNueL5Xqi69IkD4sni8LlfJL7qNBEY..."
|
/// "session_key": "AgAAAADxKHa9uFxcXzwYoNueL5Xqi69IkD4sni8LlfJL7qNBEY..."
|
||||||
/// }
|
/// }
|
||||||
Map<String, SessionKey> get inboundGroupSessions => _inboundGroupSessions;
|
Map<String, SessionKey> get inboundGroupSessions => _inboundGroupSessions;
|
||||||
Map<String, SessionKey> _inboundGroupSessions = {};
|
final _inboundGroupSessions = <String, SessionKey>{};
|
||||||
|
|
||||||
/// Add a new session key to the [sessionKeys].
|
/// Add a new session key to the [sessionKeys].
|
||||||
void setInboundGroupSession(String sessionId, Map<String, dynamic> content,
|
void setInboundGroupSession(String sessionId, Map<String, dynamic> content,
|
||||||
|
@ -228,12 +228,10 @@ class Room {
|
||||||
indexes: {},
|
indexes: {},
|
||||||
key: client.userID,
|
key: client.userID,
|
||||||
);
|
);
|
||||||
if (_fullyRestored) {
|
client.database?.storeInboundGroupSession(client.id, id, sessionId,
|
||||||
client.database?.storeInboundGroupSession(client.id, id, sessionId,
|
inboundGroupSession.pickle(client.userID), json.encode(content),
|
||||||
inboundGroupSession.pickle(client.userID), json.encode(content),
|
json.encode({}),
|
||||||
json.encode({}),
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
_tryAgainDecryptLastMessage();
|
_tryAgainDecryptLastMessage();
|
||||||
onSessionKeyReceived.add(sessionId);
|
onSessionKeyReceived.add(sessionId);
|
||||||
}
|
}
|
||||||
|
@ -1040,51 +1038,6 @@ class Room {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> restoreGroupSessionKeys({
|
|
||||||
dynamic outboundGroupSession, // DbOutboundGroupSession, optionally as future
|
|
||||||
dynamic inboundGroupSessions, // DbSessionKey, as iterator and optionally as future
|
|
||||||
}) async {
|
|
||||||
// Restore the inbound and outbound session keys
|
|
||||||
if (client.encryptionEnabled && client.database != null) {
|
|
||||||
outboundGroupSession ??= client.database.getDbOutboundGroupSession(client.id, id);
|
|
||||||
inboundGroupSessions ??= client.database.getDbInboundGroupSessions(client.id, id);
|
|
||||||
if (outboundGroupSession is Future) {
|
|
||||||
outboundGroupSession = await outboundGroupSession;
|
|
||||||
}
|
|
||||||
if (inboundGroupSessions is Future) {
|
|
||||||
inboundGroupSessions = await inboundGroupSessions;
|
|
||||||
}
|
|
||||||
if (outboundGroupSession != false && outboundGroupSession != null) {
|
|
||||||
try {
|
|
||||||
_outboundGroupSession = olm.OutboundGroupSession();
|
|
||||||
_outboundGroupSession.unpickle(
|
|
||||||
client.userID, outboundGroupSession.pickle);
|
|
||||||
} catch (e) {
|
|
||||||
_outboundGroupSession = null;
|
|
||||||
print('[LibOlm] Unable to unpickle outboundGroupSession: ' +
|
|
||||||
e.toString());
|
|
||||||
}
|
|
||||||
_outboundGroupSessionDevices =
|
|
||||||
List<String>.from(json.decode(outboundGroupSession.deviceIds));
|
|
||||||
}
|
|
||||||
if (inboundGroupSessions?.isNotEmpty ?? false) {
|
|
||||||
_inboundGroupSessions ??= {};
|
|
||||||
for (final sessionKey in inboundGroupSessions) {
|
|
||||||
try {
|
|
||||||
_inboundGroupSessions[sessionKey.sessionId] = SessionKey.fromDb(sessionKey, client.userID);
|
|
||||||
} catch (e) {
|
|
||||||
print('[LibOlm] Could not unpickle inboundGroupSession: ' + e.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_tryAgainDecryptLastMessage();
|
|
||||||
_fullyRestored = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _fullyRestored = false;
|
|
||||||
|
|
||||||
/// Returns a Room from a json String which comes normally from the store. If the
|
/// Returns a Room from a json String which comes normally from the store. If the
|
||||||
/// state are also given, the method will await them.
|
/// state are also given, the method will await them.
|
||||||
static Future<Room> getRoomFromTableRow(
|
static Future<Room> getRoomFromTableRow(
|
||||||
|
@ -1093,8 +1046,6 @@ class Room {
|
||||||
{
|
{
|
||||||
dynamic states, // DbRoomState, as iterator and optionally as future
|
dynamic states, // DbRoomState, as iterator and optionally as future
|
||||||
dynamic roomAccountData, // DbRoomAccountData, as iterator and optionally as future
|
dynamic roomAccountData, // DbRoomAccountData, as iterator and optionally as future
|
||||||
dynamic outboundGroupSession, // DbOutboundGroupSession, optionally as future
|
|
||||||
dynamic inboundGroupSessions, // DbSessionKey, as iterator and optionally as future
|
|
||||||
}) async {
|
}) async {
|
||||||
final newRoom = Room(
|
final newRoom = Room(
|
||||||
id: row.roomId,
|
id: row.roomId,
|
||||||
|
@ -1141,12 +1092,6 @@ class Room {
|
||||||
}
|
}
|
||||||
newRoom.roomAccountData = newRoomAccountData;
|
newRoom.roomAccountData = newRoomAccountData;
|
||||||
|
|
||||||
// Restore the inbound and outbound session keys
|
|
||||||
await newRoom.restoreGroupSessionKeys(
|
|
||||||
outboundGroupSession: outboundGroupSession,
|
|
||||||
inboundGroupSessions: inboundGroupSessions,
|
|
||||||
);
|
|
||||||
|
|
||||||
return newRoom;
|
return newRoom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1167,6 +1112,7 @@ class Room {
|
||||||
for (var i = 0; i < events.length; i++) {
|
for (var i = 0; i < events.length; i++) {
|
||||||
if (events[i].type == EventTypes.Encrypted &&
|
if (events[i].type == EventTypes.Encrypted &&
|
||||||
events[i].content['body'] == DecryptError.UNKNOWN_SESSION) {
|
events[i].content['body'] == DecryptError.UNKNOWN_SESSION) {
|
||||||
|
await events[i].loadSession();
|
||||||
events[i] = events[i].decrypted;
|
events[i] = events[i].decrypted;
|
||||||
if (events[i].type != EventTypes.Encrypted) {
|
if (events[i].type != EventTypes.Encrypted) {
|
||||||
await client.database.storeEventUpdate(client.id,
|
await client.database.storeEventUpdate(client.id,
|
||||||
|
@ -1745,6 +1691,30 @@ class Room {
|
||||||
}
|
}
|
||||||
return deviceKeys;
|
return deviceKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _restoredOutboundGroupSession = false;
|
||||||
|
|
||||||
|
Future<void> restoreOutboundGroupSession() async {
|
||||||
|
if (_restoredOutboundGroupSession || client.database == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final outboundSession = await client.database.getDbOutboundGroupSession(client.id, id);
|
||||||
|
if (outboundSession != null) {
|
||||||
|
try {
|
||||||
|
_outboundGroupSession = olm.OutboundGroupSession();
|
||||||
|
_outboundGroupSession.unpickle(
|
||||||
|
client.userID, outboundSession.pickle);
|
||||||
|
_outboundGroupSessionDevices =
|
||||||
|
List<String>.from(json.decode(outboundSession.deviceIds));
|
||||||
|
} catch (e) {
|
||||||
|
_outboundGroupSession = null;
|
||||||
|
_outboundGroupSessionDevices = null;
|
||||||
|
print('[LibOlm] Unable to unpickle outboundGroupSession: ' +
|
||||||
|
e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_restoredOutboundGroupSession = true;
|
||||||
|
}
|
||||||
|
|
||||||
/// Encrypts the given json payload and creates a send-ready m.room.encrypted
|
/// Encrypts the given json payload and creates a send-ready m.room.encrypted
|
||||||
/// payload. This will create a new outgoingGroupSession if necessary.
|
/// payload. This will create a new outgoingGroupSession if necessary.
|
||||||
|
@ -1755,6 +1725,13 @@ class Room {
|
||||||
if (encryptionAlgorithm != 'm.megolm.v1.aes-sha2') {
|
if (encryptionAlgorithm != 'm.megolm.v1.aes-sha2') {
|
||||||
throw ('Unknown encryption algorithm');
|
throw ('Unknown encryption algorithm');
|
||||||
}
|
}
|
||||||
|
if (!_restoredOutboundGroupSession && client.database != null) {
|
||||||
|
// try to restore an outbound group session from the database
|
||||||
|
await restoreOutboundGroupSession();
|
||||||
|
}
|
||||||
|
// and clear the outbound session, if it needs clearing
|
||||||
|
await clearOutboundGroupSession();
|
||||||
|
// create a new one if none exists...
|
||||||
if (_outboundGroupSession == null) {
|
if (_outboundGroupSession == null) {
|
||||||
await createOutboundGroupSession();
|
await createOutboundGroupSession();
|
||||||
}
|
}
|
||||||
|
@ -1776,6 +1753,30 @@ class Room {
|
||||||
return encryptedPayload;
|
return encryptedPayload;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> loadInboundGroupSessionKey(String sessionId) async {
|
||||||
|
if (sessionId == null || inboundGroupSessions.containsKey(sessionId)) return; // nothing to do
|
||||||
|
final session = await client.database.getDbInboundGroupSession(client.id, id, sessionId);
|
||||||
|
if (session == null) return; // no session found
|
||||||
|
try {
|
||||||
|
_inboundGroupSessions[sessionId] = SessionKey.fromDb(session, client.userID);
|
||||||
|
} catch (e) {
|
||||||
|
print('[LibOlm] Could not unpickle inboundGroupSession: ' + e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> loadInboundGroupSessionKeyForEvent(Event event) async {
|
||||||
|
if (client.database == null) return; // nothing to do, no database
|
||||||
|
if (event.type != EventTypes.Encrypted) return;
|
||||||
|
if (!client.encryptionEnabled) {
|
||||||
|
throw (DecryptError.NOT_ENABLED);
|
||||||
|
}
|
||||||
|
if (event.content['algorithm'] != 'm.megolm.v1.aes-sha2') {
|
||||||
|
throw (DecryptError.UNKNOWN_ALGORITHM);
|
||||||
|
}
|
||||||
|
final String sessionId = event.content['session_id'];
|
||||||
|
return loadInboundGroupSessionKey(sessionId);
|
||||||
|
}
|
||||||
|
|
||||||
/// Decrypts the given [event] with one of the available ingoingGroupSessions.
|
/// Decrypts the given [event] with one of the available ingoingGroupSessions.
|
||||||
/// Returns a m.bad.encrypted event if it fails and does nothing if the event
|
/// Returns a m.bad.encrypted event if it fails and does nothing if the event
|
||||||
/// was not encrypted.
|
/// was not encrypted.
|
||||||
|
|
|
@ -53,7 +53,7 @@ class EventUpdate {
|
||||||
var decrpytedEvent =
|
var decrpytedEvent =
|
||||||
room.decryptGroupMessage(Event.fromJson(content, room, sortOrder));
|
room.decryptGroupMessage(Event.fromJson(content, room, sortOrder));
|
||||||
return EventUpdate(
|
return EventUpdate(
|
||||||
eventType: eventType,
|
eventType: decrpytedEvent.typeKey,
|
||||||
roomID: roomID,
|
roomID: roomID,
|
||||||
type: type,
|
type: type,
|
||||||
content: decrpytedEvent.toJson(),
|
content: decrpytedEvent.toJson(),
|
||||||
|
|
|
@ -116,6 +116,19 @@ class Timeline {
|
||||||
try {
|
try {
|
||||||
if (eventUpdate.roomID != room.id) return;
|
if (eventUpdate.roomID != room.id) return;
|
||||||
|
|
||||||
|
// try to decrypt the event first, if needed
|
||||||
|
if (eventUpdate.eventType == 'm.room.encrypted' && room.client.database != null) {
|
||||||
|
try {
|
||||||
|
await room.loadInboundGroupSessionKey(eventUpdate.content['content']['session_id']);
|
||||||
|
eventUpdate = eventUpdate.decrypt(room);
|
||||||
|
if (eventUpdate.eventType != 'm.room.encrypted') {
|
||||||
|
await room.client.database.storeEventUpdate(room.client.id, eventUpdate);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
print('[WARNING] (_handleEventUpdate) Failed to decrypt event: ${err.toString()}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (eventUpdate.type == 'timeline' || eventUpdate.type == 'history') {
|
if (eventUpdate.type == 'timeline' || eventUpdate.type == 'history') {
|
||||||
// Redaction events are handled as modification for existing events.
|
// Redaction events are handled as modification for existing events.
|
||||||
if (eventUpdate.eventType == 'm.room.redaction') {
|
if (eventUpdate.eventType == 'm.room.redaction') {
|
||||||
|
|
|
@ -16,6 +16,7 @@ class RoomKeyRequest extends ToDeviceEvent {
|
||||||
|
|
||||||
Future<void> forwardKey() async {
|
Future<void> forwardKey() async {
|
||||||
var room = this.room;
|
var room = this.room;
|
||||||
|
await room.loadInboundGroupSessionKey(content['body']['session_id']);
|
||||||
final session = room.inboundGroupSessions[content['body']['session_id']];
|
final session = room.inboundGroupSessions[content['body']['session_id']];
|
||||||
var forwardedKeys = <dynamic>[client.identityKey];
|
var forwardedKeys = <dynamic>[client.identityKey];
|
||||||
for (final key in session.forwardingCurve25519KeyChain) {
|
for (final key in session.forwardingCurve25519KeyChain) {
|
||||||
|
|
|
@ -667,6 +667,7 @@ void main() {
|
||||||
expect(client2.deviceID, client1.deviceID);
|
expect(client2.deviceID, client1.deviceID);
|
||||||
expect(client2.deviceName, client1.deviceName);
|
expect(client2.deviceName, client1.deviceName);
|
||||||
if (client2.encryptionEnabled) {
|
if (client2.encryptionEnabled) {
|
||||||
|
await client2.rooms[1].restoreOutboundGroupSession();
|
||||||
expect(client2.pickledOlmAccount, client1.pickledOlmAccount);
|
expect(client2.pickledOlmAccount, client1.pickledOlmAccount);
|
||||||
expect(json.encode(client2.rooms[1].inboundGroupSessions[sessionKey]),
|
expect(json.encode(client2.rooms[1].inboundGroupSessions[sessionKey]),
|
||||||
json.encode(client1.rooms[1].inboundGroupSessions[sessionKey]));
|
json.encode(client1.rooms[1].inboundGroupSessions[sessionKey]));
|
||||||
|
|
Loading…
Reference in a new issue