-
-
Notifications
You must be signed in to change notification settings - Fork 683
Fix key backup cached event ordering #5281
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
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -205,7 +205,7 @@ export class RustBackupManager extends TypedEventEmitter<RustBackupCryptoEvents, | |
| this.logger.info( | ||
| `handleBackupSecretReceived: Valid decryption key for the current server-side backup version (${latestBackupInfo.version}) received`, | ||
| ); | ||
| await this.saveBackupDecryptionKey(backupDecryptionKey, latestBackupInfo.version); | ||
| await this.saveBackupDecryptionKeyForCurrentBackup(backupDecryptionKey, latestBackupInfo); | ||
| return true; | ||
| } catch (e) { | ||
| this.logger.warn("handleBackupSecretReceived: Unable to validate backup decryption key", e); | ||
|
|
@@ -214,16 +214,88 @@ export class RustBackupManager extends TypedEventEmitter<RustBackupCryptoEvents, | |
| return false; | ||
| } | ||
|
|
||
| public async saveBackupDecryptionKey( | ||
| private async storeBackupDecryptionKey( | ||
| backupDecryptionKey: RustSdkCryptoJs.BackupDecryptionKey, | ||
| version: string, | ||
| ): Promise<void> { | ||
| await this.olmMachine.saveBackupDecryptionKey(backupDecryptionKey, version); | ||
| } | ||
|
Comment on lines
+217
to
+222
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think there's any point in creating a dedicated function for this single line of code. |
||
|
|
||
| public async saveBackupDecryptionKey( | ||
| backupDecryptionKey: RustSdkCryptoJs.BackupDecryptionKey, | ||
| version: string, | ||
| ): Promise<void> { | ||
| await this.storeBackupDecryptionKey(backupDecryptionKey, version); | ||
| // Emit an event that we have a new backup decryption key, so that the sdk can start | ||
| // importing keys from backup if needed. | ||
| this.emitBackupDecryptionKeyCached(version); | ||
| } | ||
|
|
||
| private emitBackupDecryptionKeyCached(version: string): void { | ||
| this.emit(CryptoEvent.KeyBackupDecryptionKeyCached, version); | ||
| } | ||
|
Comment on lines
+234
to
236
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. likewise: |
||
|
|
||
| /** | ||
| * Mark a newly-created backup as the current usable backup. | ||
| * | ||
| * The server has already accepted this backup, so this uses the creation | ||
| * response to update local backup state instead of racing a follow-up GET | ||
| * against eventual consistency. | ||
| * | ||
| * @param backupInfo - The newly-created backup details. | ||
| */ | ||
| public async enableKeyBackupFromCreation(backupInfo: KeyBackupCreationInfo): Promise<void> { | ||
| await this.enableKeyBackupFromInfo({ | ||
| algorithm: backupInfo.algorithm, | ||
| auth_data: backupInfo.authData, | ||
| version: backupInfo.version, | ||
| }); | ||
|
Comment on lines
+248
to
+252
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why not just |
||
|
|
||
| this.emitBackupDecryptionKeyCached(backupInfo.version); | ||
| } | ||
|
|
||
| private async enableKeyBackupFromInfo(backupInfo: KeyBackupInfo): Promise<void> { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this could also do with a doc comment. How does it differ from |
||
| const version = backupInfo.version; | ||
| if (!version) { | ||
| throw new Error("Cannot enable key backup without version"); | ||
| } | ||
|
Comment on lines
+259
to
+261
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you can enforce this via the type system: declare the |
||
|
|
||
| const previousServerBackupInfo = this.serverBackupInfo; | ||
| const previousCheckedForBackup = this.checkedForBackup; | ||
| this.serverBackupInfo = backupInfo; | ||
| this.checkedForBackup = true; | ||
|
|
||
| try { | ||
| const activeVersion = await this.getActiveBackupVersion(); | ||
| if (activeVersion === null) { | ||
| await this.enableKeyBackup(backupInfo); | ||
| } else if (activeVersion !== version) { | ||
| await this.disableKeyBackup(); | ||
| await this.enableKeyBackup(backupInfo); | ||
| } | ||
|
Comment on lines
+269
to
+275
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is (a subset of) the same logic as in
Comment on lines
+269
to
+275
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What will happen if this races with an ongoing call to |
||
| } catch (e) { | ||
| this.serverBackupInfo = previousServerBackupInfo; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I somewhat wonder if this is necessary, given we know that these are in fact the details of the backup on the server. |
||
| this.checkedForBackup = previousCheckedForBackup; | ||
| throw e; | ||
| } | ||
| } | ||
|
|
||
| private async saveBackupDecryptionKeyForCurrentBackup( | ||
| backupDecryptionKey: RustSdkCryptoJs.BackupDecryptionKey, | ||
| backupInfo: KeyBackupInfo, | ||
| ): Promise<void> { | ||
|
Comment on lines
+283
to
+286
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could do with a doc comment. How does it differ from
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since it seems to be a helper for |
||
| const version = backupInfo.version; | ||
| if (!version) { | ||
| throw new Error("Cannot save backup decryption key for backup without version"); | ||
| } | ||
|
|
||
| await this.storeBackupDecryptionKey(backupDecryptionKey, version); | ||
| await this.enableKeyBackupFromInfo(backupInfo); | ||
|
|
||
| // Emit after the backup is configured so listeners can immediately use the new backup. | ||
| this.emitBackupDecryptionKeyCached(version); | ||
| } | ||
|
|
||
| /** | ||
| * Import a list of room keys previously exported by exportRoomKeys | ||
| * | ||
|
|
@@ -568,7 +640,7 @@ export class RustBackupManager extends TypedEventEmitter<RustBackupCryptoEvents, | |
| }, | ||
| ); | ||
|
|
||
| await this.saveBackupDecryptionKey(randomKey, res.version); | ||
| await this.storeBackupDecryptionKey(randomKey, res.version); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note to self: this change is the main point of the PR. It avoids emitting a |
||
|
|
||
| return { | ||
| version: res.version, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1382,8 +1382,7 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, CryptoEventH | |
| await this.secretStorage.store("m.megolm_backup.v1", backupInfo.decryptionKey.toBase64()); | ||
| } | ||
|
|
||
| // we can check and start async | ||
| this.checkKeyBackupAndEnable(); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure why it's OK to remove this call to
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this removes it from the just created backup path in i dropped it because the new helper still does the important local enable work: cache so the intent is not to remove backup enabling; only to avoid the extra check before triggering if paths that still call RustCrypto.checkKeyBackupAndEnable: |
||
| await this.backupManager.enableKeyBackupFromCreation(backupInfo); | ||
| } | ||
|
|
||
| /** | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not really following why this needs to change. Your description only mentions a problem happening during
setupKeyBackup, but this is unrelated tosetupKeyBackup. Does a similar problem also affect receiving the backup decryption key from other devices? I'm not sure why it would, because we don't updateRustBackupManager.serverBackupInfohere (as far as I can tell), so there is nothing to race against?