This commit is contained in:
Sorunome 2020-05-22 13:18:45 +02:00
parent 29721f00a8
commit c9a0c5302a
No known key found for this signature in database
GPG Key ID: B19471D07FC9BE9C
5 changed files with 91 additions and 48 deletions

View File

@ -1664,7 +1664,8 @@ class Client {
if (entry.isValid) { if (entry.isValid) {
// is this a new key or the same one as an old one? // is this a new key or the same one as an old one?
// better store an update - the signatures might have changed! // better store an update - the signatures might have changed!
if (!oldKeys.containsKey(deviceId) || oldKeys[deviceId].ed25519Key == entry.ed25519Key) { if (!oldKeys.containsKey(deviceId) ||
oldKeys[deviceId].ed25519Key == entry.ed25519Key) {
if (oldKeys.containsKey(deviceId)) { if (oldKeys.containsKey(deviceId)) {
// be sure to save the verified status // be sure to save the verified status
entry.setDirectVerified(oldKeys[deviceId].directVerified); entry.setDirectVerified(oldKeys[deviceId].directVerified);
@ -1681,7 +1682,8 @@ class Client {
// This shouldn't ever happen. The same device ID has gotten // This shouldn't ever happen. The same device ID has gotten
// a new public key. So we ignore the update. TODO: ask krille // a new public key. So we ignore the update. TODO: ask krille
// if we should instead use the new key with unknown verified / blocked status // if we should instead use the new key with unknown verified / blocked status
_userDeviceKeys[userId].deviceKeys[deviceId] = oldKeys[deviceId]; _userDeviceKeys[userId].deviceKeys[deviceId] =
oldKeys[deviceId];
} }
if (database != null) { if (database != null) {
dbActions.add(() => database.storeUserDeviceKey( dbActions.add(() => database.storeUserDeviceKey(
@ -1713,24 +1715,33 @@ class Client {
} }
} }
// next we parse and persist the cross signing keys // next we parse and persist the cross signing keys
for (final keyType in ['master_keys', 'self_signing_keys', 'user_signing_keys']) { for (final keyType in [
'master_keys',
'self_signing_keys',
'user_signing_keys'
]) {
if (!(response[keyType] is Map)) { if (!(response[keyType] is Map)) {
continue; continue;
} }
for (final rawDeviceKeyListEntry in response[keyType].entries) { for (final rawDeviceKeyListEntry in response[keyType].entries) {
final String userId = rawDeviceKeyListEntry.key; final String userId = rawDeviceKeyListEntry.key;
final oldKeys = Map<String, CrossSigningKey>.from(_userDeviceKeys[userId].crossSigningKeys); final oldKeys = Map<String, CrossSigningKey>.from(
_userDeviceKeys[userId].crossSigningKeys);
_userDeviceKeys[userId].crossSigningKeys = {}; _userDeviceKeys[userId].crossSigningKeys = {};
// add the types we arne't handling atm back // add the types we arne't handling atm back
for (final oldEntry in oldKeys.entries) { for (final oldEntry in oldKeys.entries) {
if (!oldEntry.value.usage.contains(keyType.substring(0, keyType.length - '_keys'.length))) { if (!oldEntry.value.usage.contains(
_userDeviceKeys[userId].crossSigningKeys[oldEntry.key] = oldEntry.value; keyType.substring(0, keyType.length - '_keys'.length))) {
_userDeviceKeys[userId].crossSigningKeys[oldEntry.key] =
oldEntry.value;
} }
} }
final entry = CrossSigningKey.fromJson(rawDeviceKeyListEntry.value, this); final entry =
CrossSigningKey.fromJson(rawDeviceKeyListEntry.value, this);
if (entry.isValid) { if (entry.isValid) {
final publicKey = entry.publicKey; final publicKey = entry.publicKey;
if (!oldKeys.containsKey(publicKey) || oldKeys[publicKey].ed25519Key == entry.ed25519Key) { if (!oldKeys.containsKey(publicKey) ||
oldKeys[publicKey].ed25519Key == entry.ed25519Key) {
if (oldKeys.containsKey(publicKey)) { if (oldKeys.containsKey(publicKey)) {
// be sure to save the verification status // be sure to save the verification status
entry.setDirectVerified(oldKeys[publicKey].directVerified); entry.setDirectVerified(oldKeys[publicKey].directVerified);
@ -1742,34 +1753,37 @@ class Client {
// This shouldn't ever happen. The same device ID has gotten // This shouldn't ever happen. The same device ID has gotten
// a new public key. So we ignore the update. TODO: ask krille // a new public key. So we ignore the update. TODO: ask krille
// if we should instead use the new key with unknown verified / blocked status // if we should instead use the new key with unknown verified / blocked status
_userDeviceKeys[userId].crossSigningKeys[publicKey] = oldKeys[publicKey]; _userDeviceKeys[userId].crossSigningKeys[publicKey] =
oldKeys[publicKey];
} }
if (database != null) { if (database != null) {
dbActions.add(() => database.storeUserCrossSigningKey( dbActions.add(() => database.storeUserCrossSigningKey(
id, id,
userId, userId,
publicKey, publicKey,
json.encode(entry.toJson()), json.encode(entry.toJson()),
entry.directVerified, entry.directVerified,
entry.blocked, entry.blocked,
)); ));
} }
} }
// delete old/unused entries // delete old/unused entries
if (database != null) { if (database != null) {
for (final oldCrossSigningKeyEntry in oldKeys.entries) { for (final oldCrossSigningKeyEntry in oldKeys.entries) {
final publicKey = oldCrossSigningKeyEntry.key; final publicKey = oldCrossSigningKeyEntry.key;
if (!_userDeviceKeys[userId].crossSigningKeys.containsKey(publicKey)) { if (!_userDeviceKeys[userId]
.crossSigningKeys
.containsKey(publicKey)) {
// we need to remove an old key // we need to remove an old key
dbActions.add( dbActions.add(() => database.removeUserCrossSigningKey(
() => database.removeUserCrossSigningKey(id, userId, publicKey)); id, userId, publicKey));
} }
} }
} }
_userDeviceKeys[userId].outdated = false; _userDeviceKeys[userId].outdated = false;
if (database != null) { if (database != null) {
dbActions dbActions.add(
.add(() => database.storeUserDeviceKeysInfo(id, userId, false)); () => database.storeUserDeviceKeysInfo(id, userId, false));
} }
} }
} }

View File

@ -47,7 +47,8 @@ class Database extends _$Database {
await m.createTable(userCrossSigningKeys); await m.createTable(userCrossSigningKeys);
await m.createIndex(userCrossSigningKeysIndex); await m.createIndex(userCrossSigningKeysIndex);
// mark all keys as outdated so that the cross signing keys will be fetched // mark all keys as outdated so that the cross signing keys will be fetched
await m.issueCustomQuery('UPDATE user_device_keys SET outdated = true'); await m.issueCustomQuery(
'UPDATE user_device_keys SET outdated = true');
from++; from++;
} }
}, },
@ -59,7 +60,8 @@ class Database extends _$Database {
return res.first; return res.first;
} }
Future<Map<String, sdk.DeviceKeysList>> getUserDeviceKeys(sdk.Client client) async { Future<Map<String, sdk.DeviceKeysList>> getUserDeviceKeys(
sdk.Client client) async {
final deviceKeys = await getAllUserDeviceKeys(client.id).get(); final deviceKeys = await getAllUserDeviceKeys(client.id).get();
if (deviceKeys.isEmpty) { if (deviceKeys.isEmpty) {
return {}; return {};
@ -68,10 +70,11 @@ class Database extends _$Database {
final crossSigningKeys = await getAllUserCrossSigningKeys(client.id).get(); final crossSigningKeys = await getAllUserCrossSigningKeys(client.id).get();
final res = <String, sdk.DeviceKeysList>{}; final res = <String, sdk.DeviceKeysList>{};
for (final entry in deviceKeys) { for (final entry in deviceKeys) {
res[entry.userId] = sdk.DeviceKeysList.fromDb(entry, res[entry.userId] = sdk.DeviceKeysList.fromDb(
deviceKeysKeys.where((k) => k.userId == entry.userId).toList(), entry,
crossSigningKeys.where((k) => k.userId == entry.userId).toList(), deviceKeysKeys.where((k) => k.userId == entry.userId).toList(),
client); crossSigningKeys.where((k) => k.userId == entry.userId).toList(),
client);
} }
return res; return res;
} }

View File

@ -3,7 +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 '../database/database.dart' show DbUserDeviceKey, DbUserDeviceKeysKey, DbUserCrossSigningKey; import '../database/database.dart'
show DbUserDeviceKey, DbUserDeviceKeysKey, DbUserCrossSigningKey;
import '../event.dart'; import '../event.dart';
import 'key_verification.dart'; import 'key_verification.dart';
@ -14,7 +15,11 @@ class DeviceKeysList {
Map<String, DeviceKeys> deviceKeys = {}; Map<String, DeviceKeys> deviceKeys = {};
Map<String, CrossSigningKey> crossSigningKeys = {}; Map<String, CrossSigningKey> crossSigningKeys = {};
DeviceKeysList.fromDb(DbUserDeviceKey dbEntry, List<DbUserDeviceKeysKey> childEntries, List<DbUserCrossSigningKey> crossSigningEntries, Client cl) { DeviceKeysList.fromDb(
DbUserDeviceKey dbEntry,
List<DbUserDeviceKeysKey> childEntries,
List<DbUserCrossSigningKey> crossSigningEntries,
Client cl) {
client = cl; client = cl;
userId = dbEntry.userId; userId = dbEntry.userId;
outdated = dbEntry.outdated; outdated = dbEntry.outdated;
@ -93,7 +98,9 @@ abstract class _SignedKey {
try { try {
return hasValidSignatureChain(); return hasValidSignatureChain();
} catch (err, stacktrace) { } catch (err, stacktrace) {
print('[Cross Signing] Error during trying to determine signature chain: ' + err.toString()); print(
'[Cross Signing] Error during trying to determine signature chain: ' +
err.toString());
print(stacktrace); print(stacktrace);
return false; return false;
} }
@ -134,7 +141,8 @@ abstract class _SignedKey {
visited.add(setKey); visited.add(setKey);
for (final signatureEntries in signatures.entries) { for (final signatureEntries in signatures.entries) {
final otherUserId = signatureEntries.key; final otherUserId = signatureEntries.key;
if (!(signatureEntries.value is Map) || !client.userDeviceKeys.containsKey(otherUserId)) { if (!(signatureEntries.value is Map) ||
!client.userDeviceKeys.containsKey(otherUserId)) {
continue; continue;
} }
for (final signatureEntry in signatureEntries.value.entries) { for (final signatureEntry in signatureEntries.value.entries) {
@ -147,7 +155,8 @@ abstract class _SignedKey {
_SignedKey key; _SignedKey key;
if (client.userDeviceKeys[otherUserId].deviceKeys.containsKey(keyId)) { if (client.userDeviceKeys[otherUserId].deviceKeys.containsKey(keyId)) {
key = client.userDeviceKeys[otherUserId].deviceKeys[keyId]; key = client.userDeviceKeys[otherUserId].deviceKeys[keyId];
} else if (client.userDeviceKeys[otherUserId].crossSigningKeys.containsKey(keyId)) { } else if (client.userDeviceKeys[otherUserId].crossSigningKeys
.containsKey(keyId)) {
key = client.userDeviceKeys[otherUserId].crossSigningKeys[keyId]; key = client.userDeviceKeys[otherUserId].crossSigningKeys[keyId];
} else { } else {
continue; continue;
@ -157,7 +166,9 @@ abstract class _SignedKey {
} }
var haveValidSignature = false; var haveValidSignature = false;
var gotSignatureFromCache = false; var gotSignatureFromCache = false;
if (validSignatures != null && validSignatures.containsKey(otherUserId) && validSignatures[otherUserId].containsKey(fullKeyId)) { if (validSignatures != null &&
validSignatures.containsKey(otherUserId) &&
validSignatures[otherUserId].containsKey(fullKeyId)) {
if (validSignatures[otherUserId][fullKeyId] == true) { if (validSignatures[otherUserId][fullKeyId] == true) {
haveValidSignature = true; haveValidSignature = true;
gotSignatureFromCache = true; gotSignatureFromCache = true;
@ -209,16 +220,19 @@ class CrossSigningKey extends _SignedKey {
String get publicKey => identifier; String get publicKey => identifier;
List<String> usage; List<String> usage;
bool get isValid => userId != null && publicKey != null && keys != null && ed25519Key != null; bool get isValid =>
userId != null && publicKey != null && keys != null && ed25519Key != null;
Future<void> setVerified(bool newVerified) { Future<void> setVerified(bool newVerified) {
_verified = newVerified; _verified = newVerified;
return client.database?.setVerifiedUserCrossSigningKey(newVerified, client.id, userId, publicKey); return client.database?.setVerifiedUserCrossSigningKey(
newVerified, client.id, userId, publicKey);
} }
Future<void> setBlocked(bool newBlocked) { Future<void> setBlocked(bool newBlocked) {
blocked = newBlocked; blocked = newBlocked;
return client.database?.setBlockedUserCrossSigningKey(newBlocked, client.id, userId, publicKey); return client.database?.setBlockedUserCrossSigningKey(
newBlocked, client.id, userId, publicKey);
} }
CrossSigningKey.fromDb(DbUserCrossSigningKey dbEntry, Client cl) { CrossSigningKey.fromDb(DbUserCrossSigningKey dbEntry, Client cl) {
@ -229,7 +243,9 @@ class CrossSigningKey extends _SignedKey {
identifier = dbEntry.publicKey; identifier = dbEntry.publicKey;
usage = json['usage'].cast<String>(); usage = json['usage'].cast<String>();
keys = json['keys'] != null ? Map<String, String>.from(json['keys']) : null; keys = json['keys'] != null ? Map<String, String>.from(json['keys']) : null;
signatures = json['signatures'] != null ? Map<String, dynamic>.from(json['signatures']) : null; signatures = json['signatures'] != null
? Map<String, dynamic>.from(json['signatures'])
: null;
_verified = dbEntry.verified; _verified = dbEntry.verified;
blocked = dbEntry.blocked; blocked = dbEntry.blocked;
} }

View File

@ -291,7 +291,8 @@ class KeyVerification {
return []; return [];
} }
Future<void> verifyKeys(Map<String, String> keys, Future<bool> Function(String, dynamic) verifier) async { Future<void> verifyKeys(Map<String, String> keys,
Future<bool> Function(String, dynamic) verifier) async {
final verifiedDevices = <String>[]; final verifiedDevices = <String>[];
if (!client.userDeviceKeys.containsKey(userId)) { if (!client.userDeviceKeys.containsKey(userId)) {
@ -310,9 +311,11 @@ class KeyVerification {
return; return;
} }
verifiedDevices.add(verifyDeviceId); verifiedDevices.add(verifyDeviceId);
} else if (client.userDeviceKeys[userId].crossSigningKeys.containsKey(verifyDeviceId)) { } else if (client.userDeviceKeys[userId].crossSigningKeys
.containsKey(verifyDeviceId)) {
// this is a cross signing key! // this is a cross signing key!
if (!(await verifier(keyInfo, client.userDeviceKeys[userId].crossSigningKeys[verifyDeviceId]))) { if (!(await verifier(keyInfo,
client.userDeviceKeys[userId].crossSigningKeys[verifyDeviceId]))) {
await cancel('m.key_mismatch'); await cancel('m.key_mismatch');
return; return;
} }
@ -321,10 +324,14 @@ class KeyVerification {
} }
// okay, we reached this far, so all the devices are verified! // okay, we reached this far, so all the devices are verified!
for (final verifyDeviceId in verifiedDevices) { for (final verifyDeviceId in verifiedDevices) {
if (client.userDeviceKeys[userId].deviceKeys.containsKey(verifyDeviceId)) { if (client.userDeviceKeys[userId].deviceKeys
await client.userDeviceKeys[userId].deviceKeys[verifyDeviceId].setVerified(true); .containsKey(verifyDeviceId)) {
} else if (client.userDeviceKeys[userId].crossSigningKeys.containsKey(verifyDeviceId)) { await client.userDeviceKeys[userId].deviceKeys[verifyDeviceId]
await client.userDeviceKeys[userId].crossSigningKeys[verifyDeviceId].setVerified(true); .setVerified(true);
} else if (client.userDeviceKeys[userId].crossSigningKeys
.containsKey(verifyDeviceId)) {
await client.userDeviceKeys[userId].crossSigningKeys[verifyDeviceId]
.setVerified(true);
// TODO: sign the other persons master key // TODO: sign the other persons master key
} }
} }
@ -708,9 +715,13 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod {
} }
await request.verifyKeys(mac, (String mac, dynamic device) async { await request.verifyKeys(mac, (String mac, dynamic device) async {
if (device is DeviceKeys) { if (device is DeviceKeys) {
return mac == _calculateMac(device.ed25519Key, baseInfo + 'ed25519:' + device.deviceId); return mac ==
_calculateMac(
device.ed25519Key, baseInfo + 'ed25519:' + device.deviceId);
} else if (device is CrossSigningKey) { } else if (device is CrossSigningKey) {
return mac == _calculateMac(device.ed25519Key, baseInfo + 'ed25519:' + device.publicKey); return mac ==
_calculateMac(
device.ed25519Key, baseInfo + 'ed25519:' + device.publicKey);
} }
return false; return false;
}); });

View File

@ -65,8 +65,7 @@ void main() {
final key = DeviceKeys.fromJson(rawJson, null); final key = DeviceKeys.fromJson(rawJson, null);
rawJson.remove('verified'); rawJson.remove('verified');
rawJson.remove('blocked'); rawJson.remove('blocked');
expect(json.encode(key.toJson()), expect(json.encode(key.toJson()), json.encode(rawJson));
json.encode(rawJson));
expect(key.verified, false); expect(key.verified, false);
expect(key.blocked, true); expect(key.blocked, true);
expect(json.encode(DeviceKeysList.fromJson(rawListJson, null).toJson()), expect(json.encode(DeviceKeysList.fromJson(rawListJson, null).toJson()),