/*
* Famedly Matrix SDK
* Copyright (C) 2019, 2020, 2021 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 .
*/
import 'dart:convert';
import 'package:async/async.dart';
import 'package:canonical_json/canonical_json.dart';
import 'package:collection/collection.dart';
import 'package:vodozemac/vodozemac.dart' as vod;
import 'package:matrix/encryption/encryption.dart';
import 'package:matrix/encryption/utils/json_signature_check_extension.dart';
import 'package:matrix/encryption/utils/olm_session.dart';
import 'package:matrix/encryption/utils/pickle_key.dart';
import 'package:matrix/matrix.dart';
import 'package:matrix/msc_extensions/msc_3814_dehydrated_devices/api.dart';
import 'package:matrix/src/utils/run_benchmarked.dart';
import 'package:matrix/src/utils/run_in_root.dart';
class OlmManager {
final Encryption encryption;
Client get client => encryption.client;
vod.Account? _olmAccount;
String? ourDeviceId;
/// Returns the base64 encoded keys to store them in a store.
/// This String should **never** leave the device!
String? get pickledOlmAccount {
return enabled
? _olmAccount!.toPickleEncrypted(client.userID!.toPickleKey())
: null;
}
String? get fingerprintKey =>
enabled ? _olmAccount!.identityKeys.ed25519.toBase64() : null;
String? get identityKey =>
enabled ? _olmAccount!.identityKeys.curve25519.toBase64() : null;
String? pickleOlmAccountWithKey(String key) =>
enabled ? _olmAccount!.toPickleEncrypted(key.toPickleKey()) : null;
bool get enabled => _olmAccount != null;
OlmManager(this.encryption);
/// A map from Curve25519 identity keys to existing olm sessions.
Map> get olmSessions => _olmSessions;
final Map> _olmSessions = {};
// NOTE(Nico): On initial login we pass null to create a new account
Future init({
String? olmAccount,
required String? deviceId,
String? pickleKey,
String? dehydratedDeviceAlgorithm,
}) async {
ourDeviceId = deviceId;
if (olmAccount == null) {
_olmAccount = vod.Account();
if (!await uploadKeys(
uploadDeviceKeys: true,
updateDatabase: false,
dehydratedDeviceAlgorithm: dehydratedDeviceAlgorithm,
dehydratedDevicePickleKey:
dehydratedDeviceAlgorithm != null ? pickleKey : null,
)) {
throw ('Upload key failed');
}
} else {
try {
_olmAccount = vod.Account.fromPickleEncrypted(
pickle: olmAccount,
pickleKey: (pickleKey ?? client.userID!).toPickleKey(),
);
} catch (e) {
Logs().d(
'Unable to unpickle account in vodozemac format. Trying Olm format...',
e,
);
_olmAccount = vod.Account.fromOlmPickleEncrypted(
pickle: olmAccount,
pickleKey: utf8.encode(pickleKey ?? client.userID!),
);
}
}
}
/// Adds a signature to this json from this olm account and returns the signed
/// json.
Map signJson(Map payload) {
if (!enabled) throw ('Encryption is disabled');
final signableJson = SignableJsonMap(payload);
final canonical = canonicalJson.encode(signableJson.jsonMap);
final signature = _olmAccount!.sign(String.fromCharCodes(canonical));
final userSignatures = signableJson.signatures[client.userID!] ??= {};
userSignatures['ed25519:$ourDeviceId'] = signature.toBase64();
return signableJson.toJson();
}
String signString(String s) {
return _olmAccount!.sign(s).toBase64();
}
bool _uploadKeysLock = false;
CancelableOperation