From dc411c9b140603bf17942079137f50c0699b0429 Mon Sep 17 00:00:00 2001 From: td Date: Wed, 20 Dec 2023 21:10:31 +0530 Subject: [PATCH] fix: validate account_data values instead of checking them in syncUpdates --- lib/encryption/ssss.dart | 25 +++++++++---- lib/encryption/utils/bootstrap.dart | 58 +++++++++++------------------ test/encryption/ssss_test.dart | 9 +++-- 3 files changed, 44 insertions(+), 48 deletions(-) diff --git a/lib/encryption/ssss.dart b/lib/encryption/ssss.dart index b24854e3..198dd98b 100644 --- a/lib/encryption/ssss.dart +++ b/lib/encryption/ssss.dart @@ -248,15 +248,16 @@ class SSSS { }() .firstWhere((keyId) => getKey(keyId) == null); - final accountDataType = EventTypes.secretStorageKey(keyId); + final accountDataTypeKeyId = EventTypes.secretStorageKey(keyId); // noooow we set the account data - final waitForAccountData = client.onSync.stream.firstWhere((syncUpdate) => - syncUpdate.accountData != null && - syncUpdate.accountData! - .any((accountData) => accountData.type == accountDataType)); + await client.setAccountData( - client.userID!, accountDataType, content.toJson()); - await waitForAccountData; + client.userID!, accountDataTypeKeyId, content.toJson()); + + while (!client.accountData.containsKey(accountDataTypeKeyId)) { + Logs().v('Waiting accountData to have $accountDataTypeKeyId'); + await client.oneShotSync(); + } final key = open(keyId); await key.setPrivateKey(privateKey); @@ -327,7 +328,7 @@ class SSSS { } final enc = encryptedContent.tryGetMap(keyId); if (enc == null) { - throw Exception('Wrong / unknown key'); + throw Exception('Wrong / unknown key: $type, $keyId'); } final ciphertext = enc.tryGet('ciphertext'); final iv = enc.tryGet('iv'); @@ -750,6 +751,14 @@ class OpenSSSS { throw Exception('SSSS not unlocked'); } await ssss.store(type, secret, keyId, privateKey, add: add); + while (!ssss.client.accountData.containsKey(type) || + !(ssss.client.accountData[type]!.content + .tryGetMap('encrypted')! + .containsKey(keyId)) || + await getStored(type) != secret) { + Logs().d('Wait for secret of $type to match in accountdata'); + await ssss.client.oneShotSync(); + } } Future validateAndStripOtherKeys(String type, String secret) async { diff --git a/lib/encryption/utils/bootstrap.dart b/lib/encryption/utils/bootstrap.dart index 286a5ea1..6c6f5fe7 100644 --- a/lib/encryption/utils/bootstrap.dart +++ b/lib/encryption/utils/bootstrap.dart @@ -90,6 +90,8 @@ class Bootstrap { // cache the secret analyzing so that we don't drop stuff a different client sets during bootstrapping Map>? _secretsCache; + + /// returns ssss from accountdata, eg: m.megolm_backup.v1, or your m.cross_signing stuff Map> analyzeSecrets() { final secretsCache = _secretsCache; if (secretsCache != null) { @@ -292,12 +294,12 @@ class Bootstrap { } // alright, we re-encrypted all the secrets. We delete the dead weight only *after* we set our key to the default key } - final updatedAccountData = client.onSync.stream.firstWhere((syncUpdate) => - syncUpdate.accountData != null && - syncUpdate.accountData!.any((accountData) => - accountData.type == EventTypes.SecretStorageDefaultKey)); await encryption.ssss.setDefaultKeyId(newSsssKey!.keyId); - await updatedAccountData; + while (encryption.ssss.defaultKeyId != newSsssKey!.keyId) { + Logs().v( + 'Waiting accountData to have the correct m.secret_storage.default_key'); + await client.oneShotSync(); + } if (oldSsssKeys != null) { for (final entry in secretMap!.entries) { Logs().v('Validate and stripe other keys ${entry.key}...'); @@ -479,33 +481,23 @@ class Bootstrap { )); Logs().v('Device signing keys have been uploaded.'); // aaaand set the SSSS secrets - final futures = >[]; if (masterKey != null) { - futures.add( - client.onSync.stream - .firstWhere((syncUpdate) => - masterKey?.publicKey != null && - client.userDeviceKeys[client.userID]?.masterKey?.ed25519Key == - masterKey?.publicKey) - .then((_) => Logs().v('New Master Key was created')), - ); + while (!(masterKey.publicKey != null && + client.userDeviceKeys[client.userID]?.masterKey?.ed25519Key == + masterKey.publicKey)) { + Logs().v('Waiting for master to be created'); + await client.oneShotSync(); + } } - for (final entry in secretsToStore.entries) { - futures.add( - client.onSync.stream - .firstWhere((syncUpdate) => - syncUpdate.accountData != null && - syncUpdate.accountData! - .any((accountData) => accountData.type == entry.key)) - .then((_) => - Logs().v('New Key with type ${entry.key} was created')), - ); - Logs().v('Store new SSSS key ${entry.key}...'); - await newSsssKey?.store(entry.key, entry.value); + if (newSsssKey != null) { + final storeFutures = >[]; + for (final entry in secretsToStore.entries) { + storeFutures.add(newSsssKey!.store(entry.key, entry.value)); + } + Logs().v('Store new SSSS key entries...'); + await Future.wait(storeFutures); } - Logs().v( - 'Wait for MasterKey and ${secretsToStore.entries.length} keys to be created'); - await Future.wait(futures); + final keysToSign = []; if (masterKey != null) { if (client.userDeviceKeys[client.userID]?.masterKey?.ed25519Key != @@ -581,14 +573,6 @@ class Bootstrap { ); Logs().v('Store the secret...'); await newSsssKey?.store(megolmKey, base64.encode(privKey)); - Logs().v('Wait for secret to come down sync'); - - if (!await encryption.keyManager.isCached()) { - await client.onSync.stream.firstWhere((syncUpdate) => - syncUpdate.accountData != null && - syncUpdate.accountData! - .any((accountData) => accountData.type == megolmKey)); - } Logs().v( 'And finally set all megolm keys as needing to be uploaded again...'); diff --git a/test/encryption/ssss_test.dart b/test/encryption/ssss_test.dart index 4cb25c41..e3827f1c 100644 --- a/test/encryption/ssss_test.dart +++ b/test/encryption/ssss_test.dart @@ -107,9 +107,12 @@ void main() { await handle.unlock(recoveryKey: ssssKey); expect(handle.isUnlocked, true); FakeMatrixApi.calledEndpoints.clear(); - await handle.store('best animal', 'foxies'); - // alright, since we don't properly sync we will manually have to update - // account_data for this test + + // OpenSSSS store waits for accountdata to be updated before returning + // but we can't update that before the below endpoint is not hit. + await handle.ssss + .store('best animal', 'foxies', handle.keyId, handle.privateKey!); + final content = FakeMatrixApi .calledEndpoints[ '/client/v3/user/%40test%3AfakeServer.notExisting/account_data/best%20animal']!