in-room verification to verify users instead of devices

This commit is contained in:
Sorunome 2020-05-27 21:35:00 +02:00
parent aefe029c0a
commit dda0b17724
No known key found for this signature in database
GPG key ID: B19471D07FC9BE9C
5 changed files with 122 additions and 7 deletions

View file

@ -1216,6 +1216,54 @@ class Client {
}
}
void _handleRoomKeyVerificationRequest(EventUpdate update) {
final event = update.content;
final type = event['type'].startsWith('m.key.verification.')
? event['type']
: event['content']['msgtype'];
if (!type.startsWith('m.key.verification.')) {
return;
}
if (type == 'm.key.verification.request') {
event['content']['timestamp'] = event['origin_server_ts'];
}
final transactionId =
KeyVerification.getTransactionId(event['content']) ?? event['event_id'];
if (_keyVerificationRequests.containsKey(transactionId)) {
final req = _keyVerificationRequests[transactionId];
if (event['sender'] != userID) {
req.handlePayload(type, event['content'], event['event_id']);
} else if (req.userId == userID && req.deviceId == null) {
req
.handlePayload(type, event['content'], event['event_id'])
.then((ret) {
if (req.deviceId != deviceID) {
req.otherDeviceAccepted();
req.dispose();
_keyVerificationRequests.remove(transactionId);
}
});
}
} else if (event['sender'] != userID) {
final room =
getRoomById(update.roomID) ?? Room(id: update.roomID, client: this);
final newKeyRequest =
KeyVerification(client: this, userId: event['sender'], room: room);
newKeyRequest
.handlePayload(type, event['content'], event['event_id'])
.then((res) {
if (newKeyRequest.state != KeyVerificationState.askAccept) {
// something went wrong, let's just dispose the request
newKeyRequest.dispose();
} else {
// new request! Let's notify it and stuff
_keyVerificationRequests[transactionId] = newKeyRequest;
onKeyVerificationRequest.add(newKeyRequest);
}
});
}
}
Future<void> _handleRooms(
Map<String, dynamic> rooms, Membership membership) async {
for (final entry in rooms.entries) {
@ -1416,6 +1464,11 @@ class Client {
await database.storeEventUpdate(id, update);
}
_updateRoomsByEventUpdate(update);
if (event['type'].startsWith('m.key.verification.') ||
(event['type'] == 'm.room.message' &&
event['content']['msgtype'].startsWith('m.key.verification.'))) {
_handleRoomKeyVerificationRequest(update);
}
onEvent.add(update);
if (event['type'] == 'm.call.invite') {

View file

@ -39,7 +39,10 @@ class CrossSigning {
} finally {
keyObj.free();
}
if (masterPubkey == null || !client.userDeviceKeys.containsKey(client.userID) || !client.userDeviceKeys[client.userID].deviceKeys.containsKey(client.deviceID)) {
if (masterPubkey == null ||
!client.userDeviceKeys.containsKey(client.userID) ||
!client.userDeviceKeys[client.userID].deviceKeys
.containsKey(client.deviceID)) {
throw 'Master or user keys not found';
}
final masterKey = client.userDeviceKeys[client.userID].masterKey;
@ -49,7 +52,10 @@ class CrossSigning {
// master key is valid, set it to verified
masterKey.setVerified(true, false);
// and now sign bout our own key and our master key
await sign([masterKey, client.userDeviceKeys[client.userID].deviceKeys[client.deviceID]]);
await sign([
masterKey,
client.userDeviceKeys[client.userID].deviceKeys[client.deviceID]
]);
}
bool signable(List<SignedKey> keys) {
@ -82,6 +88,7 @@ class CrossSigning {
if (!signatures[key.userId].containsKey(key.identifier)) {
signatures[key.userId][key.identifier] =
Map<String, dynamic>.from(key.toJson());
// we don't need to send all old signatures, so let's just remove them
signatures[key.userId][key.identifier].remove('signatures');
}
if (!signatures[key.userId][key.identifier].containsKey('signatures')) {

View file

@ -3,6 +3,8 @@ import 'package:canonical_json/canonical_json.dart';
import 'package:olm/olm.dart' as olm;
import '../client.dart';
import '../user.dart';
import '../room.dart';
import '../database/database.dart'
show DbUserDeviceKey, DbUserDeviceKeysKey, DbUserCrossSigningKey;
import '../event.dart';
@ -49,6 +51,20 @@ class DeviceKeysList {
return UserVerifiedStatus.unknown;
}
Future<KeyVerification> startVerification() async {
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(client: client, 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(
DbUserDeviceKey dbEntry,
List<DbUserDeviceKeysKey> childEntries,

View file

@ -83,11 +83,11 @@ List<int> _bytesToInt(Uint8List bytes, int totalBits) {
return ret;
}
final VERIFICATION_METHODS = [_KeyVerificationMethodSas.type];
final VERIFICATION_METHODS = ['m.sas.v1'];
_KeyVerificationMethod _makeVerificationMethod(
String type, KeyVerification request) {
if (type == _KeyVerificationMethodSas.type) {
if (type == 'm.sas.v1') {
return _KeyVerificationMethodSas(request: request);
}
throw 'Unkown method type';
@ -138,7 +138,7 @@ class KeyVerification {
Future<void> sendStart() async {
await send('m.key.verification.request', {
'methods': VERIFICATION_METHODS,
'timestamp': DateTime.now().millisecondsSinceEpoch,
if (room == null) 'timestamp': DateTime.now().millisecondsSinceEpoch,
});
startedVerification = true;
setState(KeyVerificationState.waitingAccept);
@ -189,6 +189,7 @@ class KeyVerification {
setState(KeyVerificationState.askAccept);
break;
case 'm.key.verification.ready':
_deviceId ??= payload['from_device'];
possibleMethods =
_intersect(VERIFICATION_METHODS, payload['methods']);
if (possibleMethods.isEmpty) {
@ -205,6 +206,32 @@ class KeyVerification {
_deviceId ??= payload['from_device'];
print('Setting device id start: ' + _deviceId.toString());
transactionId ??= eventId ?? payload['transaction_id'];
if (method != null) {
print('DUPLICATE START');
// the other side sent us a start, even though we already sent one
if (payload['method'] == method.type) {
// same method. Determine priority
final ourEntry = '${client.userID}|${client.deviceID}';
final entries = [ourEntry, '${userId}|${deviceId}'];
entries.sort();
if (entries.first == ourEntry) {
// our start won, nothing to do
print('we won, nothing to do');
return;
} else {
print('They won, handing off');
// the other start won, let's hand off
startedVerification = false; // it is now as if they started
lastStep =
'm.key.verification.request'; // we fake the last step
method.dispose(); // in case anything got created already
}
} else {
// methods don't match up, let's cancel this
await cancel('m.unexpected_message');
return;
}
}
if (!(await verifyLastStep(['m.key.verification.request', null]))) {
return; // abort
}
@ -221,6 +248,7 @@ class KeyVerification {
startPaylaod = payload;
setState(KeyVerificationState.askAccept);
} else {
print('handling start in method.....');
await method.handlePayload(type, payload);
}
break;
@ -247,6 +275,13 @@ class KeyVerification {
}
}
void otherDeviceAccepted() {
canceled = true;
canceledCode = 'm.accepted';
canceledReason = 'm.accepted';
setState(KeyVerificationState.error);
}
Future<void> openSSSS(
{String password, String recoveryKey, bool skip = false}) async {
final next = () {
@ -482,6 +517,9 @@ abstract class _KeyVerificationMethod {
return false;
}
String _type;
String get type => _type;
Future<void> sendStart();
void dispose() {}
}
@ -495,7 +533,8 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod {
_KeyVerificationMethodSas({KeyVerification request})
: super(request: request);
static String type = 'm.sas.v1';
@override
String _type = 'm.sas.v1';
String keyAgreementProtocol;
String hash;

View file

@ -24,7 +24,7 @@ dependencies:
olm:
git:
url: https://gitlab.com/famedly/libraries/dart-olm.git
ref: 8749474d611f02a89893e067b6e479ebfd40c51d
ref: a6cde466e707b54e39df8d035a0a79714621bc0b
matrix_file_e2ee:
path: /home/sorunome/repos/famedly/matrix_file_e2ee