requestify cross-signing endpoints

This commit is contained in:
Sorunome 2020-06-06 15:17:05 +02:00
parent e1679d59be
commit b4e83caa89
No known key found for this signature in database
GPG key ID: B19471D07FC9BE9C
6 changed files with 139 additions and 38 deletions

View file

@ -117,34 +117,23 @@ class CrossSigning {
Future<void> sign(List<SignableKey> keys) async { Future<void> sign(List<SignableKey> keys) async {
Uint8List selfSigningKey; Uint8List selfSigningKey;
Uint8List userSigningKey; Uint8List userSigningKey;
final signatures = <String, dynamic>{}; final signedKeys = <MatrixSignableKey>[];
var signedKey = false;
final addSignature = final addSignature =
(SignableKey key, SignableKey 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;
} }
if (!signatures.containsKey(key.userId)) { final signedKey = signedKeys.firstWhere(
signatures[key.userId] = <String, dynamic>{}; (k) => k.userId == key.userId && k.identifier == key.identifier,
orElse: () => null) ??
key.cloneForSigning();
signedKey.signatures ??= <String, Map<String, String>>{};
if (!signedKey.signatures.containsKey(signedWith.userId)) {
signedKey.signatures[signedWith.userId] = <String, String>{};
} }
if (!signatures[key.userId].containsKey(key.identifier)) { signedKey.signatures[signedWith.userId]
signatures[key.userId][key.identifier] =
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');
}
if (!signatures[key.userId][key.identifier].containsKey('signatures')) {
signatures[key.userId][key.identifier]
['signatures'] = <String, dynamic>{};
}
if (!signatures[key.userId][key.identifier]['signatures']
.containsKey(signedWith.userId)) {
signatures[key.userId][key.identifier]['signatures']
[signedWith.userId] = <String, dynamic>{};
}
signatures[key.userId][key.identifier]['signatures'][signedWith.userId]
['ed25519:${signedWith.identifier}'] = signature; ['ed25519:${signedWith.identifier}'] = signature;
signedKey = true; signedKeys.add(signedKey);
}; };
for (final key in keys) { for (final key in keys) {
if (key.userId == client.userID) { if (key.userId == client.userID) {
@ -182,13 +171,9 @@ class CrossSigning {
} }
} }
} }
if (signedKey) { if (signedKeys.isNotEmpty) {
// post our new keys! // post our new keys!
await client.jsonRequest( await client.api.uploadKeySignatures(signedKeys);
type: RequestType.POST,
action: '/client/r0/keys/signatures/upload',
data: signatures,
);
} }
} }

View file

@ -58,6 +58,7 @@ export 'package:famedlysdk/matrix_api/model/third_party_location.dart';
export 'package:famedlysdk/matrix_api/model/third_party_user.dart'; export 'package:famedlysdk/matrix_api/model/third_party_user.dart';
export 'package:famedlysdk/matrix_api/model/timeline_history_response.dart'; export 'package:famedlysdk/matrix_api/model/timeline_history_response.dart';
export 'package:famedlysdk/matrix_api/model/turn_server_credentials.dart'; export 'package:famedlysdk/matrix_api/model/turn_server_credentials.dart';
export 'package:famedlysdk/matrix_api/model/upload_key_signatures_response.dart';
export 'package:famedlysdk/matrix_api/model/user_search_result.dart'; export 'package:famedlysdk/matrix_api/model/user_search_result.dart';
export 'package:famedlysdk/matrix_api/model/well_known_informations.dart'; export 'package:famedlysdk/matrix_api/model/well_known_informations.dart';
export 'package:famedlysdk/matrix_api/model/who_is_info.dart'; export 'package:famedlysdk/matrix_api/model/who_is_info.dart';

View file

@ -54,6 +54,7 @@ import 'model/tag.dart';
import 'model/third_party_identifier.dart'; import 'model/third_party_identifier.dart';
import 'model/third_party_user.dart'; import 'model/third_party_user.dart';
import 'model/turn_server_credentials.dart'; import 'model/turn_server_credentials.dart';
import 'model/upload_key_signatures_response.dart';
import 'model/well_known_informations.dart'; import 'model/well_known_informations.dart';
import 'model/who_is_info.dart'; import 'model/who_is_info.dart';
@ -1503,6 +1504,55 @@ class MatrixApi {
return DeviceListsUpdate.fromJson(response); return DeviceListsUpdate.fromJson(response);
} }
/// Uploads your own cross-signing keys.
/// https://12682-24998719-gh.circle-artifacts.com/0/scripts/gen/client_server/unstable.html#post-matrix-client-r0-keys-device-signing-upload
Future<void> uploadDeviceSigningKeys({
MatrixCrossSigningKey masterKey,
MatrixCrossSigningKey selfSigningKey,
MatrixCrossSigningKey userSigningKey,
}) async {
await request(
RequestType.POST,
'/client/r0/keys/device_signing/upload',
data: {
'master_key': masterKey.toJson(),
'self_signing_key': selfSigningKey.toJson(),
'user_signing_key': userSigningKey.toJson(),
},
);
}
/// Uploads new signatures of keys
/// https://12682-24998719-gh.circle-artifacts.com/0/scripts/gen/client_server/unstable.html#post-matrix-client-r0-keys-signatures-upload
Future<UploadKeySignaturesResponse> uploadKeySignatures(
List<MatrixSignableKey> keys) async {
final payload = <String, dynamic>{};
for (final key in keys) {
if (key.identifier == null ||
key.signatures == null ||
key.signatures.isEmpty) {
continue;
}
if (!payload.containsKey(key.userId)) {
payload[key.userId] = <String, dynamic>{};
}
if (payload[key.userId].containsKey(key.identifier)) {
// we need to merge signature objects
payload[key.userId][key.identifier]['signatures']
.addAll(key.signatures);
} else {
// we can just add signatures
payload[key.userId][key.identifier] = key.toJson();
}
}
final response = await request(
RequestType.POST,
'/client/r0/keys/signatures/upload',
data: payload,
);
return UploadKeySignaturesResponse.fromJson(response);
}
/// Gets all currently active pushers for the authenticated user. /// Gets all currently active pushers for the authenticated user.
/// https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-pushers /// https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-pushers
Future<List<Pusher>> requestPushers() async { Future<List<Pusher>> requestPushers() async {

View file

@ -18,11 +18,13 @@
class MatrixSignableKey { class MatrixSignableKey {
String userId; String userId;
String identifier;
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;
MatrixSignableKey(this.userId, this.keys, this.signatures, {this.unsigned}); MatrixSignableKey(this.userId, this.identifier, 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;
@ -31,9 +33,10 @@ class MatrixSignableKey {
_json = json; _json = json;
userId = json['user_id']; userId = json['user_id'];
keys = Map<String, String>.from(json['keys']); keys = Map<String, String>.from(json['keys']);
signatures = json['signatures'] is Map ? Map<String, Map<String, String>>.from( signatures = json['signatures'] is Map
(json['signatures'] as Map) ? Map<String, Map<String, String>>.from((json['signatures'] as Map)
.map((k, v) => MapEntry(k, Map<String, String>.from(v)))) : null; .map((k, v) => MapEntry(k, Map<String, String>.from(v))))
: null;
unsigned = json['unsigned'] is Map unsigned = json['unsigned'] is Map
? Map<String, dynamic>.from(json['unsigned']) ? Map<String, dynamic>.from(json['unsigned'])
: null; : null;
@ -56,7 +59,7 @@ class MatrixSignableKey {
class MatrixCrossSigningKey extends MatrixSignableKey { class MatrixCrossSigningKey extends MatrixSignableKey {
List<String> usage; List<String> usage;
String get publicKey => keys?.values?.first; String get publicKey => identifier;
MatrixCrossSigningKey( MatrixCrossSigningKey(
String userId, String userId,
@ -64,12 +67,13 @@ class MatrixCrossSigningKey extends MatrixSignableKey {
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,
}) : super(userId, keys, signatures, unsigned: unsigned); }) : super(userId, keys?.values?.first, keys, signatures, unsigned: unsigned);
@override @override
MatrixCrossSigningKey.fromJson(Map<String, dynamic> json) MatrixCrossSigningKey.fromJson(Map<String, dynamic> json)
: super.fromJson(json) { : super.fromJson(json) {
usage = List<String>.from(json['usage']); usage = List<String>.from(json['usage']);
identifier = keys?.values?.first;
} }
@override @override
@ -81,23 +85,23 @@ class MatrixCrossSigningKey extends MatrixSignableKey {
} }
class MatrixDeviceKeys extends MatrixSignableKey { class MatrixDeviceKeys extends MatrixSignableKey {
String deviceId; String get deviceId => identifier;
List<String> algorithms; List<String> algorithms;
String get deviceDisplayName => String get deviceDisplayName =>
unsigned != null ? unsigned['device_display_name'] : null; unsigned != null ? unsigned['device_display_name'] : null;
MatrixDeviceKeys( MatrixDeviceKeys(
String userId, String userId,
this.deviceId, String deviceId,
this.algorithms, this.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,
}) : super(userId, keys, signatures, unsigned: unsigned); }) : super(userId, deviceId, keys, signatures, unsigned: unsigned);
@override @override
MatrixDeviceKeys.fromJson(Map<String, dynamic> json) : super.fromJson(json) { MatrixDeviceKeys.fromJson(Map<String, dynamic> json) : super.fromJson(json) {
deviceId = json['device_id']; identifier = json['device_id'];
algorithms = json['algorithms'].cast<String>(); algorithms = json['algorithms'].cast<String>();
} }

View file

@ -0,0 +1,55 @@
/*
* 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/>.
*/
import 'matrix_exception.dart';
class UploadKeySignaturesResponse {
Map<String, Map<String, MatrixException>> failures;
UploadKeySignaturesResponse.fromJson(Map<String, dynamic> json) {
failures = json['failures'] != null
? (json['failures'] as Map).map(
(k, v) => MapEntry(
k,
(v as Map).map((k, v) => MapEntry(
k,
MatrixException.fromJson(v),
)),
),
)
: null;
}
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
if (failures != null) {
data['failures'] = failures.map(
(k, v) => MapEntry(
k,
v.map(
(k, v) => MapEntry(
k,
v.raw,
),
),
),
);
}
return data;
}
}

View file

@ -100,7 +100,6 @@ class DeviceKeysList {
abstract class SignableKey extends MatrixSignableKey { abstract class SignableKey extends MatrixSignableKey {
Client client; Client client;
String identifier;
Map<String, dynamic> validSignatures; Map<String, dynamic> validSignatures;
bool _verified; bool _verified;
bool blocked; bool blocked;
@ -123,6 +122,13 @@ abstract class SignableKey extends MatrixSignableKey {
blocked = false; blocked = false;
} }
MatrixSignableKey cloneForSigning() {
final newKey =
MatrixSignableKey.fromJson(Map<String, dynamic>.from(toJson()));
newKey.signatures.clear();
return newKey;
}
String get signingContent { String get signingContent {
final data = Map<String, dynamic>.from(super.toJson()); 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