Merge pull request #4434 from vector-im/manu/4430_security_settings_update

Security settings: Improve the SECURE BACKUP section
This commit is contained in:
manuroe
2021-06-17 14:29:47 +02:00
committed by GitHub
24 changed files with 1370 additions and 354 deletions
+3
View File
@@ -10,6 +10,9 @@ Changes to be released in next version
* Integrated FLEX for debug builds.
* VoIP: Add dial pad for PSTN capable servers to menu on homescreen.
* VoIP: Replace call bar with PiP tiles for every type of calls.
* Security settings: Display the cross-signing section (#4430).
* Security settings: The Secure backup section has been updated to match element-web UX (#4430).
* Wording: Replace Recovery Passphrase and Recovery Key by Security Phrase and Security Key (#4268).
* Room directory: Join room by alias or id (#4429).
🐛 Bugfix
+58 -50
View File
@@ -568,7 +568,7 @@ Tap the + to start adding people.";
"settings_key_backup_info" = "Encrypted messages are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.";
"settings_key_backup_info_checking" = "Checking…";
"settings_key_backup_info_none" = "Your keys are not being backed up from this session.";
"settings_key_backup_info_signout_warning" = "Connect this session to key backup before signing out to avoid losing any keys that may only be on this device.";
"settings_key_backup_info_signout_warning" = "Back up your keys before signing out to avoid losing them.";
"settings_key_backup_info_version" = "Key Backup Version: %@";
"settings_key_backup_info_algorithm" = "Algorithm: %@";
"settings_key_backup_info_valid" = "This session is backing up your keys.";
@@ -622,10 +622,13 @@ Tap the + to start adding people.";
"security_settings_crypto_sessions_description_2" = "If you dont recognise a login, change your password and reset Secure Backup.";
"security_settings_secure_backup" = "SECURE BACKUP";
"security_settings_secure_backup_description" = "Safeguard against losing access to encrypted messages & data by backing up encryption keys on your server.";
"security_settings_secure_backup_description" = "Back up your encryption keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Security Key.";
"security_settings_secure_backup_info_checking" = "Checking…";
"security_settings_secure_backup_info_valid" = "This session is backing up your keys.";
"security_settings_secure_backup_setup" = "Set up";
"security_settings_secure_backup_synchronise" = "Synchronise";
"security_settings_secure_backup_delete" = "Delete";
"security_settings_secure_backup_reset" = "Reset";
"security_settings_secure_backup_restore" = "Restore from Backup";
"security_settings_secure_backup_delete" = "Delete Backup";
"security_settings_backup" = "MESSAGE BACKUP";
@@ -633,9 +636,9 @@ Tap the + to start adding people.";
"security_settings_crosssigning_info_not_bootstrapped" = "Cross-signing is not yet set up.";
"security_settings_crosssigning_info_exists" = "Your account has a cross-signing identity, but it is not yet trusted by this session. Complete security of this session.";
"security_settings_crosssigning_info_trusted" = "Cross-signing is enabled. You can trust other users and your other sessions based on cross-signing but you cannot cross-sign from this session because it does not have cross-signing private keys. Complete security of this session.";
"security_settings_crosssigning_info_ok" = "Cross-signing is enabled.";
"security_settings_crosssigning_bootstrap" = "Bootstrap cross-signing";
"security_settings_crosssigning_reset" = "Reset cross-signing";
"security_settings_crosssigning_info_ok" = "Cross-signing is ready for use.";
"security_settings_crosssigning_bootstrap" = "Set up";
"security_settings_crosssigning_reset" = "Reset";
"security_settings_crosssigning_complete_security" = "Complete security";
"security_settings_cryptography" = "CRYPTOGRAPHY";
@@ -899,7 +902,7 @@ Tap the + to start adding people.";
// Key backup wrong version
"e2e_key_backup_wrong_version_title" = "New Key Backup";
"e2e_key_backup_wrong_version" = "A new secure message key backup has been detected.\n\nIf this wasnt you, set a new passphrase in Settings.";
"e2e_key_backup_wrong_version" = "A new secure message key backup has been detected.\n\nIf this wasnt you, set a new Security Phrase in Settings.";
"e2e_key_backup_wrong_version_button_settings" = "Settings";
"e2e_key_backup_wrong_version_button_wasme" = "It was me";
@@ -1025,7 +1028,7 @@ Tap the + to start adding people.";
"secure_key_backup_setup_intro_use_security_key_title" = "Use a Security Key";
"secure_key_backup_setup_intro_use_security_key_info" = "Generate a security key to store somewhere safe like a password manager or a safe.";
"secure_key_backup_setup_intro_use_security_passphrase_title" = "Use a Security Passphrase";
"secure_key_backup_setup_intro_use_security_passphrase_title" = "Use a Security Phrase";
"secure_key_backup_setup_intro_use_security_passphrase_info" = "Enter a secret phrase only you know, and generate a key for backup.";
"secure_key_backup_setup_existing_backup_error_title" = "A backup for messages already exists";
@@ -1064,66 +1067,69 @@ Tap the + to start adding people.";
// Passphrase
"key_backup_setup_passphrase_title" = "Secure your backup with a Passphrase";
"key_backup_setup_passphrase_info" = "We'll store an encrypted copy of your keys on our server. Protect your backup with a passphrase to keep it secure.\n\nFor maximum security, this should be different from your account password.";
"key_backup_setup_passphrase_title" = "Secure your backup with a Security Phrase";
"key_backup_setup_passphrase_info" = "We'll store an encrypted copy of your keys on our server. Protect your backup with a phrase to keep it secure.\n\nFor maximum security, this should be different from your account password.";
"key_backup_setup_passphrase_passphrase_title" = "Enter";
"key_backup_setup_passphrase_passphrase_placeholder" = "Enter passphrase";
"key_backup_setup_passphrase_passphrase_placeholder" = "Enter phrase";
"key_backup_setup_passphrase_passphrase_valid" = "Great!";
"key_backup_setup_passphrase_passphrase_invalid" = "Try adding a word";
"key_backup_setup_passphrase_confirm_passphrase_title" = "Confirm";
"key_backup_setup_passphrase_confirm_passphrase_placeholder" = "Confirm passphrase";
"key_backup_setup_passphrase_confirm_passphrase_placeholder" = "Confirm phrase";
"key_backup_setup_passphrase_confirm_passphrase_valid" = "Great!";
"key_backup_setup_passphrase_confirm_passphrase_invalid" = "Passphrase doesnt match";
"key_backup_setup_passphrase_set_passphrase_action" = "Set Passphrase";
"key_backup_setup_passphrase_setup_recovery_key_info" = "Or, secure your backup with a Recovery Key, saving it somewhere safe.";
"key_backup_setup_passphrase_setup_recovery_key_action" = "(Advanced) Set up with Recovery Key";
"key_backup_setup_passphrase_confirm_passphrase_invalid" = "phrase doesnt match";
"key_backup_setup_passphrase_set_passphrase_action" = "Set Phrase";
"key_backup_setup_passphrase_setup_recovery_key_info" = "Or, secure your backup with a Security Key, saving it somewhere safe.";
"key_backup_setup_passphrase_setup_recovery_key_action" = "(Advanced) Set up with Security Key";
// Success
"key_backup_setup_success_title" = "Success!";
// Success from passphrase
"key_backup_setup_success_from_passphrase_info" = "Your keys are being backed up.\n\nYour recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.\n\nKeep your recovery key somewhere very secure, like a password manager (or a safe).";
"key_backup_setup_success_from_passphrase_save_recovery_key_action" = "Save Recovery Key";
"key_backup_setup_success_from_passphrase_info" = "Your keys are being backed up.\n\nYour Security Key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.\n\nKeep your Security Key somewhere very secure, like a password manager (or a safe).";
"key_backup_setup_success_from_passphrase_save_recovery_key_action" = "Save Security Key";
"key_backup_setup_success_from_passphrase_done_action" = "Done";
// Success from recovery key
"key_backup_setup_success_from_recovery_key_info" = "Your keys are being backed up.\n\nMake a copy of this recovery key and keep it safe.";
"key_backup_setup_success_from_recovery_key_recovery_key_title" = "Recovery Key";
"key_backup_setup_success_from_recovery_key_info" = "Your keys are being backed up.\n\nMake a copy of this Security Key and keep it safe.";
"key_backup_setup_success_from_recovery_key_recovery_key_title" = "Security Key";
"key_backup_setup_success_from_recovery_key_make_copy_action" = "Make a Copy";
"key_backup_setup_success_from_recovery_key_made_copy_action" = "I've made a copy";
// Success from secure backup
"key_backup_setup_success_from_secure_backup_info" = "Your keys are being backed up.";
// MARK: Key backup recover
"key_backup_recover_title" = "Secure Messages";
"key_backup_recover_invalid_passphrase_title" = "Incorrect Recovery Passphrase";
"key_backup_recover_invalid_passphrase" = "Backup could not be decrypted with this passphrase: please verify that you entered the correct recovery passphrase.";
"key_backup_recover_invalid_recovery_key_title" = "Recovery Key Mismatch";
"key_backup_recover_invalid_recovery_key" = "Backup could not be decrypted with this key: please verify that you entered the correct recovery key.";
"key_backup_recover_invalid_passphrase_title" = "Incorrect Security Phrase";
"key_backup_recover_invalid_passphrase" = "Backup could not be decrypted with this phrase: please verify that you entered the correct Security Phrase.";
"key_backup_recover_invalid_recovery_key_title" = "Security Key Mismatch";
"key_backup_recover_invalid_recovery_key" = "Backup could not be decrypted with this key: please verify that you entered the correct Security Key.";
// Recover from private key
"key_backup_recover_from_private_key_info" = "Restoring backup…";
// Recover from passphrase
"key_backup_recover_from_passphrase_info" = "Use your recovery passphrase to unlock your secure message history";
"key_backup_recover_from_passphrase_info" = "Use your Security Phrase to unlock your secure message history";
"key_backup_recover_from_passphrase_passphrase_title" = "Enter";
"key_backup_recover_from_passphrase_passphrase_placeholder" = "Enter Passphrase";
"key_backup_recover_from_passphrase_passphrase_placeholder" = "Enter Phrase";
"key_backup_recover_from_passphrase_recover_action" = "Unlock History";
"key_backup_recover_from_passphrase_lost_passphrase_action_part1" = "Dont know your recovery passphrase? You can ";
"key_backup_recover_from_passphrase_lost_passphrase_action_part2" = "use your recovery key";
"key_backup_recover_from_passphrase_lost_passphrase_action_part1" = "Dont know your Security Phrase? You can ";
"key_backup_recover_from_passphrase_lost_passphrase_action_part2" = "use your Security Key";
"key_backup_recover_from_passphrase_lost_passphrase_action_part3" = ".";
// Recover from recovery key
"key_backup_recover_from_recovery_key_info" = "Use your recovery key to unlock your secure message history";
"key_backup_recover_from_recovery_key_info" = "Use your Security Key to unlock your secure message history";
"key_backup_recover_from_recovery_key_recovery_key_title" = "Enter";
"key_backup_recover_from_recovery_key_recovery_key_placeholder" = "Enter Recovery Key";
"key_backup_recover_from_recovery_key_recovery_key_placeholder" = "Enter Security Key";
"key_backup_recover_from_recovery_key_recover_action" = "Unlock History";
"key_backup_recover_from_recovery_key_lost_recovery_key_action" = "Lost your recovery key? You can set up a new one in settings.";
"key_backup_recover_from_recovery_key_lost_recovery_key_action" = "Lost your Security Key You can set up a new one in settings.";
// Success
@@ -1200,8 +1206,8 @@ Tap the + to start adding people.";
"device_verification_self_verify_wait_new_sign_in_title" = "Verify this login";
"device_verification_self_verify_wait_information" = "Verify this session from one of your other sessions, granting it access to encrypted messages.\n\nUse the latest Element on your other devices:";
"device_verification_self_verify_wait_additional_information" = "This works with Element and other cross-signing capable Matrix clients.";
"device_verification_self_verify_wait_recover_secrets_without_passphrase" = "Use Recovery Key";
"device_verification_self_verify_wait_recover_secrets_with_passphrase" = "Use Recovery Passphrase or Key";
"device_verification_self_verify_wait_recover_secrets_without_passphrase" = "Use Security Key";
"device_verification_self_verify_wait_recover_secrets_with_passphrase" = "Use Security Phrase or Key";
"device_verification_self_verify_wait_recover_secrets_additional_information" = "If you can't access an existing session";
"device_verification_self_verify_wait_recover_secrets_checking_availability" = "Checking for other verification capabilities ...";
@@ -1435,38 +1441,40 @@ Tap the + to start adding people.";
// Recover with passphrase
"secrets_recovery_with_passphrase_title" = "Recovery Passphrase";
"secrets_recovery_with_passphrase_information_default" = "Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery passphrase.";
"secrets_recovery_with_passphrase_information_verify_device" = "Use your Recovery Passphrase to verify this device.";
"secrets_recovery_with_passphrase_title" = "Security Phrase";
"secrets_recovery_with_passphrase_information_default" = "Access your secure message history and your cross-signing identity for verifying other sessions by entering your Security Phrase.";
"secrets_recovery_with_passphrase_information_verify_device" = "Use your Security Phrase to verify this device.";
"secrets_recovery_with_passphrase_passphrase_title" = "Enter";
"secrets_recovery_with_passphrase_passphrase_placeholder" = "Enter Recovery Passphrase";
"secrets_recovery_with_passphrase_recover_action" = "Use Passphrase";
"secrets_recovery_with_passphrase_passphrase_placeholder" = "Enter Security Phrase";
"secrets_recovery_with_passphrase_recover_action" = "Use Phrase";
"secrets_recovery_with_passphrase_lost_passphrase_action_part1" = "Dont know your recovery passphrase? You can ";
"secrets_recovery_with_passphrase_lost_passphrase_action_part2" = "use your recovery key";
"secrets_recovery_with_passphrase_lost_passphrase_action_part1" = "Dont know your Security Phrase? You can ";
"secrets_recovery_with_passphrase_lost_passphrase_action_part2" = "use your Security Key";
"secrets_recovery_with_passphrase_lost_passphrase_action_part3" = ".";
"secrets_recovery_with_passphrase_invalid_passphrase_title" = "Unable to access secret storage";
"secrets_recovery_with_passphrase_invalid_passphrase_message" = "Please verify that you entered the correct recovery passphrase.";
"secrets_recovery_with_passphrase_invalid_passphrase_message" = "Please verify that you entered the correct Security Phrase.";
// Recover with key
"secrets_recovery_with_key_title" = "Recovery Key";
"secrets_recovery_with_key_information_default" = "Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery key.";
"secrets_recovery_with_key_information_verify_device" = "Use your Recovery Key to verify this device.";
"secrets_recovery_with_key_title" = "Security Key";
"secrets_recovery_with_key_information_default" = "Access your secure message history and your cross-signing identity for verifying other sessions by entering your Security Key.";
"secrets_recovery_with_key_information_verify_device" = "Use your Security Key to verify this device.";
"secrets_recovery_with_key_information_unlock_secure_backup_with_phrase" = "Enter your Security Phrase to continue.";
"secrets_recovery_with_key_information_unlock_secure_backup_with_key" = "Enter your Security Key to continue.";
"secrets_recovery_with_key_recovery_key_title" = "Enter";
"secrets_recovery_with_key_recovery_key_placeholder" = "Enter Recovery Key";
"secrets_recovery_with_key_recovery_key_placeholder" = "Enter Security Key";
"secrets_recovery_with_key_recover_action" = "Use Key";
"secrets_recovery_with_key_invalid_recovery_key_title" = "Unable to access secret storage";
"secrets_recovery_with_key_invalid_recovery_key_message" = "Please verify that you entered the correct recovery key.";
"secrets_recovery_with_key_invalid_recovery_key_message" = "Please verify that you entered the correct Security Key.";
// MARK: - Secrets set up
// Recovery Key
"secrets_setup_recovery_key_title" = "Save your Security Key";
"secrets_setup_recovery_key_information" = "Store your Recovery Key somewhere safe. It can be used to unlock your encrypted messages & data.";
"secrets_setup_recovery_key_information" = "Store your Security Key somewhere safe. It can be used to unlock your encrypted messages & data.";
"secrets_setup_recovery_key_loading" = "Loading…";
"secrets_setup_recovery_key_export_action" = "Save";
"secrets_setup_recovery_key_done_action" = "Done";
@@ -1483,10 +1491,10 @@ Tap the + to start adding people.";
"secrets_setup_recovery_passphrase_confirm_information" = "Enter your Security Phrase again to confirm it.";
"secrets_setup_recovery_passphrase_confirm_passphrase_title" = "Confirm";
"secrets_setup_recovery_passphrase_confirm_passphrase_placeholder" = "Confirm passphrase";
"secrets_setup_recovery_passphrase_confirm_passphrase_placeholder" = "Confirm phrase";
"key_backup_setup_passphrase_confirm_passphrase_valid" = "Great!";
"key_backup_setup_passphrase_confirm_passphrase_invalid" = "Passphrase doesnt match";
"key_backup_setup_passphrase_confirm_passphrase_invalid" = "Phrase doesnt match";
"secrets_setup_recovery_passphrase_summary_title" = "Save your Security Phrase";
"secrets_setup_recovery_passphrase_summary_information" = "Remember your Security Phrase. It can be used to unlock your encrypted messages & data.";
+5
View File
@@ -97,6 +97,11 @@ internal enum StoryboardScene {
internal static let initialScene = InitialSceneType<Riot.KeyBackupSetupSuccessFromRecoveryKeyViewController>(storyboard: KeyBackupSetupSuccessFromRecoveryKeyViewController.self)
}
internal enum KeyBackupSetupSuccessFromSecureBackupViewController: StoryboardType {
internal static let storyboardName = "KeyBackupSetupSuccessFromSecureBackupViewController"
internal static let initialScene = InitialSceneType<Riot.KeyBackupSetupSuccessFromSecureBackupViewController>(storyboard: KeyBackupSetupSuccessFromSecureBackupViewController.self)
}
internal enum KeyVerificationDataLoadingViewController: StoryboardType {
internal static let storyboardName = "KeyVerificationDataLoadingViewController"
+76 -52
View File
@@ -1034,11 +1034,11 @@ internal enum VectorL10n {
internal static var deviceVerificationSelfVerifyWaitRecoverSecretsCheckingAvailability: String {
return VectorL10n.tr("Vector", "device_verification_self_verify_wait_recover_secrets_checking_availability")
}
/// Use Recovery Passphrase or Key
/// Use Security Phrase or Key
internal static var deviceVerificationSelfVerifyWaitRecoverSecretsWithPassphrase: String {
return VectorL10n.tr("Vector", "device_verification_self_verify_wait_recover_secrets_with_passphrase")
}
/// Use Recovery Key
/// Use Security Key
internal static var deviceVerificationSelfVerifyWaitRecoverSecretsWithoutPassphrase: String {
return VectorL10n.tr("Vector", "device_verification_self_verify_wait_recover_secrets_without_passphrase")
}
@@ -1142,7 +1142,7 @@ internal enum VectorL10n {
internal static var e2eEnablingOnAppUpdate: String {
return VectorL10n.tr("Vector", "e2e_enabling_on_app_update")
}
/// A new secure message key backup has been detected.\n\nIf this wasnt you, set a new passphrase in Settings.
/// A new secure message key backup has been detected.\n\nIf this wasnt you, set a new Security Phrase in Settings.
internal static var e2eKeyBackupWrongVersion: String {
return VectorL10n.tr("Vector", "e2e_key_backup_wrong_version")
}
@@ -1614,15 +1614,15 @@ internal enum VectorL10n {
internal static var keyBackupRecoverDoneAction: String {
return VectorL10n.tr("Vector", "key_backup_recover_done_action")
}
/// Use your recovery passphrase to unlock your secure message history
/// Use your Security Phrase to unlock your secure message history
internal static var keyBackupRecoverFromPassphraseInfo: String {
return VectorL10n.tr("Vector", "key_backup_recover_from_passphrase_info")
}
/// Dont know your recovery passphrase? You can
/// Dont know your Security Phrase? You can
internal static var keyBackupRecoverFromPassphraseLostPassphraseActionPart1: String {
return VectorL10n.tr("Vector", "key_backup_recover_from_passphrase_lost_passphrase_action_part1")
}
/// use your recovery key
/// use your Security Key
internal static var keyBackupRecoverFromPassphraseLostPassphraseActionPart2: String {
return VectorL10n.tr("Vector", "key_backup_recover_from_passphrase_lost_passphrase_action_part2")
}
@@ -1630,7 +1630,7 @@ internal enum VectorL10n {
internal static var keyBackupRecoverFromPassphraseLostPassphraseActionPart3: String {
return VectorL10n.tr("Vector", "key_backup_recover_from_passphrase_lost_passphrase_action_part3")
}
/// Enter Passphrase
/// Enter Phrase
internal static var keyBackupRecoverFromPassphrasePassphrasePlaceholder: String {
return VectorL10n.tr("Vector", "key_backup_recover_from_passphrase_passphrase_placeholder")
}
@@ -1646,11 +1646,11 @@ internal enum VectorL10n {
internal static var keyBackupRecoverFromPrivateKeyInfo: String {
return VectorL10n.tr("Vector", "key_backup_recover_from_private_key_info")
}
/// Use your recovery key to unlock your secure message history
/// Use your Security Key to unlock your secure message history
internal static var keyBackupRecoverFromRecoveryKeyInfo: String {
return VectorL10n.tr("Vector", "key_backup_recover_from_recovery_key_info")
}
/// Lost your recovery key? You can set up a new one in settings.
/// Lost your Security Key You can set up a new one in settings.
internal static var keyBackupRecoverFromRecoveryKeyLostRecoveryKeyAction: String {
return VectorL10n.tr("Vector", "key_backup_recover_from_recovery_key_lost_recovery_key_action")
}
@@ -1658,7 +1658,7 @@ internal enum VectorL10n {
internal static var keyBackupRecoverFromRecoveryKeyRecoverAction: String {
return VectorL10n.tr("Vector", "key_backup_recover_from_recovery_key_recover_action")
}
/// Enter Recovery Key
/// Enter Security Key
internal static var keyBackupRecoverFromRecoveryKeyRecoveryKeyPlaceholder: String {
return VectorL10n.tr("Vector", "key_backup_recover_from_recovery_key_recovery_key_placeholder")
}
@@ -1666,19 +1666,19 @@ internal enum VectorL10n {
internal static var keyBackupRecoverFromRecoveryKeyRecoveryKeyTitle: String {
return VectorL10n.tr("Vector", "key_backup_recover_from_recovery_key_recovery_key_title")
}
/// Backup could not be decrypted with this passphrase: please verify that you entered the correct recovery passphrase.
/// Backup could not be decrypted with this phrase: please verify that you entered the correct Security Phrase.
internal static var keyBackupRecoverInvalidPassphrase: String {
return VectorL10n.tr("Vector", "key_backup_recover_invalid_passphrase")
}
/// Incorrect Recovery Passphrase
/// Incorrect Security Phrase
internal static var keyBackupRecoverInvalidPassphraseTitle: String {
return VectorL10n.tr("Vector", "key_backup_recover_invalid_passphrase_title")
}
/// Backup could not be decrypted with this key: please verify that you entered the correct recovery key.
/// Backup could not be decrypted with this key: please verify that you entered the correct Security Key.
internal static var keyBackupRecoverInvalidRecoveryKey: String {
return VectorL10n.tr("Vector", "key_backup_recover_invalid_recovery_key")
}
/// Recovery Key Mismatch
/// Security Key Mismatch
internal static var keyBackupRecoverInvalidRecoveryKeyTitle: String {
return VectorL10n.tr("Vector", "key_backup_recover_invalid_recovery_key_title")
}
@@ -1714,11 +1714,11 @@ internal enum VectorL10n {
internal static var keyBackupSetupIntroTitle: String {
return VectorL10n.tr("Vector", "key_backup_setup_intro_title")
}
/// Passphrase doesnt match
/// Phrase doesnt match
internal static var keyBackupSetupPassphraseConfirmPassphraseInvalid: String {
return VectorL10n.tr("Vector", "key_backup_setup_passphrase_confirm_passphrase_invalid")
}
/// Confirm passphrase
/// Confirm phrase
internal static var keyBackupSetupPassphraseConfirmPassphrasePlaceholder: String {
return VectorL10n.tr("Vector", "key_backup_setup_passphrase_confirm_passphrase_placeholder")
}
@@ -1730,7 +1730,7 @@ internal enum VectorL10n {
internal static var keyBackupSetupPassphraseConfirmPassphraseValid: String {
return VectorL10n.tr("Vector", "key_backup_setup_passphrase_confirm_passphrase_valid")
}
/// We'll store an encrypted copy of your keys on our server. Protect your backup with a passphrase to keep it secure.\n\nFor maximum security, this should be different from your account password.
/// We'll store an encrypted copy of your keys on our server. Protect your backup with a phrase to keep it secure.\n\nFor maximum security, this should be different from your account password.
internal static var keyBackupSetupPassphraseInfo: String {
return VectorL10n.tr("Vector", "key_backup_setup_passphrase_info")
}
@@ -1738,7 +1738,7 @@ internal enum VectorL10n {
internal static var keyBackupSetupPassphrasePassphraseInvalid: String {
return VectorL10n.tr("Vector", "key_backup_setup_passphrase_passphrase_invalid")
}
/// Enter passphrase
/// Enter phrase
internal static var keyBackupSetupPassphrasePassphrasePlaceholder: String {
return VectorL10n.tr("Vector", "key_backup_setup_passphrase_passphrase_placeholder")
}
@@ -1750,19 +1750,19 @@ internal enum VectorL10n {
internal static var keyBackupSetupPassphrasePassphraseValid: String {
return VectorL10n.tr("Vector", "key_backup_setup_passphrase_passphrase_valid")
}
/// Set Passphrase
/// Set Phrase
internal static var keyBackupSetupPassphraseSetPassphraseAction: String {
return VectorL10n.tr("Vector", "key_backup_setup_passphrase_set_passphrase_action")
}
/// (Advanced) Set up with Recovery Key
/// (Advanced) Set up with Security Key
internal static var keyBackupSetupPassphraseSetupRecoveryKeyAction: String {
return VectorL10n.tr("Vector", "key_backup_setup_passphrase_setup_recovery_key_action")
}
/// Or, secure your backup with a Recovery Key, saving it somewhere safe.
/// Or, secure your backup with a Security Key, saving it somewhere safe.
internal static var keyBackupSetupPassphraseSetupRecoveryKeyInfo: String {
return VectorL10n.tr("Vector", "key_backup_setup_passphrase_setup_recovery_key_info")
}
/// Secure your backup with a Passphrase
/// Secure your backup with a Security Phrase
internal static var keyBackupSetupPassphraseTitle: String {
return VectorL10n.tr("Vector", "key_backup_setup_passphrase_title")
}
@@ -1782,15 +1782,15 @@ internal enum VectorL10n {
internal static var keyBackupSetupSuccessFromPassphraseDoneAction: String {
return VectorL10n.tr("Vector", "key_backup_setup_success_from_passphrase_done_action")
}
/// Your keys are being backed up.\n\nYour recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.\n\nKeep your recovery key somewhere very secure, like a password manager (or a safe).
/// Your keys are being backed up.\n\nYour Security Key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.\n\nKeep your Security Key somewhere very secure, like a password manager (or a safe).
internal static var keyBackupSetupSuccessFromPassphraseInfo: String {
return VectorL10n.tr("Vector", "key_backup_setup_success_from_passphrase_info")
}
/// Save Recovery Key
/// Save Security Key
internal static var keyBackupSetupSuccessFromPassphraseSaveRecoveryKeyAction: String {
return VectorL10n.tr("Vector", "key_backup_setup_success_from_passphrase_save_recovery_key_action")
}
/// Your keys are being backed up.\n\nMake a copy of this recovery key and keep it safe.
/// Your keys are being backed up.\n\nMake a copy of this Security Key and keep it safe.
internal static var keyBackupSetupSuccessFromRecoveryKeyInfo: String {
return VectorL10n.tr("Vector", "key_backup_setup_success_from_recovery_key_info")
}
@@ -1802,10 +1802,14 @@ internal enum VectorL10n {
internal static var keyBackupSetupSuccessFromRecoveryKeyMakeCopyAction: String {
return VectorL10n.tr("Vector", "key_backup_setup_success_from_recovery_key_make_copy_action")
}
/// Recovery Key
/// Security Key
internal static var keyBackupSetupSuccessFromRecoveryKeyRecoveryKeyTitle: String {
return VectorL10n.tr("Vector", "key_backup_setup_success_from_recovery_key_recovery_key_title")
}
/// Your keys are being backed up.
internal static var keyBackupSetupSuccessFromSecureBackupInfo: String {
return VectorL10n.tr("Vector", "key_backup_setup_success_from_secure_backup_info")
}
/// Success!
internal static var keyBackupSetupSuccessTitle: String {
return VectorL10n.tr("Vector", "key_backup_setup_success_title")
@@ -3554,15 +3558,23 @@ internal enum VectorL10n {
internal static var secretsRecoveryResetActionPart2: String {
return VectorL10n.tr("Vector", "secrets_recovery_reset_action_part_2")
}
/// Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery key.
/// Access your secure message history and your cross-signing identity for verifying other sessions by entering your Security Key.
internal static var secretsRecoveryWithKeyInformationDefault: String {
return VectorL10n.tr("Vector", "secrets_recovery_with_key_information_default")
}
/// Use your Recovery Key to verify this device.
/// Enter your Security Key to continue.
internal static var secretsRecoveryWithKeyInformationUnlockSecureBackupWithKey: String {
return VectorL10n.tr("Vector", "secrets_recovery_with_key_information_unlock_secure_backup_with_key")
}
/// Enter your Security Phrase to continue.
internal static var secretsRecoveryWithKeyInformationUnlockSecureBackupWithPhrase: String {
return VectorL10n.tr("Vector", "secrets_recovery_with_key_information_unlock_secure_backup_with_phrase")
}
/// Use your Security Key to verify this device.
internal static var secretsRecoveryWithKeyInformationVerifyDevice: String {
return VectorL10n.tr("Vector", "secrets_recovery_with_key_information_verify_device")
}
/// Please verify that you entered the correct recovery key.
/// Please verify that you entered the correct Security Key.
internal static var secretsRecoveryWithKeyInvalidRecoveryKeyMessage: String {
return VectorL10n.tr("Vector", "secrets_recovery_with_key_invalid_recovery_key_message")
}
@@ -3574,7 +3586,7 @@ internal enum VectorL10n {
internal static var secretsRecoveryWithKeyRecoverAction: String {
return VectorL10n.tr("Vector", "secrets_recovery_with_key_recover_action")
}
/// Enter Recovery Key
/// Enter Security Key
internal static var secretsRecoveryWithKeyRecoveryKeyPlaceholder: String {
return VectorL10n.tr("Vector", "secrets_recovery_with_key_recovery_key_placeholder")
}
@@ -3582,19 +3594,19 @@ internal enum VectorL10n {
internal static var secretsRecoveryWithKeyRecoveryKeyTitle: String {
return VectorL10n.tr("Vector", "secrets_recovery_with_key_recovery_key_title")
}
/// Recovery Key
/// Security Key
internal static var secretsRecoveryWithKeyTitle: String {
return VectorL10n.tr("Vector", "secrets_recovery_with_key_title")
}
/// Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery passphrase.
/// Access your secure message history and your cross-signing identity for verifying other sessions by entering your Security Phrase.
internal static var secretsRecoveryWithPassphraseInformationDefault: String {
return VectorL10n.tr("Vector", "secrets_recovery_with_passphrase_information_default")
}
/// Use your Recovery Passphrase to verify this device.
/// Use your Security Phrase to verify this device.
internal static var secretsRecoveryWithPassphraseInformationVerifyDevice: String {
return VectorL10n.tr("Vector", "secrets_recovery_with_passphrase_information_verify_device")
}
/// Please verify that you entered the correct recovery passphrase.
/// Please verify that you entered the correct Security Phrase.
internal static var secretsRecoveryWithPassphraseInvalidPassphraseMessage: String {
return VectorL10n.tr("Vector", "secrets_recovery_with_passphrase_invalid_passphrase_message")
}
@@ -3602,11 +3614,11 @@ internal enum VectorL10n {
internal static var secretsRecoveryWithPassphraseInvalidPassphraseTitle: String {
return VectorL10n.tr("Vector", "secrets_recovery_with_passphrase_invalid_passphrase_title")
}
/// Dont know your recovery passphrase? You can
/// Dont know your Security Phrase? You can
internal static var secretsRecoveryWithPassphraseLostPassphraseActionPart1: String {
return VectorL10n.tr("Vector", "secrets_recovery_with_passphrase_lost_passphrase_action_part1")
}
/// use your recovery key
/// use your Security Key
internal static var secretsRecoveryWithPassphraseLostPassphraseActionPart2: String {
return VectorL10n.tr("Vector", "secrets_recovery_with_passphrase_lost_passphrase_action_part2")
}
@@ -3614,7 +3626,7 @@ internal enum VectorL10n {
internal static var secretsRecoveryWithPassphraseLostPassphraseActionPart3: String {
return VectorL10n.tr("Vector", "secrets_recovery_with_passphrase_lost_passphrase_action_part3")
}
/// Enter Recovery Passphrase
/// Enter Security Phrase
internal static var secretsRecoveryWithPassphrasePassphrasePlaceholder: String {
return VectorL10n.tr("Vector", "secrets_recovery_with_passphrase_passphrase_placeholder")
}
@@ -3622,11 +3634,11 @@ internal enum VectorL10n {
internal static var secretsRecoveryWithPassphrasePassphraseTitle: String {
return VectorL10n.tr("Vector", "secrets_recovery_with_passphrase_passphrase_title")
}
/// Use Passphrase
/// Use Phrase
internal static var secretsRecoveryWithPassphraseRecoverAction: String {
return VectorL10n.tr("Vector", "secrets_recovery_with_passphrase_recover_action")
}
/// Recovery Passphrase
/// Security Phrase
internal static var secretsRecoveryWithPassphraseTitle: String {
return VectorL10n.tr("Vector", "secrets_recovery_with_passphrase_title")
}
@@ -3662,7 +3674,7 @@ internal enum VectorL10n {
internal static var secretsSetupRecoveryKeyExportAction: String {
return VectorL10n.tr("Vector", "secrets_setup_recovery_key_export_action")
}
/// Store your Recovery Key somewhere safe. It can be used to unlock your encrypted messages & data.
/// Store your Security Key somewhere safe. It can be used to unlock your encrypted messages & data.
internal static var secretsSetupRecoveryKeyInformation: String {
return VectorL10n.tr("Vector", "secrets_setup_recovery_key_information")
}
@@ -3690,7 +3702,7 @@ internal enum VectorL10n {
internal static var secretsSetupRecoveryPassphraseConfirmInformation: String {
return VectorL10n.tr("Vector", "secrets_setup_recovery_passphrase_confirm_information")
}
/// Confirm passphrase
/// Confirm phrase
internal static var secretsSetupRecoveryPassphraseConfirmPassphrasePlaceholder: String {
return VectorL10n.tr("Vector", "secrets_setup_recovery_passphrase_confirm_passphrase_placeholder")
}
@@ -3770,7 +3782,7 @@ internal enum VectorL10n {
internal static var secureKeyBackupSetupIntroUseSecurityPassphraseInfo: String {
return VectorL10n.tr("Vector", "secure_key_backup_setup_intro_use_security_passphrase_info")
}
/// Use a Security Passphrase
/// Use a Security Phrase
internal static var secureKeyBackupSetupIntroUseSecurityPassphraseTitle: String {
return VectorL10n.tr("Vector", "secure_key_backup_setup_intro_use_security_passphrase_title")
}
@@ -3806,7 +3818,7 @@ internal enum VectorL10n {
internal static var securitySettingsCrosssigning: String {
return VectorL10n.tr("Vector", "security_settings_crosssigning")
}
/// Bootstrap cross-signing
/// Set up
internal static var securitySettingsCrosssigningBootstrap: String {
return VectorL10n.tr("Vector", "security_settings_crosssigning_bootstrap")
}
@@ -3822,7 +3834,7 @@ internal enum VectorL10n {
internal static var securitySettingsCrosssigningInfoNotBootstrapped: String {
return VectorL10n.tr("Vector", "security_settings_crosssigning_info_not_bootstrapped")
}
/// Cross-signing is enabled.
/// Cross-signing is ready for use.
internal static var securitySettingsCrosssigningInfoOk: String {
return VectorL10n.tr("Vector", "security_settings_crosssigning_info_ok")
}
@@ -3830,7 +3842,7 @@ internal enum VectorL10n {
internal static var securitySettingsCrosssigningInfoTrusted: String {
return VectorL10n.tr("Vector", "security_settings_crosssigning_info_trusted")
}
/// Reset cross-signing
/// Reset
internal static var securitySettingsCrosssigningReset: String {
return VectorL10n.tr("Vector", "security_settings_crosssigning_reset")
}
@@ -3858,22 +3870,34 @@ internal enum VectorL10n {
internal static var securitySettingsSecureBackup: String {
return VectorL10n.tr("Vector", "security_settings_secure_backup")
}
/// Delete
/// Delete Backup
internal static var securitySettingsSecureBackupDelete: String {
return VectorL10n.tr("Vector", "security_settings_secure_backup_delete")
}
/// Safeguard against losing access to encrypted messages & data by backing up encryption keys on your server.
/// Back up your encryption keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Security Key.
internal static var securitySettingsSecureBackupDescription: String {
return VectorL10n.tr("Vector", "security_settings_secure_backup_description")
}
/// Checking
internal static var securitySettingsSecureBackupInfoChecking: String {
return VectorL10n.tr("Vector", "security_settings_secure_backup_info_checking")
}
/// This session is backing up your keys.
internal static var securitySettingsSecureBackupInfoValid: String {
return VectorL10n.tr("Vector", "security_settings_secure_backup_info_valid")
}
/// Reset
internal static var securitySettingsSecureBackupReset: String {
return VectorL10n.tr("Vector", "security_settings_secure_backup_reset")
}
/// Restore from Backup
internal static var securitySettingsSecureBackupRestore: String {
return VectorL10n.tr("Vector", "security_settings_secure_backup_restore")
}
/// Set up
internal static var securitySettingsSecureBackupSetup: String {
return VectorL10n.tr("Vector", "security_settings_secure_backup_setup")
}
/// Synchronise
internal static var securitySettingsSecureBackupSynchronise: String {
return VectorL10n.tr("Vector", "security_settings_secure_backup_synchronise")
}
/// Security
internal static var securitySettingsTitle: String {
return VectorL10n.tr("Vector", "security_settings_title")
@@ -4250,7 +4274,7 @@ internal enum VectorL10n {
internal static var settingsKeyBackupInfoProgressDone: String {
return VectorL10n.tr("Vector", "settings_key_backup_info_progress_done")
}
/// Connect this session to key backup before signing out to avoid losing any keys that may only be on this device.
/// Back up your keys before signing out to avoid losing them.
internal static var settingsKeyBackupInfoSignoutWarning: String {
return VectorL10n.tr("Vector", "settings_key_backup_info_signout_warning")
}
+1 -1
View File
@@ -167,7 +167,7 @@
- (void)presentSecureBackupSetup
{
SecureBackupSetupCoordinatorBridgePresenter *keyBackupSetupCoordinatorBridgePresenter = [[SecureBackupSetupCoordinatorBridgePresenter alloc] initWithSession:self.mainSession];
SecureBackupSetupCoordinatorBridgePresenter *keyBackupSetupCoordinatorBridgePresenter = [[SecureBackupSetupCoordinatorBridgePresenter alloc] initWithSession:self.mainSession allowOverwrite:NO];
keyBackupSetupCoordinatorBridgePresenter.delegate = self;
[keyBackupSetupCoordinatorBridgePresenter presentFrom:self animated:YES];
@@ -44,11 +44,11 @@ final class KeyBackupSetupCoordinator: KeyBackupSetupCoordinatorType {
// MARK: - Public methods
func start() {
// Set key backup setup intro as root controller
let keyBackupSetupIntroViewController = self.createSetupIntroViewController()
keyBackupSetupIntroViewController.delegate = self
self.navigationRouter.setRootModule(keyBackupSetupIntroViewController)
if self.session.crypto.recoveryService.hasRecovery() {
showUnlockSecureBackup()
} else {
showSetupIntro()
}
}
func toPresentable() -> UIViewController {
@@ -57,6 +57,13 @@ final class KeyBackupSetupCoordinator: KeyBackupSetupCoordinatorType {
// MARK: - Private methods
private func showSetupIntro() {
// Set key backup setup intro as root controller
let keyBackupSetupIntroViewController = self.createSetupIntroViewController()
keyBackupSetupIntroViewController.delegate = self
self.navigationRouter.setRootModule(keyBackupSetupIntroViewController)
}
private func createSetupIntroViewController() -> KeyBackupSetupIntroViewController {
let backupState = self.session.crypto.backup?.state ?? MXKeyBackupStateUnknown
@@ -80,6 +87,17 @@ final class KeyBackupSetupCoordinator: KeyBackupSetupCoordinatorType {
return KeyBackupSetupIntroViewController.instantiate(isABackupAlreadyExists: isABackupAlreadyExists, encryptionKeysExportPresenter: encryptionKeysExportPresenter)
}
private func showUnlockSecureBackup() {
let recoveryGoal: SecretsRecoveryGoal = .unlockSecureBackup { (privateKey, completion) in
self.createKeyBackupUsingSecureBackup(privateKey: privateKey, completion: completion)
}
let coordinator = SecretsRecoveryCoordinator(session: self.session, recoveryMode: .passphraseOrKey, recoveryGoal: recoveryGoal, navigationRouter: self.navigationRouter)
coordinator.delegate = self
coordinator.start()
self.add(childCoordinator: coordinator)
}
private func showSetupPassphrase(animated: Bool) {
let keyBackupSetupPassphraseCoordinator = KeyBackupSetupPassphraseCoordinator(session: self.session)
keyBackupSetupPassphraseCoordinator.delegate = self
@@ -104,6 +122,33 @@ final class KeyBackupSetupCoordinator: KeyBackupSetupCoordinatorType {
viewController.delegate = self
self.navigationRouter.push(viewController, animated: animated, popCompletion: nil)
}
private func showSetupWithSecureBackupSuccess(animated: Bool) {
let viewController = KeyBackupSetupSuccessFromSecureBackupViewController.instantiate()
viewController.delegate = self
self.navigationRouter.push(viewController, animated: animated, popCompletion: nil)
}
private func createKeyBackupUsingSecureBackup(privateKey: Data, completion: @escaping (Result<Void, Error>) -> Void) {
guard let keyBackup = session.crypto.backup, let recoveryService = session.crypto.recoveryService else {
return
}
keyBackup.prepareKeyBackupVersion(withPassword: nil, success: { megolmBackupCreationInfo in
keyBackup.createKeyBackupVersion(megolmBackupCreationInfo, success: { _ in
recoveryService.updateRecovery(forSecrets: [MXSecretId.keyBackup.takeUnretainedValue() as String], withPrivateKey: privateKey) {
completion(.success(Void()))
} failure: { error in
completion(.failure(error))
}
}, failure: { error in
completion(.failure(error))
})
}, failure: { error in
completion(.failure(error))
})
}
}
// MARK: - KeyBackupSetupIntroViewControllerDelegate
@@ -133,6 +178,17 @@ extension KeyBackupSetupCoordinator: KeyBackupSetupPassphraseCoordinatorDelegate
}
}
// MARK: - SecretsRecoveryCoordinatorDelegate
extension KeyBackupSetupCoordinator: SecretsRecoveryCoordinatorDelegate {
func secretsRecoveryCoordinatorDidRecover(_ coordinator: SecretsRecoveryCoordinatorType) {
self.showSetupWithSecureBackupSuccess(animated: true)
}
func secretsRecoveryCoordinatorDidCancel(_ coordinator: SecretsRecoveryCoordinatorType) {
self.delegate?.keyBackupSetupCoordinatorDidCancel(self)
}
}
// MARK: - KeyBackupSetupSuccessFromPassphraseViewControllerDelegate
extension KeyBackupSetupCoordinator: KeyBackupSetupSuccessFromPassphraseViewControllerDelegate {
func keyBackupSetupSuccessFromPassphraseViewControllerDidTapDoneAction(_ viewController: KeyBackupSetupSuccessFromPassphraseViewController) {
@@ -146,3 +202,10 @@ extension KeyBackupSetupCoordinator: KeyBackupSetupSuccessFromRecoveryKeyViewCon
self.delegate?.keyBackupSetupCoordinatorDidSetupRecoveryKey(self)
}
}
// MARK: - KeyBackupSetupSuccessFromSecureBackupViewControllerDelegate
extension KeyBackupSetupCoordinator: KeyBackupSetupSuccessFromSecureBackupViewControllerDelegate {
func keyBackupSetupSuccessFromSecureBackupViewControllerDidTapDoneAction(_ viewController: KeyBackupSetupSuccessFromSecureBackupViewController) {
self.delegate?.keyBackupSetupCoordinatorDidSetupRecoveryKey(self)
}
}
@@ -0,0 +1,140 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="b2C-jK-KPV">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Key Backup Setup Success From Secure Backup View Controller-->
<scene sceneID="A4c-pi-pNE">
<objects>
<viewController extendedLayoutIncludesOpaqueBars="YES" automaticallyAdjustsScrollViewInsets="NO" id="b2C-jK-KPV" customClass="KeyBackupSetupSuccessFromSecureBackupViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="YAN-xg-EgW">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="S5g-F9-L3B">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="a4X-9Q-mkr">
<rect key="frame" x="0.0" y="0.0" width="375" height="505"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="o2g-Ga-wwR">
<rect key="frame" x="0.0" y="0.0" width="375" height="505"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="key_backup_logo" translatesAutoresizingMaskIntoConstraints="NO" id="epg-Qy-eoD">
<rect key="frame" x="163.5" y="40" width="48" height="46"/>
<constraints>
<constraint firstAttribute="width" constant="48" id="V1N-K1-elw"/>
<constraint firstAttribute="height" constant="46" id="Yzm-7B-uv2"/>
</constraints>
</imageView>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="30" translatesAutoresizingMaskIntoConstraints="NO" id="xlp-uc-Mug">
<rect key="frame" x="20" y="121" width="335" height="68.5"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Success!" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="X7g-Ha-15I">
<rect key="frame" x="0.0" y="0.0" width="335" height="20.5"/>
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Your keys are being backed up." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3eY-3I-XYH">
<rect key="frame" x="0.0" y="50.5" width="335" height="18"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</stackView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="7aK-CF-d5d">
<rect key="frame" x="0.0" y="435" width="375" height="50"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="QxC-gf-8VN">
<rect key="frame" x="0.0" y="0.0" width="375" height="50"/>
<constraints>
<constraint firstAttribute="height" constant="50" id="qB9-Kq-7Jw"/>
</constraints>
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="16"/>
<state key="normal" title="Done">
<color key="titleColor" red="0.47843137250000001" green="0.78823529410000004" blue="0.63137254899999995" alpha="1" colorSpace="calibratedRGB"/>
</state>
<state key="disabled">
<color key="titleColor" red="0.47843137250000001" green="0.78823529410000004" blue="0.63137254899999995" alpha="0.5" colorSpace="calibratedRGB"/>
</state>
<connections>
<action selector="doneButtonAction:" destination="b2C-jK-KPV" eventType="touchUpInside" id="Vs7-qI-yBp"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="QxC-gf-8VN" secondAttribute="bottom" id="HQu-va-hRH"/>
<constraint firstAttribute="trailing" secondItem="QxC-gf-8VN" secondAttribute="trailing" id="N4c-nq-GMC"/>
<constraint firstItem="QxC-gf-8VN" firstAttribute="leading" secondItem="7aK-CF-d5d" secondAttribute="leading" id="Z8A-af-23Q"/>
<constraint firstItem="QxC-gf-8VN" firstAttribute="top" secondItem="7aK-CF-d5d" secondAttribute="top" id="sed-9I-71h"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="7aK-CF-d5d" firstAttribute="leading" secondItem="o2g-Ga-wwR" secondAttribute="leading" id="1Hy-bc-46Y"/>
<constraint firstAttribute="width" priority="750" constant="500" id="4a2-Az-ekr"/>
<constraint firstItem="epg-Qy-eoD" firstAttribute="centerX" secondItem="o2g-Ga-wwR" secondAttribute="centerX" id="K4J-2Y-7bW"/>
<constraint firstItem="xlp-uc-Mug" firstAttribute="leading" secondItem="o2g-Ga-wwR" secondAttribute="leading" constant="20" id="drb-kN-IRX"/>
<constraint firstItem="xlp-uc-Mug" firstAttribute="top" secondItem="epg-Qy-eoD" secondAttribute="bottom" constant="35" id="hyp-kS-l81"/>
<constraint firstAttribute="bottom" secondItem="7aK-CF-d5d" secondAttribute="bottom" constant="20" id="nPL-nn-PpM"/>
<constraint firstItem="epg-Qy-eoD" firstAttribute="top" secondItem="o2g-Ga-wwR" secondAttribute="top" constant="40" id="p1e-CF-Kln"/>
<constraint firstAttribute="trailing" secondItem="7aK-CF-d5d" secondAttribute="trailing" id="wl8-ha-UIh"/>
<constraint firstAttribute="trailing" secondItem="xlp-uc-Mug" secondAttribute="trailing" constant="20" id="xIU-IL-ycK"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="o2g-Ga-wwR" firstAttribute="centerX" secondItem="a4X-9Q-mkr" secondAttribute="centerX" id="8Ps-hI-nHv"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="o2g-Ga-wwR" secondAttribute="trailing" id="GBk-AU-PAx"/>
<constraint firstItem="o2g-Ga-wwR" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="a4X-9Q-mkr" secondAttribute="leading" id="Z9f-1y-sJV"/>
<constraint firstAttribute="height" constant="505" id="dth-O0-khE"/>
<constraint firstAttribute="bottom" secondItem="o2g-Ga-wwR" secondAttribute="bottom" id="kJP-Gd-vDF"/>
<constraint firstItem="o2g-Ga-wwR" firstAttribute="top" secondItem="a4X-9Q-mkr" secondAttribute="top" id="zZX-fo-MMO"/>
</constraints>
</view>
</subviews>
<constraints>
<constraint firstItem="a4X-9Q-mkr" firstAttribute="top" secondItem="S5g-F9-L3B" secondAttribute="top" id="47v-Wy-vaA"/>
<constraint firstItem="a4X-9Q-mkr" firstAttribute="width" secondItem="S5g-F9-L3B" secondAttribute="width" id="REA-MB-gew"/>
<constraint firstAttribute="bottom" secondItem="a4X-9Q-mkr" secondAttribute="bottom" id="Sqk-TW-ccx"/>
<constraint firstAttribute="trailing" secondItem="a4X-9Q-mkr" secondAttribute="trailing" id="keL-QJ-bja"/>
<constraint firstItem="a4X-9Q-mkr" firstAttribute="leading" secondItem="S5g-F9-L3B" secondAttribute="leading" id="maD-UF-Wim"/>
</constraints>
</scrollView>
</subviews>
<viewLayoutGuide key="safeArea" id="hUM-zt-PJj"/>
<color key="backgroundColor" red="0.94509803920000002" green="0.96078431369999995" blue="0.97254901959999995" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstItem="hUM-zt-PJj" firstAttribute="trailing" secondItem="S5g-F9-L3B" secondAttribute="trailing" id="EAb-7P-Zsd"/>
<constraint firstAttribute="bottom" secondItem="S5g-F9-L3B" secondAttribute="bottom" id="Ham-fU-JWM"/>
<constraint firstItem="hUM-zt-PJj" firstAttribute="top" secondItem="S5g-F9-L3B" secondAttribute="top" id="Zkl-TJ-HEv"/>
<constraint firstItem="S5g-F9-L3B" firstAttribute="leading" secondItem="hUM-zt-PJj" secondAttribute="leading" id="sme-S5-pY5"/>
</constraints>
</view>
<connections>
<outlet property="doneButton" destination="QxC-gf-8VN" id="eOc-Ec-FgG"/>
<outlet property="doneButtonBackgroundView" destination="7aK-CF-d5d" id="y7U-Ol-8Nk"/>
<outlet property="informationLabel" destination="3eY-3I-XYH" id="ZT2-Lo-JlX"/>
<outlet property="keyBackupLogoImageView" destination="epg-Qy-eoD" id="YOa-Pv-XrT"/>
<outlet property="titleLabel" destination="X7g-Ha-15I" id="yMg-6G-6lF"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Yhm-Kv-QZF" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-3178" y="-675"/>
</scene>
</scenes>
<resources>
<image name="key_backup_logo" width="48" height="46"/>
</resources>
</document>
@@ -0,0 +1,120 @@
/*
Copyright 2021 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import UIKit
protocol KeyBackupSetupSuccessFromSecureBackupViewControllerDelegate: class {
func keyBackupSetupSuccessFromSecureBackupViewControllerDidTapDoneAction(_ viewController: KeyBackupSetupSuccessFromSecureBackupViewController)
}
final class KeyBackupSetupSuccessFromSecureBackupViewController: UIViewController {
// MARK: - Properties
// MARK: Outlets
@IBOutlet private weak var keyBackupLogoImageView: UIImageView!
@IBOutlet private weak var titleLabel: UILabel!
@IBOutlet private weak var informationLabel: UILabel!
@IBOutlet private weak var doneButtonBackgroundView: UIView!
@IBOutlet private weak var doneButton: UIButton!
// MARK: Private
private var theme: Theme!
// MARK: Public
weak var delegate: KeyBackupSetupSuccessFromSecureBackupViewControllerDelegate?
// MARK: - Setup
class func instantiate() -> KeyBackupSetupSuccessFromSecureBackupViewController {
let viewController = StoryboardScene.KeyBackupSetupSuccessFromSecureBackupViewController.initialScene.instantiate()
viewController.theme = ThemeService.shared().theme
return viewController
}
// MARK: - Life cycle
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.title = VectorL10n.keyBackupSetupTitle
self.setupViews()
self.registerThemeServiceDidChangeThemeNotification()
self.update(theme: self.theme)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Hide back button
self.navigationItem.setHidesBackButton(true, animated: animated)
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return self.theme.statusBarStyle
}
// MARK: - Private
private func setupViews() {
let keybackupLogoImage = Asset.Images.keyBackupLogo.image.withRenderingMode(.alwaysTemplate)
self.keyBackupLogoImageView.image = keybackupLogoImage
self.titleLabel.text = VectorL10n.keyBackupSetupSuccessTitle
self.informationLabel.text = VectorL10n.keyBackupSetupSuccessFromSecureBackupInfo
self.doneButton.setTitle(VectorL10n.keyBackupSetupSuccessFromPassphraseDoneAction, for: .normal)
}
private func update(theme: Theme) {
self.theme = theme
self.view.backgroundColor = theme.headerBackgroundColor
if let navigationBar = self.navigationController?.navigationBar {
theme.applyStyle(onNavigationBar: navigationBar)
}
self.keyBackupLogoImageView.tintColor = theme.textPrimaryColor
self.titleLabel.textColor = theme.textPrimaryColor
self.informationLabel.textColor = theme.textPrimaryColor
self.doneButtonBackgroundView.backgroundColor = theme.backgroundColor
theme.applyStyle(onButton: self.doneButton)
}
private func registerThemeServiceDidChangeThemeNotification() {
NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil)
}
@objc private func themeDidChange() {
self.update(theme: ThemeService.shared().theme)
}
// MARK: - Actions
@IBAction private func doneButtonAction(_ sender: Any) {
self.delegate?.keyBackupSetupSuccessFromSecureBackupViewControllerDidTapDoneAction(self)
}
}
@@ -103,6 +103,8 @@ final class SecretsRecoveryWithKeyViewController: UIViewController {
switch self.viewModel.recoveryGoal {
case .default, .keyBackup, .restoreSecureBackup:
informationText = VectorL10n.secretsRecoveryWithKeyInformationDefault
case .unlockSecureBackup(_):
informationText = VectorL10n.secretsRecoveryWithKeyInformationUnlockSecureBackupWithKey
case .verifyDevice:
informationText = VectorL10n.secretsRecoveryWithKeyInformationVerifyDevice
}
@@ -106,6 +106,8 @@ final class SecretsRecoveryWithPassphraseViewController: UIViewController {
switch self.viewModel.recoveryGoal {
case .default, .keyBackup, .restoreSecureBackup:
informationText = VectorL10n.secretsRecoveryWithPassphraseInformationDefault
case .unlockSecureBackup(_):
informationText = VectorL10n.secretsRecoveryWithKeyInformationUnlockSecureBackupWithPhrase
case .verifyDevice:
informationText = VectorL10n.secretsRecoveryWithPassphraseInformationVerifyDevice
}
@@ -73,27 +73,13 @@ final class SecretsRecoveryWithPassphraseViewModel: SecretsRecoveryWithPassphras
return
}
let secretIds: [String]?
if case SecretsRecoveryGoal.keyBackup = self.recoveryGoal {
secretIds = [MXSecretId.keyBackup.takeUnretainedValue() as String]
} else {
secretIds = nil
switch self.recoveryGoal {
case .unlockSecureBackup(let block):
self.execute(block: block, privateKey: privateKey)
default:
self.recoverSecrets(privateKey: privateKey)
}
self.recoveryService.recoverSecrets(secretIds, withPrivateKey: privateKey, recoverServices: true, success: { [weak self] _ in
guard let self = self else {
return
}
self.update(viewState: .loaded)
self.coordinatorDelegate?.secretsRecoveryWithPassphraseViewModelDidRecover(self)
}, failure: { [weak self] error in
guard let self = self else {
return
}
self.update(viewState: .error(error))
})
}, failure: { [weak self] error in
guard let self = self else {
return
@@ -102,6 +88,54 @@ final class SecretsRecoveryWithPassphraseViewModel: SecretsRecoveryWithPassphras
})
}
private func recoverSecrets(privateKey: Data) {
let secretIds: [String]?
if case SecretsRecoveryGoal.keyBackup = self.recoveryGoal {
secretIds = [MXSecretId.keyBackup.takeUnretainedValue() as String]
} else {
secretIds = nil
}
self.recoveryService.recoverSecrets(secretIds, withPrivateKey: privateKey, recoverServices: true, success: { [weak self] _ in
guard let self = self else {
return
}
self.update(viewState: .loaded)
self.coordinatorDelegate?.secretsRecoveryWithPassphraseViewModelDidRecover(self)
}, failure: { [weak self] error in
guard let self = self else {
return
}
self.update(viewState: .error(error))
})
}
private func execute(block: @escaping (_ privateKey: Data, _ completion: @escaping (Result<Void, Error>) -> Void) -> Void, privateKey: Data) {
// Check the private key is valid before using it
self.recoveryService.checkPrivateKey(privateKey) { match in
guard match else {
// Reuse already managed error
let error = NSError(domain: MXRecoveryServiceErrorDomain, code: Int(MXRecoveryServiceErrorCode.badRecoveryKeyErrorCode.rawValue), userInfo: nil)
self.update(viewState: .error(error))
return
}
// Run the extenal code while the view state is .loading
block(privateKey) { result in
MXLog.debug("[SecretsRecoveryWithPassphraseViewModel] execute: Block returned: \(result)")
switch result {
case .success:
self.update(viewState: .loaded)
self.coordinatorDelegate?.secretsRecoveryWithPassphraseViewModelDidRecover(self)
case .failure(let error):
self.update(viewState: .error(error))
}
}
}
}
private func update(viewState: SecretsRecoveryWithPassphraseViewState) {
self.viewDelegate?.secretsRecoveryWithPassphraseViewModel(self, didUpdateViewState: viewState)
}
@@ -32,7 +32,7 @@ final class SecretsRecoveryCoordinatorBridgePresenter: NSObject {
private let session: MXSession
private let recoveryMode: SecretsRecoveryMode
let recoveryGoal: SecretsRecoveryGoal
let recoveryGoal: SecretsRecoveryGoalBridge
private var coordinator: SecretsRecoveryCoordinator?
@@ -46,14 +46,14 @@ final class SecretsRecoveryCoordinatorBridgePresenter: NSObject {
// MARK: - Setup
init(session: MXSession, recoveryMode: SecretsRecoveryMode, recoveryGoal: SecretsRecoveryGoal) {
init(session: MXSession, recoveryMode: SecretsRecoveryMode, recoveryGoal: SecretsRecoveryGoalBridge) {
self.session = session
self.recoveryMode = recoveryMode
self.recoveryGoal = recoveryGoal
super.init()
}
init(session: MXSession, recoveryGoal: SecretsRecoveryGoal) {
init(session: MXSession, recoveryGoal: SecretsRecoveryGoalBridge) {
self.session = session
if case SecretsRecoveryAvailability.available(let secretMode) = session.crypto.recoveryService.vc_availability {
@@ -74,7 +74,7 @@ final class SecretsRecoveryCoordinatorBridgePresenter: NSObject {
func present(from viewController: UIViewController, animated: Bool) {
let coordinator = SecretsRecoveryCoordinator(session: self.session, recoveryMode: self.recoveryMode, recoveryGoal: self.recoveryGoal)
let coordinator = SecretsRecoveryCoordinator(session: self.session, recoveryMode: self.recoveryMode, recoveryGoal: self.recoveryGoal.goal)
coordinator.delegate = self
let presentable = coordinator.toPresentable()
@@ -16,10 +16,28 @@
import Foundation
enum SecretsRecoveryGoal {
case `default`
case keyBackup
/// Unlock the secure backup (4S) to get the private key and execute a closure during the flow
case unlockSecureBackup ((_ privateKey: Data, _ completion: @escaping (Result<Void, Error>) -> Void) -> Void)
case verifyDevice
case restoreSecureBackup
}
@objc
enum SecretsRecoveryGoal: Int {
enum SecretsRecoveryGoalBridge: Int {
case `default`
case keyBackup
case verifyDevice
case restoreSecureBackup
var goal: SecretsRecoveryGoal {
switch self {
case .default: return .default
case .keyBackup: return .keyBackup
case .verifyDevice: return .verifyDevice
case .restoreSecureBackup: return .restoreSecureBackup
}
}
}
@@ -39,8 +39,9 @@ final class SecretsSetupRecoveryKeyCoordinator: SecretsSetupRecoveryKeyCoordinat
init(recoveryService: MXRecoveryService,
passphrase: String?,
passphraseOnly: Bool) {
let secretsSetupRecoveryKeyViewModel = SecretsSetupRecoveryKeyViewModel(recoveryService: recoveryService, passphrase: passphrase, passphraseOnly: passphraseOnly)
passphraseOnly: Bool,
allowOverwrite: Bool = false) {
let secretsSetupRecoveryKeyViewModel = SecretsSetupRecoveryKeyViewModel(recoveryService: recoveryService, passphrase: passphrase, passphraseOnly: passphraseOnly, allowOverwrite: allowOverwrite)
let secretsSetupRecoveryKeyViewController = SecretsSetupRecoveryKeyViewController.instantiate(with: secretsSetupRecoveryKeyViewModel)
self.secretsSetupRecoveryKeyViewModel = secretsSetupRecoveryKeyViewModel
self.secretsSetupRecoveryKeyViewController = secretsSetupRecoveryKeyViewController
@@ -27,6 +27,7 @@ final class SecretsSetupRecoveryKeyViewModel: SecretsSetupRecoveryKeyViewModelTy
private let recoveryService: MXRecoveryService
private let passphrase: String?
private let passphraseOnly: Bool
private let allowOverwrite: Bool
// MARK: Public
@@ -35,10 +36,11 @@ final class SecretsSetupRecoveryKeyViewModel: SecretsSetupRecoveryKeyViewModelTy
// MARK: - Setup
init(recoveryService: MXRecoveryService, passphrase: String?, passphraseOnly: Bool) {
init(recoveryService: MXRecoveryService, passphrase: String?, passphraseOnly: Bool, allowOverwrite: Bool = false) {
self.recoveryService = recoveryService
self.passphrase = passphrase
self.passphraseOnly = passphraseOnly
self.allowOverwrite = allowOverwrite
}
// MARK: - Public
@@ -61,6 +63,16 @@ final class SecretsSetupRecoveryKeyViewModel: SecretsSetupRecoveryKeyViewModelTy
private func createSecureKey() {
self.update(viewState: .loading)
if allowOverwrite && self.recoveryService.hasRecovery() {
MXLog.debug("[SecretsSetupRecoveryKeyViewModel] createSecureKey: Overwrite existing secure backup")
self.recoveryService.deleteRecovery(withDeleteServicesBackups: true) {
self.createSecureKey()
} failure: { error in
self.update(viewState: .error(error))
}
return
}
self.recoveryService.createRecovery(forSecrets: nil, withPassphrase: self.passphrase, createServicesBackups: true, success: { secretStorageKeyCreationInfo in
self.update(viewState: .recoveryCreated(secretStorageKeyCreationInfo.recoveryKey))
@@ -30,6 +30,7 @@ final class SecureBackupSetupCoordinator: SecureBackupSetupCoordinatorType {
private let recoveryService: MXRecoveryService
private let keyBackup: MXKeyBackup?
private let checkKeyBackup: Bool
private let allowOverwrite: Bool
// MARK: Public
@@ -45,11 +46,12 @@ final class SecureBackupSetupCoordinator: SecureBackupSetupCoordinatorType {
/// - session: The MXSession.
/// - checkKeyBackup: Indicate false to ignore existing key backup.
/// - navigationRouter: Use existing navigation router to plug this flow or let nil to use new one.
init(session: MXSession, checkKeyBackup: Bool = true, navigationRouter: NavigationRouterType? = nil) {
init(session: MXSession, checkKeyBackup: Bool = true, allowOverwrite: Bool = false, navigationRouter: NavigationRouterType? = nil) {
self.session = session
self.recoveryService = session.crypto.recoveryService
self.keyBackup = session.crypto.backup
self.checkKeyBackup = checkKeyBackup
self.allowOverwrite = allowOverwrite
if let navigationRouter = navigationRouter {
self.navigationRouter = navigationRouter
@@ -85,7 +87,7 @@ final class SecureBackupSetupCoordinator: SecureBackupSetupCoordinatorType {
}
private func showSetupKey(passphraseOnly: Bool, passphrase: String? = nil) {
let coordinator = SecretsSetupRecoveryKeyCoordinator(recoveryService: self.recoveryService, passphrase: passphrase, passphraseOnly: passphraseOnly)
let coordinator = SecretsSetupRecoveryKeyCoordinator(recoveryService: self.recoveryService, passphrase: passphrase, passphraseOnly: passphraseOnly, allowOverwrite: allowOverwrite)
coordinator.delegate = self
coordinator.start()
@@ -33,6 +33,7 @@ final class SecureBackupSetupCoordinatorBridgePresenter: NSObject {
// MARK: Private
private let session: MXSession
private let allowOverwrite: Bool
private var coordinator: SecureBackupSetupCoordinator?
// MARK: Public
@@ -40,9 +41,10 @@ final class SecureBackupSetupCoordinatorBridgePresenter: NSObject {
weak var delegate: SecureBackupSetupCoordinatorBridgePresenterDelegate?
// MARK: - Setup
init(session: MXSession) {
init(session: MXSession, allowOverwrite: Bool) {
self.session = session
self.allowOverwrite = allowOverwrite
super.init()
}
@@ -54,7 +56,7 @@ final class SecureBackupSetupCoordinatorBridgePresenter: NSObject {
// }
func present(from viewController: UIViewController, animated: Bool) {
let secureBackupSetupCoordinator = SecureBackupSetupCoordinator(session: self.session)
let secureBackupSetupCoordinator = SecureBackupSetupCoordinator(session: self.session, allowOverwrite: self.allowOverwrite)
secureBackupSetupCoordinator.delegate = self
viewController.present(secureBackupSetupCoordinator.toPresentable(), animated: animated, completion: nil)
secureBackupSetupCoordinator.start()
@@ -0,0 +1,336 @@
/*
Copyright 2021 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import UIKit
@objc protocol SettingsSecureBackupTableViewSectionDelegate: class {
// Table view rendering
func settingsSecureBackupTableViewSectionDidUpdate(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection)
func settingsSecureBackupTableViewSection(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection, textCellForRow: Int) -> MXKTableViewCellWithTextView
func settingsSecureBackupTableViewSection(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection, descriptionCellForRow: Int) -> MXKTableViewCell
func settingsSecureBackupTableViewSection(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection, buttonCellForRow: Int) -> MXKTableViewCellWithButton
// Secure backup
func settingsSecureBackupTableViewSectionShowSecureBackupReset(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection)
// Key backup
func settingsSecureBackupTableViewSectionShowKeyBackupCreate(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection)
func settingsSecureBackupTableViewSection(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection, showKeyBackupRecover keyBackupVersion: MXKeyBackupVersion)
func settingsSecureBackupTableViewSection(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection, showKeyBackupDeleteConfirm keyBackupVersion: MXKeyBackupVersion)
// Life cycle
func settingsSecureBackupTableViewSection(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection, showActivityIndicator show: Bool)
func settingsSecureBackupTableViewSection(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection, showError error: Error)
}
private enum BackupRows {
case info(text: String)
case description(text: String)
case createSecureBackupAction
case resetSecureBackupAction
case createKeyBackupAction
case restoreFromKeyBackupAction(keyBackupVersion: MXKeyBackupVersion, title: String)
case deleteKeyBackupAction(keyBackupVersion: MXKeyBackupVersion)
}
/// SettingsSecureBackupTableViewSection provides UITableViewCells to manage secure backup and key backup.
///
/// All states are described in SettingsSecureBackupViewState.
/// All actions in SettingsSecureBackupViewAction.
@objc final class SettingsSecureBackupTableViewSection: NSObject {
// MARK: - Properties
@objc weak var delegate: SettingsSecureBackupTableViewSectionDelegate?
// MARK: Private
// This view class holds the model because the model is in pure Swift
// whereas this class can be used from objC
private var viewModel: SettingsSecureBackupViewModelType!
// Need to know the state to make `cellForRow` deliver cells accordingly
private var viewState: SettingsSecureBackupViewState = .loading {
didSet {
self.updateBackupRows()
}
}
private var userDevice: MXDeviceInfo
private var backupRows: [BackupRows] = []
// MARK: - Public
@objc init(withRecoveryService recoveryService: MXRecoveryService, keyBackup: MXKeyBackup, userDevice: MXDeviceInfo) {
self.viewModel = SettingsSecureBackupViewModel(recoveryService: recoveryService, keyBackup: keyBackup)
self.userDevice = userDevice
super.init()
self.viewModel.viewDelegate = self
self.viewModel.process(viewAction: .load)
}
@objc func numberOfRows() -> Int {
return self.backupRows.count
}
@objc func cellForRow(atRow row: Int) -> UITableViewCell {
let backupRow = self.backupRows[row]
var cell: UITableViewCell
switch backupRow {
case .info(let text):
cell = self.textCell(atRow: row, text: text)
case .description(let text):
cell = self.descriptionCell(atRow: row, text: text)
case .createSecureBackupAction:
cell = self.buttonCellForCreateSecureBackup(atRow: row)
case .resetSecureBackupAction:
cell = self.buttonCellForResetSecureBackup(atRow: row)
case .createKeyBackupAction:
cell = self.buttonCellForCreateKeyBackup(atRow: row)
case .restoreFromKeyBackupAction(keyBackupVersion: let keyBackupVersion, let title):
cell = self.buttonCellForRestoreFromKeyBackup(keyBackupVersion: keyBackupVersion, title: title, atRow: row)
case .deleteKeyBackupAction(keyBackupVersion: let keyBackupVersion):
cell = self.buttonCellForDeleteKeyBackup(keyBackupVersion: keyBackupVersion, atRow: row)
}
return cell
}
@objc func reload() {
self.viewModel.process(viewAction: .load)
}
@objc func deleteKeyBackup(keyBackupVersion: MXKeyBackupVersion) {
self.viewModel.process(viewAction: .deleteKeyBackup(keyBackupVersion))
}
// MARK: - Data Computing
private func updateBackupRows() {
let backupRows: [BackupRows]
switch self.viewState {
case .loading:
backupRows = [
.info(text: VectorL10n.securitySettingsSecureBackupInfoChecking),
.description(text: VectorL10n.securitySettingsSecureBackupDescription)
]
case .noSecureBackup(let keyBackupState):
switch keyBackupState {
case .noKeyBackup:
let noBackup = VectorL10n.settingsKeyBackupInfoNone
let signoutWarning = VectorL10n.settingsKeyBackupInfoSignoutWarning
let infoText = [noBackup, signoutWarning].joined(separator: "\n")
backupRows = [
.info(text: infoText),
.createSecureBackupAction,
.description(text: VectorL10n.securitySettingsSecureBackupDescription)
]
case .keyBackup(let keyBackupVersion, _, _),
.keyBackupNotTrusted(let keyBackupVersion, _): // Manage the key backup in the same way for the moment
backupRows = [
.info(text: VectorL10n.securitySettingsSecureBackupInfoValid),
.restoreFromKeyBackupAction(keyBackupVersion: keyBackupVersion, title: VectorL10n.securitySettingsSecureBackupRestore),
.deleteKeyBackupAction(keyBackupVersion: keyBackupVersion),
.description(text: VectorL10n.securitySettingsSecureBackupDescription)
]
}
case .secureBackup(let keyBackupState):
switch keyBackupState {
case .noKeyBackup:
let noBackup = VectorL10n.settingsKeyBackupInfoNone
let signoutWarning = VectorL10n.settingsKeyBackupInfoSignoutWarning
let infoText = [noBackup, signoutWarning].joined(separator: "\n")
backupRows = [
.info(text: infoText),
.createKeyBackupAction,
.resetSecureBackupAction,
.description(text: VectorL10n.securitySettingsSecureBackupDescription)
]
case .keyBackup(let keyBackupVersion, _, _),
.keyBackupNotTrusted(let keyBackupVersion, _): // Manage the key backup in the same way for the moment
backupRows = [
.info(text: VectorL10n.securitySettingsSecureBackupInfoValid),
.restoreFromKeyBackupAction(keyBackupVersion: keyBackupVersion, title: VectorL10n.securitySettingsSecureBackupRestore),
.deleteKeyBackupAction(keyBackupVersion: keyBackupVersion),
.resetSecureBackupAction,
.description(text: VectorL10n.securitySettingsSecureBackupDescription)
]
}
}
self.backupRows = backupRows
}
// MARK: - Cells -
private func textCell(atRow row: Int, text: String) -> UITableViewCell {
guard let delegate = self.delegate else {
return UITableViewCell()
}
let cell = delegate.settingsSecureBackupTableViewSection(self, textCellForRow: row)
cell.mxkTextView.text = text
return cell
}
private func descriptionCell(atRow row: Int, text: String) -> UITableViewCell {
guard let delegate = self.delegate else {
return UITableViewCell()
}
let cell = delegate.settingsSecureBackupTableViewSection(self, descriptionCellForRow: row)
cell.textLabel?.text = text
return cell
}
// MARK: - Button cells
private func buttonCellForCreateSecureBackup(atRow row: Int) -> UITableViewCell {
guard let delegate = self.delegate else {
return UITableViewCell()
}
let cell: MXKTableViewCellWithButton = delegate.settingsSecureBackupTableViewSection(self, buttonCellForRow: row)
let btnTitle = VectorL10n.securitySettingsSecureBackupSetup
cell.mxkButton.setTitle(btnTitle, for: .normal)
cell.mxkButton.setTitle(btnTitle, for: .highlighted)
cell.mxkButton.vc_addAction {
self.viewModel.process(viewAction: .createSecureBackup)
}
return cell
}
private func buttonCellForResetSecureBackup(atRow row: Int) -> UITableViewCell {
guard let delegate = self.delegate else {
return UITableViewCell()
}
let cell: MXKTableViewCellWithButton = delegate.settingsSecureBackupTableViewSection(self, buttonCellForRow: row)
let btnTitle = VectorL10n.securitySettingsSecureBackupReset
cell.mxkButton.setTitle(btnTitle, for: .normal)
cell.mxkButton.setTitle(btnTitle, for: .highlighted)
cell.mxkButton.tintColor = ThemeService.shared().theme.warningColor
cell.mxkButton.vc_addAction {
self.viewModel.process(viewAction: .resetSecureBackup)
}
return cell
}
private func buttonCellForCreateKeyBackup(atRow row: Int) -> UITableViewCell {
guard let delegate = self.delegate else {
return UITableViewCell()
}
let cell: MXKTableViewCellWithButton = delegate.settingsSecureBackupTableViewSection(self, buttonCellForRow: row)
let btnTitle = VectorL10n.securitySettingsSecureBackupSetup
cell.mxkButton.setTitle(btnTitle, for: .normal)
cell.mxkButton.setTitle(btnTitle, for: .highlighted)
cell.mxkButton.vc_addAction {
self.viewModel.process(viewAction: .createKeyBackup)
}
return cell
}
private func buttonCellForRestoreFromKeyBackup(keyBackupVersion: MXKeyBackupVersion, title: String, atRow row: Int) -> UITableViewCell {
guard let delegate = self.delegate else {
return UITableViewCell()
}
let cell: MXKTableViewCellWithButton = delegate.settingsSecureBackupTableViewSection(self, buttonCellForRow: row)
cell.mxkButton.setTitle(title, for: .normal)
cell.mxkButton.setTitle(title, for: .highlighted)
cell.mxkButton.vc_addAction {
self.viewModel.process(viewAction: .restoreFromKeyBackup(keyBackupVersion))
}
return cell
}
private func buttonCellForDeleteKeyBackup(keyBackupVersion: MXKeyBackupVersion, atRow row: Int) -> UITableViewCell {
guard let delegate = self.delegate else {
return UITableViewCell()
}
let cell: MXKTableViewCellWithButton = delegate.settingsSecureBackupTableViewSection(self, buttonCellForRow: row)
let btnTitle = VectorL10n.securitySettingsSecureBackupDelete
cell.mxkButton.setTitle(btnTitle, for: .normal)
cell.mxkButton.setTitle(btnTitle, for: .highlighted)
cell.mxkButton.tintColor = ThemeService.shared().theme.warningColor
cell.mxkButton.vc_addAction {
self.viewModel.process(viewAction: .confirmDeleteKeyBackup(keyBackupVersion))
}
return cell
}
}
// MARK: - KeyBackupSetupRecoveryKeyViewModelViewDelegate
extension SettingsSecureBackupTableViewSection: SettingsSecureBackupViewModelViewDelegate {
func settingsSecureBackupViewModel(_ viewModel: SettingsSecureBackupViewModelType, didUpdateViewState viewState: SettingsSecureBackupViewState) {
self.viewState = viewState
// The tableview datasource will call `self.cellForRow()`
self.delegate?.settingsSecureBackupTableViewSectionDidUpdate(self)
}
func settingsSecureBackupViewModel(_ viewModel: SettingsSecureBackupViewModelType, didUpdateNetworkRequestViewState networkRequestViewSate: SettingsSecureBackupNetworkRequestViewState) {
switch networkRequestViewSate {
case .loading:
self.delegate?.settingsSecureBackupTableViewSection(self, showActivityIndicator: true)
case .loaded:
self.delegate?.settingsSecureBackupTableViewSection(self, showActivityIndicator: false)
case .error(let error):
self.delegate?.settingsSecureBackupTableViewSection(self, showError: error)
}
}
func settingsSecureBackupViewModelShowSecureBackupReset(_ viewModel: SettingsSecureBackupViewModelType) {
self.delegate?.settingsSecureBackupTableViewSectionShowSecureBackupReset(self)
}
func settingsSecureBackupViewModelShowKeyBackupCreate(_ viewModel: SettingsSecureBackupViewModelType) {
self.delegate?.settingsSecureBackupTableViewSectionShowKeyBackupCreate(self)
}
func settingsSecureBackupViewModel(_ viewModel: SettingsSecureBackupViewModelType, showKeyBackupRecover keyBackupVersion: MXKeyBackupVersion) {
self.delegate?.settingsSecureBackupTableViewSection(self, showKeyBackupRecover: keyBackupVersion)
}
func settingsSecureBackupViewModel(_ viewModel: SettingsSecureBackupViewModelType, showKeyBackupDeleteConfirm keyBackupVersion: MXKeyBackupVersion) {
self.delegate?.settingsSecureBackupTableViewSection(self, showKeyBackupDeleteConfirm: keyBackupVersion)
}
}
@@ -0,0 +1,27 @@
/*
Copyright 2021 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import UIKit
enum SettingsSecureBackupViewAction {
case load
case createSecureBackup
case resetSecureBackup
case createKeyBackup
case restoreFromKeyBackup(MXKeyBackupVersion)
case confirmDeleteKeyBackup(MXKeyBackupVersion)
case deleteKeyBackup(MXKeyBackupVersion)
}
@@ -0,0 +1,154 @@
/*
Copyright 2021 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import UIKit
final class SettingsSecureBackupViewModel: SettingsSecureBackupViewModelType {
// MARK: - Properties
weak var viewDelegate: SettingsSecureBackupViewModelViewDelegate?
// MARK: Private
private let recoveryService: MXRecoveryService
private let keyBackup: MXKeyBackup
init(recoveryService: MXRecoveryService, keyBackup: MXKeyBackup) {
self.recoveryService = recoveryService
self.keyBackup = keyBackup
self.registerKeyBackupVersionDidChangeStateNotification()
}
private func registerKeyBackupVersionDidChangeStateNotification() {
NotificationCenter.default.addObserver(self, selector: #selector(keyBackupDidStateChange), name: NSNotification.Name.mxKeyBackupDidStateChange, object: self.keyBackup)
}
@objc private func keyBackupDidStateChange() {
self.checkKeyBackupState()
}
func process(viewAction: SettingsSecureBackupViewAction) {
guard let viewDelegate = self.viewDelegate else {
return
}
switch viewAction {
case .load:
viewDelegate.settingsSecureBackupViewModel(self, didUpdateViewState: .loading)
self.checkKeyBackupState()
case .resetSecureBackup,
.createSecureBackup: // The implement supports both
viewDelegate.settingsSecureBackupViewModelShowSecureBackupReset(self)
case .createKeyBackup:
viewDelegate.settingsSecureBackupViewModelShowKeyBackupCreate(self)
case .restoreFromKeyBackup(let keyBackupVersion):
viewDelegate.settingsSecureBackupViewModel(self, showKeyBackupRecover: keyBackupVersion)
case .confirmDeleteKeyBackup(let keyBackupVersion):
viewDelegate.settingsSecureBackupViewModel(self, showKeyBackupDeleteConfirm: keyBackupVersion)
case .deleteKeyBackup(let keyBackupVersion):
self.deleteKeyBackupVersion(keyBackupVersion)
}
}
// MARK: - Private
private func checkKeyBackupState() {
// Check homeserver update in background
self.keyBackup.forceRefresh(nil, failure: nil)
if let keyBackupVersion = self.keyBackup.keyBackupVersion {
self.keyBackup.trust(for: keyBackupVersion, onComplete: { [weak self] (keyBackupVersionTrust) in
guard let sself = self else {
return
}
sself.computeState(withBackupVersionTrust: keyBackupVersionTrust)
})
} else {
computeState()
}
}
private func computeState(withBackupVersionTrust keyBackupVersionTrust: MXKeyBackupVersionTrust? = nil) {
var viewState: SettingsSecureBackupViewState?
var keyBackupState: SettingsSecureBackupViewState.KeyBackupState?
switch self.keyBackup.state {
case MXKeyBackupStateUnknown,
MXKeyBackupStateCheckingBackUpOnHomeserver:
viewState = .loading
case MXKeyBackupStateDisabled, MXKeyBackupStateEnabling:
keyBackupState = .noKeyBackup
case MXKeyBackupStateNotTrusted:
guard let keyBackupVersion = self.keyBackup.keyBackupVersion, let keyBackupVersionTrust = keyBackupVersionTrust else {
return
}
keyBackupState = .keyBackupNotTrusted(keyBackupVersion, keyBackupVersionTrust)
case MXKeyBackupStateReadyToBackUp, MXKeyBackupStateWillBackUp, MXKeyBackupStateBackingUp:
guard let keyBackupVersion = self.keyBackup.keyBackupVersion, let keyBackupVersionTrust = keyBackupVersionTrust else {
return
}
// Get the backup progress before updating the state
self.keyBackup.backupProgress { [weak self] (progress) in
guard let self = self else {
return
}
let keyBackupState: SettingsSecureBackupViewState.KeyBackupState = .keyBackup(keyBackupVersion, keyBackupVersionTrust, progress)
let viewState: SettingsSecureBackupViewState = self.recoveryService.hasRecovery() ? .secureBackup(keyBackupState) : .noSecureBackup(keyBackupState)
self.viewDelegate?.settingsSecureBackupViewModel(self, didUpdateViewState: viewState)
}
default:
break
}
// Turn secure backup and key back states into view state
if let keyBackupState = keyBackupState {
viewState = recoveryService.hasRecovery() ? .secureBackup(keyBackupState) : .noSecureBackup(keyBackupState)
}
if let viewState = viewState {
self.viewDelegate?.settingsSecureBackupViewModel(self, didUpdateViewState: viewState)
}
}
private func deleteKeyBackupVersion(_ keyBackupVersion: MXKeyBackupVersion) {
guard let keyBackupVersionVersion = keyBackupVersion.version else {
return
}
self.viewDelegate?.settingsSecureBackupViewModel(self, didUpdateNetworkRequestViewState: .loading)
self.keyBackup.deleteVersion(keyBackupVersionVersion, success: { [weak self] () in
guard let sself = self else {
return
}
sself.viewDelegate?.settingsSecureBackupViewModel(sself, didUpdateNetworkRequestViewState: .loaded)
}, failure: { [weak self] error in
guard let sself = self else {
return
}
sself.viewDelegate?.settingsSecureBackupViewModel(sself, didUpdateNetworkRequestViewState: .error(error))
})
}
}
@@ -0,0 +1,35 @@
/*
Copyright 2021 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import UIKit
protocol SettingsSecureBackupViewModelViewDelegate: class {
func settingsSecureBackupViewModel(_ viewModel: SettingsSecureBackupViewModelType, didUpdateViewState viewState: SettingsSecureBackupViewState)
func settingsSecureBackupViewModel(_ viewModel: SettingsSecureBackupViewModelType, didUpdateNetworkRequestViewState networkRequestViewSate: SettingsSecureBackupNetworkRequestViewState)
func settingsSecureBackupViewModelShowSecureBackupReset(_ viewModel: SettingsSecureBackupViewModelType)
func settingsSecureBackupViewModelShowKeyBackupCreate(_ viewModel: SettingsSecureBackupViewModelType)
func settingsSecureBackupViewModel(_ viewModel: SettingsSecureBackupViewModelType, showKeyBackupRecover keyBackupVersion: MXKeyBackupVersion)
func settingsSecureBackupViewModel(_ viewModel: SettingsSecureBackupViewModelType, showKeyBackupDeleteConfirm keyBackupVersion: MXKeyBackupVersion)
}
protocol SettingsSecureBackupViewModelType {
var viewDelegate: SettingsSecureBackupViewModelViewDelegate? { get set }
func process(viewAction: SettingsSecureBackupViewAction)
}
@@ -0,0 +1,49 @@
/*
Copyright 2021 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import UIKit
/// State of the Secure Backup section in securtiy settings.
///
/// It is a mixed of the state of the Secure Backup(4S) and the state of the Key Backup.
///
/// - loading: Load current state
/// - noSecureBackup: The account has no secure backup
/// - secureBackup: The account has a secure backup
enum SettingsSecureBackupViewState {
case loading
case noSecureBackup(KeyBackupState)
case secureBackup(KeyBackupState)
/// Internal key backup state. It is independent from the secure backup state.
///
/// - noKeyBackup: There is no backup on the homeserver
/// - keyBackup: There is a valid running backup on the homeserver. Keys are being sent to it
/// - keyBackupNotTrusted: There is a backup on the homeserver but it is not trusted
enum KeyBackupState {
case noKeyBackup
case keyBackup(MXKeyBackupVersion, MXKeyBackupVersionTrust, Progress)
case keyBackupNotTrusted(MXKeyBackupVersion, MXKeyBackupVersionTrust)
}
}
/// State representing a network request made by the module
/// Only SettingsSecureBackupViewAction.delete generates such states
enum SettingsSecureBackupNetworkRequestViewState {
case loading
case loaded
case error(Error)
}
@@ -36,9 +36,9 @@ enum
SECTION_PIN_CODE,
SECTION_CRYPTO_SESSIONS,
SECTION_SECURE_BACKUP,
SECTION_CROSSSIGNING,
SECTION_CRYPTOGRAPHY,
#ifdef CROSS_SIGNING_AND_BACKUP_DEV
SECTION_CROSSSIGNING,
SECTION_KEYBACKUP,
#endif
SECTION_ADVANCED,
@@ -51,23 +51,6 @@ enum {
CROSSSIGNING_SECOND_ACTION, // Reset
};
enum {
SECURE_BACKUP_DESCRIPTION,
// TODO: We can display the state of 4S both locally and on the server. Then, provide actions according to all combinations.
// - Does the 4S contains all the 4 keys server side?
// - Advice the user to do a recovery if there is less keys locally
// - Advice them to do a recovery if local keys are obsolete -> We cannot know now
// - Advice them to fix a secure backup if there is 4S but no key backup
// - Warm them if there is no 4S and they do not have all 3 signing keys locally. They will set up a not complete secure backup
#ifdef CROSS_SIGNING_AND_BACKUP_DEV
SECURE_BACKUP_INFO,
#endif
SECURE_BACKUP_SETUP,
SECURE_BACKUP_RESTORE,
SECURE_BACKUP_DELETE,
SECURE_BACKUP_MANAGE_MANUALLY, // TODO: What to do with that?
};
enum {
PIN_CODE_SETTING,
PIN_CODE_DESCRIPTION,
@@ -90,9 +73,10 @@ enum {
@interface SecurityViewController () <
SettingsSecureBackupTableViewSectionDelegate,
KeyBackupSetupCoordinatorBridgePresenterDelegate,
#ifdef CROSS_SIGNING_AND_BACKUP_DEV
SettingsKeyBackupTableViewSectionDelegate,
KeyBackupSetupCoordinatorBridgePresenterDelegate,
KeyBackupRecoverCoordinatorBridgePresenterDelegate,
#endif
UIDocumentInteractionControllerDelegate,
@@ -107,9 +91,6 @@ TableViewSectionsDelegate>
// Devices
NSMutableArray<MXDevice *> *devicesArray;
// SECURE_BACKUP_* rows to display
NSArray<NSNumber *> *secureBackupSectionState;
// Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change.
id kThemeServiceDidChangeThemeNotificationObserver;
@@ -123,13 +104,15 @@ TableViewSectionsDelegate>
// The current pushed view controller
UIViewController *pushedViewController;
SettingsSecureBackupTableViewSection *secureBackupSection;
#ifdef CROSS_SIGNING_AND_BACKUP_DEV
SettingsKeyBackupTableViewSection *keyBackupSection;
KeyBackupSetupCoordinatorBridgePresenter *keyBackupSetupCoordinatorBridgePresenter;
#endif
KeyBackupSetupCoordinatorBridgePresenter *keyBackupSetupCoordinatorBridgePresenter;
KeyBackupRecoverCoordinatorBridgePresenter *keyBackupRecoverCoordinatorBridgePresenter;
SecretsRecoveryCoordinatorBridgePresenter *secretsRecoveryCoordinatorBridgePresenter;
}
@@ -183,7 +166,6 @@ TableViewSectionsDelegate>
self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 50;
#ifdef CROSS_SIGNING_AND_BACKUP_DEV
if (self.mainSession.crypto.backup)
{
MXDeviceInfo *deviceInfo = [self.mainSession.crypto.deviceList storedDevice:self.mainSession.matrixRestClient.credentials.userId
@@ -191,11 +173,15 @@ TableViewSectionsDelegate>
if (deviceInfo)
{
secureBackupSection = [[SettingsSecureBackupTableViewSection alloc] initWithRecoveryService:self.mainSession.crypto.recoveryService keyBackup:self.mainSession.crypto.backup userDevice:deviceInfo];
secureBackupSection.delegate = self;
#ifdef CROSS_SIGNING_AND_BACKUP_DEV
keyBackupSection = [[SettingsKeyBackupTableViewSection alloc] initWithKeyBackup:self.mainSession.crypto.backup userDevice:deviceInfo];
keyBackupSection.delegate = self;
#endif
}
}
#endif
// Observe user interface theme change.
kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
@@ -258,11 +244,8 @@ TableViewSectionsDelegate>
kThemeServiceDidChangeThemeNotificationObserver = nil;
}
#ifdef CROSS_SIGNING_AND_BACKUP_DEV
keyBackupSetupCoordinatorBridgePresenter = nil;
#endif
keyBackupRecoverCoordinatorBridgePresenter = nil;
}
- (void)viewWillAppear:(BOOL)animated
@@ -355,13 +338,19 @@ TableViewSectionsDelegate>
Section *secureBackupSection = [Section sectionWithTag:SECTION_SECURE_BACKUP];
secureBackupSection.headerTitle = NSLocalizedStringFromTable(@"security_settings_secure_backup", @"Vector", nil);
[secureBackupSection addRowsWithCount:[self numberOfRowsInSecureBackupSection]];
if (secureBackupSection.rows.count)
{
[sections addObject:secureBackupSection];
}
[secureBackupSection addRowsWithCount:self->secureBackupSection.numberOfRows];
[sections addObject:secureBackupSection];
// Cross-Signing
Section *crossSigningSection = [Section sectionWithTag:SECTION_CROSSSIGNING];
crossSigningSection.headerTitle = NSLocalizedStringFromTable(@"security_settings_crosssigning", @"Vector", nil);
[crossSigningSection addRowsWithCount:[self numberOfRowsInCrossSigningSection]];
[sections addObject:crossSigningSection];
// Cryptograhpy
@@ -385,15 +374,6 @@ TableViewSectionsDelegate>
#ifdef CROSS_SIGNING_AND_BACKUP_DEV
// Cross-Signing
Section *crossSigningSection = [Section sectionWithTag:SECTION_CROSSSIGNING];
crossSigningSection.headerTitle = NSLocalizedStringFromTable(@"security_settings_crosssigning", @"Vector", nil);
[crossSigningSection addRowsWithCount:[self numberOfRowsInCrossSigningSection]];
[sections addObject:crossSigningSection];
// Keybackup
Section *keybackupSection = [Section sectionWithTag:SECTION_KEYBACKUP];
@@ -582,8 +562,6 @@ TableViewSectionsDelegate>
- (void)reloadData
{
[self refreshSecureBackupSectionData];
// Update table view sections and trigger a tableView reloadData
[self updateSections];
}
@@ -874,56 +852,6 @@ TableViewSectionsDelegate>
#pragma mark - SSSS
- (void)refreshSecureBackupSectionData
{
MXRecoveryService *recoveryService = self.mainSession.crypto.recoveryService;
NSMutableArray *secureBackupSectionState = [NSMutableArray new];
if (recoveryService.hasRecovery)
{
if (RiotSettings.shared.settingsSecurityScreenShowRestoreBackup)
{
[secureBackupSectionState addObject:@(SECURE_BACKUP_RESTORE)];
}
if (RiotSettings.shared.settingsSecurityScreenShowDeleteBackup)
{
[secureBackupSectionState addObject:@(SECURE_BACKUP_DELETE)];
}
}
else
{
if (RiotSettings.shared.settingsSecurityScreenShowSetupBackup)
{
[secureBackupSectionState addObject:@(SECURE_BACKUP_SETUP)];
}
}
if (secureBackupSectionState.count)
{
[secureBackupSectionState addObject:@(SECURE_BACKUP_DESCRIPTION)];
}
#ifdef CROSS_SIGNING_AND_BACKUP_DEV
[secureBackupSectionState addObject:@(SECURE_BACKUP_INFO)];
#endif
self->secureBackupSectionState = secureBackupSectionState;
}
- (NSUInteger)secureBackupSectionEnumForRow:(NSUInteger)row
{
if (row < secureBackupSectionState.count)
{
return secureBackupSectionState[row].unsignedIntegerValue;
}
return SECURE_BACKUP_DESCRIPTION;
}
- (NSUInteger)numberOfRowsInSecureBackupSection
{
return secureBackupSectionState.count;
}
- (NSString*)secureBackupInformation
{
NSString *secureBackupInformation;
@@ -1060,7 +988,7 @@ TableViewSectionsDelegate>
- (void)setupSecureBackup2
{
SecureBackupSetupCoordinatorBridgePresenter *secureBackupSetupCoordinatorBridgePresenter = [[SecureBackupSetupCoordinatorBridgePresenter alloc] initWithSession:self.mainSession];
SecureBackupSetupCoordinatorBridgePresenter *secureBackupSetupCoordinatorBridgePresenter = [[SecureBackupSetupCoordinatorBridgePresenter alloc] initWithSession:self.mainSession allowOverwrite:YES];
secureBackupSetupCoordinatorBridgePresenter.delegate = self;
[secureBackupSetupCoordinatorBridgePresenter presentFrom:self animated:YES];
@@ -1068,32 +996,6 @@ TableViewSectionsDelegate>
self.secureBackupSetupCoordinatorBridgePresenter = secureBackupSetupCoordinatorBridgePresenter;
}
- (void)restoreFromSecureBackup
{
secretsRecoveryCoordinatorBridgePresenter = [[SecretsRecoveryCoordinatorBridgePresenter alloc] initWithSession:self.mainSession recoveryGoal:SecretsRecoveryGoalRestoreSecureBackup];
[secretsRecoveryCoordinatorBridgePresenter presentFrom:self animated:true];
secretsRecoveryCoordinatorBridgePresenter.delegate = self;
}
- (void)deleteSecureBackup
{
MXRecoveryService *recoveryService = self.mainSession.crypto.recoveryService;
if (recoveryService)
{
[self startActivityIndicator];
[recoveryService deleteRecoveryWithDeleteServicesBackups:YES success:^{
[self stopActivityIndicator];
[self reloadData];
} failure:^(NSError * _Nonnull error) {
[self stopActivityIndicator];
[self reloadData];
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
}
}
#pragma mark - Segues
@@ -1228,6 +1130,17 @@ TableViewSectionsDelegate>
return textViewCell;
}
- (MXKTableViewCell*)descriptionCellForTableView:(UITableView*)tableView atIndexPath:(NSIndexPath *)indexPath
{
MXKTableViewCell *cell = [self getDefaultTableViewCell:tableView];
cell.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
cell.textLabel.numberOfLines = 0;
cell.contentView.backgroundColor = ThemeService.shared.theme.headerBackgroundColor;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}
- (MXKTableViewCellWithButton *)buttonCellForTableView:(UITableView*)tableView atIndexPath:(NSIndexPath *)indexPath
{
MXKTableViewCellWithButton *cell = [self.tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithButton defaultReuseIdentifier] forIndexPath:indexPath];
@@ -1360,71 +1273,14 @@ TableViewSectionsDelegate>
}
else if (section == SECTION_SECURE_BACKUP)
{
switch ([self secureBackupSectionEnumForRow:row])
{
case SECURE_BACKUP_DESCRIPTION:
{
cell = [self descriptionCellForTableView:tableView
withText:NSLocalizedStringFromTable(@"security_settings_secure_backup_description", @"Vector", nil)];
break;
}
#ifdef CROSS_SIGNING_AND_BACKUP_DEV
case SECURE_BACKUP_INFO:
{
cell = [self descriptionCellForTableView:tableView
withText:self.secureBackupInformation];
break;
}
#endif
case SECURE_BACKUP_SETUP:
{
MXKTableViewCellWithButton *buttonCell = [self buttonCellWithTitle:NSLocalizedStringFromTable(@"security_settings_secure_backup_setup", @"Vector", nil)
action:@selector(setupSecureBackup)
forTableView:tableView
atIndexPath:indexPath];
cell = buttonCell;
break;
}
case SECURE_BACKUP_RESTORE:
{
MXKTableViewCellWithButton *buttonCell = [self buttonCellWithTitle:NSLocalizedStringFromTable(@"security_settings_secure_backup_synchronise", @"Vector", nil)
action:@selector(restoreFromSecureBackup)
forTableView:tableView
atIndexPath:indexPath];
cell = buttonCell;
break;
}
case SECURE_BACKUP_DELETE:
{
MXKTableViewCellWithButton *buttonCell = [self buttonCellWithTitle:NSLocalizedStringFromTable(@"security_settings_secure_backup_delete", @"Vector", nil)
action:@selector(deleteSecureBackup)
forTableView:tableView
atIndexPath:indexPath];
buttonCell.mxkButton.tintColor = ThemeService.shared.theme.warningColor;
cell = buttonCell;
break;
}
case SECURE_BACKUP_MANAGE_MANUALLY:
{
MXKTableViewCellWithTextView *textCell = [self textViewCellForTableView:tableView atIndexPath:indexPath];
textCell.mxkTextView.text = @"Advanced: Manually manage keys"; // TODO
[textCell vc_setAccessoryDisclosureIndicatorWithCurrentTheme];
cell = textCell;
break;
}
}
cell = [secureBackupSection cellForRowAtRow:row];
}
#ifdef CROSS_SIGNING_AND_BACKUP_DEV
else if (section == SECTION_KEYBACKUP)
{
cell = [keyBackupSection cellForRowAtRow:row];
}
#endif
else if (section == SECTION_CROSSSIGNING)
{
switch (row)
@@ -1444,7 +1300,6 @@ TableViewSectionsDelegate>
break;
}
}
#endif
else if (section == SECTION_CRYPTOGRAPHY)
{
switch (row)
@@ -1739,6 +1594,155 @@ TableViewSectionsDelegate>
[self.setPinCoordinatorBridgePresenter presentFrom:self animated:YES];
}
#pragma mark - SettingsSecureBackupTableViewSectionDelegate
- (void)settingsSecureBackupTableViewSectionDidUpdate:(SettingsSecureBackupTableViewSection *)settingsSecureBackupTableViewSection
{
[self reloadData];
}
- (MXKTableViewCellWithTextView *)settingsSecureBackupTableViewSection:(SettingsSecureBackupTableViewSection *)settingsSecureBackupTableViewSection textCellForRow:(NSInteger)textCellForRow
{
MXKTableViewCellWithTextView *cell;
NSIndexPath *indexPath = [self.tableViewSections exactIndexPathForRowTag:textCellForRow sectionTag:SECTION_SECURE_BACKUP];
if (indexPath)
{
cell = [self textViewCellForTableView:self.tableView atIndexPath:indexPath];
}
return cell;
}
- (MXKTableViewCell *)settingsSecureBackupTableViewSection:(SettingsSecureBackupTableViewSection *)settingsSecureBackupTableViewSection descriptionCellForRow:(NSInteger)textCellForRow
{
MXKTableViewCell *cell;
NSIndexPath *indexPath = [self.tableViewSections exactIndexPathForRowTag:textCellForRow sectionTag:SECTION_SECURE_BACKUP];
if (indexPath)
{
cell = [self descriptionCellForTableView:self.tableView atIndexPath:indexPath];
}
return cell;
}
- (MXKTableViewCellWithButton *)settingsSecureBackupTableViewSection:(SettingsSecureBackupTableViewSection *)settingsSecureBackupTableViewSection buttonCellForRow:(NSInteger)buttonCellForRow
{
MXKTableViewCellWithButton *cell;
NSIndexPath *indexPath = [self.tableViewSections exactIndexPathForRowTag:buttonCellForRow sectionTag:SECTION_SECURE_BACKUP];
if (indexPath)
{
cell = [self buttonCellForTableView:self.tableView atIndexPath:indexPath];
}
return cell;
}
- (void)settingsSecureBackupTableViewSectionShowSecureBackupReset:(SettingsSecureBackupTableViewSection *)settingsSecureBackupTableViewSection
{
[self setupSecureBackup];
}
- (void)settingsSecureBackupTableViewSectionShowKeyBackupCreate:(SettingsSecureBackupTableViewSection *)settingsSecureBackupTableViewSection
{
[self showKeyBackupSetupFromSignOutFlow:NO];
}
- (void)settingsSecureBackupTableViewSection:(SettingsSecureBackupTableViewSection *)settingsSecureBackupTableViewSection showKeyBackupRecover:(MXKeyBackupVersion *)keyBackupVersion
{
self.currentkeyBackupVersion = keyBackupVersion;
// If key backup key is stored in SSSS, ask for secrets recovery before restoring key backup.
if (!self.mainSession.crypto.backup.hasPrivateKeyInCryptoStore
&& self.mainSession.crypto.recoveryService.hasRecovery
&& [self.mainSession.crypto.recoveryService hasSecretWithSecretId:MXSecretId.keyBackup])
{
[self showSecretsRecovery];
}
else
{
[self showKeyBackupRecover:keyBackupVersion fromViewController:self];
}
}
- (void)settingsSecureBackupTableViewSection:(SettingsSecureBackupTableViewSection *)settingsSecureBackupTableViewSection showKeyBackupDeleteConfirm:(MXKeyBackupVersion *)keyBackupVersion
{
MXWeakify(self);
[currentAlert dismissViewControllerAnimated:NO completion:nil];
currentAlert =
[UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"settings_key_backup_delete_confirmation_prompt_title", @"Vector", nil)
message:NSLocalizedStringFromTable(@"settings_key_backup_delete_confirmation_prompt_msg", @"Vector", nil)
preferredStyle:UIAlertControllerStyleAlert];
[currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"]
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
self->currentAlert = nil;
}]];
[currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"settings_key_backup_button_delete", @"Vector", nil)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
self->currentAlert = nil;
[self->secureBackupSection deleteKeyBackupWithKeyBackupVersion:keyBackupVersion];
}]];
[currentAlert mxk_setAccessibilityIdentifier: @"SettingsVCDeleteKeyBackup"];
[self presentViewController:currentAlert animated:YES completion:nil];
}
- (void)settingsSecureBackupTableViewSection:(SettingsSecureBackupTableViewSection *)settingsSecureBackupTableViewSection showActivityIndicator:(BOOL)show
{
if (show)
{
[self startActivityIndicator];
}
else
{
[self stopActivityIndicator];
}
}
- (void)settingsSecureBackupTableViewSection:(SettingsSecureBackupTableViewSection *)settingsSecureBackupTableViewSection showError:(NSError *)error
{
[[AppDelegate theDelegate] showErrorAsAlert:error];
}
#pragma mark - KeyBackupRecoverCoordinatorBridgePresenter
- (void)showKeyBackupSetupFromSignOutFlow:(BOOL)showFromSignOutFlow
{
keyBackupSetupCoordinatorBridgePresenter = [[KeyBackupSetupCoordinatorBridgePresenter alloc] initWithSession:self.mainSession];
[keyBackupSetupCoordinatorBridgePresenter presentFrom:self
isStartedFromSignOut:showFromSignOutFlow
animated:true];
keyBackupSetupCoordinatorBridgePresenter.delegate = self;
}
- (void)keyBackupSetupCoordinatorBridgePresenterDelegateDidCancel:(KeyBackupSetupCoordinatorBridgePresenter *)bridgePresenter {
[keyBackupSetupCoordinatorBridgePresenter dismissWithAnimated:true];
keyBackupSetupCoordinatorBridgePresenter = nil;
}
- (void)keyBackupSetupCoordinatorBridgePresenterDelegateDidSetupRecoveryKey:(KeyBackupSetupCoordinatorBridgePresenter *)bridgePresenter {
[keyBackupSetupCoordinatorBridgePresenter dismissWithAnimated:true];
keyBackupSetupCoordinatorBridgePresenter = nil;
[secureBackupSection reload];
}
#pragma mark - SettingsKeyBackupTableViewSectionDelegate
#ifdef CROSS_SIGNING_AND_BACKUP_DEV
- (void)settingsKeyBackupTableViewSectionDidUpdate:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection
@@ -1768,7 +1772,7 @@ TableViewSectionsDelegate>
if (indexPath)
{
[self buttonCellForTableView:self.tableView atIndexPath:indexPath];
cell = [self buttonCellForTableView:self.tableView atIndexPath:indexPath];
}
return cell;
@@ -1843,31 +1847,6 @@ TableViewSectionsDelegate>
[[AppDelegate theDelegate] showErrorAsAlert:error];
}
#pragma mark - KeyBackupRecoverCoordinatorBridgePresenter
- (void)showKeyBackupSetupFromSignOutFlow:(BOOL)showFromSignOutFlow
{
keyBackupSetupCoordinatorBridgePresenter = [[KeyBackupSetupCoordinatorBridgePresenter alloc] initWithSession:self.mainSession];
[keyBackupSetupCoordinatorBridgePresenter presentFrom:self
isStartedFromSignOut:showFromSignOutFlow
animated:true];
keyBackupSetupCoordinatorBridgePresenter.delegate = self;
}
- (void)keyBackupSetupCoordinatorBridgePresenterDelegateDidCancel:(KeyBackupSetupCoordinatorBridgePresenter *)bridgePresenter {
[keyBackupSetupCoordinatorBridgePresenter dismissWithAnimated:true];
keyBackupSetupCoordinatorBridgePresenter = nil;
}
- (void)keyBackupSetupCoordinatorBridgePresenterDelegateDidSetupRecoveryKey:(KeyBackupSetupCoordinatorBridgePresenter *)bridgePresenter {
[keyBackupSetupCoordinatorBridgePresenter dismissWithAnimated:true];
keyBackupSetupCoordinatorBridgePresenter = nil;
[keyBackupSection reload];
}
#endif
#pragma mark - KeyBackupRecoverCoordinatorBridgePresenter
@@ -1904,7 +1883,7 @@ TableViewSectionsDelegate>
- (void)showSecretsRecovery
{
secretsRecoveryCoordinatorBridgePresenter = [[SecretsRecoveryCoordinatorBridgePresenter alloc] initWithSession:self.mainSession recoveryGoal:SecretsRecoveryGoalKeyBackup];
secretsRecoveryCoordinatorBridgePresenter = [[SecretsRecoveryCoordinatorBridgePresenter alloc] initWithSession:self.mainSession recoveryGoal:SecretsRecoveryGoalBridgeKeyBackup];
[secretsRecoveryCoordinatorBridgePresenter presentFrom:self animated:true];
secretsRecoveryCoordinatorBridgePresenter.delegate = self;
@@ -1920,7 +1899,7 @@ TableViewSectionsDelegate>
{
UIViewController *presentedViewController = [coordinatorBridgePresenter toPresentable];
if (coordinatorBridgePresenter.recoveryGoal == SecretsRecoveryGoalKeyBackup)
if (coordinatorBridgePresenter.recoveryGoal == SecretsRecoveryGoalBridgeKeyBackup)
{
// Go to the true key backup recovery screen
if ([presentedViewController isKindOfClass:UINavigationController.class])
@@ -4023,7 +4023,7 @@ TableViewSectionsDelegate>
- (void)setupSecureBackup2
{
SecureBackupSetupCoordinatorBridgePresenter *secureBackupSetupCoordinatorBridgePresenter = [[SecureBackupSetupCoordinatorBridgePresenter alloc] initWithSession:self.mainSession];
SecureBackupSetupCoordinatorBridgePresenter *secureBackupSetupCoordinatorBridgePresenter = [[SecureBackupSetupCoordinatorBridgePresenter alloc] initWithSession:self.mainSession allowOverwrite:YES];
secureBackupSetupCoordinatorBridgePresenter.delegate = self;
[secureBackupSetupCoordinatorBridgePresenter presentFrom:self animated:YES];