better smoothen out keys

This commit is contained in:
Sorunome 2020-06-06 14:28:18 +02:00
parent 4154c7d0eb
commit e1679d59be
No known key found for this signature in database
GPG Key ID: B19471D07FC9BE9C
11 changed files with 113 additions and 195 deletions

View File

@ -20,4 +20,5 @@ library encryption;
export './encryption/encryption.dart'; export './encryption/encryption.dart';
export './encryption/key_manager.dart'; export './encryption/key_manager.dart';
export './encryption/ssss.dart';
export './encryption/utils/key_verification.dart'; export './encryption/utils/key_verification.dart';

View File

@ -100,7 +100,7 @@ class CrossSigning {
]); ]);
} }
bool signable(List<SignedKey> keys) { bool signable(List<SignableKey> keys) {
for (final key in keys) { for (final key in keys) {
if (key is CrossSigningKey && key.usage.contains('master')) { if (key is CrossSigningKey && key.usage.contains('master')) {
return true; return true;
@ -114,13 +114,13 @@ class CrossSigning {
return false; return false;
} }
Future<void> sign(List<SignedKey> keys) async { Future<void> sign(List<SignableKey> keys) async {
Uint8List selfSigningKey; Uint8List selfSigningKey;
Uint8List userSigningKey; Uint8List userSigningKey;
final signatures = <String, dynamic>{}; final signatures = <String, dynamic>{};
var signedKey = false; var signedKey = false;
final addSignature = final addSignature =
(SignedKey key, SignedKey signedWith, String signature) { (SignableKey key, SignableKey signedWith, String signature) {
if (key == null || signedWith == null || signature == null) { if (key == null || signedWith == null || signature == null) {
return; return;
} }

View File

@ -221,11 +221,7 @@ class SSSS {
'mac': encrypted.mac, 'mac': encrypted.mac,
}; };
// store the thing in your account data // store the thing in your account data
await client.jsonRequest( await client.api.setAccountData(client.userID, type, content);
type: RequestType.PUT,
action: '/client/r0/user/${client.userID}/account_data/${type}',
data: content,
);
if (CACHE_TYPES.contains(type) && client.database != null) { if (CACHE_TYPES.contains(type) && client.database != null) {
// cache the thing // cache the thing
await client.database await client.database

View File

@ -128,7 +128,7 @@ class KeyVerification {
List<String> possibleMethods; List<String> possibleMethods;
Map<String, dynamic> startPaylaod; Map<String, dynamic> startPaylaod;
String _nextAction; String _nextAction;
List<SignedKey> _verifiedDevices; List<SignableKey> _verifiedDevices;
DateTime lastActivity; DateTime lastActivity;
String lastStep; String lastStep;
@ -404,8 +404,8 @@ class KeyVerification {
} }
Future<void> verifyKeys(Map<String, String> keys, Future<void> verifyKeys(Map<String, String> keys,
Future<bool> Function(String, SignedKey) verifier) async { Future<bool> Function(String, SignableKey) verifier) async {
_verifiedDevices = <SignedKey>[]; _verifiedDevices = <SignableKey>[];
if (!client.userDeviceKeys.containsKey(userId)) { if (!client.userDeviceKeys.containsKey(userId)) {
await cancel('m.key_mismatch'); await cancel('m.key_mismatch');
@ -863,7 +863,7 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod {
mac[entry.key] = entry.value; mac[entry.key] = entry.value;
} }
} }
await request.verifyKeys(mac, (String mac, SignedKey key) async { await request.verifyKeys(mac, (String mac, SignableKey key) async {
return mac == return mac ==
_calculateMac(key.ed25519Key, baseInfo + 'ed25519:' + key.identifier); _calculateMac(key.ed25519Key, baseInfo + 'ed25519:' + key.identifier);
}); });

View File

@ -31,9 +31,8 @@ export 'package:famedlysdk/matrix_api/model/filter.dart';
export 'package:famedlysdk/matrix_api/model/keys_query_response.dart'; export 'package:famedlysdk/matrix_api/model/keys_query_response.dart';
export 'package:famedlysdk/matrix_api/model/login_response.dart'; export 'package:famedlysdk/matrix_api/model/login_response.dart';
export 'package:famedlysdk/matrix_api/model/login_types.dart'; export 'package:famedlysdk/matrix_api/model/login_types.dart';
export 'package:famedlysdk/matrix_api/model/matrix_cross_signing_key.dart';
export 'package:famedlysdk/matrix_api/model/matrix_device_keys.dart';
export 'package:famedlysdk/matrix_api/model/matrix_exception.dart'; export 'package:famedlysdk/matrix_api/model/matrix_exception.dart';
export 'package:famedlysdk/matrix_api/model/matrix_keys.dart';
export 'package:famedlysdk/matrix_api/model/message_types.dart'; export 'package:famedlysdk/matrix_api/model/message_types.dart';
export 'package:famedlysdk/matrix_api/model/presence_content.dart'; export 'package:famedlysdk/matrix_api/model/presence_content.dart';
export 'package:famedlysdk/matrix_api/model/notifications_query_response.dart'; export 'package:famedlysdk/matrix_api/model/notifications_query_response.dart';

View File

@ -36,8 +36,8 @@ import 'package:mime_type/mime_type.dart';
import 'package:moor/moor.dart'; import 'package:moor/moor.dart';
import 'model/device.dart'; import 'model/device.dart';
import 'model/matrix_device_keys.dart';
import 'model/matrix_event.dart'; import 'model/matrix_event.dart';
import 'model/matrix_keys.dart';
import 'model/event_context.dart'; import 'model/event_context.dart';
import 'model/events_sync_update.dart'; import 'model/events_sync_update.dart';
import 'model/login_response.dart'; import 'model/login_response.dart';

View File

@ -16,8 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import 'matrix_device_keys.dart'; import 'matrix_keys.dart';
import 'matrix_cross_signing_key.dart';
class KeysQueryResponse { class KeysQueryResponse {
Map<String, dynamic> failures; Map<String, dynamic> failures;

View File

@ -1,65 +0,0 @@
/*
* Famedly Matrix SDK
* Copyright (C) 2020 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 <https://www.gnu.org/licenses/>.
*/
class MatrixCrossSigningKey {
String userId;
List<String> usage;
Map<String, String> keys;
Map<String, Map<String, String>> signatures;
Map<String, dynamic> unsigned;
String get publicKey => keys?.values?.first;
MatrixCrossSigningKey(
this.userId,
this.usage,
this.keys,
this.signatures, {
this.unsigned,
});
// This object is used for signing so we need the raw json too
Map<String, dynamic> _json;
MatrixCrossSigningKey.fromJson(Map<String, dynamic> json) {
_json = json;
userId = json['user_id'];
usage = List<String>.from(json['usage']);
keys = Map<String, String>.from(json['keys']);
signatures = Map<String, Map<String, String>>.from(
(json['signatures'] as Map)
.map((k, v) => MapEntry(k, Map<String, String>.from(v))));
unsigned = json['unsigned'] != null
? Map<String, dynamic>.from(json['unsigned'])
: null;
}
Map<String, dynamic> toJson() {
final data = _json ?? <String, dynamic>{};
data['user_id'] = userId;
data['usage'] = usage;
data['keys'] = keys;
if (signatures != null) {
data['signatures'] = signatures;
}
if (unsigned != null) {
data['unsigned'] = unsigned;
}
return data;
}
}

View File

@ -16,38 +16,25 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
class MatrixDeviceKeys { class MatrixSignableKey {
String userId; String userId;
String deviceId;
List<String> algorithms;
Map<String, String> keys; Map<String, String> keys;
Map<String, Map<String, String>> signatures; Map<String, Map<String, String>> signatures;
Map<String, dynamic> unsigned; Map<String, dynamic> unsigned;
String get deviceDisplayName =>
unsigned != null ? unsigned['device_display_name'] : null; MatrixSignableKey(this.userId, this.keys, this.signatures, {this.unsigned});
// This object is used for signing so we need the raw json too // This object is used for signing so we need the raw json too
Map<String, dynamic> _json; Map<String, dynamic> _json;
MatrixDeviceKeys( MatrixSignableKey.fromJson(Map<String, dynamic> json) {
this.userId,
this.deviceId,
this.algorithms,
this.keys,
this.signatures, {
this.unsigned,
});
MatrixDeviceKeys.fromJson(Map<String, dynamic> json) {
_json = json; _json = json;
userId = json['user_id']; userId = json['user_id'];
deviceId = json['device_id'];
algorithms = json['algorithms'].cast<String>();
keys = Map<String, String>.from(json['keys']); keys = Map<String, String>.from(json['keys']);
signatures = Map<String, Map<String, String>>.from( signatures = json['signatures'] is Map ? Map<String, Map<String, String>>.from(
(json['signatures'] as Map) (json['signatures'] as Map)
.map((k, v) => MapEntry(k, Map<String, String>.from(v)))); .map((k, v) => MapEntry(k, Map<String, String>.from(v)))) : null;
unsigned = json['unsigned'] != null unsigned = json['unsigned'] is Map
? Map<String, dynamic>.from(json['unsigned']) ? Map<String, dynamic>.from(json['unsigned'])
: null; : null;
} }
@ -55,8 +42,6 @@ class MatrixDeviceKeys {
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final data = _json ?? <String, dynamic>{}; final data = _json ?? <String, dynamic>{};
data['user_id'] = userId; data['user_id'] = userId;
data['device_id'] = deviceId;
data['algorithms'] = algorithms;
data['keys'] = keys; data['keys'] = keys;
if (signatures != null) { if (signatures != null) {
@ -68,3 +53,59 @@ class MatrixDeviceKeys {
return data; return data;
} }
} }
class MatrixCrossSigningKey extends MatrixSignableKey {
List<String> usage;
String get publicKey => keys?.values?.first;
MatrixCrossSigningKey(
String userId,
this.usage,
Map<String, String> keys,
Map<String, Map<String, String>> signatures, {
Map<String, dynamic> unsigned,
}) : super(userId, keys, signatures, unsigned: unsigned);
@override
MatrixCrossSigningKey.fromJson(Map<String, dynamic> json)
: super.fromJson(json) {
usage = List<String>.from(json['usage']);
}
@override
Map<String, dynamic> toJson() {
final data = super.toJson();
data['usage'] = usage;
return data;
}
}
class MatrixDeviceKeys extends MatrixSignableKey {
String deviceId;
List<String> algorithms;
String get deviceDisplayName =>
unsigned != null ? unsigned['device_display_name'] : null;
MatrixDeviceKeys(
String userId,
this.deviceId,
this.algorithms,
Map<String, String> keys,
Map<String, Map<String, String>> signatures, {
Map<String, dynamic> unsigned,
}) : super(userId, keys, signatures, unsigned: unsigned);
@override
MatrixDeviceKeys.fromJson(Map<String, dynamic> json) : super.fromJson(json) {
deviceId = json['device_id'];
algorithms = json['algorithms'].cast<String>();
}
@override
Map<String, dynamic> toJson() {
final data = super.toJson();
data['device_id'] = deviceId;
data['algorithms'] = algorithms;
return data;
}
}

View File

@ -21,7 +21,7 @@ class DeviceKeysList {
Map<String, DeviceKeys> deviceKeys = {}; Map<String, DeviceKeys> deviceKeys = {};
Map<String, CrossSigningKey> crossSigningKeys = {}; Map<String, CrossSigningKey> crossSigningKeys = {};
SignedKey getKey(String id) { SignableKey getKey(String id) {
if (deviceKeys.containsKey(id)) { if (deviceKeys.containsKey(id)) {
return deviceKeys[id]; return deviceKeys[id];
} }
@ -98,13 +98,9 @@ class DeviceKeysList {
DeviceKeysList(this.userId); DeviceKeysList(this.userId);
} }
abstract class SignedKey { abstract class SignableKey extends MatrixSignableKey {
Client client; Client client;
String userId;
String identifier; String identifier;
Map<String, dynamic> content;
Map<String, String> keys;
Map<String, dynamic> signatures;
Map<String, dynamic> validSignatures; Map<String, dynamic> validSignatures;
bool _verified; bool _verified;
bool blocked; bool blocked;
@ -120,8 +116,15 @@ abstract class SignedKey {
bool get crossVerified => hasValidSignatureChain(); bool get crossVerified => hasValidSignatureChain();
bool get signed => hasValidSignatureChain(verifiedOnly: false); bool get signed => hasValidSignatureChain(verifiedOnly: false);
SignableKey.fromJson(Map<String, dynamic> json, Client cl)
: client = cl,
super.fromJson(json) {
_verified = false;
blocked = false;
}
String get signingContent { String get signingContent {
final data = Map<String, dynamic>.from(content); final data = Map<String, dynamic>.from(super.toJson());
// some old data might have the custom verified and blocked keys // some old data might have the custom verified and blocked keys
data.remove('verified'); data.remove('verified');
data.remove('blocked'); data.remove('blocked');
@ -166,7 +169,7 @@ abstract class SignedKey {
continue; continue;
} }
final keyId = fullKeyId.substring('ed25519:'.length); final keyId = fullKeyId.substring('ed25519:'.length);
SignedKey key; SignableKey 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 } else if (client.userDeviceKeys[otherUserId].crossSigningKeys
@ -236,8 +239,9 @@ abstract class SignedKey {
Future<void> setBlocked(bool newBlocked); Future<void> setBlocked(bool newBlocked);
@override
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final data = Map<String, dynamic>.from(content); final data = Map<String, dynamic>.from(super.toJson());
// some old data may have the verified and blocked keys which are unneeded now // some old data may have the verified and blocked keys which are unneeded now
data.remove('verified'); data.remove('verified');
data.remove('blocked'); data.remove('blocked');
@ -248,7 +252,7 @@ abstract class SignedKey {
String toString() => json.encode(toJson()); String toString() => json.encode(toJson());
} }
class CrossSigningKey extends SignedKey { class CrossSigningKey extends SignableKey {
String get publicKey => identifier; String get publicKey => identifier;
List<String> usage; List<String> usage;
@ -269,59 +273,35 @@ class CrossSigningKey extends SignedKey {
newBlocked, client.id, userId, publicKey); newBlocked, client.id, userId, publicKey);
} }
CrossSigningKey.fromMatrixCrossSigningKey( CrossSigningKey.fromMatrixCrossSigningKey(MatrixCrossSigningKey k, Client cl)
MatrixCrossSigningKey k, Client cl) { : super.fromJson(Map<String, dynamic>.from(k.toJson()), cl) {
client = cl; final json = toJson();
content = Map<String, dynamic>.from(k.toJson());
userId = k.userId;
identifier = k.publicKey; identifier = k.publicKey;
usage = content['usage'].cast<String>(); usage = json['usage'].cast<String>();
keys = content['keys'] != null
? Map<String, String>.from(content['keys'])
: null;
signatures = content['signatures'] != null
? Map<String, dynamic>.from(content['signatures'])
: null;
_verified = false;
blocked = false;
} }
CrossSigningKey.fromDb(DbUserCrossSigningKey dbEntry, Client cl) { CrossSigningKey.fromDb(DbUserCrossSigningKey dbEntry, Client cl)
client = cl; : super.fromJson(Event.getMapFromPayload(dbEntry.content), cl) {
final json = Event.getMapFromPayload(dbEntry.content); final json = toJson();
content = Map<String, dynamic>.from(json);
userId = dbEntry.userId;
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;
signatures = json['signatures'] != null
? Map<String, dynamic>.from(json['signatures'])
: null;
_verified = dbEntry.verified; _verified = dbEntry.verified;
blocked = dbEntry.blocked; blocked = dbEntry.blocked;
} }
CrossSigningKey.fromJson(Map<String, dynamic> json, Client cl) { CrossSigningKey.fromJson(Map<String, dynamic> json, Client cl)
client = cl; : super.fromJson(Map<String, dynamic>.from(json), cl) {
content = Map<String, dynamic>.from(json); final json = toJson();
userId = json['user_id'];
usage = json['usage'].cast<String>(); usage = json['usage'].cast<String>();
keys = json['keys'] != null ? Map<String, String>.from(json['keys']) : null; if (keys != null && keys.isNotEmpty) {
signatures = json['signatures'] != null
? Map<String, dynamic>.from(json['signatures'])
: null;
_verified = json['verified'] ?? false;
blocked = json['blocked'] ?? false;
if (keys != null) {
identifier = keys.values.first; identifier = keys.values.first;
} }
} }
} }
class DeviceKeys extends SignedKey { class DeviceKeys extends SignableKey {
String get deviceId => identifier; String get deviceId => identifier;
List<String> algorithms; List<String> algorithms;
Map<String, dynamic> unsigned;
String get curve25519Key => keys['curve25519:$deviceId']; String get curve25519Key => keys['curve25519:$deviceId'];
@ -346,60 +326,27 @@ class DeviceKeys extends SignedKey {
?.setBlockedUserDeviceKey(newBlocked, client.id, userId, deviceId); ?.setBlockedUserDeviceKey(newBlocked, client.id, userId, deviceId);
} }
DeviceKeys.fromMatrixDeviceKeys(MatrixDeviceKeys k, Client cl) { DeviceKeys.fromMatrixDeviceKeys(MatrixDeviceKeys k, Client cl)
client = cl; : super.fromJson(Map<String, dynamic>.from(k.toJson()), cl) {
content = Map<String, dynamic>.from(k.toJson()); final json = toJson();
userId = k.userId;
identifier = k.deviceId; identifier = k.deviceId;
algorithms = content['algorithms'].cast<String>(); algorithms = json['algorithms'].cast<String>();
keys = content['keys'] != null
? Map<String, String>.from(content['keys'])
: null;
signatures = content['signatures'] != null
? Map<String, dynamic>.from(content['signatures'])
: null;
unsigned = content['unsigned'] != null
? Map<String, dynamic>.from(content['unsigned'])
: null;
_verified = false;
blocked = false;
} }
DeviceKeys.fromDb(DbUserDeviceKeysKey dbEntry, Client cl) { DeviceKeys.fromDb(DbUserDeviceKeysKey dbEntry, Client cl)
client = cl; : super.fromJson(Event.getMapFromPayload(dbEntry.content), cl) {
final json = Event.getMapFromPayload(dbEntry.content); final json = toJson();
content = Map<String, dynamic>.from(json);
userId = dbEntry.userId;
identifier = dbEntry.deviceId; identifier = dbEntry.deviceId;
algorithms = content['algorithms'].cast<String>(); algorithms = json['algorithms'].cast<String>();
keys = content['keys'] != null
? Map<String, String>.from(content['keys'])
: null;
signatures = content['signatures'] != null
? Map<String, dynamic>.from(content['signatures'])
: null;
unsigned = json['unsigned'] != null
? Map<String, dynamic>.from(json['unsigned'])
: null;
_verified = dbEntry.verified; _verified = dbEntry.verified;
blocked = dbEntry.blocked; blocked = dbEntry.blocked;
} }
DeviceKeys.fromJson(Map<String, dynamic> json, Client cl) { DeviceKeys.fromJson(Map<String, dynamic> json, Client cl)
client = cl; : super.fromJson(Map<String, dynamic>.from(json), cl) {
content = Map<String, dynamic>.from(json); final json = toJson();
userId = json['user_id'];
identifier = json['device_id']; identifier = json['device_id'];
algorithms = json['algorithms'].cast<String>(); algorithms = json['algorithms'].cast<String>();
keys = json['keys'] != null ? Map<String, String>.from(json['keys']) : null;
signatures = json['signatures'] != null
? Map<String, dynamic>.from(json['signatures'])
: null;
unsigned = json['unsigned'] != null
? Map<String, dynamic>.from(json['unsigned'])
: null;
_verified = json['verified'] ?? false;
blocked = json['blocked'] ?? false;
} }
KeyVerification startVerification() { KeyVerification startVerification() {

View File

@ -18,7 +18,7 @@
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:famedlysdk/matrix_api.dart'; import 'package:famedlysdk/matrix_api.dart';
import 'package:famedlysdk/matrix_api/model/matrix_device_keys.dart'; import 'package:famedlysdk/matrix_api/model/matrix_keys.dart';
import 'package:famedlysdk/matrix_api/model/filter.dart'; import 'package:famedlysdk/matrix_api/model/filter.dart';
import 'package:famedlysdk/matrix_api/model/matrix_exception.dart'; import 'package:famedlysdk/matrix_api/model/matrix_exception.dart';
import 'package:famedlysdk/matrix_api/model/presence_content.dart'; import 'package:famedlysdk/matrix_api/model/presence_content.dart';