Merge branch 'room-feature-key-sharing' into 'master'
[Room] Implement key sharing See merge request famedly/famedlysdk!217
This commit is contained in:
commit
8a3547a1ee
|
@ -35,6 +35,7 @@ export 'package:famedlysdk/src/utils/open_id_credentials.dart';
|
||||||
export 'package:famedlysdk/src/utils/profile.dart';
|
export 'package:famedlysdk/src/utils/profile.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';
|
||||||
|
|
|
@ -33,6 +33,7 @@ import 'package:famedlysdk/src/sync/user_update.dart';
|
||||||
import 'package:famedlysdk/src/utils/device_keys_list.dart';
|
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/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';
|
||||||
|
@ -620,6 +621,10 @@ class Client {
|
||||||
/// Will be called on call answers.
|
/// Will be called on call answers.
|
||||||
final StreamController<Event> onCallAnswer = StreamController.broadcast();
|
final StreamController<Event> onCallAnswer = StreamController.broadcast();
|
||||||
|
|
||||||
|
/// Will be called when another device is requesting session keys for a room.
|
||||||
|
final StreamController<RoomKeyRequest> onRoomKeyRequest =
|
||||||
|
StreamController.broadcast();
|
||||||
|
|
||||||
/// Matrix synchronisation is done with https long polling. This needs a
|
/// Matrix synchronisation is done with https long polling. This needs a
|
||||||
/// timeout which is usually 30 seconds.
|
/// timeout which is usually 30 seconds.
|
||||||
int syncTimeoutSec = 30;
|
int syncTimeoutSec = 30;
|
||||||
|
@ -1320,17 +1325,67 @@ class Client {
|
||||||
try {
|
try {
|
||||||
switch (toDeviceEvent.type) {
|
switch (toDeviceEvent.type) {
|
||||||
case "m.room_key":
|
case "m.room_key":
|
||||||
|
case "m.forwarded_room_key":
|
||||||
Room room = getRoomById(toDeviceEvent.content["room_id"]);
|
Room room = getRoomById(toDeviceEvent.content["room_id"]);
|
||||||
if (room != null && toDeviceEvent.content["session_id"] is String) {
|
if (room != null) {
|
||||||
final String sessionId = toDeviceEvent.content["session_id"];
|
final String sessionId = toDeviceEvent.content["session_id"];
|
||||||
if (room != null) {
|
if (room != null) {
|
||||||
room.setSessionKey(sessionId, toDeviceEvent.content);
|
if (toDeviceEvent.type == "m.room_key" &&
|
||||||
|
userDeviceKeys.containsKey(toDeviceEvent.sender) &&
|
||||||
|
userDeviceKeys[toDeviceEvent.sender].deviceKeys.containsKey(
|
||||||
|
toDeviceEvent.content["requesting_device_id"])) {
|
||||||
|
toDeviceEvent.content["sender_claimed_ed25519_key"] =
|
||||||
|
userDeviceKeys[toDeviceEvent.sender]
|
||||||
|
.deviceKeys[
|
||||||
|
toDeviceEvent.content["requesting_device_id"]]
|
||||||
|
.ed25519Key;
|
||||||
|
}
|
||||||
|
room.setSessionKey(
|
||||||
|
sessionId,
|
||||||
|
toDeviceEvent.content,
|
||||||
|
forwarded: toDeviceEvent.type == "m.forwarded_room_key",
|
||||||
|
);
|
||||||
|
if (toDeviceEvent.type == "m.forwarded_room_key") {
|
||||||
|
sendToDevice(
|
||||||
|
[],
|
||||||
|
"m.room_key_request",
|
||||||
|
{
|
||||||
|
"action": "request_cancellation",
|
||||||
|
"request_id": base64.encode(
|
||||||
|
utf8.encode(toDeviceEvent.content["room_id"])),
|
||||||
|
"requesting_device_id": room.client.deviceID,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "m.room_key_request":
|
||||||
|
if (!toDeviceEvent.content.containsKey("body")) break;
|
||||||
|
Room 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"]];
|
||||||
|
if (room.sessionKeys.containsKey(sessionId)) {
|
||||||
|
final RoomKeyRequest roomKeyRequest =
|
||||||
|
RoomKeyRequest.fromToDeviceEvent(toDeviceEvent, this);
|
||||||
|
if (deviceKeys.userId == userID &&
|
||||||
|
deviceKeys.verified &&
|
||||||
|
!deviceKeys.blocked) {
|
||||||
|
roomKeyRequest.forwardKey();
|
||||||
|
} else {
|
||||||
|
onRoomKeyRequest.add(roomKeyRequest);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
print("[Matrix] Error while processing to-device-event: " + e.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1650,16 +1705,22 @@ class Client {
|
||||||
storeAPI?.setItem("/clients/$userID/olm-sessions", json.encode(pickleMap));
|
storeAPI?.setItem("/clients/$userID/olm-sessions", json.encode(pickleMap));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends an encrypted [message] of this [type] to these [deviceKeys].
|
/// Sends an encrypted [message] of this [type] to these [deviceKeys]. To send
|
||||||
Future<void> sendToDevice(List<DeviceKeys> deviceKeys, String type,
|
/// the request to all devices of the current user, pass an empty list to [deviceKeys].
|
||||||
Map<String, dynamic> message) async {
|
Future<void> sendToDevice(
|
||||||
if (!encryptionEnabled) return;
|
List<DeviceKeys> deviceKeys, String type, Map<String, dynamic> message,
|
||||||
|
{bool encrypted = true}) async {
|
||||||
|
if (encrypted && !encryptionEnabled) return;
|
||||||
// Don't send this message to blocked devices.
|
// Don't send this message to blocked devices.
|
||||||
if (deviceKeys?.isEmpty ?? true) return;
|
if (deviceKeys.isNotEmpty) {
|
||||||
deviceKeys.removeWhere((DeviceKeys deviceKeys) =>
|
deviceKeys.removeWhere((DeviceKeys deviceKeys) =>
|
||||||
deviceKeys.blocked || deviceKeys.deviceId == deviceID);
|
deviceKeys.blocked || deviceKeys.deviceId == deviceID);
|
||||||
if (deviceKeys?.isEmpty ?? true) return;
|
if (deviceKeys.isEmpty) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> sendToDeviceMessage = message;
|
||||||
|
|
||||||
|
if (encrypted) {
|
||||||
// Create new sessions with devices if there is no existing session yet.
|
// Create new sessions with devices if there is no existing session yet.
|
||||||
List<DeviceKeys> deviceKeysWithoutSession =
|
List<DeviceKeys> deviceKeysWithoutSession =
|
||||||
List<DeviceKeys>.from(deviceKeys);
|
List<DeviceKeys>.from(deviceKeys);
|
||||||
|
@ -1668,7 +1729,7 @@ class Client {
|
||||||
if (deviceKeysWithoutSession.isNotEmpty) {
|
if (deviceKeysWithoutSession.isNotEmpty) {
|
||||||
await startOutgoingOlmSessions(deviceKeysWithoutSession);
|
await startOutgoingOlmSessions(deviceKeysWithoutSession);
|
||||||
}
|
}
|
||||||
Map<String, dynamic> encryptedMessage = {
|
sendToDeviceMessage = {
|
||||||
"algorithm": "m.olm.v1.curve25519-aes-sha2",
|
"algorithm": "m.olm.v1.curve25519-aes-sha2",
|
||||||
"sender_key": identityKey,
|
"sender_key": identityKey,
|
||||||
"ciphertext": Map<String, dynamic>(),
|
"ciphertext": Map<String, dynamic>(),
|
||||||
|
@ -1676,7 +1737,8 @@ class Client {
|
||||||
for (DeviceKeys device in deviceKeys) {
|
for (DeviceKeys device in deviceKeys) {
|
||||||
List<olm.Session> existingSessions = olmSessions[device.curve25519Key];
|
List<olm.Session> existingSessions = olmSessions[device.curve25519Key];
|
||||||
if (existingSessions == null || existingSessions.isEmpty) continue;
|
if (existingSessions == null || existingSessions.isEmpty) continue;
|
||||||
existingSessions.sort((a, b) => a.session_id().compareTo(b.session_id()));
|
existingSessions
|
||||||
|
.sort((a, b) => a.session_id().compareTo(b.session_id()));
|
||||||
|
|
||||||
final Map<String, dynamic> payload = {
|
final Map<String, dynamic> payload = {
|
||||||
"type": type,
|
"type": type,
|
||||||
|
@ -1689,26 +1751,34 @@ class Client {
|
||||||
final olm.EncryptResult encryptResult =
|
final olm.EncryptResult encryptResult =
|
||||||
existingSessions.first.encrypt(json.encode(payload));
|
existingSessions.first.encrypt(json.encode(payload));
|
||||||
storeOlmSession(device.curve25519Key, existingSessions.first);
|
storeOlmSession(device.curve25519Key, existingSessions.first);
|
||||||
encryptedMessage["ciphertext"][device.curve25519Key] = {
|
sendToDeviceMessage["ciphertext"][device.curve25519Key] = {
|
||||||
"type": encryptResult.type,
|
"type": encryptResult.type,
|
||||||
"body": encryptResult.body,
|
"body": encryptResult.body,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
type = "m.room.encrypted";
|
||||||
|
}
|
||||||
|
|
||||||
// Send with send-to-device messaging
|
// Send with send-to-device messaging
|
||||||
Map<String, dynamic> data = {
|
Map<String, dynamic> data = {
|
||||||
"messages": Map<String, dynamic>(),
|
"messages": Map<String, dynamic>(),
|
||||||
};
|
};
|
||||||
for (DeviceKeys device in deviceKeys) {
|
if (deviceKeys.isEmpty) {
|
||||||
|
data["messages"][this.userID] = Map<String, dynamic>();
|
||||||
|
data["messages"][this.userID]["*"] = sendToDeviceMessage;
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < deviceKeys.length; i++) {
|
||||||
|
DeviceKeys device = deviceKeys[i];
|
||||||
if (!data["messages"].containsKey(device.userId)) {
|
if (!data["messages"].containsKey(device.userId)) {
|
||||||
data["messages"][device.userId] = Map<String, dynamic>();
|
data["messages"][device.userId] = Map<String, dynamic>();
|
||||||
}
|
}
|
||||||
data["messages"][device.userId][device.deviceId] = encryptedMessage;
|
data["messages"][device.userId][device.deviceId] = sendToDeviceMessage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
final String messageID = "msg${DateTime.now().millisecondsSinceEpoch}";
|
final String messageID = "msg${DateTime.now().millisecondsSinceEpoch}";
|
||||||
await jsonRequest(
|
await jsonRequest(
|
||||||
type: HTTPType.PUT,
|
type: HTTPType.PUT,
|
||||||
action: "/client/r0/sendToDevice/m.room.encrypted/$messageID",
|
action: "/client/r0/sendToDevice/$type/$messageID",
|
||||||
data: data,
|
data: data,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -388,6 +388,42 @@ class Event {
|
||||||
/// 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);
|
||||||
|
|
||||||
|
/// If this event is encrypted and the decryption was not successful because
|
||||||
|
/// the session is unknown, this requests the session key from other devices
|
||||||
|
/// in the room. If the event is not encrypted or the decryption failed because
|
||||||
|
/// of a different error, this throws an exception.
|
||||||
|
Future<void> requestKey() async {
|
||||||
|
if (this.type != EventTypes.Encrypted ||
|
||||||
|
this.messageType != MessageTypes.BadEncrypted ||
|
||||||
|
this.content["body"] != DecryptError.UNKNOWN_SESSION) {
|
||||||
|
throw ("Session key not unknown");
|
||||||
|
}
|
||||||
|
await room.client.sendToDevice(
|
||||||
|
[],
|
||||||
|
"m.room_key_request",
|
||||||
|
{
|
||||||
|
"action": "request_cancellation",
|
||||||
|
"request_id": base64.encode(utf8.encode(content["session_id"])),
|
||||||
|
"requesting_device_id": room.client.deviceID,
|
||||||
|
});
|
||||||
|
await room.client.sendToDevice(
|
||||||
|
[],
|
||||||
|
"m.room_key_request",
|
||||||
|
{
|
||||||
|
"action": "request",
|
||||||
|
"body": {
|
||||||
|
"algorithm": "m.megolm.v1.aes-sha2",
|
||||||
|
"room_id": roomId,
|
||||||
|
"sender_key": content["sender_key"],
|
||||||
|
"session_id": content["session_id"],
|
||||||
|
},
|
||||||
|
"request_id": base64.encode(utf8.encode(content["session_id"])),
|
||||||
|
"requesting_device_id": room.client.deviceID,
|
||||||
|
},
|
||||||
|
encrypted: false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum MessageTypes {
|
enum MessageTypes {
|
||||||
|
|
|
@ -152,13 +152,18 @@ class Room {
|
||||||
Map<String, SessionKey> _sessionKeys = {};
|
Map<String, SessionKey> _sessionKeys = {};
|
||||||
|
|
||||||
/// Add a new session key to the [sessionKeys].
|
/// Add a new session key to the [sessionKeys].
|
||||||
void setSessionKey(String sessionId, Map<String, dynamic> content) {
|
void setSessionKey(String sessionId, Map<String, dynamic> content,
|
||||||
|
{bool forwarded = false}) {
|
||||||
if (sessionKeys.containsKey(sessionId)) return;
|
if (sessionKeys.containsKey(sessionId)) return;
|
||||||
olm.InboundGroupSession inboundGroupSession;
|
olm.InboundGroupSession inboundGroupSession;
|
||||||
if (content["algorithm"] == "m.megolm.v1.aes-sha2") {
|
if (content["algorithm"] == "m.megolm.v1.aes-sha2") {
|
||||||
try {
|
try {
|
||||||
inboundGroupSession = olm.InboundGroupSession();
|
inboundGroupSession = olm.InboundGroupSession();
|
||||||
|
if (forwarded) {
|
||||||
|
inboundGroupSession.import_session(content["session_key"]);
|
||||||
|
} else {
|
||||||
inboundGroupSession.create(content["session_key"]);
|
inboundGroupSession.create(content["session_key"]);
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
inboundGroupSession = null;
|
inboundGroupSession = null;
|
||||||
print("[LibOlm] Could not create new InboundGroupSession: " +
|
print("[LibOlm] Could not create new InboundGroupSession: " +
|
||||||
|
@ -176,6 +181,7 @@ class Room {
|
||||||
"/clients/${client.deviceID}/rooms/${this.id}/session_keys",
|
"/clients/${client.deviceID}/rooms/${this.id}/session_keys",
|
||||||
json.encode(sessionKeys));
|
json.encode(sessionKeys));
|
||||||
}
|
}
|
||||||
|
onSessionKeyReceived.add(sessionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the [Event] for the given [typeKey] and optional [stateKey].
|
/// Returns the [Event] for the given [typeKey] and optional [stateKey].
|
||||||
|
@ -219,6 +225,11 @@ class Room {
|
||||||
/// room id.
|
/// room id.
|
||||||
final StreamController<String> onUpdate = StreamController.broadcast();
|
final StreamController<String> onUpdate = StreamController.broadcast();
|
||||||
|
|
||||||
|
/// If there is a new session key received, this will be triggered with
|
||||||
|
/// the session ID.
|
||||||
|
final StreamController<String> onSessionKeyReceived =
|
||||||
|
StreamController.broadcast();
|
||||||
|
|
||||||
/// The name of the room if set by a participant.
|
/// The name of the room if set by a participant.
|
||||||
String get name => states["m.room.name"] != null
|
String get name => states["m.room.name"] != null
|
||||||
? states["m.room.name"].content["name"]
|
? states["m.room.name"].content["name"]
|
||||||
|
|
|
@ -42,6 +42,7 @@ class Timeline {
|
||||||
final onTimelineInsertCallback onInsert;
|
final onTimelineInsertCallback onInsert;
|
||||||
|
|
||||||
StreamSubscription<EventUpdate> sub;
|
StreamSubscription<EventUpdate> sub;
|
||||||
|
StreamSubscription<String> sessionIdReceivedSub;
|
||||||
bool _requestingHistoryLock = false;
|
bool _requestingHistoryLock = false;
|
||||||
|
|
||||||
Map<String, Event> _eventCache = {};
|
Map<String, Event> _eventCache = {};
|
||||||
|
@ -77,6 +78,30 @@ class Timeline {
|
||||||
|
|
||||||
Timeline({this.room, this.events, this.onUpdate, this.onInsert}) {
|
Timeline({this.room, this.events, this.onUpdate, this.onInsert}) {
|
||||||
sub ??= room.client.onEvent.stream.listen(_handleEventUpdate);
|
sub ??= room.client.onEvent.stream.listen(_handleEventUpdate);
|
||||||
|
sessionIdReceivedSub ??=
|
||||||
|
room.onSessionKeyReceived.stream.listen(_sessionKeyReceived);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Don't forget to call this before you dismiss this object!
|
||||||
|
void cancelSubscriptions() {
|
||||||
|
sub?.cancel();
|
||||||
|
sessionIdReceivedSub?.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _sessionKeyReceived(String sessionId) {
|
||||||
|
bool decryptAtLeastOneEvent = false;
|
||||||
|
for (int i = 0; i < events.length; i++) {
|
||||||
|
if (events[i].type == EventTypes.Encrypted &&
|
||||||
|
events[i].messageType == MessageTypes.BadEncrypted &&
|
||||||
|
events[i].content["body"] == DecryptError.UNKNOWN_SESSION &&
|
||||||
|
events[i].content["session_id"] == sessionId) {
|
||||||
|
events[i] = events[i].decrypted;
|
||||||
|
if (events[i].type != EventTypes.Encrypted) {
|
||||||
|
decryptAtLeastOneEvent = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (decryptAtLeastOneEvent) onUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
int _findEvent({String event_id, String unsigned_txid}) {
|
int _findEvent({String event_id, String unsigned_txid}) {
|
||||||
|
|
36
lib/src/utils/room_key_request.dart
Normal file
36
lib/src/utils/room_key_request.dart
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import 'package:famedlysdk/famedlysdk.dart';
|
||||||
|
import 'package:famedlysdk/src/utils/session_key.dart';
|
||||||
|
|
||||||
|
class RoomKeyRequest extends ToDeviceEvent {
|
||||||
|
Client client;
|
||||||
|
RoomKeyRequest.fromToDeviceEvent(ToDeviceEvent toDeviceEvent, Client client) {
|
||||||
|
this.client = client;
|
||||||
|
this.sender = toDeviceEvent.sender;
|
||||||
|
this.content = toDeviceEvent.content;
|
||||||
|
this.type = toDeviceEvent.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
Room get room => client.getRoomById(this.content["body"]["room_id"]);
|
||||||
|
|
||||||
|
DeviceKeys get requestingDevice =>
|
||||||
|
client.userDeviceKeys[sender].deviceKeys[content["requesting_device_id"]];
|
||||||
|
|
||||||
|
Future<void> forwardKey() async {
|
||||||
|
Room room = this.room;
|
||||||
|
final SessionKey session =
|
||||||
|
room.sessionKeys[this.content["body"]["session_id"]];
|
||||||
|
List<dynamic> forwardedKeys = [client.identityKey];
|
||||||
|
for (final key in session.forwardingCurve25519KeyChain) {
|
||||||
|
forwardedKeys.add(key);
|
||||||
|
}
|
||||||
|
await requestingDevice.setVerified(true, client);
|
||||||
|
Map<String, dynamic> message = session.content;
|
||||||
|
message["forwarding_curve25519_key_chain"] = forwardedKeys;
|
||||||
|
message["session_key"] = session.inboundGroupSession.export_session(0);
|
||||||
|
await client.sendToDevice(
|
||||||
|
[requestingDevice],
|
||||||
|
"m.forwarded_room_key",
|
||||||
|
message,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,10 @@ class SessionKey {
|
||||||
Map<String, int> indexes;
|
Map<String, int> indexes;
|
||||||
InboundGroupSession inboundGroupSession;
|
InboundGroupSession inboundGroupSession;
|
||||||
final String key;
|
final String key;
|
||||||
|
List<dynamic> get forwardingCurve25519KeyChain =>
|
||||||
|
content["forwarding_curve25519_key_chain"] ?? [];
|
||||||
|
String get senderClaimedEd25519Key =>
|
||||||
|
content["sender_claimed_ed25519_key"] ?? "";
|
||||||
|
|
||||||
SessionKey({this.content, this.inboundGroupSession, this.key, this.indexes});
|
SessionKey({this.content, this.inboundGroupSession, this.key, this.indexes});
|
||||||
|
|
||||||
|
|
|
@ -209,5 +209,42 @@ void main() {
|
||||||
expect(resp1, null);
|
expect(resp1, null);
|
||||||
expect(resp2, "42");
|
expect(resp2, "42");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("requestKey", () async {
|
||||||
|
Client matrix = Client("testclient", debug: true);
|
||||||
|
matrix.httpClient = FakeMatrixApi();
|
||||||
|
await matrix.checkServer("https://fakeServer.notExisting");
|
||||||
|
await matrix.login("test", "1234");
|
||||||
|
|
||||||
|
Event event = Event.fromJson(
|
||||||
|
jsonObj, Room(id: "!1234:example.com", client: matrix));
|
||||||
|
String exception;
|
||||||
|
try {
|
||||||
|
await event.requestKey();
|
||||||
|
} catch (e) {
|
||||||
|
exception = e;
|
||||||
|
}
|
||||||
|
expect(exception, "Session key not unknown");
|
||||||
|
|
||||||
|
event = Event.fromJson({
|
||||||
|
"event_id": id,
|
||||||
|
"sender": senderID,
|
||||||
|
"origin_server_ts": timestamp,
|
||||||
|
"type": "m.room.encrypted",
|
||||||
|
"room_id": "1234",
|
||||||
|
"status": 2,
|
||||||
|
"content": json.encode({
|
||||||
|
"msgtype": "m.bad.encrypted",
|
||||||
|
"body": DecryptError.UNKNOWN_SESSION,
|
||||||
|
"algorithm": "m.megolm.v1.aes-sha2",
|
||||||
|
"ciphertext": "AwgAEnACgAkLmt6qF84IK++J7UDH2Za1YVchHyprqTqsg...",
|
||||||
|
"device_id": "RJYKSTBOIE",
|
||||||
|
"sender_key": "IlRMeOPX2e0MurIyfWEucYBRVOEEUMrOHqn/8mLqMjA",
|
||||||
|
"session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ"
|
||||||
|
}),
|
||||||
|
}, Room(id: "!1234:example.com", client: matrix));
|
||||||
|
|
||||||
|
await event.requestKey();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,7 @@ class FakeMatrixApi extends MockClient {
|
||||||
return Response(json.encode(res), 405);
|
return Response(json.encode(res), 405);
|
||||||
}
|
}
|
||||||
} else if (method == "PUT" &&
|
} else if (method == "PUT" &&
|
||||||
action.contains("/client/r0/sendToDevice/m.room.encrypted/")) {
|
action.contains("/client/r0/sendToDevice/")) {
|
||||||
return Response(json.encode({}), 200);
|
return Response(json.encode({}), 200);
|
||||||
} else if (method == "GET" &&
|
} else if (method == "GET" &&
|
||||||
action.contains("/client/r0/rooms/") &&
|
action.contains("/client/r0/rooms/") &&
|
||||||
|
|
70
test/room_key_request_test.dart
Normal file
70
test/room_key_request_test.dart
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 Zender & Kurtz GbR.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Christian Pauly <krille@famedly.com>
|
||||||
|
* Marcel Radzio <mtrnord@famedly.com>
|
||||||
|
*
|
||||||
|
* This file is part of famedlysdk.
|
||||||
|
*
|
||||||
|
* famedlysdk is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* famedlysdk is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with famedlysdk. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'package:famedlysdk/famedlysdk.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import 'fake_matrix_api.dart';
|
||||||
|
import 'fake_store.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
/// All Tests related to device keys
|
||||||
|
group("Room Key Request", () {
|
||||||
|
test("fromJson", () async {
|
||||||
|
Map<String, dynamic> rawJson = {
|
||||||
|
"content": {
|
||||||
|
"action": "request",
|
||||||
|
"body": {
|
||||||
|
"algorithm": "m.megolm.v1.aes-sha2",
|
||||||
|
"room_id": "!726s6s6q:example.com",
|
||||||
|
"sender_key": "RF3s+E7RkTQTGF2d8Deol0FkQvgII2aJDf3/Jp5mxVU",
|
||||||
|
"session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ"
|
||||||
|
},
|
||||||
|
"request_id": "1495474790150.19",
|
||||||
|
"requesting_device_id": "JLAFKJWSCS"
|
||||||
|
},
|
||||||
|
"type": "m.room_key_request",
|
||||||
|
"sender": "@alice:example.com"
|
||||||
|
};
|
||||||
|
ToDeviceEvent toDeviceEvent = ToDeviceEvent.fromJson(rawJson);
|
||||||
|
expect(toDeviceEvent.content, rawJson["content"]);
|
||||||
|
expect(toDeviceEvent.sender, rawJson["sender"]);
|
||||||
|
expect(toDeviceEvent.type, rawJson["type"]);
|
||||||
|
|
||||||
|
Client matrix = Client("testclient", debug: true);
|
||||||
|
matrix.httpClient = FakeMatrixApi();
|
||||||
|
matrix.storeAPI = FakeStore(matrix, {});
|
||||||
|
await matrix.checkServer("https://fakeServer.notExisting");
|
||||||
|
await matrix.login("test", "1234");
|
||||||
|
Room room = matrix.getRoomById("!726s6s6q:example.com");
|
||||||
|
if (matrix.encryptionEnabled) {
|
||||||
|
await room.createOutboundGroupSession();
|
||||||
|
rawJson["content"]["body"]["session_id"] = room.sessionKeys.keys.first;
|
||||||
|
|
||||||
|
RoomKeyRequest roomKeyRequest = RoomKeyRequest.fromToDeviceEvent(
|
||||||
|
ToDeviceEvent.fromJson(rawJson), matrix);
|
||||||
|
await roomKeyRequest.forwardKey();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in a new issue