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/key_manager.dart';
export './encryption/ssss.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) {
if (key is CrossSigningKey && key.usage.contains('master')) {
return true;
@ -114,13 +114,13 @@ class CrossSigning {
return false;
}
Future<void> sign(List<SignedKey> keys) async {
Future<void> sign(List<SignableKey> keys) async {
Uint8List selfSigningKey;
Uint8List userSigningKey;
final signatures = <String, dynamic>{};
var signedKey = false;
final addSignature =
(SignedKey key, SignedKey signedWith, String signature) {
(SignableKey key, SignableKey signedWith, String signature) {
if (key == null || signedWith == null || signature == null) {
return;
}

View File

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

View File

@ -128,7 +128,7 @@ class KeyVerification {
List<String> possibleMethods;
Map<String, dynamic> startPaylaod;
String _nextAction;
List<SignedKey> _verifiedDevices;
List<SignableKey> _verifiedDevices;
DateTime lastActivity;
String lastStep;
@ -404,8 +404,8 @@ class KeyVerification {
}
Future<void> verifyKeys(Map<String, String> keys,
Future<bool> Function(String, SignedKey) verifier) async {
_verifiedDevices = <SignedKey>[];
Future<bool> Function(String, SignableKey) verifier) async {
_verifiedDevices = <SignableKey>[];
if (!client.userDeviceKeys.containsKey(userId)) {
await cancel('m.key_mismatch');
@ -863,7 +863,7 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod {
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 ==
_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/login_response.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_keys.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/notifications_query_response.dart';

View File

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

View File

@ -16,8 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import 'matrix_device_keys.dart';
import 'matrix_cross_signing_key.dart';
import 'matrix_keys.dart';
class KeysQueryResponse {
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/>.
*/
class MatrixDeviceKeys {
class MatrixSignableKey {
String userId;
String deviceId;
List<String> algorithms;
Map<String, String> keys;
Map<String, Map<String, String>> signatures;
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
Map<String, dynamic> _json;
MatrixDeviceKeys(
this.userId,
this.deviceId,
this.algorithms,
this.keys,
this.signatures, {
this.unsigned,
});
MatrixDeviceKeys.fromJson(Map<String, dynamic> json) {
MatrixSignableKey.fromJson(Map<String, dynamic> json) {
_json = json;
userId = json['user_id'];
deviceId = json['device_id'];
algorithms = json['algorithms'].cast<String>();
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)
.map((k, v) => MapEntry(k, Map<String, String>.from(v))));
unsigned = json['unsigned'] != null
.map((k, v) => MapEntry(k, Map<String, String>.from(v)))) : null;
unsigned = json['unsigned'] is Map
? Map<String, dynamic>.from(json['unsigned'])
: null;
}
@ -55,8 +42,6 @@ class MatrixDeviceKeys {
Map<String, dynamic> toJson() {
final data = _json ?? <String, dynamic>{};
data['user_id'] = userId;
data['device_id'] = deviceId;
data['algorithms'] = algorithms;
data['keys'] = keys;
if (signatures != null) {
@ -68,3 +53,59 @@ class MatrixDeviceKeys {
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, CrossSigningKey> crossSigningKeys = {};
SignedKey getKey(String id) {
SignableKey getKey(String id) {
if (deviceKeys.containsKey(id)) {
return deviceKeys[id];
}
@ -98,13 +98,9 @@ class DeviceKeysList {
DeviceKeysList(this.userId);
}
abstract class SignedKey {
abstract class SignableKey extends MatrixSignableKey {
Client client;
String userId;
String identifier;
Map<String, dynamic> content;
Map<String, String> keys;
Map<String, dynamic> signatures;
Map<String, dynamic> validSignatures;
bool _verified;
bool blocked;
@ -120,8 +116,15 @@ abstract class SignedKey {
bool get crossVerified => hasValidSignatureChain();
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 {
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
data.remove('verified');
data.remove('blocked');
@ -166,7 +169,7 @@ abstract class SignedKey {
continue;
}
final keyId = fullKeyId.substring('ed25519:'.length);
SignedKey key;
SignableKey key;
if (client.userDeviceKeys[otherUserId].deviceKeys.containsKey(keyId)) {
key = client.userDeviceKeys[otherUserId].deviceKeys[keyId];
} else if (client.userDeviceKeys[otherUserId].crossSigningKeys
@ -236,8 +239,9 @@ abstract class SignedKey {
Future<void> setBlocked(bool newBlocked);
@override
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
data.remove('verified');
data.remove('blocked');
@ -248,7 +252,7 @@ abstract class SignedKey {
String toString() => json.encode(toJson());
}
class CrossSigningKey extends SignedKey {
class CrossSigningKey extends SignableKey {
String get publicKey => identifier;
List<String> usage;
@ -269,59 +273,35 @@ class CrossSigningKey extends SignedKey {
newBlocked, client.id, userId, publicKey);
}
CrossSigningKey.fromMatrixCrossSigningKey(
MatrixCrossSigningKey k, Client cl) {
client = cl;
content = Map<String, dynamic>.from(k.toJson());
userId = k.userId;
CrossSigningKey.fromMatrixCrossSigningKey(MatrixCrossSigningKey k, Client cl)
: super.fromJson(Map<String, dynamic>.from(k.toJson()), cl) {
final json = toJson();
identifier = k.publicKey;
usage = content['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;
usage = json['usage'].cast<String>();
}
CrossSigningKey.fromDb(DbUserCrossSigningKey dbEntry, Client cl) {
client = cl;
final json = Event.getMapFromPayload(dbEntry.content);
content = Map<String, dynamic>.from(json);
userId = dbEntry.userId;
CrossSigningKey.fromDb(DbUserCrossSigningKey dbEntry, Client cl)
: super.fromJson(Event.getMapFromPayload(dbEntry.content), cl) {
final json = toJson();
identifier = dbEntry.publicKey;
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;
blocked = dbEntry.blocked;
}
CrossSigningKey.fromJson(Map<String, dynamic> json, Client cl) {
client = cl;
content = Map<String, dynamic>.from(json);
userId = json['user_id'];
CrossSigningKey.fromJson(Map<String, dynamic> json, Client cl)
: super.fromJson(Map<String, dynamic>.from(json), cl) {
final json = toJson();
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 = json['verified'] ?? false;
blocked = json['blocked'] ?? false;
if (keys != null) {
if (keys != null && keys.isNotEmpty) {
identifier = keys.values.first;
}
}
}
class DeviceKeys extends SignedKey {
class DeviceKeys extends SignableKey {
String get deviceId => identifier;
List<String> algorithms;
Map<String, dynamic> unsigned;
String get curve25519Key => keys['curve25519:$deviceId'];
@ -346,60 +326,27 @@ class DeviceKeys extends SignedKey {
?.setBlockedUserDeviceKey(newBlocked, client.id, userId, deviceId);
}
DeviceKeys.fromMatrixDeviceKeys(MatrixDeviceKeys k, Client cl) {
client = cl;
content = Map<String, dynamic>.from(k.toJson());
userId = k.userId;
DeviceKeys.fromMatrixDeviceKeys(MatrixDeviceKeys k, Client cl)
: super.fromJson(Map<String, dynamic>.from(k.toJson()), cl) {
final json = toJson();
identifier = k.deviceId;
algorithms = content['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;
algorithms = json['algorithms'].cast<String>();
}
DeviceKeys.fromDb(DbUserDeviceKeysKey dbEntry, Client cl) {
client = cl;
final json = Event.getMapFromPayload(dbEntry.content);
content = Map<String, dynamic>.from(json);
userId = dbEntry.userId;
DeviceKeys.fromDb(DbUserDeviceKeysKey dbEntry, Client cl)
: super.fromJson(Event.getMapFromPayload(dbEntry.content), cl) {
final json = toJson();
identifier = dbEntry.deviceId;
algorithms = content['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;
algorithms = json['algorithms'].cast<String>();
_verified = dbEntry.verified;
blocked = dbEntry.blocked;
}
DeviceKeys.fromJson(Map<String, dynamic> json, Client cl) {
client = cl;
content = Map<String, dynamic>.from(json);
userId = json['user_id'];
DeviceKeys.fromJson(Map<String, dynamic> json, Client cl)
: super.fromJson(Map<String, dynamic>.from(json), cl) {
final json = toJson();
identifier = json['device_id'];
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() {

View File

@ -18,7 +18,7 @@
import 'dart:typed_data';
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/matrix_exception.dart';
import 'package:famedlysdk/matrix_api/model/presence_content.dart';