/*
* Famedly Matrix SDK
* Copyright (C) 2020, 2021 Famedly GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
import 'dart:convert';
import 'package:canonical_json/canonical_json.dart';
import 'package:collection/collection.dart' show IterableExtension;
import 'package:matrix/matrix.dart';
import 'package:olm/olm.dart' as olm;
import '../../encryption.dart';
enum UserVerifiedStatus { verified, unknown, unknownDevice }
class DeviceKeysList {
Client client;
String userId;
bool outdated = true;
Map deviceKeys = {};
Map crossSigningKeys = {};
SignableKey? getKey(String id) => deviceKeys[id] ?? crossSigningKeys[id];
CrossSigningKey? getCrossSigningKey(String type) =>
crossSigningKeys.values.firstWhereOrNull((k) => k.usage.contains(type));
CrossSigningKey? get masterKey => getCrossSigningKey('master');
CrossSigningKey? get selfSigningKey => getCrossSigningKey('self_signing');
CrossSigningKey? get userSigningKey => getCrossSigningKey('user_signing');
UserVerifiedStatus get verified {
if (masterKey == null) {
return UserVerifiedStatus.unknown;
}
if (masterKey!.verified) {
for (final key in deviceKeys.values) {
if (!key.verified) {
return UserVerifiedStatus.unknownDevice;
}
}
return UserVerifiedStatus.verified;
} else {
for (final key in deviceKeys.values) {
if (!key.verified) {
return UserVerifiedStatus.unknown;
}
}
return UserVerifiedStatus.verified;
}
}
/// Starts a verification with this device. This might need to create a new
/// direct chat to send the verification request over this room. For this you
/// can set parameters here.
Future startVerification({
bool? newDirectChatEnableEncryption,
List? newDirectChatInitialState,
}) async {
final encryption = client.encryption;
if (encryption == null) {
throw Exception('Encryption not enabled');
}
if (userId != client.userID) {
// in-room verification with someone else
final roomId = await client.startDirectChat(
userId,
enableEncryption: newDirectChatEnableEncryption,
initialState: newDirectChatInitialState,
waitForSync: false,
);
final room =
client.getRoomById(roomId) ?? Room(id: roomId, client: client);
final request =
KeyVerification(encryption: 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: encryption, userId: userId, deviceId: '*');
await request.start();
encryption.keyVerificationManager.addRequest(request);
return request;
}
}
DeviceKeysList.fromDbJson(
Map dbEntry,
List