Merge pull request #1614 from famedly/nico/dehydrated-devices-v2
feat: Update dehydrated devices implementation to current MSC
This commit is contained in:
commit
d35872b4ad
|
|
@ -68,15 +68,20 @@ class Encryption {
|
||||||
}
|
}
|
||||||
|
|
||||||
// initial login passes null to init a new olm account
|
// initial login passes null to init a new olm account
|
||||||
Future<void> init(String? olmAccount,
|
Future<void> init(
|
||||||
{String? deviceId,
|
String? olmAccount, {
|
||||||
String? pickleKey,
|
String? deviceId,
|
||||||
bool isDehydratedDevice = false}) async {
|
String? pickleKey,
|
||||||
|
String? dehydratedDeviceAlgorithm,
|
||||||
|
}) async {
|
||||||
ourDeviceId = deviceId ?? client.deviceID!;
|
ourDeviceId = deviceId ?? client.deviceID!;
|
||||||
|
final isDehydratedDevice = dehydratedDeviceAlgorithm != null;
|
||||||
await olmManager.init(
|
await olmManager.init(
|
||||||
olmAccount: olmAccount,
|
olmAccount: olmAccount,
|
||||||
deviceId: isDehydratedDevice ? deviceId : ourDeviceId,
|
deviceId: isDehydratedDevice ? deviceId : ourDeviceId,
|
||||||
pickleKey: pickleKey);
|
pickleKey: pickleKey,
|
||||||
|
dehydratedDeviceAlgorithm: dehydratedDeviceAlgorithm,
|
||||||
|
);
|
||||||
|
|
||||||
if (!isDehydratedDevice) keyManager.startAutoUploadKeys();
|
if (!isDehydratedDevice) keyManager.startAutoUploadKeys();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,10 +57,12 @@ class OlmManager {
|
||||||
final Map<String, List<OlmSession>> _olmSessions = {};
|
final Map<String, List<OlmSession>> _olmSessions = {};
|
||||||
|
|
||||||
// NOTE(Nico): On initial login we pass null to create a new account
|
// NOTE(Nico): On initial login we pass null to create a new account
|
||||||
Future<void> init(
|
Future<void> init({
|
||||||
{String? olmAccount,
|
String? olmAccount,
|
||||||
required String? deviceId,
|
required String? deviceId,
|
||||||
String? pickleKey}) async {
|
String? pickleKey,
|
||||||
|
String? dehydratedDeviceAlgorithm,
|
||||||
|
}) async {
|
||||||
ourDeviceId = deviceId;
|
ourDeviceId = deviceId;
|
||||||
if (olmAccount == null) {
|
if (olmAccount == null) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -68,10 +70,12 @@ class OlmManager {
|
||||||
_olmAccount = olm.Account();
|
_olmAccount = olm.Account();
|
||||||
_olmAccount!.create();
|
_olmAccount!.create();
|
||||||
if (!await uploadKeys(
|
if (!await uploadKeys(
|
||||||
uploadDeviceKeys: true,
|
uploadDeviceKeys: true,
|
||||||
updateDatabase: false,
|
updateDatabase: false,
|
||||||
// dehydrated devices don't have a device id when created, so skip upload in that case.
|
dehydratedDeviceAlgorithm: dehydratedDeviceAlgorithm,
|
||||||
skipAllUploads: deviceId == null)) {
|
dehydratedDevicePickleKey:
|
||||||
|
dehydratedDeviceAlgorithm != null ? pickleKey : null,
|
||||||
|
)) {
|
||||||
throw ('Upload key failed');
|
throw ('Upload key failed');
|
||||||
}
|
}
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
|
|
@ -131,7 +135,8 @@ class OlmManager {
|
||||||
int? oldKeyCount = 0,
|
int? oldKeyCount = 0,
|
||||||
bool updateDatabase = true,
|
bool updateDatabase = true,
|
||||||
bool? unusedFallbackKey = false,
|
bool? unusedFallbackKey = false,
|
||||||
bool skipAllUploads = false,
|
String? dehydratedDeviceAlgorithm,
|
||||||
|
String? dehydratedDevicePickleKey,
|
||||||
int retry = 1,
|
int retry = 1,
|
||||||
}) async {
|
}) async {
|
||||||
final olmAccount = _olmAccount;
|
final olmAccount = _olmAccount;
|
||||||
|
|
@ -179,11 +184,6 @@ class OlmManager {
|
||||||
await encryption.olmDatabase?.updateClientKeys(pickledOlmAccount!);
|
await encryption.olmDatabase?.updateClientKeys(pickledOlmAccount!);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (skipAllUploads) {
|
|
||||||
_uploadKeysLock = false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// and now generate the payload to upload
|
// and now generate the payload to upload
|
||||||
var deviceKeys = <String, dynamic>{
|
var deviceKeys = <String, dynamic>{
|
||||||
'user_id': client.userID,
|
'user_id': client.userID,
|
||||||
|
|
@ -239,23 +239,36 @@ class OlmManager {
|
||||||
|
|
||||||
// Workaround: Make sure we stop if we got logged out in the meantime.
|
// Workaround: Make sure we stop if we got logged out in the meantime.
|
||||||
if (!client.isLogged()) return true;
|
if (!client.isLogged()) return true;
|
||||||
final currentUpload = this.currentUpload =
|
|
||||||
CancelableOperation.fromFuture(ourDeviceId == client.deviceID
|
if (ourDeviceId != client.deviceID) {
|
||||||
? client.uploadKeys(
|
if (dehydratedDeviceAlgorithm == null ||
|
||||||
deviceKeys: uploadDeviceKeys
|
dehydratedDevicePickleKey == null) {
|
||||||
? MatrixDeviceKeys.fromJson(deviceKeys)
|
throw Exception(
|
||||||
: null,
|
'You need to provide both the pickle key and the algorithm to use dehydrated devices!');
|
||||||
oneTimeKeys: signedOneTimeKeys,
|
}
|
||||||
fallbackKeys: signedFallbackKeys,
|
|
||||||
)
|
await client.uploadDehydratedDevice(
|
||||||
: client.uploadKeysForDevice(
|
deviceId: ourDeviceId!,
|
||||||
ourDeviceId!,
|
initialDeviceDisplayName: 'Dehydrated Device',
|
||||||
deviceKeys: uploadDeviceKeys
|
deviceKeys:
|
||||||
? MatrixDeviceKeys.fromJson(deviceKeys)
|
uploadDeviceKeys ? MatrixDeviceKeys.fromJson(deviceKeys) : null,
|
||||||
: null,
|
oneTimeKeys: signedOneTimeKeys,
|
||||||
oneTimeKeys: signedOneTimeKeys,
|
fallbackKeys: signedFallbackKeys,
|
||||||
fallbackKeys: signedFallbackKeys,
|
deviceData: {
|
||||||
));
|
'algorithm': dehydratedDeviceAlgorithm,
|
||||||
|
'device': encryption.olmManager
|
||||||
|
.pickleOlmAccountWithKey(dehydratedDevicePickleKey),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
final currentUpload =
|
||||||
|
this.currentUpload = CancelableOperation.fromFuture(client.uploadKeys(
|
||||||
|
deviceKeys:
|
||||||
|
uploadDeviceKeys ? MatrixDeviceKeys.fromJson(deviceKeys) : null,
|
||||||
|
oneTimeKeys: signedOneTimeKeys,
|
||||||
|
fallbackKeys: signedFallbackKeys,
|
||||||
|
));
|
||||||
final response = await currentUpload.valueOrCancellation();
|
final response = await currentUpload.valueOrCancellation();
|
||||||
if (response == null) {
|
if (response == null) {
|
||||||
_uploadKeysLock = false;
|
_uploadKeysLock = false;
|
||||||
|
|
@ -276,8 +289,8 @@ class OlmManager {
|
||||||
// we failed to upload the keys. If we only tried to upload one time keys, try to recover by removing them and generating new ones.
|
// we failed to upload the keys. If we only tried to upload one time keys, try to recover by removing them and generating new ones.
|
||||||
if (!uploadDeviceKeys &&
|
if (!uploadDeviceKeys &&
|
||||||
unusedFallbackKey != false &&
|
unusedFallbackKey != false &&
|
||||||
!skipAllUploads &&
|
|
||||||
retry > 0 &&
|
retry > 0 &&
|
||||||
|
dehydratedDeviceAlgorithm != null &&
|
||||||
signedOneTimeKeys.isNotEmpty &&
|
signedOneTimeKeys.isNotEmpty &&
|
||||||
exception.error == MatrixError.M_UNKNOWN) {
|
exception.error == MatrixError.M_UNKNOWN) {
|
||||||
Logs().w('Rotating otks because upload failed', exception);
|
Logs().w('Rotating otks because upload failed', exception);
|
||||||
|
|
@ -302,7 +315,6 @@ class OlmManager {
|
||||||
oldKeyCount: oldKeyCount,
|
oldKeyCount: oldKeyCount,
|
||||||
updateDatabase: updateDatabase,
|
updateDatabase: updateDatabase,
|
||||||
unusedFallbackKey: unusedFallbackKey,
|
unusedFallbackKey: unusedFallbackKey,
|
||||||
skipAllUploads: skipAllUploads,
|
|
||||||
retry: retry - 1);
|
retry: retry - 1);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
||||||
|
|
@ -29,41 +29,29 @@ import 'package:matrix/msc_extensions/msc_3814_dehydrated_devices/model/dehydrat
|
||||||
/// Endpoints related to MSC3814, dehydrated devices v2 aka shrivelled sessions
|
/// Endpoints related to MSC3814, dehydrated devices v2 aka shrivelled sessions
|
||||||
/// https://github.com/matrix-org/matrix-spec-proposals/pull/3814
|
/// https://github.com/matrix-org/matrix-spec-proposals/pull/3814
|
||||||
extension DehydratedDeviceMatrixApi on MatrixApi {
|
extension DehydratedDeviceMatrixApi on MatrixApi {
|
||||||
/// Publishes end-to-end encryption keys for the specified device.
|
|
||||||
/// https://github.com/matrix-org/matrix-spec-proposals/pull/3814
|
|
||||||
Future<Map<String, int>> uploadKeysForDevice(String device,
|
|
||||||
{MatrixDeviceKeys? deviceKeys,
|
|
||||||
Map<String, dynamic>? oneTimeKeys,
|
|
||||||
Map<String, dynamic>? fallbackKeys}) async {
|
|
||||||
final response = await request(
|
|
||||||
RequestType.POST,
|
|
||||||
'/client/v3/keys/upload/${Uri.encodeComponent(device)}',
|
|
||||||
data: {
|
|
||||||
if (deviceKeys != null) 'device_keys': deviceKeys.toJson(),
|
|
||||||
if (oneTimeKeys != null) 'one_time_keys': oneTimeKeys,
|
|
||||||
if (fallbackKeys != null) ...{
|
|
||||||
'fallback_keys': fallbackKeys,
|
|
||||||
'org.matrix.msc2732.fallback_keys': fallbackKeys,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
return Map<String, int>.from(
|
|
||||||
response.tryGetMap<String, Object?>('one_time_key_counts') ??
|
|
||||||
<String, int>{});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// uploads a dehydrated device.
|
/// uploads a dehydrated device.
|
||||||
/// https://github.com/matrix-org/matrix-spec-proposals/pull/3814
|
/// https://github.com/matrix-org/matrix-spec-proposals/pull/3814
|
||||||
Future<String> uploadDehydratedDevice(
|
Future<String> uploadDehydratedDevice({
|
||||||
{String? initialDeviceDisplayName,
|
required String deviceId,
|
||||||
Map<String, dynamic>? deviceData}) async {
|
String? initialDeviceDisplayName,
|
||||||
|
Map<String, dynamic>? deviceData,
|
||||||
|
MatrixDeviceKeys? deviceKeys,
|
||||||
|
Map<String, dynamic>? oneTimeKeys,
|
||||||
|
Map<String, dynamic>? fallbackKeys,
|
||||||
|
}) async {
|
||||||
final response = await request(
|
final response = await request(
|
||||||
RequestType.PUT,
|
RequestType.PUT,
|
||||||
'/client/unstable/org.matrix.msc3814.v1/dehydrated_device',
|
'/client/unstable/org.matrix.msc3814.v1/dehydrated_device',
|
||||||
data: {
|
data: {
|
||||||
|
'device_id': deviceId,
|
||||||
if (initialDeviceDisplayName != null)
|
if (initialDeviceDisplayName != null)
|
||||||
'initial_device_display_name': initialDeviceDisplayName,
|
'initial_device_display_name': initialDeviceDisplayName,
|
||||||
if (deviceData != null) 'device_data': deviceData,
|
if (deviceData != null) 'device_data': deviceData,
|
||||||
|
if (deviceKeys != null) 'device_keys': deviceKeys.toJson(),
|
||||||
|
if (oneTimeKeys != null) 'one_time_keys': oneTimeKeys,
|
||||||
|
if (fallbackKeys != null) ...{
|
||||||
|
'fallback_keys': fallbackKeys,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return response['device_id'] as String;
|
return response['device_id'] as String;
|
||||||
|
|
@ -82,15 +70,15 @@ extension DehydratedDeviceMatrixApi on MatrixApi {
|
||||||
/// fetch events sent to a dehydrated device.
|
/// fetch events sent to a dehydrated device.
|
||||||
/// https://github.com/matrix-org/matrix-spec-proposals/pull/3814
|
/// https://github.com/matrix-org/matrix-spec-proposals/pull/3814
|
||||||
Future<DehydratedDeviceEvents> getDehydratedDeviceEvents(String deviceId,
|
Future<DehydratedDeviceEvents> getDehydratedDeviceEvents(String deviceId,
|
||||||
{String? from, int limit = 100}) async {
|
{String? nextBatch, int limit = 100}) async {
|
||||||
final response = await request(
|
final response = await request(RequestType.POST,
|
||||||
RequestType.GET,
|
'/client/unstable/org.matrix.msc3814.v1/dehydrated_device/$deviceId/events',
|
||||||
'/client/unstable/org.matrix.msc3814.v1/dehydrated_device/$deviceId/events',
|
query: {
|
||||||
query: {
|
'limit': limit.toString(),
|
||||||
if (from != null) 'from': from,
|
},
|
||||||
'limit': limit.toString(),
|
data: {
|
||||||
},
|
if (nextBatch != null) 'next_batch': nextBatch,
|
||||||
);
|
});
|
||||||
return DehydratedDeviceEvents.fromJson(response);
|
return DehydratedDeviceEvents.fromJson(response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
library msc_3814_dehydrated_devices;
|
library msc_3814_dehydrated_devices;
|
||||||
|
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:matrix/encryption.dart';
|
import 'package:matrix/encryption.dart';
|
||||||
import 'package:matrix/matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
|
|
@ -78,10 +79,12 @@ extension DehydratedDeviceHandler on Client {
|
||||||
// We need to be careful to not use the client.deviceId here and such.
|
// We need to be careful to not use the client.deviceId here and such.
|
||||||
final encryption = Encryption(client: this);
|
final encryption = Encryption(client: this);
|
||||||
try {
|
try {
|
||||||
await encryption.init(pickledDevice,
|
await encryption.init(
|
||||||
deviceId: device.deviceId,
|
pickledDevice,
|
||||||
pickleKey: pickleDeviceKey,
|
deviceId: device.deviceId,
|
||||||
isDehydratedDevice: true);
|
pickleKey: pickleDeviceKey,
|
||||||
|
dehydratedDeviceAlgorithm: _dehydratedDeviceAlgorithm,
|
||||||
|
);
|
||||||
|
|
||||||
if (dehydratedDeviceIdentity.curve25519Key != encryption.identityKey ||
|
if (dehydratedDeviceIdentity.curve25519Key != encryption.identityKey ||
|
||||||
dehydratedDeviceIdentity.ed25519Key != encryption.fingerprintKey) {
|
dehydratedDeviceIdentity.ed25519Key != encryption.fingerprintKey) {
|
||||||
|
|
@ -97,7 +100,7 @@ extension DehydratedDeviceHandler on Client {
|
||||||
|
|
||||||
do {
|
do {
|
||||||
events = await getDehydratedDeviceEvents(device.deviceId,
|
events = await getDehydratedDeviceEvents(device.deviceId,
|
||||||
from: events?.nextBatch);
|
nextBatch: events?.nextBatch);
|
||||||
|
|
||||||
for (final e in events.events ?? []) {
|
for (final e in events.events ?? []) {
|
||||||
// We are only interested in roomkeys, which ALWAYS need to be encrypted.
|
// We are only interested in roomkeys, which ALWAYS need to be encrypted.
|
||||||
|
|
@ -111,6 +114,12 @@ extension DehydratedDeviceHandler on Client {
|
||||||
}
|
}
|
||||||
} while (events.events?.isNotEmpty == true);
|
} while (events.events?.isNotEmpty == true);
|
||||||
|
|
||||||
|
// make sure the sessions we just received get uploaded before we upload a new device (which deletes the old device).
|
||||||
|
await this
|
||||||
|
.encryption
|
||||||
|
?.keyManager
|
||||||
|
.uploadInboundGroupSessions(skipIfInProgress: false);
|
||||||
|
|
||||||
await _uploadNewDevice(secureStorage);
|
await _uploadNewDevice(secureStorage);
|
||||||
} finally {
|
} finally {
|
||||||
await encryption.dispose();
|
await encryption.dispose();
|
||||||
|
|
@ -136,18 +145,22 @@ extension DehydratedDeviceHandler on Client {
|
||||||
_ssssSecretNameForDehydratedDevice, pickleDeviceKey);
|
_ssssSecretNameForDehydratedDevice, pickleDeviceKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const chars =
|
||||||
|
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890';
|
||||||
|
final rnd = Random();
|
||||||
|
|
||||||
|
final deviceIdSuffix = String.fromCharCodes(Iterable.generate(
|
||||||
|
10, (_) => chars.codeUnitAt(rnd.nextInt(chars.length))));
|
||||||
|
final String device = 'FAM$deviceIdSuffix';
|
||||||
|
|
||||||
// Generate a new olm account for the dehydrated device.
|
// Generate a new olm account for the dehydrated device.
|
||||||
await encryption.init(null,
|
|
||||||
deviceId: null, isDehydratedDevice: true, pickleKey: pickleDeviceKey);
|
|
||||||
String device;
|
|
||||||
try {
|
try {
|
||||||
device = await uploadDehydratedDevice(
|
await encryption.init(
|
||||||
initialDeviceDisplayName: 'Dehydrated Device',
|
null,
|
||||||
deviceData: {
|
deviceId: device,
|
||||||
'algorithm': _dehydratedDeviceAlgorithm,
|
pickleKey: pickleDeviceKey,
|
||||||
'device': encryption.olmManager
|
dehydratedDeviceAlgorithm: _dehydratedDeviceAlgorithm,
|
||||||
.pickleOlmAccountWithKey(pickleDeviceKey),
|
);
|
||||||
});
|
|
||||||
} on MatrixException catch (_) {
|
} on MatrixException catch (_) {
|
||||||
// dehydrated devices unsupported, do noting.
|
// dehydrated devices unsupported, do noting.
|
||||||
Logs().i('Dehydrated devices unsupported, skipping upload.');
|
Logs().i('Dehydrated devices unsupported, skipping upload.');
|
||||||
|
|
@ -158,11 +171,6 @@ extension DehydratedDeviceHandler on Client {
|
||||||
encryption.ourDeviceId = device;
|
encryption.ourDeviceId = device;
|
||||||
encryption.olmManager.ourDeviceId = device;
|
encryption.olmManager.ourDeviceId = device;
|
||||||
|
|
||||||
await encryption.olmManager.uploadKeys(
|
|
||||||
uploadDeviceKeys: true,
|
|
||||||
updateDatabase: false,
|
|
||||||
unusedFallbackKey: true);
|
|
||||||
|
|
||||||
// cross sign the device from our currently signed in device
|
// cross sign the device from our currently signed in device
|
||||||
await updateUserDeviceKeys(additionalUsers: {userID!});
|
await updateUserDeviceKeys(additionalUsers: {userID!});
|
||||||
final keysToSign = <SignableKey>[
|
final keysToSign = <SignableKey>[
|
||||||
|
|
|
||||||
|
|
@ -207,7 +207,10 @@ class FakeMatrixApi extends BaseClient {
|
||||||
}
|
}
|
||||||
res = {};
|
res = {};
|
||||||
} else {
|
} else {
|
||||||
res = {'errcode': 'M_UNRECOGNIZED', 'error': 'Unrecognized request'};
|
res = {
|
||||||
|
'errcode': 'M_UNRECOGNIZED',
|
||||||
|
'error': 'Unrecognized request: $action'
|
||||||
|
};
|
||||||
statusCode = 405;
|
statusCode = 405;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1978,29 +1981,6 @@ class FakeMatrixApi extends BaseClient {
|
||||||
'device_id': 'DEHYDDEV',
|
'device_id': 'DEHYDDEV',
|
||||||
'device_data': {'algorithm': 'some.famedly.proprietary.algorithm'},
|
'device_data': {'algorithm': 'some.famedly.proprietary.algorithm'},
|
||||||
},
|
},
|
||||||
'/client/unstable/org.matrix.msc3814.v1/dehydrated_device/DEHYDDEV/events?limit=100':
|
|
||||||
(var _) => {
|
|
||||||
'events': [
|
|
||||||
{
|
|
||||||
// this is the commented out m.room_key event - only encrypted
|
|
||||||
'sender': '@othertest:fakeServer.notExisting',
|
|
||||||
'content': {
|
|
||||||
'algorithm': AlgorithmTypes.olmV1Curve25519AesSha2,
|
|
||||||
'sender_key':
|
|
||||||
'JBG7ZaPn54OBC7TuIEiylW3BZ+7WcGQhFBPB9pogbAg',
|
|
||||||
'ciphertext': {
|
|
||||||
'7rvl3jORJkBiK4XX1e5TnGnqz068XfYJ0W++Ml63rgk': {
|
|
||||||
'type': 0,
|
|
||||||
'body':
|
|
||||||
'Awogyh7K4iLUQjcOxIfi7q7LhBBqv9w0mQ6JI9+U9tv7iF4SIHC6xb5YFWf9voRnmDBbd+0vxD/xDlVNRDlPIKliLGkYGiAkEbtlo+fng4ELtO4gSLKVbcFn7tZwZCEUE8H2miBsCCKABgMKIFrKDJwB7gM3lXPt9yVoh6gQksafKt7VFCNRN5KLKqsDEAAi0AX5EfTV7jJ1ZWAbxftjoSN6kCVIxzGclbyg1HjchmNCX7nxNCHWl+q5ZgqHYZVu2n2mCVmIaKD0kvoEZeY3tV1Itb6zf67BLaU0qgW/QzHCHg5a44tNLjucvL2mumHjIG8k0BY2uh+52HeiMCvSOvtDwHg7nzCASGdqPVCj9Kzw6z7F6nL4e3mYim8zvJd7f+mD9z3ARrypUOLGkTGYbB2PQOovf0Do8WzcaRzfaUCnuu/YVZWKK7DPgG8uhw/TjR6XtraAKZysF+4DJYMG9SQWx558r6s7Z5EUOF5CU2M35w1t1Xxllb3vrS83dtf9LPCrBhLsEBeYEUBE2+bTBfl0BDKqLiB0Cc0N0ixOcHIt6e40wAvW622/gMgHlpNSx8xG12u0s6h6EMWdCXXLWd9fy2q6glFUHvA67A35q7O+M8DVml7Y9xG55Y3DHkMDc9cwgwFkBDCAYQe6pQF1nlKytcVCGREpBs/gq69gHAStMQ8WEg38Lf8u8eBr2DFexrN4U+QAk+S//P3fJgf0bQx/Eosx4fvWSz9En41iC+ADCsWQpMbwHn4JWvtAbn3oW0XmL/OgThTkJMLiCymduYAa1Hnt7a3tP0KTL2/x11F02ggQHL28cCjq5W4zUGjWjl5wo2PsKB6t8aAvMg2ujGD2rCjb4yrv5VIzAKMOZLyj7K0vSK9gwDLQ/4vq+QnKUBG5zrcOze0hX+kz2909/tmAdeCH61Ypw7gbPUJAKnmKYUiB/UgwkJvzMJSsk/SEs5SXosHDI+HsJHJp4Mp4iKD0xRMst+8f9aTjaWwh8ZvELE1ZOhhCbF3RXhxi3x2Nu8ORIz+vhEQ1NOlMc7UIo98Fk/96T36vL/fviowT4C/0AlaapZDJBmKwhmwqisMjY2n1vY29oM2p5BzY1iwP7q9BYdRFst6xwo57TNSuRwQw7IhFsf0k+ABuPEZy5xB5nPHyIRTf/pr3Hw',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'type': 'm.room.encrypted',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'next_batch': 'd1',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
'POST': {
|
'POST': {
|
||||||
'/client/v3/delete_devices': (var req) => {},
|
'/client/v3/delete_devices': (var req) => {},
|
||||||
|
|
@ -2409,6 +2389,29 @@ class FakeMatrixApi extends BaseClient {
|
||||||
'/client/v3/rooms/!localpart%3Aserver.abc/invite': (var reqI) => {},
|
'/client/v3/rooms/!localpart%3Aserver.abc/invite': (var reqI) => {},
|
||||||
'/client/v3/keys/signatures/upload': (var reqI) => {'failures': {}},
|
'/client/v3/keys/signatures/upload': (var reqI) => {'failures': {}},
|
||||||
'/client/v3/room_keys/version': (var reqI) => {'version': '5'},
|
'/client/v3/room_keys/version': (var reqI) => {'version': '5'},
|
||||||
|
'/client/unstable/org.matrix.msc3814.v1/dehydrated_device/DEHYDDEV/events?limit=100':
|
||||||
|
(var _) => {
|
||||||
|
'events': [
|
||||||
|
{
|
||||||
|
// this is the commented out m.room_key event - only encrypted
|
||||||
|
'sender': '@othertest:fakeServer.notExisting',
|
||||||
|
'content': {
|
||||||
|
'algorithm': AlgorithmTypes.olmV1Curve25519AesSha2,
|
||||||
|
'sender_key':
|
||||||
|
'JBG7ZaPn54OBC7TuIEiylW3BZ+7WcGQhFBPB9pogbAg',
|
||||||
|
'ciphertext': {
|
||||||
|
'7rvl3jORJkBiK4XX1e5TnGnqz068XfYJ0W++Ml63rgk': {
|
||||||
|
'type': 0,
|
||||||
|
'body':
|
||||||
|
'Awogyh7K4iLUQjcOxIfi7q7LhBBqv9w0mQ6JI9+U9tv7iF4SIHC6xb5YFWf9voRnmDBbd+0vxD/xDlVNRDlPIKliLGkYGiAkEbtlo+fng4ELtO4gSLKVbcFn7tZwZCEUE8H2miBsCCKABgMKIFrKDJwB7gM3lXPt9yVoh6gQksafKt7VFCNRN5KLKqsDEAAi0AX5EfTV7jJ1ZWAbxftjoSN6kCVIxzGclbyg1HjchmNCX7nxNCHWl+q5ZgqHYZVu2n2mCVmIaKD0kvoEZeY3tV1Itb6zf67BLaU0qgW/QzHCHg5a44tNLjucvL2mumHjIG8k0BY2uh+52HeiMCvSOvtDwHg7nzCASGdqPVCj9Kzw6z7F6nL4e3mYim8zvJd7f+mD9z3ARrypUOLGkTGYbB2PQOovf0Do8WzcaRzfaUCnuu/YVZWKK7DPgG8uhw/TjR6XtraAKZysF+4DJYMG9SQWx558r6s7Z5EUOF5CU2M35w1t1Xxllb3vrS83dtf9LPCrBhLsEBeYEUBE2+bTBfl0BDKqLiB0Cc0N0ixOcHIt6e40wAvW622/gMgHlpNSx8xG12u0s6h6EMWdCXXLWd9fy2q6glFUHvA67A35q7O+M8DVml7Y9xG55Y3DHkMDc9cwgwFkBDCAYQe6pQF1nlKytcVCGREpBs/gq69gHAStMQ8WEg38Lf8u8eBr2DFexrN4U+QAk+S//P3fJgf0bQx/Eosx4fvWSz9En41iC+ADCsWQpMbwHn4JWvtAbn3oW0XmL/OgThTkJMLiCymduYAa1Hnt7a3tP0KTL2/x11F02ggQHL28cCjq5W4zUGjWjl5wo2PsKB6t8aAvMg2ujGD2rCjb4yrv5VIzAKMOZLyj7K0vSK9gwDLQ/4vq+QnKUBG5zrcOze0hX+kz2909/tmAdeCH61Ypw7gbPUJAKnmKYUiB/UgwkJvzMJSsk/SEs5SXosHDI+HsJHJp4Mp4iKD0xRMst+8f9aTjaWwh8ZvELE1ZOhhCbF3RXhxi3x2Nu8ORIz+vhEQ1NOlMc7UIo98Fk/96T36vL/fviowT4C/0AlaapZDJBmKwhmwqisMjY2n1vY29oM2p5BzY1iwP7q9BYdRFst6xwo57TNSuRwQw7IhFsf0k+ABuPEZy5xB5nPHyIRTf/pr3Hw',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'type': 'm.room.encrypted',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'next_batch': 'd1',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
'PUT': {
|
'PUT': {
|
||||||
'/client/v3/user/${Uri.encodeComponent('@alice:example.com')}/account_data/io.element.recent_emoji}':
|
'/client/v3/user/${Uri.encodeComponent('@alice:example.com')}/account_data/io.element.recent_emoji}':
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ void main() {
|
||||||
final client = await getClient();
|
final client = await getClient();
|
||||||
|
|
||||||
final ret = await client.uploadDehydratedDevice(
|
final ret = await client.uploadDehydratedDevice(
|
||||||
|
deviceId: 'DEHYDDEV',
|
||||||
initialDeviceDisplayName: 'DehydratedDevice',
|
initialDeviceDisplayName: 'DehydratedDevice',
|
||||||
deviceData: {'algorithm': 'some.famedly.proprietary.algorith'});
|
deviceData: {'algorithm': 'some.famedly.proprietary.algorith'});
|
||||||
expect(
|
expect(
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue