in-room verification to verify users instead of devices
This commit is contained in:
parent
aefe029c0a
commit
dda0b17724
|
@ -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(
|
Future<void> _handleRooms(
|
||||||
Map<String, dynamic> rooms, Membership membership) async {
|
Map<String, dynamic> rooms, Membership membership) async {
|
||||||
for (final entry in rooms.entries) {
|
for (final entry in rooms.entries) {
|
||||||
|
@ -1416,6 +1464,11 @@ class Client {
|
||||||
await database.storeEventUpdate(id, update);
|
await database.storeEventUpdate(id, update);
|
||||||
}
|
}
|
||||||
_updateRoomsByEventUpdate(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);
|
onEvent.add(update);
|
||||||
|
|
||||||
if (event['type'] == 'm.call.invite') {
|
if (event['type'] == 'm.call.invite') {
|
||||||
|
|
|
@ -39,7 +39,10 @@ class CrossSigning {
|
||||||
} finally {
|
} finally {
|
||||||
keyObj.free();
|
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';
|
throw 'Master or user keys not found';
|
||||||
}
|
}
|
||||||
final masterKey = client.userDeviceKeys[client.userID].masterKey;
|
final masterKey = client.userDeviceKeys[client.userID].masterKey;
|
||||||
|
@ -49,7 +52,10 @@ class CrossSigning {
|
||||||
// master key is valid, set it to verified
|
// master key is valid, set it to verified
|
||||||
masterKey.setVerified(true, false);
|
masterKey.setVerified(true, false);
|
||||||
// and now sign bout our own key and our master key
|
// 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) {
|
bool signable(List<SignedKey> keys) {
|
||||||
|
@ -82,6 +88,7 @@ class CrossSigning {
|
||||||
if (!signatures[key.userId].containsKey(key.identifier)) {
|
if (!signatures[key.userId].containsKey(key.identifier)) {
|
||||||
signatures[key.userId][key.identifier] =
|
signatures[key.userId][key.identifier] =
|
||||||
Map<String, dynamic>.from(key.toJson());
|
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');
|
signatures[key.userId][key.identifier].remove('signatures');
|
||||||
}
|
}
|
||||||
if (!signatures[key.userId][key.identifier].containsKey('signatures')) {
|
if (!signatures[key.userId][key.identifier].containsKey('signatures')) {
|
||||||
|
|
|
@ -3,6 +3,8 @@ import 'package:canonical_json/canonical_json.dart';
|
||||||
import 'package:olm/olm.dart' as olm;
|
import 'package:olm/olm.dart' as olm;
|
||||||
|
|
||||||
import '../client.dart';
|
import '../client.dart';
|
||||||
|
import '../user.dart';
|
||||||
|
import '../room.dart';
|
||||||
import '../database/database.dart'
|
import '../database/database.dart'
|
||||||
show DbUserDeviceKey, DbUserDeviceKeysKey, DbUserCrossSigningKey;
|
show DbUserDeviceKey, DbUserDeviceKeysKey, DbUserCrossSigningKey;
|
||||||
import '../event.dart';
|
import '../event.dart';
|
||||||
|
@ -49,6 +51,20 @@ class DeviceKeysList {
|
||||||
return UserVerifiedStatus.unknown;
|
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(
|
DeviceKeysList.fromDb(
|
||||||
DbUserDeviceKey dbEntry,
|
DbUserDeviceKey dbEntry,
|
||||||
List<DbUserDeviceKeysKey> childEntries,
|
List<DbUserDeviceKeysKey> childEntries,
|
||||||
|
|
|
@ -83,11 +83,11 @@ List<int> _bytesToInt(Uint8List bytes, int totalBits) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
final VERIFICATION_METHODS = [_KeyVerificationMethodSas.type];
|
final VERIFICATION_METHODS = ['m.sas.v1'];
|
||||||
|
|
||||||
_KeyVerificationMethod _makeVerificationMethod(
|
_KeyVerificationMethod _makeVerificationMethod(
|
||||||
String type, KeyVerification request) {
|
String type, KeyVerification request) {
|
||||||
if (type == _KeyVerificationMethodSas.type) {
|
if (type == 'm.sas.v1') {
|
||||||
return _KeyVerificationMethodSas(request: request);
|
return _KeyVerificationMethodSas(request: request);
|
||||||
}
|
}
|
||||||
throw 'Unkown method type';
|
throw 'Unkown method type';
|
||||||
|
@ -138,7 +138,7 @@ class KeyVerification {
|
||||||
Future<void> sendStart() async {
|
Future<void> sendStart() async {
|
||||||
await send('m.key.verification.request', {
|
await send('m.key.verification.request', {
|
||||||
'methods': VERIFICATION_METHODS,
|
'methods': VERIFICATION_METHODS,
|
||||||
'timestamp': DateTime.now().millisecondsSinceEpoch,
|
if (room == null) 'timestamp': DateTime.now().millisecondsSinceEpoch,
|
||||||
});
|
});
|
||||||
startedVerification = true;
|
startedVerification = true;
|
||||||
setState(KeyVerificationState.waitingAccept);
|
setState(KeyVerificationState.waitingAccept);
|
||||||
|
@ -189,6 +189,7 @@ class KeyVerification {
|
||||||
setState(KeyVerificationState.askAccept);
|
setState(KeyVerificationState.askAccept);
|
||||||
break;
|
break;
|
||||||
case 'm.key.verification.ready':
|
case 'm.key.verification.ready':
|
||||||
|
_deviceId ??= payload['from_device'];
|
||||||
possibleMethods =
|
possibleMethods =
|
||||||
_intersect(VERIFICATION_METHODS, payload['methods']);
|
_intersect(VERIFICATION_METHODS, payload['methods']);
|
||||||
if (possibleMethods.isEmpty) {
|
if (possibleMethods.isEmpty) {
|
||||||
|
@ -205,6 +206,32 @@ class KeyVerification {
|
||||||
_deviceId ??= payload['from_device'];
|
_deviceId ??= payload['from_device'];
|
||||||
print('Setting device id start: ' + _deviceId.toString());
|
print('Setting device id start: ' + _deviceId.toString());
|
||||||
transactionId ??= eventId ?? payload['transaction_id'];
|
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]))) {
|
if (!(await verifyLastStep(['m.key.verification.request', null]))) {
|
||||||
return; // abort
|
return; // abort
|
||||||
}
|
}
|
||||||
|
@ -221,6 +248,7 @@ class KeyVerification {
|
||||||
startPaylaod = payload;
|
startPaylaod = payload;
|
||||||
setState(KeyVerificationState.askAccept);
|
setState(KeyVerificationState.askAccept);
|
||||||
} else {
|
} else {
|
||||||
|
print('handling start in method.....');
|
||||||
await method.handlePayload(type, payload);
|
await method.handlePayload(type, payload);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -247,6 +275,13 @@ class KeyVerification {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void otherDeviceAccepted() {
|
||||||
|
canceled = true;
|
||||||
|
canceledCode = 'm.accepted';
|
||||||
|
canceledReason = 'm.accepted';
|
||||||
|
setState(KeyVerificationState.error);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> openSSSS(
|
Future<void> openSSSS(
|
||||||
{String password, String recoveryKey, bool skip = false}) async {
|
{String password, String recoveryKey, bool skip = false}) async {
|
||||||
final next = () {
|
final next = () {
|
||||||
|
@ -482,6 +517,9 @@ abstract class _KeyVerificationMethod {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String _type;
|
||||||
|
String get type => _type;
|
||||||
|
|
||||||
Future<void> sendStart();
|
Future<void> sendStart();
|
||||||
void dispose() {}
|
void dispose() {}
|
||||||
}
|
}
|
||||||
|
@ -495,7 +533,8 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod {
|
||||||
_KeyVerificationMethodSas({KeyVerification request})
|
_KeyVerificationMethodSas({KeyVerification request})
|
||||||
: super(request: request);
|
: super(request: request);
|
||||||
|
|
||||||
static String type = 'm.sas.v1';
|
@override
|
||||||
|
String _type = 'm.sas.v1';
|
||||||
|
|
||||||
String keyAgreementProtocol;
|
String keyAgreementProtocol;
|
||||||
String hash;
|
String hash;
|
||||||
|
|
|
@ -24,7 +24,7 @@ dependencies:
|
||||||
olm:
|
olm:
|
||||||
git:
|
git:
|
||||||
url: https://gitlab.com/famedly/libraries/dart-olm.git
|
url: https://gitlab.com/famedly/libraries/dart-olm.git
|
||||||
ref: 8749474d611f02a89893e067b6e479ebfd40c51d
|
ref: a6cde466e707b54e39df8d035a0a79714621bc0b
|
||||||
|
|
||||||
matrix_file_e2ee:
|
matrix_file_e2ee:
|
||||||
path: /home/sorunome/repos/famedly/matrix_file_e2ee
|
path: /home/sorunome/repos/famedly/matrix_file_e2ee
|
||||||
|
|
Loading…
Reference in a new issue