Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Element-R: Refactor per-session key backup download #3929

Merged
merged 34 commits into from
Dec 8, 2023

Conversation

BillCarsonFr
Copy link
Member

@BillCarsonFr BillCarsonFr commented Nov 29, 2023

Fixes element-hq/element-web#26535
Fixes element-hq/element-web#26312

Refactoring for the code that is downloading keys from backup per session when a decryption error occurs.
Will fix cache/4S server desync as well as partial history decryption problems

Note for reviewer:

Previously when a decryption error was encountered (in EventDecryptor) a call was made to startQueryKeyBackupRateLimited. This method was querying the backup and importing the key on success.

This mechanism was too limited, because if the call was made before the backup was set up it would fail and it was never retried later on (unless the app is refreshed). It was also always trying to use the decryption key that is in cache even if it's not valid any more.
The current implementation would fail to properly request a lot of sessions, and was probably saved by key_requesting that is going on in parallel.

This has been refactored by adding a new Object dedicated to per session downloads:
The PerSessionKeyBackupDownloader will queue up all requests to backup and process them one by one. It is resistant to invalid configurations (not trusted, no decryption key) and resistant to configuration changes. It will automatically resume querying when the backup is configured correctly.

Checklist

  • Tests written for new code (and old code if feasible)
  • Linter and other CI checks pass
  • Sign-off given on the changes (see CONTRIBUTING.md)

This change is marked as an internal change (Task), so will not be included in the changelog.

@BillCarsonFr BillCarsonFr added the T-Task Tasks for the team like planning label Nov 29, 2023
@BillCarsonFr BillCarsonFr marked this pull request as ready for review December 1, 2023 11:12
@BillCarsonFr BillCarsonFr requested a review from a team as a code owner December 1, 2023 11:12
@richvdh richvdh changed the title Valere/element r/backup/per session download Element-R: Refactor per-session key backup download Dec 1, 2023
src/rust-crypto/PerSessionKeyBackupDownloader.ts Outdated Show resolved Hide resolved
src/rust-crypto/PerSessionKeyBackupDownloader.ts Outdated Show resolved Hide resolved
src/rust-crypto/PerSessionKeyBackupDownloader.ts Outdated Show resolved Hide resolved
src/rust-crypto/PerSessionKeyBackupDownloader.ts Outdated Show resolved Hide resolved
src/rust-crypto/PerSessionKeyBackupDownloader.ts Outdated Show resolved Hide resolved
src/rust-crypto/PerSessionKeyBackupDownloader.ts Outdated Show resolved Hide resolved
Copy link
Member

@robintown robintown left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was halfway through review when I saw Rich's comments come in, so I'll defer to him

src/rust-crypto/PerSessionKeyBackupDownloader.ts Outdated Show resolved Hide resolved
Copy link
Member

@richvdh richvdh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is broadly sensible, though I haven't yet managed to follow all the logic through from start to finish. I've made a bunch of suggestions to make it easier to follow: please could you try them, and generally make sure that everything is consistent, and then I'll take another check on the logic here.

Comment on lines +457 to +463

/**
* Creates a new backup decryptor for the given private key.
* @param decryptionKey - The private key to use for decryption.
*/
public createBackupDecryptor(decryptionKey: RustSdkCryptoJs.BackupDecryptionKey): BackupDecryptor {
return new RustBackupDecryptor(decryptionKey);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the point of this? why not inline it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It abstracts the implem RustBackupDecryptor from BackupDecryptor, also make it easier to test. just have to mock that instead of mocking somehow the constructor?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can mock entire classes with something like https://jestjs.io/docs/es6-class-mocks#mocking-non-default-class-exports, which might be better if you can be bothered. Otherwise this is ok for now.

src/rust-crypto/backup.ts Show resolved Hide resolved
@@ -82,7 +81,7 @@ interface ISignableObject {
unsigned?: object;
}

const KEY_BACKUP_CHECK_RATE_LIMIT = 5000; // ms
const KEY_BACKUP_BACKOFF = 5000; // ms
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggest moving this into PerSessionKeyBackupDownloader

Copy link
Member

@richvdh richvdh Dec 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still think we should move this. It feels odd that we have to keep this const here and pass in an extra parameter to PerSessionKeyBackupDownloader.

(just make it the default value for backOffDuration?)

src/rust-crypto/rust-crypto.ts Outdated Show resolved Hide resolved
*
* The PerSessionKeyBackupDownloader is resistant to backup configuration changes: it will automatically resume querying when
* the backup is configured correctly.
*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
*

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again can this be a lint thing? why do that manually as untold convention

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe, I don't know.

CONFIGURATION_ERROR = "CONFIGURATION_ERROR",
}

/** Helper type for requested session*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can give more helpful documentation here:

Suggested change
/** Helper type for requested session*/
/** Details of a megolm session whose key we are trying to fetch. */

src/rust-crypto/PerSessionKeyBackupDownloader.ts Outdated Show resolved Hide resolved
src/rust-crypto/PerSessionKeyBackupDownloader.ts Outdated Show resolved Hide resolved
src/rust-crypto/PerSessionKeyBackupDownloader.ts Outdated Show resolved Hide resolved
Copy link
Member

@richvdh richvdh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking much clearer and easier to follow now; I think generally it looks good. Still quite a few things which could be cleaned up but will leave it up to you how many you do.

I'd particularly like to get rid of forceCheck if possible: I think it's an area of significant confusion.

Also I think pauseLoop is very badly named.

src/rust-crypto/PerSessionKeyBackupDownloader.ts Outdated Show resolved Hide resolved
@@ -82,7 +81,7 @@ interface ISignableObject {
unsigned?: object;
}

const KEY_BACKUP_CHECK_RATE_LIMIT = 5000; // ms
const KEY_BACKUP_BACKOFF = 5000; // ms
Copy link
Member

@richvdh richvdh Dec 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still think we should move this. It feels odd that we have to keep this const here and pass in an extra parameter to PerSessionKeyBackupDownloader.

(just make it the default value for backOffDuration?)

src/rust-crypto/PerSessionKeyBackupDownloader.ts Outdated Show resolved Hide resolved
Comment on lines +169 to +175
private async getBackupDecryptionKey(): Promise<RustSdkCryptoJs.BackupKeys | null> {
try {
return await this.olmMachine.getBackupKeys();
} catch (e) {
return null;
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think this should be inlined

@BillCarsonFr BillCarsonFr added this pull request to the merge queue Dec 7, 2023
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Dec 7, 2023
const configuration = await this.getOrCreateBackupConfiguration();
if (!configuration) {
// Backup is not configured correctly, so stop the loop.
this.downloadLoopRunning = false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is redundant FWIW: the finally below` will handle it.

Likewise the other one on line 298.

@BillCarsonFr BillCarsonFr added this pull request to the merge queue Dec 8, 2023
Merged via the queue into develop with commit 13c7e0e Dec 8, 2023
25 checks passed
@BillCarsonFr BillCarsonFr deleted the valere/element-r/backup/per_session_download branch December 8, 2023 14:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-Task Tasks for the team like planning
Projects
None yet
3 participants