refactor: Add secretstoragekeycontent

This commit is contained in:
Christian Pauly 2020-12-10 09:39:27 +01:00
parent 49f0679fbf
commit b563aec7bb
13 changed files with 226 additions and 139 deletions

View File

@ -24,15 +24,12 @@ import 'package:olm/olm.dart' as olm;
import '../famedlysdk.dart';
import 'encryption.dart';
const SELF_SIGNING_KEY = EventTypes.CrossSigningSelfSigning;
const USER_SIGNING_KEY = EventTypes.CrossSigningUserSigning;
const MASTER_KEY = 'm.cross_signing.master';
class CrossSigning {
final Encryption encryption;
Client get client => encryption.client;
CrossSigning(this.encryption) {
encryption.ssss.setValidator(SELF_SIGNING_KEY, (String secret) async {
encryption.ssss.setValidator(EventTypes.CrossSigningSelfSigning,
(String secret) async {
final keyObj = olm.PkSigning();
try {
return keyObj.init_with_seed(base64.decode(secret)) ==
@ -43,7 +40,8 @@ class CrossSigning {
keyObj.free();
}
});
encryption.ssss.setValidator(USER_SIGNING_KEY, (String secret) async {
encryption.ssss.setValidator(EventTypes.CrossSigningUserSigning,
(String secret) async {
final keyObj = olm.PkSigning();
try {
return keyObj.init_with_seed(base64.decode(secret)) ==
@ -57,23 +55,27 @@ class CrossSigning {
}
bool get enabled =>
client.accountData[SELF_SIGNING_KEY] != null &&
client.accountData[USER_SIGNING_KEY] != null &&
client.accountData[MASTER_KEY] != null;
client.accountData[EventTypes.CrossSigningSelfSigning] != null &&
client.accountData[EventTypes.CrossSigningUserSigning] != null &&
client.accountData[EventTypes.CrossSigningMasterKey] != null;
Future<bool> isCached() async {
if (!enabled) {
return false;
}
return (await encryption.ssss.getCached(SELF_SIGNING_KEY)) != null &&
(await encryption.ssss.getCached(USER_SIGNING_KEY)) != null;
return (await encryption.ssss
.getCached(EventTypes.CrossSigningSelfSigning)) !=
null &&
(await encryption.ssss.getCached(EventTypes.CrossSigningUserSigning)) !=
null;
}
Future<void> selfSign({String passphrase, String recoveryKey}) async {
final handle = encryption.ssss.open(MASTER_KEY);
final handle = encryption.ssss.open(EventTypes.CrossSigningMasterKey);
await handle.unlock(passphrase: passphrase, recoveryKey: recoveryKey);
await handle.maybeCacheAll();
final masterPrivateKey = base64.decode(await handle.getStored(MASTER_KEY));
final masterPrivateKey =
base64.decode(await handle.getStored(EventTypes.CrossSigningMasterKey));
final keyObj = olm.PkSigning();
String masterPubkey;
try {
@ -85,11 +87,11 @@ class CrossSigning {
!client.userDeviceKeys.containsKey(client.userID) ||
!client.userDeviceKeys[client.userID].deviceKeys
.containsKey(client.deviceID)) {
throw 'Master or user keys not found';
throw Exception('Master or user keys not found');
}
final masterKey = client.userDeviceKeys[client.userID].masterKey;
if (masterKey == null || masterKey.ed25519Key != masterPubkey) {
throw 'Master pubkey key doesn\'t match';
throw Exception('Master pubkey key doesn\'t match');
}
// master key is valid, set it to verified
await masterKey.setVerified(true, false);
@ -146,8 +148,9 @@ class CrossSigning {
// we don't care about signing other cross-signing keys
} else {
// okay, we'll sign a device key with our self signing key
selfSigningKey ??= base64
.decode(await encryption.ssss.getCached(SELF_SIGNING_KEY) ?? '');
selfSigningKey ??= base64.decode(await encryption.ssss
.getCached(EventTypes.CrossSigningSelfSigning) ??
'');
if (selfSigningKey.isNotEmpty) {
final signature = _sign(key.signingContent, selfSigningKey);
addSignature(key,
@ -156,8 +159,9 @@ class CrossSigning {
}
} else if (key is CrossSigningKey && key.usage.contains('master')) {
// we are signing someone elses master key
userSigningKey ??= base64
.decode(await encryption.ssss.getCached(USER_SIGNING_KEY) ?? '');
userSigningKey ??= base64.decode(await encryption.ssss
.getCached(EventTypes.CrossSigningUserSigning) ??
'');
if (userSigningKey.isNotEmpty) {
final signature = _sign(key.signingContent, userSigningKey);
addSignature(key, client.userDeviceKeys[client.userID].userSigningKey,

View File

@ -46,6 +46,13 @@ class Encryption {
String get fingerprintKey => olmManager.fingerprintKey;
String get identityKey => olmManager.identityKey;
/// ToDeviceEventDecryptionError erros are coming here.
final StreamController<ToDeviceEventDecryptionError>
onToDeviceEventDecryptionError = StreamController.broadcast();
/// All other erros are coming here.
final StreamController<SdkError> onError = StreamController.broadcast();
KeyManager keyManager;
OlmManager olmManager;
KeyVerificationManager keyVerificationManager;
@ -132,7 +139,21 @@ class Encryption {
}
Future<ToDeviceEvent> decryptToDeviceEvent(ToDeviceEvent event) async {
return await olmManager.decryptToDeviceEvent(event);
try {
return await olmManager.decryptToDeviceEvent(event);
} catch (e, s) {
Logs.error(
'[LibOlm] Could not decrypt to device event from ${event.sender} with content: ${event.content}\n${e.toString()}',
s);
onToDeviceEventDecryptionError.add(
ToDeviceEventDecryptionError(
exception: e is Exception ? e : Exception(e),
stackTrace: s,
toDeviceEvent: event,
),
);
return event;
}
}
Event decryptRoomEventSync(String roomId, Event event) {

View File

@ -53,6 +53,9 @@ Map<String, dynamic> _deepcopy(Map<String, dynamic> data) {
return json.decode(json.encode(data));
}
/// SSSS: **S**ecure **S**ecret **S**torage and **S**haring
/// Read more about SSSS at:
/// https://matrix.org/docs/guides/implementing-more-advanced-e-2-ee-features-such-as-cross-signing#3-implementing-ssss
class SSSS {
final Encryption encryption;
Client get client => encryption.client;
@ -113,7 +116,7 @@ class SSSS {
.encode(Hmac(sha256, keys.hmacKey).convert(cipher).bytes)
.replaceAll(RegExp(r'=+$'), '');
if (hmac != data.mac.replaceAll(RegExp(r'=+$'), '')) {
throw 'Bad MAC';
throw Exception('Bad MAC');
}
final decipher = AES(Key(keys.aesKey), mode: AESMode.ctr, padding: null)
.decrypt(Encrypted(cipher), iv: IV(base64.decode(data.iv)));
@ -128,26 +131,26 @@ class SSSS {
parity ^= b;
}
if (parity != 0) {
throw 'Incorrect parity';
throw Exception('Incorrect parity');
}
for (var i = 0; i < OLM_RECOVERY_KEY_PREFIX.length; i++) {
if (result[i] != OLM_RECOVERY_KEY_PREFIX[i]) {
throw 'Incorrect prefix';
throw Exception('Incorrect prefix');
}
}
if (result.length != OLM_RECOVERY_KEY_PREFIX.length + SSSS_KEY_LENGTH + 1) {
throw 'Incorrect length';
throw Exception('Incorrect length');
}
return Uint8List.fromList(result.sublist(OLM_RECOVERY_KEY_PREFIX.length,
OLM_RECOVERY_KEY_PREFIX.length + SSSS_KEY_LENGTH));
}
static Uint8List keyFromPassphrase(String passphrase, _PassphraseInfo info) {
static Uint8List keyFromPassphrase(String passphrase, PassphraseInfo info) {
if (info.algorithm != AlgorithmTypes.pbkdf2) {
throw 'Unknown algorithm';
throw Exception('Unknown algorithm');
}
final generator = PBKDF2(hashAlgorithm: sha512);
return Uint8List.fromList(generator.generateKey(passphrase, info.salt,
@ -162,55 +165,45 @@ class SSSS {
_cacheCallbacks[type] = callback;
}
String get defaultKeyId {
final keyData = client.accountData[EventTypes.SecretStorageDefaultKey];
if (keyData == null || !(keyData.content['key'] is String)) {
return null;
}
return keyData.content['key'];
}
String get defaultKeyId => client
.accountData[EventTypes.SecretStorageDefaultKey]
?.parsedSecretStorageDefaultKeyContent
?.key;
Future<void> setDefaultKeyId(String keyId) async {
await client.setAccountData(
client.userID, EventTypes.SecretStorageDefaultKey, <String, dynamic>{
'key': keyId,
});
client.userID,
EventTypes.SecretStorageDefaultKey,
(SecretStorageDefaultKeyContent()..key = keyId).toJson(),
);
}
BasicEvent getKey(String keyId) {
return client.accountData[EventTypes.secretStorageKey(keyId)];
SecretStorageKeyContent getKey(String keyId) {
return client.accountData[EventTypes.secretStorageKey(keyId)]
?.parsedSecretStorageKeyContent;
}
bool isKeyValid(String keyId) {
final keyData = getKey(keyId);
if (keyData == null) {
return false;
}
return keyData.content['algorithm'] ==
AlgorithmTypes.secretStorageV1AesHmcSha2;
}
bool isKeyValid(String keyId) =>
getKey(keyId)?.algorithm == AlgorithmTypes.secretStorageV1AesHmcSha2;
/// Creates a new secret storage key, optional encrypts it with [passphrase]
/// and stores it in the user's [accountData].
Future<OpenSSSS> createKey([String passphrase]) async {
Uint8List privateKey;
final content = <String, dynamic>{};
final content = SecretStorageKeyContent();
if (passphrase != null) {
// we need to derive the key off of the passphrase
content['passphrase'] = <String, dynamic>{};
content['passphrase']['algorithm'] = AlgorithmTypes.pbkdf2;
content['passphrase']['salt'] = base64
content.passphrase = PassphraseInfo();
content.passphrase.algorithm = AlgorithmTypes.pbkdf2;
content.passphrase.salt = base64
.encode(SecureRandom(PBKDF2_SALT_LENGTH).bytes); // generate salt
content['passphrase']['iterations'] = PBKDF2_DEFAULT_ITERATIONS;
content['passphrase']['bits'] = SSSS_KEY_LENGTH * 8;
content.passphrase.iterations = PBKDF2_DEFAULT_ITERATIONS;
content.passphrase.bits = SSSS_KEY_LENGTH * 8;
privateKey = await runInBackground(
_keyFromPassphrase,
_KeyFromPassphraseArgs(
passphrase: passphrase,
info: _PassphraseInfo(
algorithm: content['passphrase']['algorithm'],
salt: content['passphrase']['salt'],
iterations: content['passphrase']['iterations'],
bits: content['passphrase']['bits'],
),
info: content.passphrase,
));
} else {
// we need to just generate a new key from scratch
@ -218,9 +211,9 @@ class SSSS {
}
// now that we have the private key, let's create the iv and mac
final encrypted = encryptAes(ZERO_STR, privateKey, '');
content['iv'] = encrypted.iv;
content['mac'] = encrypted.mac;
content['algorithm'] = AlgorithmTypes.secretStorageV1AesHmcSha2;
content.iv = encrypted.iv;
content.mac = encrypted.mac;
content.algorithm = AlgorithmTypes.secretStorageV1AesHmcSha2;
const KEYID_BYTE_LENGTH = 24;
@ -235,7 +228,8 @@ class SSSS {
final waitForAccountData = client.onSync.stream.firstWhere((syncUpdate) =>
syncUpdate.accountData
.any((accountData) => accountData.type == accountDataType));
await client.setAccountData(client.userID, accountDataType, content);
await client.setAccountData(
client.userID, accountDataType, content.toJson());
await waitForAccountData;
final key = open(keyId);
@ -243,19 +237,18 @@ class SSSS {
return key;
}
bool checkKey(Uint8List key, BasicEvent keyData) {
final info = keyData.content;
if (info['algorithm'] == AlgorithmTypes.secretStorageV1AesHmcSha2) {
if ((info['mac'] is String) && (info['iv'] is String)) {
final encrypted = encryptAes(ZERO_STR, key, '', info['iv']);
return info['mac'].replaceAll(RegExp(r'=+$'), '') ==
bool checkKey(Uint8List key, SecretStorageKeyContent info) {
if (info.algorithm == AlgorithmTypes.secretStorageV1AesHmcSha2) {
if ((info.mac is String) && (info.iv is String)) {
final encrypted = encryptAes(ZERO_STR, key, '', info.iv);
return info.mac.replaceAll(RegExp(r'=+$'), '') ==
encrypted.mac.replaceAll(RegExp(r'=+$'), '');
} else {
// no real information about the key, assume it is valid
return true;
}
} else {
throw 'Unknown Algorithm';
throw Exception('Unknown Algorithm');
}
}
@ -287,13 +280,13 @@ class SSSS {
Future<String> getStored(String type, String keyId, Uint8List key) async {
final secretInfo = client.accountData[type];
if (secretInfo == null) {
throw 'Not found';
throw Exception('Not found');
}
if (!(secretInfo.content['encrypted'] is Map)) {
throw 'Content is not encrypted';
throw Exception('Content is not encrypted');
}
if (!(secretInfo.content['encrypted'][keyId] is Map)) {
throw 'Wrong / unknown key';
throw Exception('Wrong / unknown key');
}
final enc = secretInfo.content['encrypted'][keyId];
final encryptInfo = _Encrypted(
@ -345,7 +338,7 @@ class SSSS {
Future<void> validateAndStripOtherKeys(
String type, String secret, String keyId, Uint8List key) async {
if (await getStored(type, keyId, key) != secret) {
throw 'Secrets do not match up!';
throw Exception('Secrets do not match up!');
}
// now remove all other keys
final content = _deepcopy(client.accountData[type].content);
@ -354,7 +347,7 @@ class SSSS {
content['encrypted'].removeWhere((k, v) => otherKeys.contains(k));
// yes, we are paranoid...
if (await getStored(type, keyId, key) != secret) {
throw 'Secrets do not match up!';
throw Exception('Secrets do not match up!');
}
// store the thing in your account data
await client.setAccountData(client.userID, type, content);
@ -561,15 +554,15 @@ class SSSS {
OpenSSSS open([String identifier]) {
identifier ??= defaultKeyId;
if (identifier == null) {
throw 'Dont know what to open';
throw Exception('Dont know what to open');
}
final keyToOpen = keyIdFromType(identifier) ?? identifier;
if (keyToOpen == null) {
throw 'No key found to open';
throw Exception('No key found to open');
}
final key = getKey(keyToOpen);
if (key == null) {
throw 'Unknown key to open';
throw Exception('Unknown key to open');
}
return OpenSSSS(ssss: this, keyId: keyToOpen, keyData: key);
}
@ -600,19 +593,10 @@ class _DerivedKeys {
_DerivedKeys({this.aesKey, this.hmacKey});
}
class _PassphraseInfo {
final String algorithm;
final String salt;
final int iterations;
final int bits;
_PassphraseInfo({this.algorithm, this.salt, this.iterations, this.bits});
}
class OpenSSSS {
final SSSS ssss;
final String keyId;
final BasicEvent keyData;
final SecretStorageKeyContent keyData;
OpenSSSS({this.ssss, this.keyId, this.keyData});
Uint8List privateKey;
@ -632,28 +616,23 @@ class OpenSSSS {
_keyFromPassphrase,
_KeyFromPassphraseArgs(
passphrase: passphrase,
info: _PassphraseInfo(
algorithm: keyData.content['passphrase']['algorithm'],
salt: keyData.content['passphrase']['salt'],
iterations: keyData.content['passphrase']['iterations'],
bits: keyData.content['passphrase']['bits'],
),
info: keyData.passphrase,
));
} else if (recoveryKey != null) {
privateKey = SSSS.decodeRecoveryKey(recoveryKey);
} else {
throw 'Nothing specified';
throw Exception('Nothing specified');
}
// verify the validity of the key
if (!ssss.checkKey(privateKey, keyData)) {
privateKey = null;
throw 'Inalid key';
throw Exception('Inalid key');
}
}
void setPrivateKey(Uint8List key) {
if (!ssss.checkKey(key, keyData)) {
throw 'Invalid key';
throw Exception('Invalid key');
}
privateKey = key;
}
@ -677,7 +656,7 @@ class OpenSSSS {
class _KeyFromPassphraseArgs {
final String passphrase;
final _PassphraseInfo info;
final PassphraseInfo info;
_KeyFromPassphraseArgs({this.passphrase, this.info});
}

View File

@ -24,7 +24,6 @@ import 'package:olm/olm.dart' as olm;
import '../encryption.dart';
import '../ssss.dart';
import '../cross_signing.dart';
import '../key_manager.dart';
import '../../famedlysdk.dart';
import '../../matrix_api/utils/logs.dart';
@ -45,6 +44,7 @@ enum BootstrapState {
done, // done
}
/// Bootstrapping SSSS and cross-signing
class Bootstrap {
final Encryption encryption;
Client get client => encryption.client;
@ -162,7 +162,7 @@ class Bootstrap {
void wipeSsss(bool wipe) {
if (state != BootstrapState.askWipeSsss) {
throw Exception('Wrong State');
throw BootstrapBadStateException('Wrong State');
}
if (wipe) {
state = BootstrapState.askNewSsss;
@ -178,7 +178,7 @@ class Bootstrap {
void useExistingSsss(bool use) {
if (state != BootstrapState.askUseExistingSsss) {
throw Exception('Wrong State');
throw BootstrapBadStateException('Wrong State');
}
if (use) {
newSsssKey = encryption.ssss.open(encryption.ssss.defaultKeyId);
@ -192,7 +192,7 @@ class Bootstrap {
void ignoreBadSecrets(bool ignore) {
if (state != BootstrapState.askBadSsss) {
throw Exception('Wrong State');
throw BootstrapBadStateException('Wrong State');
}
if (ignore) {
migrateOldSsss();
@ -221,14 +221,14 @@ class Bootstrap {
void unlockedSsss() {
if (state != BootstrapState.askUnlockSsss) {
throw Exception('Wrong State');
throw BootstrapBadStateException('Wrong State');
}
state = BootstrapState.askNewSsss;
}
Future<void> newSsss([String passphrase]) async {
if (state != BootstrapState.askNewSsss) {
throw Exception('Wrong State');
throw BootstrapBadStateException('Wrong State');
}
state = BootstrapState.loading;
try {
@ -285,10 +285,10 @@ class Bootstrap {
void openExistingSsss() {
if (state != BootstrapState.openExistingSsss) {
throw 'Bad State';
throw BootstrapBadStateException();
}
if (!newSsssKey.isUnlocked) {
throw 'Key not unlocked';
throw BootstrapBadStateException('Key not unlocked');
}
checkCrossSigning();
}
@ -306,7 +306,7 @@ class Bootstrap {
void wipeCrossSigning(bool wipe) {
if (state != BootstrapState.askWipeCrossSigning) {
throw 'Bad State';
throw BootstrapBadStateException();
}
if (wipe) {
state = BootstrapState.askSetupCrossSigning;
@ -320,7 +320,7 @@ class Bootstrap {
bool setupSelfSigningKey = false,
bool setupUserSigningKey = false}) async {
if (state != BootstrapState.askSetupCrossSigning) {
throw 'Bad State';
throw BootstrapBadStateException();
}
if (!setupMasterKey && !setupSelfSigningKey && !setupUserSigningKey) {
checkOnlineKeyBackup();
@ -345,16 +345,17 @@ class Bootstrap {
},
};
masterKey = MatrixCrossSigningKey.fromJson(json);
secretsToStore[MASTER_KEY] = base64.encode(masterSigningKey);
secretsToStore[EventTypes.CrossSigningMasterKey] =
base64.encode(masterSigningKey);
} finally {
master.free();
}
} else {
masterSigningKey =
base64.decode(await newSsssKey.getStored(MASTER_KEY) ?? '');
masterSigningKey = base64.decode(
await newSsssKey.getStored(EventTypes.CrossSigningMasterKey) ?? '');
if (masterSigningKey == null || masterSigningKey.isEmpty) {
// no master signing key :(
throw 'No master key';
throw BootstrapBadStateException('No master key');
}
final master = olm.PkSigning();
try {
@ -391,7 +392,8 @@ class Bootstrap {
},
};
selfSigningKey = MatrixCrossSigningKey.fromJson(json);
secretsToStore[SELF_SIGNING_KEY] = base64.encode(selfSigningPriv);
secretsToStore[EventTypes.CrossSigningSelfSigning] =
base64.encode(selfSigningPriv);
} finally {
selfSigning.free();
}
@ -415,7 +417,8 @@ class Bootstrap {
},
};
userSigningKey = MatrixCrossSigningKey.fromJson(json);
secretsToStore[USER_SIGNING_KEY] = base64.encode(userSigningPriv);
secretsToStore[EventTypes.CrossSigningUserSigning] =
base64.encode(userSigningPriv);
} finally {
userSigning.free();
}
@ -451,7 +454,8 @@ class Bootstrap {
if (masterKey != null) {
if (client.userDeviceKeys[client.userID].masterKey.ed25519Key !=
masterKey.publicKey) {
throw 'ERROR: New master key does not match up!';
throw BootstrapBadStateException(
'ERROR: New master key does not match up!');
}
await client.userDeviceKeys[client.userID].masterKey
.setVerified(true, false);
@ -484,7 +488,7 @@ class Bootstrap {
void wipeOnlineKeyBackup(bool wipe) {
if (state != BootstrapState.askWipeOnlineKeyBackup) {
throw 'Bad State';
throw BootstrapBadStateException();
}
if (wipe) {
state = BootstrapState.askSetupOnlineKeyBackup;
@ -495,7 +499,7 @@ class Bootstrap {
Future<void> askSetupOnlineKeyBackup(bool setup) async {
if (state != BootstrapState.askSetupOnlineKeyBackup) {
throw 'Bad State';
throw BootstrapBadStateException();
}
if (!setup) {
state = BootstrapState.done;
@ -527,6 +531,7 @@ class Bootstrap {
'[Bootstrapping] Error setting up online key backup: ' + e.toString(),
s);
state = BootstrapState.error;
encryption.onError.add(SdkError(exception: e, stackTrace: s));
return;
}
state = BootstrapState.done;
@ -541,3 +546,11 @@ class Bootstrap {
}
}
}
class BootstrapBadStateException implements Exception {
String cause;
BootstrapBadStateException([this.cause = 'Bad state']);
@override
String toString() => 'BootstrapBadStateException: $cause';
}

View File

@ -114,7 +114,7 @@ _KeyVerificationMethod _makeVerificationMethod(
if (type == 'm.sas.v1') {
return _KeyVerificationMethodSas(request: request);
}
throw 'Unkown method type';
throw Exception('Unkown method type');
}
class KeyVerification {
@ -883,7 +883,7 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod {
: theirInfo + ourInfo) +
request.transactionId;
} else {
throw 'Unknown key agreement protocol';
throw Exception('Unknown key agreement protocol');
}
return sas.generate_bytes(sasInfo, bytes);
}
@ -965,14 +965,14 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod {
olmutil.free();
return ret;
}
throw 'Unknown hash method';
throw Exception('Unknown hash method');
}
String _calculateMac(String input, String info) {
if (messageAuthenticationCode == 'hkdf-hmac-sha256') {
return sas.calculate_mac(input, info);
} else {
throw 'Unknown message authentification code';
throw Exception('Unknown message authentification code');
}
}
}

View File

@ -66,4 +66,6 @@ export 'matrix_api/model/upload_key_signatures_response.dart';
export 'matrix_api/model/user_search_result.dart';
export 'matrix_api/model/well_known_informations.dart';
export 'matrix_api/model/who_is_info.dart';
export 'matrix_api/model/events/secret_storage_default_key_content.dart';
export 'matrix_api/model/events/secret_storage_key_content.dart';
export 'matrix_api/model/events/tombstone_content.dart';

View File

@ -44,6 +44,7 @@ abstract class EventTypes {
static const String CrossSigningSelfSigning = 'm.cross_signing.self_signing';
static const String CrossSigningUserSigning = 'm.cross_signing.user_signing';
static const String CrossSigningMasterKey = 'm.cross_signing.master';
static const String MegolmBackup = 'm.megolm_backup.v1';
static const String SecretStorageDefaultKey = 'm.secret_storage.default_key';
static String secretStorageKey(String keyId) => 'm.secret_storage.key.$keyId';

View File

@ -0,0 +1,22 @@
import 'package:famedlysdk/matrix_api/model/basic_event.dart';
import '../../utils/try_get_map_extension.dart';
extension SecretStorageDefaultKeyContentBasicEventExtension on BasicEvent {
SecretStorageDefaultKeyContent get parsedSecretStorageDefaultKeyContent =>
SecretStorageDefaultKeyContent.fromJson(content);
}
class SecretStorageDefaultKeyContent {
String key;
SecretStorageDefaultKeyContent();
SecretStorageDefaultKeyContent.fromJson(Map<String, dynamic> json)
: key = json.tryGet<String>('key');
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
if (key != null) data['key'] = key;
return data;
}
}

View File

@ -0,0 +1,57 @@
import 'package:famedlysdk/matrix_api/model/basic_event.dart';
import '../../utils/try_get_map_extension.dart';
extension SecretStorageKeyContentBasicEventExtension on BasicEvent {
SecretStorageKeyContent get parsedSecretStorageKeyContent =>
SecretStorageKeyContent.fromJson(content);
}
class SecretStorageKeyContent {
PassphraseInfo passphrase;
String iv;
String mac;
String algorithm;
SecretStorageKeyContent();
SecretStorageKeyContent.fromJson(Map<String, dynamic> json)
: passphrase = json['passphrase'] is Map<String, dynamic>
? PassphraseInfo.fromJson(json['passphrase'])
: null,
iv = json.tryGet<String>('iv'),
mac = json.tryGet<String>('mac'),
algorithm = json.tryGet<String>('algorithm');
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
if (passphrase != null) data['passphrase'] = passphrase.toJson();
if (iv != null) data['iv'] = iv;
if (mac != null) data['mac'] = mac;
if (algorithm != null) data['algorithm'] = algorithm;
return data;
}
}
class PassphraseInfo {
String algorithm;
String salt;
int iterations;
int bits;
PassphraseInfo();
PassphraseInfo.fromJson(Map<String, dynamic> json)
: algorithm = json.tryGet<String>('algorithm'),
salt = json.tryGet<String>('salt'),
iterations = json.tryGet<int>('iterations'),
bits = json.tryGet<int>('bits');
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
if (algorithm != null) data['algorithm'] = algorithm;
if (salt != null) data['salt'] = salt;
if (iterations != null) data['iterations'] = iterations;
if (bits != null) data['bits'] = bits;
return data;
}
}

View File

@ -325,7 +325,7 @@ class Client extends MatrixApi {
if (response.accessToken == null ||
response.deviceId == null ||
response.userId == null) {
throw 'Registered but token, device ID or user ID is null.';
throw Exception('Registered but token, device ID or user ID is null.');
}
await init(
newToken: response.accessToken,
@ -410,6 +410,7 @@ class Client extends MatrixApi {
}
}
/// Run any request and react on user interactive authentication flows here.
Future<T> uiaRequestBackground<T>(
Future<T> Function(Map<String, dynamic> auth) request) {
final completer = Completer<T>();
@ -573,6 +574,7 @@ class Client extends MatrixApi {
final StreamController<SdkError> onSyncError = StreamController.broadcast();
/// Synchronization erros are coming here.
@Deprecated('Please use encryption.onToDeviceEventDecryptionError instead')
final StreamController<ToDeviceEventDecryptionError> onOlmError =
StreamController.broadcast();
@ -948,22 +950,7 @@ class Client extends MatrixApi {
for (var i = 0; i < events.length; i++) {
var toDeviceEvent = ToDeviceEvent.fromJson(events[i].toJson());
if (toDeviceEvent.type == EventTypes.Encrypted && encryptionEnabled) {
try {
toDeviceEvent = await encryption.decryptToDeviceEvent(toDeviceEvent);
} catch (e, s) {
Logs.error(
'[LibOlm] Could not decrypt to device event from ${toDeviceEvent.sender} with content: ${toDeviceEvent.content}\n${e.toString()}',
s);
onOlmError.add(
ToDeviceEventDecryptionError(
exception: e is Exception ? e : Exception(e),
stackTrace: s,
toDeviceEvent: toDeviceEvent,
),
);
toDeviceEvent = ToDeviceEvent.fromJson(events[i].toJson());
}
toDeviceEvent = await encryption.decryptToDeviceEvent(toDeviceEvent);
}
if (encryptionEnabled) {
await encryption.handleToDeviceEvent(toDeviceEvent);

View File

@ -59,7 +59,7 @@ class DeviceKeysList {
final roomId =
await User(userId, room: Room(client: client)).startDirectChat();
if (roomId == null) {
throw 'Unable to start new room';
throw Exception('Unable to start new room');
}
final room =
client.getRoomById(roomId) ?? Room(id: roomId, client: client);

View File

@ -18,6 +18,7 @@
import '../../famedlysdk.dart';
/// Wrapper to handle User interactive authentication requests
class UiaRequest<T> {
void Function() onUpdate;
void Function() onDone;

View File

@ -79,7 +79,7 @@ void main() {
var finished = false;
final request = UiaRequest(
request: (auth) async {
throw 'nope';
throw Exception('nope');
},
onUpdate: () => updated = true,
onDone: () => finished = true,