feat: Add broadcast to-device verification

This commit is contained in:
Sorunome 2020-10-30 11:36:08 +01:00
parent f9f18641e3
commit 5924e57cf1
No known key found for this signature in database
GPG Key ID: B19471D07FC9BE9C
4 changed files with 67 additions and 17 deletions

View File

@ -563,6 +563,7 @@ class KeyManager {
stacktrace);
}
}
// TODO: also don't request from others if we have an index of 0 now
if (!hadPreviously &&
getInboundGroupSession(room.id, sessionId, senderKey) != null) {
return; // we managed to load the session from online backup, no need to care about it now

View File

@ -65,9 +65,12 @@ class KeyVerificationManager {
return; // TODO: send cancel with unknown transaction id
}
if (_requests.containsKey(transactionId)) {
await _requests[transactionId].handlePayload(event.type, event.content);
// make sure that new requests can't come from ourself
if (!{'m.key.verification.request'}.contains(event.type)) {
await _requests[transactionId].handlePayload(event.type, event.content);
}
} else {
if (!['m.key.verification.request', 'm.key.verification.start']
if (!{'m.key.verification.request', 'm.key.verification.start'}
.contains(event.type)) {
return; // we can only start on these
}
@ -115,7 +118,7 @@ class KeyVerificationManager {
_requests.remove(transactionId);
}
} else if (event['sender'] != client.userID) {
if (!['m.key.verification.request', 'm.key.verification.start']
if (!{'m.key.verification.request', 'm.key.verification.start'}
.contains(type)) {
return; // we can only start on these
}

View File

@ -140,6 +140,9 @@ class KeyVerification {
bool canceled = false;
String canceledCode;
String canceledReason;
bool get isDone =>
canceled ||
{KeyVerificationState.error, KeyVerificationState.done}.contains(state);
KeyVerification(
{this.encryption,
@ -200,6 +203,9 @@ class KeyVerification {
Future<void> handlePayload(String type, Map<String, dynamic> payload,
[String eventId]) async {
if (isDone) {
return; // no need to do anything with already canceled requests
}
while (_handlePayloadLock) {
await Future.delayed(Duration(milliseconds: 50));
}
@ -235,6 +241,21 @@ class KeyVerification {
setState(KeyVerificationState.askAccept);
break;
case 'm.key.verification.ready':
if (deviceId == '*') {
_deviceId = payload['from_device']; // gotta set the real device id
// and broadcast the cancel to the other devices
final devices = List<DeviceKeys>.from(
client.userDeviceKeys[userId].deviceKeys.values);
devices.removeWhere(
(d) => {deviceId, client.deviceID}.contains(d.deviceId));
final cancelPayload = <String, dynamic>{
'reason': 'Another device accepted the request',
'code': 'm.accepted',
};
makePayload(cancelPayload);
await client.sendToDeviceEncrypted(
devices, 'm.key.verification.cancel', cancelPayload);
}
_deviceId ??= payload['from_device'];
possibleMethods =
_intersect(knownVerificationMethods, payload['methods']);
@ -383,6 +404,9 @@ class KeyVerification {
/// called when the user rejects an incoming verification
Future<void> rejectVerification() async {
if (isDone) {
return;
}
if (!(await verifyLastStep(
['m.key.verification.request', 'm.key.verification.start']))) {
return;
@ -557,7 +581,7 @@ class KeyVerification {
if (room != null) {
Logs.info(
'[Key Verification] Sending to ${userId} in room ${room.id}...');
if (['m.key.verification.request'].contains(type)) {
if ({'m.key.verification.request'}.contains(type)) {
payload['msgtype'] = type;
payload['to'] = userId;
payload['body'] =
@ -572,8 +596,19 @@ class KeyVerification {
} else {
Logs.info(
'[Key Verification] Sending to ${userId} device ${deviceId}...');
await client.sendToDeviceEncrypted(
[client.userDeviceKeys[userId].deviceKeys[deviceId]], type, payload);
if (deviceId == '*') {
if ({'m.key.verification.request'}.contains(type)) {
await client.sendToDevicesOfUserIds({userId}, type, payload);
} else {
Logs.error(
'[Key Verification] Tried to broadcast and un-broadcastable type: ${type}');
}
} else {
await client.sendToDeviceEncrypted(
[client.userDeviceKeys[userId].deviceKeys[deviceId]],
type,
payload);
}
}
}

View File

@ -54,18 +54,29 @@ class DeviceKeysList {
}
Future<KeyVerification> startVerification() async {
final roomId =
await User(userId, room: Room(client: client)).startDirectChat();
if (roomId == null) {
throw 'Unable to start new room';
if (userId != client.userID) {
// in-room verification with someone else
final roomId =
await User(userId, room: Room(client: client)).startDirectChat();
if (roomId == null) {
throw 'Unable to start new room';
}
final room =
client.getRoomById(roomId) ?? Room(id: roomId, client: client);
final request = KeyVerification(
encryption: client.encryption, room: room, userId: userId);
await request.start();
// no need to add to the request client object. As we are doing a room
// verification request that'll happen automatically once we know the transaction id
return request;
} else {
// broadcast self-verification
final request = KeyVerification(
encryption: client.encryption, userId: userId, deviceId: '*');
await request.start();
client.encryption.keyVerificationManager.addRequest(request);
return request;
}
final room = client.getRoomById(roomId) ?? Room(id: roomId, client: client);
final request = KeyVerification(
encryption: client.encryption, room: room, userId: userId);
await request.start();
// no need to add to the request client object. As we are doing a room
// verification request that'll happen automatically once we know the transaction id
return request;
}
DeviceKeysList.fromDb(