From c9a0a66f18fbdd059aa85a682ce26a271c0ea78b Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Thu, 7 Feb 2019 09:47:06 -0800 Subject: [PATCH] Migrate backup passphrase to be keystore-encrypted when available. --- .../securesms/backup/BackupDialog.java | 4 +- .../securesms/backup/BackupPassphrase.java | 47 +++++++++++++++++++ .../securesms/jobs/LocalBackupJob.java | 3 +- .../securesms/util/TextSecurePreferences.java | 17 +++++-- 4 files changed, 64 insertions(+), 7 deletions(-) create mode 100644 src/org/thoughtcrime/securesms/backup/BackupPassphrase.java diff --git a/src/org/thoughtcrime/securesms/backup/BackupDialog.java b/src/org/thoughtcrime/securesms/backup/BackupDialog.java index 364329049f..14b065ad4a 100644 --- a/src/org/thoughtcrime/securesms/backup/BackupDialog.java +++ b/src/org/thoughtcrime/securesms/backup/BackupDialog.java @@ -34,7 +34,7 @@ public class BackupDialog { button.setOnClickListener(v -> { CheckBox confirmationCheckBox = dialog.findViewById(R.id.confirmation_check); if (confirmationCheckBox.isChecked()) { - TextSecurePreferences.setBackupPassphrase(context, Util.join(password, " ")); + BackupPassphrase.set(context, Util.join(password, " ")); TextSecurePreferences.setBackupEnabled(context, true); LocalBackupListener.schedule(context); @@ -75,7 +75,7 @@ public class BackupDialog { .setMessage(R.string.BackupDialog_disable_and_delete_all_local_backups) .setNegativeButton(android.R.string.cancel, null) .setPositiveButton(R.string.BackupDialog_delete_backups_statement, (dialog, which) -> { - TextSecurePreferences.setBackupPassphrase(context, null); + BackupPassphrase.set(context, null); TextSecurePreferences.setBackupEnabled(context, false); BackupUtil.deleteAllBackups(); preference.setChecked(false); diff --git a/src/org/thoughtcrime/securesms/backup/BackupPassphrase.java b/src/org/thoughtcrime/securesms/backup/BackupPassphrase.java new file mode 100644 index 0000000000..2a96e3a190 --- /dev/null +++ b/src/org/thoughtcrime/securesms/backup/BackupPassphrase.java @@ -0,0 +1,47 @@ +package org.thoughtcrime.securesms.backup; + +import android.content.Context; +import android.os.Build; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import org.thoughtcrime.securesms.crypto.KeyStoreHelper; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.util.TextSecurePreferences; + +/** + * Allows the getting and setting of the backup passphrase, which is stored encrypted on API >= 23. + */ +public class BackupPassphrase { + + private static final String TAG = BackupPassphrase.class.getSimpleName(); + + public static String get(@NonNull Context context) { + String passphrase = TextSecurePreferences.getBackupPassphrase(context); + String encryptedPassphrase = TextSecurePreferences.getEncryptedBackupPassphrase(context); + + if (Build.VERSION.SDK_INT < 23 || (passphrase == null && encryptedPassphrase == null)) { + return passphrase; + } + + if (encryptedPassphrase == null) { + Log.i(TAG, "Migrating to encrypted passphrase."); + set(context, passphrase); + encryptedPassphrase = TextSecurePreferences.getEncryptedBackupPassphrase(context); + } + + KeyStoreHelper.SealedData data = KeyStoreHelper.SealedData.fromString(encryptedPassphrase); + return new String(KeyStoreHelper.unseal(data)); + } + + public static void set(@NonNull Context context, @Nullable String passphrase) { + if (passphrase == null || Build.VERSION.SDK_INT < 23) { + TextSecurePreferences.setBackupPassphrase(context, passphrase); + TextSecurePreferences.setEncryptedBackupPassphrase(context, null); + } else { + KeyStoreHelper.SealedData encryptedPassphrase = KeyStoreHelper.seal(passphrase.getBytes()); + TextSecurePreferences.setEncryptedBackupPassphrase(context, encryptedPassphrase.serialize()); + TextSecurePreferences.setBackupPassphrase(context, null); + } + } +} diff --git a/src/org/thoughtcrime/securesms/jobs/LocalBackupJob.java b/src/org/thoughtcrime/securesms/jobs/LocalBackupJob.java index 3fb82ef846..a3b956ba32 100644 --- a/src/org/thoughtcrime/securesms/jobs/LocalBackupJob.java +++ b/src/org/thoughtcrime/securesms/jobs/LocalBackupJob.java @@ -5,6 +5,7 @@ import android.Manifest; import android.content.Context; import android.support.annotation.NonNull; +import org.thoughtcrime.securesms.backup.BackupPassphrase; import org.thoughtcrime.securesms.jobmanager.SafeData; import org.thoughtcrime.securesms.logging.Log; @@ -68,7 +69,7 @@ public class LocalBackupJob extends ContextJob { R.drawable.ic_signal_backup); try { - String backupPassword = TextSecurePreferences.getBackupPassphrase(context); + String backupPassword = BackupPassphrase.get(context); File backupDirectory = StorageUtil.getBackupDirectory(); String timestamp = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.US).format(new Date()); String fileName = String.format("signal-%s.backup", timestamp); diff --git a/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java b/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java index 98ba27158d..cc82242b44 100644 --- a/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java +++ b/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java @@ -138,10 +138,11 @@ public class TextSecurePreferences { private static final String ACTIVE_SIGNED_PRE_KEY_ID = "pref_active_signed_pre_key_id"; private static final String NEXT_SIGNED_PRE_KEY_ID = "pref_next_signed_pre_key_id"; - public static final String BACKUP_ENABLED = "pref_backup_enabled"; - private static final String BACKUP_PASSPHRASE = "pref_backup_passphrase"; - private static final String BACKUP_TIME = "pref_backup_next_time"; - public static final String BACKUP_NOW = "pref_backup_create"; + public static final String BACKUP_ENABLED = "pref_backup_enabled"; + private static final String BACKUP_PASSPHRASE = "pref_backup_passphrase"; + private static final String ENCRYPTED_BACKUP_PASSPHRASE = "pref_encrypted_backup_passphrase"; + private static final String BACKUP_TIME = "pref_backup_next_time"; + public static final String BACKUP_NOW = "pref_backup_create"; public static final String SCREEN_LOCK = "pref_android_screen_lock"; public static final String SCREEN_LOCK_TIMEOUT = "pref_android_screen_lock_timeout"; @@ -231,6 +232,14 @@ public class TextSecurePreferences { return getStringPreference(context, BACKUP_PASSPHRASE, null); } + public static void setEncryptedBackupPassphrase(@NonNull Context context, @Nullable String encryptedPassphrase) { + setStringPreference(context, ENCRYPTED_BACKUP_PASSPHRASE, encryptedPassphrase); + } + + public static @Nullable String getEncryptedBackupPassphrase(@NonNull Context context) { + return getStringPreference(context, ENCRYPTED_BACKUP_PASSPHRASE, null); + } + public static void setBackupEnabled(@NonNull Context context, boolean value) { setBooleanPreference(context, BACKUP_ENABLED, value); }