mirror of https://github.com/vector-im/riot-web
				
				
				
			Merge pull request #4242 from matrix-org/dbkr/symmetric-ssss-migrate
Migration to symmetric SSSSpull/21833/head
						commit
						96ec18a662
					
				|  | @ -96,6 +96,9 @@ async function getSecretStorageKey({ keys: keyInfos }, ssssItemName) { | |||
|         { | ||||
|             keyInfo: info, | ||||
|             checkPrivateKey: async (input) => { | ||||
|                 if (!info.pubkey) { | ||||
|                     return true; | ||||
|                 } | ||||
|                 const key = await inputToKey(input); | ||||
|                 return MatrixClientPeg.get().checkSecretStoragePrivateKey(key, info.pubkey); | ||||
|             }, | ||||
|  | @ -159,6 +162,20 @@ export const crossSigningCallbacks = { | |||
|     onSecretRequested, | ||||
| }; | ||||
| 
 | ||||
| export async function promptForBackupPassphrase() { | ||||
|     let key; | ||||
| 
 | ||||
|     const RestoreKeyBackupDialog = sdk.getComponent('dialogs.keybackup.RestoreKeyBackupDialog'); | ||||
|     const { finished } = Modal.createTrackedDialog('Restore Backup', '', RestoreKeyBackupDialog, { | ||||
|             showSummary: false, keyCallback: k => key = k, | ||||
|     }, null, /* priority = */ false, /* static = */ true); | ||||
| 
 | ||||
|     const success = await finished; | ||||
|     if (!success) throw new Error("Key backup prompt cancelled"); | ||||
| 
 | ||||
|     return key; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * This helper should be used whenever you need to access secret storage. It | ||||
|  * ensures that secret storage (and also cross-signing since they each depend on | ||||
|  | @ -215,6 +232,7 @@ export async function accessSecretStorage(func = async () => { }, force = false) | |||
|                         throw new Error("Cross-signing key upload auth canceled"); | ||||
|                     } | ||||
|                 }, | ||||
|                 getBackupPassphrase: promptForBackupPassphrase, | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
|  |  | |||
|  | @ -50,6 +50,7 @@ export default class DeviceListener { | |||
|         MatrixClientPeg.get().on('crypto.devicesUpdated', this._onDevicesUpdated); | ||||
|         MatrixClientPeg.get().on('deviceVerificationChanged', this._onDeviceVerificationChanged); | ||||
|         MatrixClientPeg.get().on('userTrustStatusChanged', this._onUserTrustStatusChanged); | ||||
|         MatrixClientPeg.get().on('accountData', this._onAccountData); | ||||
|         this._recheck(); | ||||
|     } | ||||
| 
 | ||||
|  | @ -58,6 +59,7 @@ export default class DeviceListener { | |||
|             MatrixClientPeg.get().removeListener('crypto.devicesUpdated', this._onDevicesUpdated); | ||||
|             MatrixClientPeg.get().removeListener('deviceVerificationChanged', this._onDeviceVerificationChanged); | ||||
|             MatrixClientPeg.get().removeListener('userTrustStatusChanged', this._onUserTrustStatusChanged); | ||||
|             MatrixClientPeg.get().removeListener('accountData', this._onAccountData); | ||||
|         } | ||||
|         this._dismissed.clear(); | ||||
|     } | ||||
|  | @ -87,6 +89,13 @@ export default class DeviceListener { | |||
|         this._recheck(); | ||||
|     } | ||||
| 
 | ||||
|     _onAccountData = (ev) => { | ||||
|         // User may have migrated SSSS to symmetric, in which case we can dismiss that toast
 | ||||
|         if (ev.getType().startsWith('m.secret_storage.key.')) { | ||||
|             this._recheck(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // The server doesn't tell us when key backup is set up, so we poll
 | ||||
|     // & cache the result
 | ||||
|     async _getKeyBackupInfo() { | ||||
|  | @ -147,6 +156,19 @@ export default class DeviceListener { | |||
|                 } | ||||
|             } | ||||
|             return; | ||||
|         } else if (await cli.secretStorageKeyNeedsUpgrade()) { | ||||
|             if (this._dismissedThisDeviceToast) { | ||||
|                 ToastStore.sharedInstance().dismissToast(THIS_DEVICE_TOAST_KEY); | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             ToastStore.sharedInstance().addOrReplaceToast({ | ||||
|                 key: THIS_DEVICE_TOAST_KEY, | ||||
|                 title: _t("Encryption upgrade available"), | ||||
|                 icon: "verification_warning", | ||||
|                 props: {kind: 'upgrade_encryption'}, | ||||
|                 component: sdk.getComponent("toasts.SetupEncryptionToast"), | ||||
|             }); | ||||
|         } else { | ||||
|             ToastStore.sharedInstance().dismissToast(THIS_DEVICE_TOAST_KEY); | ||||
|         } | ||||
|  |  | |||
|  | @ -23,6 +23,7 @@ import { scorePassword } from '../../../../utils/PasswordScorer'; | |||
| import FileSaver from 'file-saver'; | ||||
| import { _t } from '../../../../languageHandler'; | ||||
| import Modal from '../../../../Modal'; | ||||
| import { promptForBackupPassphrase } from '../../../../CrossSigningManager'; | ||||
| 
 | ||||
| const PHASE_LOADING = 0; | ||||
| const PHASE_MIGRATE = 1; | ||||
|  | @ -243,6 +244,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent { | |||
|                     createSecretStorageKey: async () => this._keyInfo, | ||||
|                     keyBackupInfo: this.state.backupInfo, | ||||
|                     setupNewKeyBackup: !this.state.backupInfo && this.state.useKeyBackup, | ||||
|                     getKeyBackupPassphrase: promptForBackupPassphrase, | ||||
|                 }); | ||||
|             } | ||||
|             this.setState({ | ||||
|  |  | |||
|  | @ -36,6 +36,9 @@ export default class RestoreKeyBackupDialog extends React.PureComponent { | |||
|         // if false, will close the dialog as soon as the restore completes succesfully
 | ||||
|         // default: true
 | ||||
|         showSummary: PropTypes.bool, | ||||
|         // If specified, gather the key from the user but then call the function with the backup
 | ||||
|         // key rather than actually (necessarily) restoring the backup.
 | ||||
|         keyCallback: PropTypes.func, | ||||
|     }; | ||||
| 
 | ||||
|     static defaultProps = { | ||||
|  | @ -103,9 +106,18 @@ export default class RestoreKeyBackupDialog extends React.PureComponent { | |||
|             restoreType: RESTORE_TYPE_PASSPHRASE, | ||||
|         }); | ||||
|         try { | ||||
|             // We do still restore the key backup: we must ensure that the key backup key
 | ||||
|             // is the right one and restoring it is currently the only way we can do this.
 | ||||
|             const recoverInfo = await MatrixClientPeg.get().restoreKeyBackupWithPassword( | ||||
|                 this.state.passPhrase, undefined, undefined, this.state.backupInfo, | ||||
|             ); | ||||
|             if (this.props.keyCallback) { | ||||
|                 const key = await MatrixClientPeg.get().keyBackupKeyFromPassword( | ||||
|                     this.state.passPhrase, this.state.backupInfo, | ||||
|                 ); | ||||
|                 this.props.keyCallback(key); | ||||
|             } | ||||
| 
 | ||||
|             if (!this.props.showSummary) { | ||||
|                 this.props.onFinished(true); | ||||
|                 return; | ||||
|  | @ -135,6 +147,10 @@ export default class RestoreKeyBackupDialog extends React.PureComponent { | |||
|             const recoverInfo = await MatrixClientPeg.get().restoreKeyBackupWithRecoveryKey( | ||||
|                 this.state.recoveryKey, undefined, undefined, this.state.backupInfo, | ||||
|             ); | ||||
|             if (this.props.keyCallback) { | ||||
|                 const key = MatrixClientPeg.get().keyBackupKeyFromRecoveryKey(this.state.recoveryKey); | ||||
|                 this.props.keyCallback(key); | ||||
|             } | ||||
|             if (!this.props.showSummary) { | ||||
|                 this.props.onFinished(true); | ||||
|                 return; | ||||
|  |  | |||
|  | @ -33,6 +33,7 @@ export default class CrossSigningPanel extends React.PureComponent { | |||
|             crossSigningPublicKeysOnDevice: false, | ||||
|             crossSigningPrivateKeysInStorage: false, | ||||
|             secretStorageKeyInAccount: false, | ||||
|             secretStorageKeyNeedsUpgrade: null, | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|  | @ -60,6 +61,10 @@ export default class CrossSigningPanel extends React.PureComponent { | |||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     _onBootstrapClick = () => { | ||||
|         this._bootstrapSecureSecretStorage(false); | ||||
|     }; | ||||
| 
 | ||||
|     onStatusChanged = () => { | ||||
|         this._getUpdatedStatus(); | ||||
|     }; | ||||
|  | @ -74,12 +79,14 @@ export default class CrossSigningPanel extends React.PureComponent { | |||
|         const secretStorageKeyInAccount = await secretStorage.hasKey(); | ||||
|         const homeserverSupportsCrossSigning = | ||||
|             await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing"); | ||||
|         const secretStorageKeyNeedsUpgrade = await cli.secretStorageKeyNeedsUpgrade(); | ||||
| 
 | ||||
|         this.setState({ | ||||
|             crossSigningPublicKeysOnDevice, | ||||
|             crossSigningPrivateKeysInStorage, | ||||
|             secretStorageKeyInAccount, | ||||
|             homeserverSupportsCrossSigning, | ||||
|             secretStorageKeyNeedsUpgrade, | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|  | @ -124,6 +131,7 @@ export default class CrossSigningPanel extends React.PureComponent { | |||
|             crossSigningPrivateKeysInStorage, | ||||
|             secretStorageKeyInAccount, | ||||
|             homeserverSupportsCrossSigning, | ||||
|             secretStorageKeyNeedsUpgrade, | ||||
|         } = this.state; | ||||
| 
 | ||||
|         let errorSection; | ||||
|  | @ -175,7 +183,7 @@ export default class CrossSigningPanel extends React.PureComponent { | |||
|         ) { | ||||
|             bootstrapButton = ( | ||||
|                 <div className="mx_CrossSigningPanel_buttonRow"> | ||||
|                     <AccessibleButton kind="primary" onClick={this._bootstrapSecureSecretStorage}> | ||||
|                     <AccessibleButton kind="primary" onClick={this._onBootstrapClick}> | ||||
|                         {_t("Bootstrap cross-signing and secret storage")} | ||||
|                     </AccessibleButton> | ||||
|                 </div> | ||||
|  | @ -204,6 +212,10 @@ export default class CrossSigningPanel extends React.PureComponent { | |||
|                             <td>{_t("Homeserver feature support:")}</td> | ||||
|                             <td>{homeserverSupportsCrossSigning ? _t("exists") : _t("not found")}</td> | ||||
|                         </tr> | ||||
|                         <tr> | ||||
|                             <td>{_t("Secret Storage key format:")}</td> | ||||
|                             <td>{secretStorageKeyNeedsUpgrade ? _t("outdated") : _t("up to date")}</td> | ||||
|                         </tr> | ||||
|                    </tbody></table> | ||||
|                 </details> | ||||
|                 {errorSection} | ||||
|  |  | |||
|  | @ -586,6 +586,9 @@ | |||
|     "in account data": "in account data", | ||||
|     "Homeserver feature support:": "Homeserver feature support:", | ||||
|     "exists": "exists", | ||||
|     "Secret Storage key format:": "Secret Storage key format:", | ||||
|     "outdated": "outdated", | ||||
|     "up to date": "up to date", | ||||
|     "Your homeserver does not support session management.": "Your homeserver does not support session management.", | ||||
|     "Unable to load session list": "Unable to load session list", | ||||
|     "Authentication": "Authentication", | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 David Baker
						David Baker