Merge branch 'master' into soru/cross-signing
This commit is contained in:
commit
c65b5948fc
|
@ -38,7 +38,6 @@ export 'package:famedlysdk/src/utils/profile.dart';
|
||||||
export 'package:famedlysdk/src/utils/public_rooms_response.dart';
|
export 'package:famedlysdk/src/utils/public_rooms_response.dart';
|
||||||
export 'package:famedlysdk/src/utils/push_rules.dart';
|
export 'package:famedlysdk/src/utils/push_rules.dart';
|
||||||
export 'package:famedlysdk/src/utils/receipt.dart';
|
export 'package:famedlysdk/src/utils/receipt.dart';
|
||||||
export 'package:famedlysdk/src/utils/room_key_request.dart';
|
|
||||||
export 'package:famedlysdk/src/utils/states_map.dart';
|
export 'package:famedlysdk/src/utils/states_map.dart';
|
||||||
export 'package:famedlysdk/src/utils/to_device_event.dart';
|
export 'package:famedlysdk/src/utils/to_device_event.dart';
|
||||||
export 'package:famedlysdk/src/utils/turn_server_credentials.dart';
|
export 'package:famedlysdk/src/utils/turn_server_credentials.dart';
|
||||||
|
@ -47,6 +46,7 @@ export 'package:famedlysdk/src/utils/well_known_informations.dart';
|
||||||
export 'package:famedlysdk/src/account_data.dart';
|
export 'package:famedlysdk/src/account_data.dart';
|
||||||
export 'package:famedlysdk/src/client.dart';
|
export 'package:famedlysdk/src/client.dart';
|
||||||
export 'package:famedlysdk/src/event.dart';
|
export 'package:famedlysdk/src/event.dart';
|
||||||
|
export 'package:famedlysdk/src/key_manager.dart';
|
||||||
export 'package:famedlysdk/src/presence.dart';
|
export 'package:famedlysdk/src/presence.dart';
|
||||||
export 'package:famedlysdk/src/room.dart';
|
export 'package:famedlysdk/src/room.dart';
|
||||||
export 'package:famedlysdk/src/room_account_data.dart';
|
export 'package:famedlysdk/src/room_account_data.dart';
|
||||||
|
|
|
@ -35,7 +35,6 @@ import 'package:famedlysdk/src/utils/device_keys_list.dart';
|
||||||
import 'package:famedlysdk/src/utils/matrix_file.dart';
|
import 'package:famedlysdk/src/utils/matrix_file.dart';
|
||||||
import 'package:famedlysdk/src/utils/open_id_credentials.dart';
|
import 'package:famedlysdk/src/utils/open_id_credentials.dart';
|
||||||
import 'package:famedlysdk/src/utils/public_rooms_response.dart';
|
import 'package:famedlysdk/src/utils/public_rooms_response.dart';
|
||||||
import 'package:famedlysdk/src/utils/room_key_request.dart';
|
|
||||||
import 'package:famedlysdk/src/utils/session_key.dart';
|
import 'package:famedlysdk/src/utils/session_key.dart';
|
||||||
import 'package:famedlysdk/src/utils/to_device_event.dart';
|
import 'package:famedlysdk/src/utils/to_device_event.dart';
|
||||||
import 'package:famedlysdk/src/utils/turn_server_credentials.dart';
|
import 'package:famedlysdk/src/utils/turn_server_credentials.dart';
|
||||||
|
@ -59,6 +58,7 @@ import 'database/database.dart' show Database;
|
||||||
import 'utils/pusher.dart';
|
import 'utils/pusher.dart';
|
||||||
import 'utils/well_known_informations.dart';
|
import 'utils/well_known_informations.dart';
|
||||||
import 'utils/key_verification.dart';
|
import 'utils/key_verification.dart';
|
||||||
|
import 'key_manager.dart';
|
||||||
|
|
||||||
typedef RoomSorter = int Function(Room a, Room b);
|
typedef RoomSorter = int Function(Room a, Room b);
|
||||||
|
|
||||||
|
@ -83,6 +83,7 @@ class Client {
|
||||||
int get id => _id;
|
int get id => _id;
|
||||||
|
|
||||||
Database database;
|
Database database;
|
||||||
|
KeyManager keyManager;
|
||||||
|
|
||||||
bool enableE2eeRecovery;
|
bool enableE2eeRecovery;
|
||||||
|
|
||||||
|
@ -96,6 +97,7 @@ class Client {
|
||||||
/// enableE2eeRecovery: Enable additional logic to try to recover from bad e2ee sessions
|
/// enableE2eeRecovery: Enable additional logic to try to recover from bad e2ee sessions
|
||||||
Client(this.clientName,
|
Client(this.clientName,
|
||||||
{this.debug = false, this.database, this.enableE2eeRecovery = false}) {
|
{this.debug = false, this.database, this.enableE2eeRecovery = false}) {
|
||||||
|
keyManager = KeyManager(this);
|
||||||
ssss = SSSS(this);
|
ssss = SSSS(this);
|
||||||
crossSigning = CrossSigning(this);
|
crossSigning = CrossSigning(this);
|
||||||
onLoginStateChanged.stream.listen((loginState) {
|
onLoginStateChanged.stream.listen((loginState) {
|
||||||
|
@ -173,6 +175,12 @@ class Client {
|
||||||
|
|
||||||
int _timeoutFactor = 1;
|
int _timeoutFactor = 1;
|
||||||
|
|
||||||
|
int _transactionCounter = 0;
|
||||||
|
String generateUniqueTransactionId() {
|
||||||
|
_transactionCounter++;
|
||||||
|
return '${clientName}-${_transactionCounter}-${DateTime.now().millisecondsSinceEpoch}';
|
||||||
|
}
|
||||||
|
|
||||||
Room getRoomByAlias(String alias) {
|
Room getRoomByAlias(String alias) {
|
||||||
for (var i = 0; i < rooms.length; i++) {
|
for (var i = 0; i < rooms.length; i++) {
|
||||||
if (rooms[i].canonicalAlias == alias) return rooms[i];
|
if (rooms[i].canonicalAlias == alias) return rooms[i];
|
||||||
|
@ -834,6 +842,11 @@ class Client {
|
||||||
return _sync();
|
return _sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used for testing only
|
||||||
|
void setUserId(String s) {
|
||||||
|
_userID = s;
|
||||||
|
}
|
||||||
|
|
||||||
StreamSubscription _userEventSub;
|
StreamSubscription _userEventSub;
|
||||||
|
|
||||||
/// Resets all settings and stops the synchronisation.
|
/// Resets all settings and stops the synchronisation.
|
||||||
|
@ -1184,6 +1197,10 @@ class Client {
|
||||||
if (toDeviceEvent.type.startsWith('m.key.verification.')) {
|
if (toDeviceEvent.type.startsWith('m.key.verification.')) {
|
||||||
_handleToDeviceKeyVerificationRequest(toDeviceEvent);
|
_handleToDeviceKeyVerificationRequest(toDeviceEvent);
|
||||||
}
|
}
|
||||||
|
if (['m.room_key_request', 'm.forwarded_room_key']
|
||||||
|
.contains(toDeviceEvent.type)) {
|
||||||
|
keyManager.handleToDeviceEvent(toDeviceEvent);
|
||||||
|
}
|
||||||
if (toDeviceEvent.type.startsWith('m.secret.')) {
|
if (toDeviceEvent.type.startsWith('m.secret.')) {
|
||||||
ssss.handleToDeviceEvent(toDeviceEvent);
|
ssss.handleToDeviceEvent(toDeviceEvent);
|
||||||
}
|
}
|
||||||
|
@ -1601,7 +1618,6 @@ class Client {
|
||||||
try {
|
try {
|
||||||
switch (toDeviceEvent.type) {
|
switch (toDeviceEvent.type) {
|
||||||
case 'm.room_key':
|
case 'm.room_key':
|
||||||
case 'm.forwarded_room_key':
|
|
||||||
final roomId = toDeviceEvent.content['room_id'];
|
final roomId = toDeviceEvent.content['room_id'];
|
||||||
var room = getRoomById(roomId);
|
var room = getRoomById(roomId);
|
||||||
if (room == null && addToPendingIfNotFound) {
|
if (room == null && addToPendingIfNotFound) {
|
||||||
|
@ -1610,8 +1626,7 @@ class Client {
|
||||||
}
|
}
|
||||||
room ??= Room(client: this, id: roomId);
|
room ??= Room(client: this, id: roomId);
|
||||||
final String sessionId = toDeviceEvent.content['session_id'];
|
final String sessionId = toDeviceEvent.content['session_id'];
|
||||||
if (toDeviceEvent.type == 'm.room_key' &&
|
if (userDeviceKeys.containsKey(toDeviceEvent.sender) &&
|
||||||
userDeviceKeys.containsKey(toDeviceEvent.sender) &&
|
|
||||||
userDeviceKeys[toDeviceEvent.sender]
|
userDeviceKeys[toDeviceEvent.sender]
|
||||||
.deviceKeys
|
.deviceKeys
|
||||||
.containsKey(toDeviceEvent.content['requesting_device_id'])) {
|
.containsKey(toDeviceEvent.content['requesting_device_id'])) {
|
||||||
|
@ -1623,46 +1638,8 @@ class Client {
|
||||||
room.setInboundGroupSession(
|
room.setInboundGroupSession(
|
||||||
sessionId,
|
sessionId,
|
||||||
toDeviceEvent.content,
|
toDeviceEvent.content,
|
||||||
forwarded: toDeviceEvent.type == 'm.forwarded_room_key',
|
forwarded: false,
|
||||||
);
|
);
|
||||||
if (toDeviceEvent.type == 'm.forwarded_room_key') {
|
|
||||||
await sendToDevice(
|
|
||||||
[],
|
|
||||||
'm.room_key_request',
|
|
||||||
{
|
|
||||||
'action': 'request_cancellation',
|
|
||||||
'request_id': base64
|
|
||||||
.encode(utf8.encode(toDeviceEvent.content['room_id'])),
|
|
||||||
'requesting_device_id': room.client.deviceID,
|
|
||||||
},
|
|
||||||
encrypted: false,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'm.room_key_request':
|
|
||||||
if (!toDeviceEvent.content.containsKey('body')) break;
|
|
||||||
var room = getRoomById(toDeviceEvent.content['body']['room_id']);
|
|
||||||
DeviceKeys deviceKeys;
|
|
||||||
final String sessionId = toDeviceEvent.content['body']['session_id'];
|
|
||||||
if (userDeviceKeys.containsKey(toDeviceEvent.sender) &&
|
|
||||||
userDeviceKeys[toDeviceEvent.sender]
|
|
||||||
.deviceKeys
|
|
||||||
.containsKey(toDeviceEvent.content['requesting_device_id'])) {
|
|
||||||
deviceKeys = userDeviceKeys[toDeviceEvent.sender]
|
|
||||||
.deviceKeys[toDeviceEvent.content['requesting_device_id']];
|
|
||||||
await room.loadInboundGroupSessionKey(sessionId);
|
|
||||||
if (room.inboundGroupSessions.containsKey(sessionId)) {
|
|
||||||
final roomKeyRequest =
|
|
||||||
RoomKeyRequest.fromToDeviceEvent(toDeviceEvent, this);
|
|
||||||
if (deviceKeys.userId == userID &&
|
|
||||||
deviceKeys.verified &&
|
|
||||||
!deviceKeys.blocked) {
|
|
||||||
await roomKeyRequest.forwardKey();
|
|
||||||
} else if (roomKeyRequest.requestingDevice != null) {
|
|
||||||
onRoomKeyRequest.add(roomKeyRequest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -2078,6 +2055,7 @@ class Client {
|
||||||
}
|
}
|
||||||
return ToDeviceEvent(
|
return ToDeviceEvent(
|
||||||
content: plainContent['content'],
|
content: plainContent['content'],
|
||||||
|
encryptedContent: toDeviceEvent.content,
|
||||||
type: plainContent['type'],
|
type: plainContent['type'],
|
||||||
sender: toDeviceEvent.sender,
|
sender: toDeviceEvent.sender,
|
||||||
);
|
);
|
||||||
|
@ -2190,7 +2168,7 @@ class Client {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (encrypted) type = 'm.room.encrypted';
|
if (encrypted) type = 'm.room.encrypted';
|
||||||
final messageID = 'msg${DateTime.now().millisecondsSinceEpoch}';
|
final messageID = generateUniqueTransactionId();
|
||||||
await jsonRequest(
|
await jsonRequest(
|
||||||
type: HTTPType.PUT,
|
type: HTTPType.PUT,
|
||||||
action: '/client/r0/sendToDevice/$type/$messageID',
|
action: '/client/r0/sendToDevice/$type/$messageID',
|
||||||
|
|
213
lib/src/key_manager.dart
Normal file
213
lib/src/key_manager.dart
Normal file
|
@ -0,0 +1,213 @@
|
||||||
|
import 'client.dart';
|
||||||
|
import 'room.dart';
|
||||||
|
import 'utils/to_device_event.dart';
|
||||||
|
import 'utils/device_keys_list.dart';
|
||||||
|
|
||||||
|
class KeyManager {
|
||||||
|
final Client client;
|
||||||
|
final outgoingShareRequests = <String, KeyManagerKeyShareRequest>{};
|
||||||
|
final incomingShareRequests = <String, KeyManagerKeyShareRequest>{};
|
||||||
|
|
||||||
|
KeyManager(this.client);
|
||||||
|
|
||||||
|
/// Request a certain key from another device
|
||||||
|
Future<void> request(Room room, String sessionId, String senderKey) async {
|
||||||
|
// while we just send the to-device event to '*', we still need to save the
|
||||||
|
// devices themself to know where to send the cancel to after receiving a reply
|
||||||
|
final devices = await room.getUserDeviceKeys();
|
||||||
|
final requestId = client.generateUniqueTransactionId();
|
||||||
|
final request = KeyManagerKeyShareRequest(
|
||||||
|
requestId: requestId,
|
||||||
|
devices: devices,
|
||||||
|
room: room,
|
||||||
|
sessionId: sessionId,
|
||||||
|
senderKey: senderKey,
|
||||||
|
);
|
||||||
|
await client.sendToDevice(
|
||||||
|
[],
|
||||||
|
'm.room_key_request',
|
||||||
|
{
|
||||||
|
'action': 'request',
|
||||||
|
'body': {
|
||||||
|
'algorithm': 'm.megolm.v1.aes-sha2',
|
||||||
|
'room_id': room.id,
|
||||||
|
'sender_key': senderKey,
|
||||||
|
'session_id': sessionId,
|
||||||
|
},
|
||||||
|
'request_id': requestId,
|
||||||
|
'requesting_device_id': client.deviceID,
|
||||||
|
},
|
||||||
|
encrypted: false,
|
||||||
|
toUsers: await room.requestParticipants());
|
||||||
|
outgoingShareRequests[request.requestId] = request;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle an incoming to_device event that is related to key sharing
|
||||||
|
Future<void> handleToDeviceEvent(ToDeviceEvent event) async {
|
||||||
|
if (event.type == 'm.room_key_request') {
|
||||||
|
if (!event.content.containsKey('request_id')) {
|
||||||
|
return; // invalid event
|
||||||
|
}
|
||||||
|
if (event.content['action'] == 'request') {
|
||||||
|
// we are *receiving* a request
|
||||||
|
if (!event.content.containsKey('body')) {
|
||||||
|
return; // no body
|
||||||
|
}
|
||||||
|
if (!client.userDeviceKeys.containsKey(event.sender) ||
|
||||||
|
!client.userDeviceKeys[event.sender].deviceKeys
|
||||||
|
.containsKey(event.content['requesting_device_id'])) {
|
||||||
|
return; // device not found
|
||||||
|
}
|
||||||
|
final device = client.userDeviceKeys[event.sender]
|
||||||
|
.deviceKeys[event.content['requesting_device_id']];
|
||||||
|
if (device.userId == client.userID &&
|
||||||
|
device.deviceId == client.deviceID) {
|
||||||
|
return; // ignore requests by ourself
|
||||||
|
}
|
||||||
|
final room = client.getRoomById(event.content['body']['room_id']);
|
||||||
|
if (room == null) {
|
||||||
|
return; // unknown room
|
||||||
|
}
|
||||||
|
final sessionId = event.content['body']['session_id'];
|
||||||
|
// okay, let's see if we have this session at all
|
||||||
|
await room.loadInboundGroupSessionKey(sessionId);
|
||||||
|
if (!room.inboundGroupSessions.containsKey(sessionId)) {
|
||||||
|
return; // we don't have this session anyways
|
||||||
|
}
|
||||||
|
final request = KeyManagerKeyShareRequest(
|
||||||
|
requestId: event.content['request_id'],
|
||||||
|
devices: [device],
|
||||||
|
room: room,
|
||||||
|
sessionId: event.content['body']['session_id'],
|
||||||
|
senderKey: event.content['body']['sender_key'],
|
||||||
|
);
|
||||||
|
if (incomingShareRequests.containsKey(request.requestId)) {
|
||||||
|
return; // we don't want to process one and the same request multiple times
|
||||||
|
}
|
||||||
|
incomingShareRequests[request.requestId] = request;
|
||||||
|
final roomKeyRequest =
|
||||||
|
RoomKeyRequest.fromToDeviceEvent(event, this, request);
|
||||||
|
if (device.userId == client.userID &&
|
||||||
|
device.verified &&
|
||||||
|
!device.blocked) {
|
||||||
|
// alright, we can forward the key
|
||||||
|
await roomKeyRequest.forwardKey();
|
||||||
|
} else {
|
||||||
|
client.onRoomKeyRequest
|
||||||
|
.add(roomKeyRequest); // let the client handle this
|
||||||
|
}
|
||||||
|
} else if (event.content['action'] == 'request_cancellation') {
|
||||||
|
// we got told to cancel an incoming request
|
||||||
|
if (!incomingShareRequests.containsKey(event.content['request_id'])) {
|
||||||
|
return; // we don't know this request anyways
|
||||||
|
}
|
||||||
|
// alright, let's just cancel this request
|
||||||
|
final request = incomingShareRequests[event.content['request_id']];
|
||||||
|
request.canceled = true;
|
||||||
|
incomingShareRequests.remove(request.requestId);
|
||||||
|
}
|
||||||
|
} else if (event.type == 'm.forwarded_room_key') {
|
||||||
|
// we *received* an incoming key request
|
||||||
|
if (event.encryptedContent == null) {
|
||||||
|
return; // event wasn't encrypted, this is a security risk
|
||||||
|
}
|
||||||
|
final request = outgoingShareRequests.values.firstWhere(
|
||||||
|
(r) =>
|
||||||
|
r.room.id == event.content['room_id'] &&
|
||||||
|
r.sessionId == event.content['session_id'] &&
|
||||||
|
r.senderKey == event.content['sender_key'],
|
||||||
|
orElse: () => null);
|
||||||
|
if (request == null || request.canceled) {
|
||||||
|
return; // no associated request found or it got canceled
|
||||||
|
}
|
||||||
|
final device = request.devices.firstWhere(
|
||||||
|
(d) =>
|
||||||
|
d.userId == event.sender &&
|
||||||
|
d.curve25519Key == event.encryptedContent['sender_key'],
|
||||||
|
orElse: () => null);
|
||||||
|
if (device == null) {
|
||||||
|
return; // someone we didn't send our request to replied....better ignore this
|
||||||
|
}
|
||||||
|
// TODO: verify that the keys work to decrypt a message
|
||||||
|
// alright, all checks out, let's go ahead and store this session
|
||||||
|
request.room.setInboundGroupSession(request.sessionId, event.content,
|
||||||
|
forwarded: true);
|
||||||
|
request.devices.removeWhere(
|
||||||
|
(k) => k.userId == device.userId && k.deviceId == device.deviceId);
|
||||||
|
outgoingShareRequests.remove(request.requestId);
|
||||||
|
// send cancel to all other devices
|
||||||
|
if (request.devices.isEmpty) {
|
||||||
|
return; // no need to send any cancellation
|
||||||
|
}
|
||||||
|
await client.sendToDevice(
|
||||||
|
request.devices,
|
||||||
|
'm.room_key_request',
|
||||||
|
{
|
||||||
|
'action': 'request_cancellation',
|
||||||
|
'request_id': request.requestId,
|
||||||
|
'requesting_device_id': client.deviceID,
|
||||||
|
},
|
||||||
|
encrypted: false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class KeyManagerKeyShareRequest {
|
||||||
|
final String requestId;
|
||||||
|
final List<DeviceKeys> devices;
|
||||||
|
final Room room;
|
||||||
|
final String sessionId;
|
||||||
|
final String senderKey;
|
||||||
|
bool canceled;
|
||||||
|
|
||||||
|
KeyManagerKeyShareRequest(
|
||||||
|
{this.requestId,
|
||||||
|
this.devices,
|
||||||
|
this.room,
|
||||||
|
this.sessionId,
|
||||||
|
this.senderKey,
|
||||||
|
this.canceled = false});
|
||||||
|
}
|
||||||
|
|
||||||
|
class RoomKeyRequest extends ToDeviceEvent {
|
||||||
|
KeyManager keyManager;
|
||||||
|
KeyManagerKeyShareRequest request;
|
||||||
|
RoomKeyRequest.fromToDeviceEvent(ToDeviceEvent toDeviceEvent,
|
||||||
|
KeyManager keyManager, KeyManagerKeyShareRequest request) {
|
||||||
|
this.keyManager = keyManager;
|
||||||
|
this.request = request;
|
||||||
|
sender = toDeviceEvent.sender;
|
||||||
|
content = toDeviceEvent.content;
|
||||||
|
type = toDeviceEvent.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
Room get room => request.room;
|
||||||
|
|
||||||
|
DeviceKeys get requestingDevice => request.devices.first;
|
||||||
|
|
||||||
|
Future<void> forwardKey() async {
|
||||||
|
if (request.canceled) {
|
||||||
|
keyManager.incomingShareRequests.remove(request.requestId);
|
||||||
|
return; // request is canceled, don't send anything
|
||||||
|
}
|
||||||
|
var room = this.room;
|
||||||
|
await room.loadInboundGroupSessionKey(request.sessionId);
|
||||||
|
final session = room.inboundGroupSessions[request.sessionId];
|
||||||
|
var forwardedKeys = <dynamic>[keyManager.client.identityKey];
|
||||||
|
for (final key in session.forwardingCurve25519KeyChain) {
|
||||||
|
forwardedKeys.add(key);
|
||||||
|
}
|
||||||
|
var message = session.content;
|
||||||
|
message['forwarding_curve25519_key_chain'] = forwardedKeys;
|
||||||
|
|
||||||
|
message['session_key'] = session.inboundGroupSession
|
||||||
|
.export_session(session.inboundGroupSession.first_known_index());
|
||||||
|
// send the actual reply of the key back to the requester
|
||||||
|
await keyManager.client.sendToDevice(
|
||||||
|
[requestingDevice],
|
||||||
|
'm.forwarded_room_key',
|
||||||
|
message,
|
||||||
|
);
|
||||||
|
keyManager.incomingShareRequests.remove(request.requestId);
|
||||||
|
}
|
||||||
|
}
|
|
@ -837,9 +837,8 @@ class Room {
|
||||||
|
|
||||||
// Create new transaction id
|
// Create new transaction id
|
||||||
String messageID;
|
String messageID;
|
||||||
final now = DateTime.now().millisecondsSinceEpoch;
|
|
||||||
if (txid == null) {
|
if (txid == null) {
|
||||||
messageID = 'msg$now';
|
messageID = client.generateUniqueTransactionId();
|
||||||
} else {
|
} else {
|
||||||
messageID = txid;
|
messageID = txid;
|
||||||
}
|
}
|
||||||
|
@ -874,7 +873,7 @@ class Room {
|
||||||
'event_id': messageID,
|
'event_id': messageID,
|
||||||
'sender': client.userID,
|
'sender': client.userID,
|
||||||
'status': 0,
|
'status': 0,
|
||||||
'origin_server_ts': now,
|
'origin_server_ts': DateTime.now().millisecondsSinceEpoch,
|
||||||
'content': content
|
'content': content
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -1851,33 +1850,7 @@ class Room {
|
||||||
final Set<String> _requestedSessionIds = <String>{};
|
final Set<String> _requestedSessionIds = <String>{};
|
||||||
|
|
||||||
Future<void> requestSessionKey(String sessionId, String senderKey) async {
|
Future<void> requestSessionKey(String sessionId, String senderKey) async {
|
||||||
final users = await requestParticipants();
|
await client.keyManager.request(this, sessionId, senderKey);
|
||||||
await client.sendToDevice(
|
|
||||||
[],
|
|
||||||
'm.room_key_request',
|
|
||||||
{
|
|
||||||
'action': 'request_cancellation',
|
|
||||||
'request_id': base64.encode(utf8.encode(sessionId)),
|
|
||||||
'requesting_device_id': client.deviceID,
|
|
||||||
},
|
|
||||||
encrypted: false,
|
|
||||||
toUsers: users);
|
|
||||||
await client.sendToDevice(
|
|
||||||
[],
|
|
||||||
'm.room_key_request',
|
|
||||||
{
|
|
||||||
'action': 'request',
|
|
||||||
'body': {
|
|
||||||
'algorithm': 'm.megolm.v1.aes-sha2',
|
|
||||||
'room_id': id,
|
|
||||||
'sender_key': senderKey,
|
|
||||||
'session_id': sessionId,
|
|
||||||
},
|
|
||||||
'request_id': base64.encode(utf8.encode(sessionId)),
|
|
||||||
'requesting_device_id': client.deviceID,
|
|
||||||
},
|
|
||||||
encrypted: false,
|
|
||||||
toUsers: users);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> loadInboundGroupSessionKey(String sessionId,
|
Future<void> loadInboundGroupSessionKey(String sessionId,
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
import 'package:famedlysdk/famedlysdk.dart';
|
|
||||||
|
|
||||||
class RoomKeyRequest extends ToDeviceEvent {
|
|
||||||
Client client;
|
|
||||||
RoomKeyRequest.fromToDeviceEvent(ToDeviceEvent toDeviceEvent, Client client) {
|
|
||||||
this.client = client;
|
|
||||||
sender = toDeviceEvent.sender;
|
|
||||||
content = toDeviceEvent.content;
|
|
||||||
type = toDeviceEvent.type;
|
|
||||||
}
|
|
||||||
|
|
||||||
Room get room => client.getRoomById(content['body']['room_id']);
|
|
||||||
|
|
||||||
DeviceKeys get requestingDevice =>
|
|
||||||
client.userDeviceKeys[sender].deviceKeys[content['requesting_device_id']];
|
|
||||||
|
|
||||||
Future<void> forwardKey() async {
|
|
||||||
var room = this.room;
|
|
||||||
await room.loadInboundGroupSessionKey(content['body']['session_id']);
|
|
||||||
final session = room.inboundGroupSessions[content['body']['session_id']];
|
|
||||||
var forwardedKeys = <dynamic>[client.identityKey];
|
|
||||||
for (final key in session.forwardingCurve25519KeyChain) {
|
|
||||||
forwardedKeys.add(key);
|
|
||||||
}
|
|
||||||
var message = session.content;
|
|
||||||
message['forwarding_curve25519_key_chain'] = forwardedKeys;
|
|
||||||
|
|
||||||
message['session_key'] = session.inboundGroupSession
|
|
||||||
.export_session(session.inboundGroupSession.first_known_index());
|
|
||||||
await client.sendToDevice(
|
|
||||||
[requestingDevice],
|
|
||||||
'm.forwarded_room_key',
|
|
||||||
message,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,8 +2,9 @@ class ToDeviceEvent {
|
||||||
String sender;
|
String sender;
|
||||||
String type;
|
String type;
|
||||||
Map<String, dynamic> content;
|
Map<String, dynamic> content;
|
||||||
|
Map<String, dynamic> encryptedContent;
|
||||||
|
|
||||||
ToDeviceEvent({this.sender, this.type, this.content});
|
ToDeviceEvent({this.sender, this.type, this.content, this.encryptedContent});
|
||||||
|
|
||||||
ToDeviceEvent.fromJson(Map<String, dynamic> json) {
|
ToDeviceEvent.fromJson(Map<String, dynamic> json) {
|
||||||
sender = json['sender'];
|
sender = json['sender'];
|
||||||
|
|
4
test.sh
4
test.sh
|
@ -1,4 +1,6 @@
|
||||||
#!/bin/sh -e
|
#!/bin/sh -e
|
||||||
pub run test -p vm
|
pub run test -p vm
|
||||||
pub run test_coverage
|
pub run test_coverage
|
||||||
genhtml -o coverage coverage/lcov.info || true
|
pub global activate remove_from_coverage
|
||||||
|
pub global run remove_from_coverage:remove_from_coverage -f coverage/lcov.info -r '.g.dart$'
|
||||||
|
genhtml -o coverage coverage/lcov.info || true
|
||||||
|
|
|
@ -201,7 +201,7 @@ void main() {
|
||||||
await Future.delayed(Duration(milliseconds: 50));
|
await Future.delayed(Duration(milliseconds: 50));
|
||||||
expect(matrix.userDeviceKeys.length, 2);
|
expect(matrix.userDeviceKeys.length, 2);
|
||||||
expect(matrix.userDeviceKeys['@alice:example.com'].outdated, false);
|
expect(matrix.userDeviceKeys['@alice:example.com'].outdated, false);
|
||||||
expect(matrix.userDeviceKeys['@alice:example.com'].deviceKeys.length, 1);
|
expect(matrix.userDeviceKeys['@alice:example.com'].deviceKeys.length, 2);
|
||||||
expect(
|
expect(
|
||||||
matrix.userDeviceKeys['@alice:example.com'].deviceKeys['JLAFKJWSCS']
|
matrix.userDeviceKeys['@alice:example.com'].deviceKeys['JLAFKJWSCS']
|
||||||
.verified,
|
.verified,
|
||||||
|
|
|
@ -29,6 +29,8 @@ import 'package:http/http.dart';
|
||||||
import 'package:http/testing.dart';
|
import 'package:http/testing.dart';
|
||||||
|
|
||||||
class FakeMatrixApi extends MockClient {
|
class FakeMatrixApi extends MockClient {
|
||||||
|
static final calledEndpoints = <String, List<dynamic>>{};
|
||||||
|
|
||||||
FakeMatrixApi()
|
FakeMatrixApi()
|
||||||
: super((request) async {
|
: super((request) async {
|
||||||
// Collect data from Request
|
// Collect data from Request
|
||||||
|
@ -53,6 +55,10 @@ class FakeMatrixApi extends MockClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call API
|
// Call API
|
||||||
|
if (!calledEndpoints.containsKey(action)) {
|
||||||
|
calledEndpoints[action] = <dynamic>[];
|
||||||
|
}
|
||||||
|
calledEndpoints[action].add(data);
|
||||||
if (api.containsKey(method) && api[method].containsKey(action)) {
|
if (api.containsKey(method) && api[method].containsKey(action)) {
|
||||||
res = api[method][action](data);
|
res = api[method][action](data);
|
||||||
if (res.containsKey('errcode')) {
|
if (res.containsKey('errcode')) {
|
||||||
|
@ -859,6 +865,19 @@ class FakeMatrixApi extends MockClient {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'unsigned': {'device_display_name': "Alice's mobile phone"}
|
'unsigned': {'device_display_name': "Alice's mobile phone"}
|
||||||
|
},
|
||||||
|
'OTHERDEVICE': {
|
||||||
|
'user_id': '@alice:example.com',
|
||||||
|
'device_id': 'OTHERDEVICE',
|
||||||
|
'algorithms': [
|
||||||
|
'm.olm.v1.curve25519-aes-sha2',
|
||||||
|
'm.megolm.v1.aes-sha2'
|
||||||
|
],
|
||||||
|
'keys': {
|
||||||
|
'curve25519:OTHERDEVICE': 'blah',
|
||||||
|
'ed25519:OTHERDEVICE': 'blah'
|
||||||
|
},
|
||||||
|
'signatures': {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,12 +21,25 @@
|
||||||
* along with famedlysdk. If not, see <http://www.gnu.org/licenses/>.
|
* along with famedlysdk. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import 'dart:convert';
|
||||||
import 'package:famedlysdk/famedlysdk.dart';
|
import 'package:famedlysdk/famedlysdk.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
import 'fake_matrix_api.dart';
|
import 'fake_matrix_api.dart';
|
||||||
import 'fake_database.dart';
|
import 'fake_database.dart';
|
||||||
|
|
||||||
|
Map<String, dynamic> jsonDecode(dynamic payload) {
|
||||||
|
if (payload is String) {
|
||||||
|
try {
|
||||||
|
return json.decode(payload);
|
||||||
|
} catch (e) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (payload is Map<String, dynamic>) return payload;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
/// All Tests related to device keys
|
/// All Tests related to device keys
|
||||||
test('fromJson', () async {
|
test('fromJson', () async {
|
||||||
|
@ -62,9 +75,290 @@ void main() {
|
||||||
room.inboundGroupSessions.keys.first;
|
room.inboundGroupSessions.keys.first;
|
||||||
|
|
||||||
var roomKeyRequest = RoomKeyRequest.fromToDeviceEvent(
|
var roomKeyRequest = RoomKeyRequest.fromToDeviceEvent(
|
||||||
ToDeviceEvent.fromJson(rawJson), matrix);
|
ToDeviceEvent.fromJson(rawJson),
|
||||||
|
matrix.keyManager,
|
||||||
|
KeyManagerKeyShareRequest(
|
||||||
|
room: room,
|
||||||
|
sessionId: rawJson['content']['body']['session_id'],
|
||||||
|
senderKey: rawJson['content']['body']['sender_key'],
|
||||||
|
devices: [
|
||||||
|
matrix.userDeviceKeys[rawJson['sender']]
|
||||||
|
.deviceKeys[rawJson['content']['requesting_device_id']]
|
||||||
|
],
|
||||||
|
));
|
||||||
await roomKeyRequest.forwardKey();
|
await roomKeyRequest.forwardKey();
|
||||||
}
|
}
|
||||||
await matrix.dispose(closeDatabase: true);
|
await matrix.dispose(closeDatabase: true);
|
||||||
});
|
});
|
||||||
|
test('Create Request', () async {
|
||||||
|
var matrix = Client('testclient', debug: true);
|
||||||
|
matrix.httpClient = FakeMatrixApi();
|
||||||
|
matrix.database = getDatabase();
|
||||||
|
await matrix.checkServer('https://fakeServer.notExisting');
|
||||||
|
await matrix.login('test', '1234');
|
||||||
|
if (!matrix.encryptionEnabled) {
|
||||||
|
await matrix.dispose(closeDatabase: true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final requestRoom = matrix.getRoomById('!726s6s6q:example.com');
|
||||||
|
await matrix.keyManager.request(requestRoom, 'sessionId', 'senderKey');
|
||||||
|
var foundEvent = false;
|
||||||
|
for (var entry in FakeMatrixApi.calledEndpoints.entries) {
|
||||||
|
final payload = jsonDecode(entry.value.first);
|
||||||
|
if (entry.key.startsWith('/client/r0/sendToDevice/m.room_key_request') &&
|
||||||
|
(payload['messages'] is Map) &&
|
||||||
|
(payload['messages']['@alice:example.com'] is Map) &&
|
||||||
|
(payload['messages']['@alice:example.com']['*'] is Map)) {
|
||||||
|
final content = payload['messages']['@alice:example.com']['*'];
|
||||||
|
if (content['action'] == 'request' &&
|
||||||
|
content['body']['room_id'] == '!726s6s6q:example.com' &&
|
||||||
|
content['body']['sender_key'] == 'senderKey' &&
|
||||||
|
content['body']['session_id'] == 'sessionId') {
|
||||||
|
foundEvent = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect(foundEvent, true);
|
||||||
|
await matrix.dispose(closeDatabase: true);
|
||||||
|
});
|
||||||
|
final validSessionId = 'ciM/JWTPrmiWPPZNkRLDPQYf9AW/I46bxyLSr+Bx5oU';
|
||||||
|
test('Reply To Request', () async {
|
||||||
|
var matrix = Client('testclient', debug: true);
|
||||||
|
matrix.httpClient = FakeMatrixApi();
|
||||||
|
matrix.database = getDatabase();
|
||||||
|
await matrix.checkServer('https://fakeServer.notExisting');
|
||||||
|
await matrix.login('test', '1234');
|
||||||
|
if (!matrix.encryptionEnabled) {
|
||||||
|
await matrix.dispose(closeDatabase: true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
matrix.setUserId('@alice:example.com'); // we need to pretend to be alice
|
||||||
|
FakeMatrixApi.calledEndpoints.clear();
|
||||||
|
await matrix.userDeviceKeys['@alice:example.com'].deviceKeys['OTHERDEVICE']
|
||||||
|
.setVerified(true, matrix);
|
||||||
|
// test a successful share
|
||||||
|
var event = ToDeviceEvent(
|
||||||
|
sender: '@alice:example.com',
|
||||||
|
type: 'm.room_key_request',
|
||||||
|
content: {
|
||||||
|
'action': 'request',
|
||||||
|
'body': {
|
||||||
|
'algorithm': 'm.megolm.v1.aes-sha2',
|
||||||
|
'room_id': '!726s6s6q:example.com',
|
||||||
|
'sender_key': 'senderKey',
|
||||||
|
'session_id': validSessionId,
|
||||||
|
},
|
||||||
|
'request_id': 'request_1',
|
||||||
|
'requesting_device_id': 'OTHERDEVICE',
|
||||||
|
});
|
||||||
|
await matrix.keyManager.handleToDeviceEvent(event);
|
||||||
|
expect(
|
||||||
|
FakeMatrixApi.calledEndpoints.keys.any(
|
||||||
|
(k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')),
|
||||||
|
true);
|
||||||
|
|
||||||
|
// test various fail scenarios
|
||||||
|
|
||||||
|
// no body
|
||||||
|
FakeMatrixApi.calledEndpoints.clear();
|
||||||
|
event = ToDeviceEvent(
|
||||||
|
sender: '@alice:example.com',
|
||||||
|
type: 'm.room_key_request',
|
||||||
|
content: {
|
||||||
|
'action': 'request',
|
||||||
|
'request_id': 'request_2',
|
||||||
|
'requesting_device_id': 'OTHERDEVICE',
|
||||||
|
});
|
||||||
|
await matrix.keyManager.handleToDeviceEvent(event);
|
||||||
|
expect(
|
||||||
|
FakeMatrixApi.calledEndpoints.keys.any(
|
||||||
|
(k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')),
|
||||||
|
false);
|
||||||
|
|
||||||
|
// request by ourself
|
||||||
|
FakeMatrixApi.calledEndpoints.clear();
|
||||||
|
event = ToDeviceEvent(
|
||||||
|
sender: '@alice:example.com',
|
||||||
|
type: 'm.room_key_request',
|
||||||
|
content: {
|
||||||
|
'action': 'request',
|
||||||
|
'body': {
|
||||||
|
'algorithm': 'm.megolm.v1.aes-sha2',
|
||||||
|
'room_id': '!726s6s6q:example.com',
|
||||||
|
'sender_key': 'senderKey',
|
||||||
|
'session_id': validSessionId,
|
||||||
|
},
|
||||||
|
'request_id': 'request_3',
|
||||||
|
'requesting_device_id': 'JLAFKJWSCS',
|
||||||
|
});
|
||||||
|
await matrix.keyManager.handleToDeviceEvent(event);
|
||||||
|
expect(
|
||||||
|
FakeMatrixApi.calledEndpoints.keys.any(
|
||||||
|
(k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')),
|
||||||
|
false);
|
||||||
|
|
||||||
|
// device not found
|
||||||
|
FakeMatrixApi.calledEndpoints.clear();
|
||||||
|
event = ToDeviceEvent(
|
||||||
|
sender: '@alice:example.com',
|
||||||
|
type: 'm.room_key_request',
|
||||||
|
content: {
|
||||||
|
'action': 'request',
|
||||||
|
'body': {
|
||||||
|
'algorithm': 'm.megolm.v1.aes-sha2',
|
||||||
|
'room_id': '!726s6s6q:example.com',
|
||||||
|
'sender_key': 'senderKey',
|
||||||
|
'session_id': validSessionId,
|
||||||
|
},
|
||||||
|
'request_id': 'request_4',
|
||||||
|
'requesting_device_id': 'blubb',
|
||||||
|
});
|
||||||
|
await matrix.keyManager.handleToDeviceEvent(event);
|
||||||
|
expect(
|
||||||
|
FakeMatrixApi.calledEndpoints.keys.any(
|
||||||
|
(k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')),
|
||||||
|
false);
|
||||||
|
|
||||||
|
// unknown room
|
||||||
|
FakeMatrixApi.calledEndpoints.clear();
|
||||||
|
event = ToDeviceEvent(
|
||||||
|
sender: '@alice:example.com',
|
||||||
|
type: 'm.room_key_request',
|
||||||
|
content: {
|
||||||
|
'action': 'request',
|
||||||
|
'body': {
|
||||||
|
'algorithm': 'm.megolm.v1.aes-sha2',
|
||||||
|
'room_id': '!invalid:example.com',
|
||||||
|
'sender_key': 'senderKey',
|
||||||
|
'session_id': validSessionId,
|
||||||
|
},
|
||||||
|
'request_id': 'request_5',
|
||||||
|
'requesting_device_id': 'OTHERDEVICE',
|
||||||
|
});
|
||||||
|
await matrix.keyManager.handleToDeviceEvent(event);
|
||||||
|
expect(
|
||||||
|
FakeMatrixApi.calledEndpoints.keys.any(
|
||||||
|
(k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')),
|
||||||
|
false);
|
||||||
|
|
||||||
|
// unknwon session
|
||||||
|
FakeMatrixApi.calledEndpoints.clear();
|
||||||
|
event = ToDeviceEvent(
|
||||||
|
sender: '@alice:example.com',
|
||||||
|
type: 'm.room_key_request',
|
||||||
|
content: {
|
||||||
|
'action': 'request',
|
||||||
|
'body': {
|
||||||
|
'algorithm': 'm.megolm.v1.aes-sha2',
|
||||||
|
'room_id': '!726s6s6q:example.com',
|
||||||
|
'sender_key': 'senderKey',
|
||||||
|
'session_id': 'invalid',
|
||||||
|
},
|
||||||
|
'request_id': 'request_6',
|
||||||
|
'requesting_device_id': 'OTHERDEVICE',
|
||||||
|
});
|
||||||
|
await matrix.keyManager.handleToDeviceEvent(event);
|
||||||
|
expect(
|
||||||
|
FakeMatrixApi.calledEndpoints.keys.any(
|
||||||
|
(k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')),
|
||||||
|
false);
|
||||||
|
|
||||||
|
FakeMatrixApi.calledEndpoints.clear();
|
||||||
|
await matrix.dispose(closeDatabase: true);
|
||||||
|
});
|
||||||
|
test('Receive shared keys', () async {
|
||||||
|
var matrix = Client('testclient', debug: true);
|
||||||
|
matrix.httpClient = FakeMatrixApi();
|
||||||
|
matrix.database = getDatabase();
|
||||||
|
await matrix.checkServer('https://fakeServer.notExisting');
|
||||||
|
await matrix.login('test', '1234');
|
||||||
|
if (!matrix.encryptionEnabled) {
|
||||||
|
await matrix.dispose(closeDatabase: true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final requestRoom = matrix.getRoomById('!726s6s6q:example.com');
|
||||||
|
await matrix.keyManager.request(requestRoom, validSessionId, 'senderKey');
|
||||||
|
|
||||||
|
final session = requestRoom.inboundGroupSessions[validSessionId];
|
||||||
|
final sessionKey = session.inboundGroupSession
|
||||||
|
.export_session(session.inboundGroupSession.first_known_index());
|
||||||
|
requestRoom.inboundGroupSessions.clear();
|
||||||
|
var event = ToDeviceEvent(
|
||||||
|
sender: '@alice:example.com',
|
||||||
|
type: 'm.forwarded_room_key',
|
||||||
|
content: {
|
||||||
|
'algorithm': 'm.megolm.v1.aes-sha2',
|
||||||
|
'room_id': '!726s6s6q:example.com',
|
||||||
|
'session_id': validSessionId,
|
||||||
|
'session_key': sessionKey,
|
||||||
|
'sender_key': 'senderKey',
|
||||||
|
'forwarding_curve25519_key_chain': [],
|
||||||
|
},
|
||||||
|
encryptedContent: {
|
||||||
|
'sender_key': '3C5BFWi2Y8MaVvjM8M22DBmh24PmgR0nPvJOIArzgyI',
|
||||||
|
});
|
||||||
|
await matrix.keyManager.handleToDeviceEvent(event);
|
||||||
|
expect(requestRoom.inboundGroupSessions.containsKey(validSessionId), true);
|
||||||
|
|
||||||
|
// now test a few invalid scenarios
|
||||||
|
|
||||||
|
// request not found
|
||||||
|
requestRoom.inboundGroupSessions.clear();
|
||||||
|
event = ToDeviceEvent(
|
||||||
|
sender: '@alice:example.com',
|
||||||
|
type: 'm.forwarded_room_key',
|
||||||
|
content: {
|
||||||
|
'algorithm': 'm.megolm.v1.aes-sha2',
|
||||||
|
'room_id': '!726s6s6q:example.com',
|
||||||
|
'session_id': validSessionId,
|
||||||
|
'session_key': sessionKey,
|
||||||
|
'sender_key': 'senderKey',
|
||||||
|
'forwarding_curve25519_key_chain': [],
|
||||||
|
},
|
||||||
|
encryptedContent: {
|
||||||
|
'sender_key': '3C5BFWi2Y8MaVvjM8M22DBmh24PmgR0nPvJOIArzgyI',
|
||||||
|
});
|
||||||
|
await matrix.keyManager.handleToDeviceEvent(event);
|
||||||
|
expect(requestRoom.inboundGroupSessions.containsKey(validSessionId), false);
|
||||||
|
|
||||||
|
// unknown device
|
||||||
|
await matrix.keyManager.request(requestRoom, validSessionId, 'senderKey');
|
||||||
|
requestRoom.inboundGroupSessions.clear();
|
||||||
|
event = ToDeviceEvent(
|
||||||
|
sender: '@alice:example.com',
|
||||||
|
type: 'm.forwarded_room_key',
|
||||||
|
content: {
|
||||||
|
'algorithm': 'm.megolm.v1.aes-sha2',
|
||||||
|
'room_id': '!726s6s6q:example.com',
|
||||||
|
'session_id': validSessionId,
|
||||||
|
'session_key': sessionKey,
|
||||||
|
'sender_key': 'senderKey',
|
||||||
|
'forwarding_curve25519_key_chain': [],
|
||||||
|
},
|
||||||
|
encryptedContent: {
|
||||||
|
'sender_key': 'invalid',
|
||||||
|
});
|
||||||
|
await matrix.keyManager.handleToDeviceEvent(event);
|
||||||
|
expect(requestRoom.inboundGroupSessions.containsKey(validSessionId), false);
|
||||||
|
|
||||||
|
// no encrypted content
|
||||||
|
await matrix.keyManager.request(requestRoom, validSessionId, 'senderKey');
|
||||||
|
requestRoom.inboundGroupSessions.clear();
|
||||||
|
event = ToDeviceEvent(
|
||||||
|
sender: '@alice:example.com',
|
||||||
|
type: 'm.forwarded_room_key',
|
||||||
|
content: {
|
||||||
|
'algorithm': 'm.megolm.v1.aes-sha2',
|
||||||
|
'room_id': '!726s6s6q:example.com',
|
||||||
|
'session_id': validSessionId,
|
||||||
|
'session_key': sessionKey,
|
||||||
|
'sender_key': 'senderKey',
|
||||||
|
'forwarding_curve25519_key_chain': [],
|
||||||
|
});
|
||||||
|
await matrix.keyManager.handleToDeviceEvent(event);
|
||||||
|
expect(requestRoom.inboundGroupSessions.containsKey(validSessionId), false);
|
||||||
|
|
||||||
|
await matrix.dispose(closeDatabase: true);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue