Skip to content

Commit

Permalink
fix: validate account_data values instead of checking them in syncUpd…
Browse files Browse the repository at this point in the history
…ates
  • Loading branch information
td-famedly committed Dec 20, 2023
1 parent 6b66451 commit dc411c9
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 48 deletions.
25 changes: 17 additions & 8 deletions lib/encryption/ssss.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -327,7 +328,7 @@ class SSSS {
}
final enc = encryptedContent.tryGetMap<String, Object?>(keyId);
if (enc == null) {
throw Exception('Wrong / unknown key');
throw Exception('Wrong / unknown key: $type, $keyId');
}
final ciphertext = enc.tryGet<String>('ciphertext');
final iv = enc.tryGet<String>('iv');
Expand Down Expand Up @@ -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<String, Object?>('encrypted')!
.containsKey(keyId)) ||
await getStored(type) != secret) {
Logs().d('Wait for secret of $type to match in accountdata');
await ssss.client.oneShotSync();
}
}

Future<void> validateAndStripOtherKeys(String type, String secret) async {
Expand Down
58 changes: 21 additions & 37 deletions lib/encryption/utils/bootstrap.dart
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ class Bootstrap {

// cache the secret analyzing so that we don't drop stuff a different client sets during bootstrapping
Map<String, Set<String>>? _secretsCache;

/// returns ssss from accountdata, eg: m.megolm_backup.v1, or your m.cross_signing stuff
Map<String, Set<String>> analyzeSecrets() {
final secretsCache = _secretsCache;
if (secretsCache != null) {
Expand Down Expand Up @@ -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}...');
Expand Down Expand Up @@ -479,33 +481,23 @@ class Bootstrap {
));
Logs().v('Device signing keys have been uploaded.');
// aaaand set the SSSS secrets
final futures = <Future<void>>[];
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 = <Future<void>>[];
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<void>(futures);

final keysToSign = <SignableKey>[];
if (masterKey != null) {
if (client.userDeviceKeys[client.userID]?.masterKey?.ed25519Key !=
Expand Down Expand Up @@ -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...');
Expand Down
9 changes: 6 additions & 3 deletions test/encryption/ssss_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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']!
Expand Down

0 comments on commit dc411c9

Please sign in to comment.