From e294199ea332a773a218df40f5c86f62a09f14c0 Mon Sep 17 00:00:00 2001 From: Anton Chekulaev Date: Mon, 14 Dec 2020 18:16:16 +1100 Subject: [PATCH] Master secret removed. Screen lock related classes refactoring. Legacy database util classes and migrations removed. --- app/src/main/AndroidManifest.xml | 4 - .../securesms/ApplicationContext.java | 4 +- .../securesms/DatabaseUpgradeActivity.java | 426 +++-- .../securesms/MediaOverviewActivity.java | 16 +- .../securesms/MediaPreviewActivity.java | 9 +- .../securesms/MessageDetailsActivity.java | 13 +- .../securesms/PassphraseActivity.java | 77 - .../securesms/PassphraseChangeActivity.java | 177 -- .../securesms/PassphraseCreateActivity.java | 90 - .../securesms/PassphrasePromptActivity.java | 197 +-- .../PassphraseRequiredActionBarActivity.java | 12 +- .../thoughtcrime/securesms/ShareActivity.java | 1 - .../conversation/ConversationFragment.java | 4 - .../crypto/AsymmetricMasterCipher.java | 138 -- .../securesms/crypto/IdentityKeyUtil.java | 75 +- .../securesms/crypto/MasterCipher.java | 225 --- .../securesms/crypto/MasterSecret.java | 119 -- .../securesms/crypto/MasterSecretUtil.java | 374 ----- .../securesms/database/DatabaseFactory.java | 30 +- .../database/helpers/ClassicOpenHelper.java | 1448 ----------------- .../helpers/SQLCipherMigrationHelper.java | 254 --- .../database/helpers/SQLCipherOpenHelper.java | 432 +---- .../helpers/SessionStoreMigrationHelper.java | 109 -- .../securesms/giph/ui/GiphyActivity.java | 8 - .../securesms/jobs/RefreshAttributesJob.java | 1 + .../AppProtectionPreferenceFragment.java | 115 +- .../securesms/service/KeyCachingService.java | 67 +- .../securesms/util/TextSecurePreferences.java | 2 +- .../res/layout/change_passphrase_activity.xml | 83 - .../res/layout/create_passphrase_activity.xml | 31 - .../res/layout/prompt_passphrase_activity.xml | 68 +- app/src/main/res/menu/log_submit.xml | 7 - .../res/xml/preferences_app_protection.xml | 40 +- 33 files changed, 323 insertions(+), 4333 deletions(-) delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/PassphraseActivity.java delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/PassphraseChangeActivity.java delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/PassphraseCreateActivity.java delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/crypto/AsymmetricMasterCipher.java delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/crypto/MasterCipher.java delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/crypto/MasterSecret.java delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/crypto/MasterSecretUtil.java delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/database/helpers/ClassicOpenHelper.java delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherMigrationHelper.java delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/database/helpers/SessionStoreMigrationHelper.java delete mode 100644 app/src/main/res/layout/change_passphrase_activity.xml delete mode 100644 app/src/main/res/layout/create_passphrase_activity.xml delete mode 100644 app/src/main/res/menu/log_submit.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 708b45450f..de0c11d55b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -312,10 +312,6 @@ android:screenOrientation="portrait" android:theme="@style/Theme.TextSecure.DayNight.NoActionBar" android:windowSoftInputMode="stateHidden" /> - UPGRADE_VERSIONS = new TreeSet() {{ - add(NO_MORE_KEY_EXCHANGE_PREFIX_VERSION); - add(TOFU_IDENTITIES_VERSION); - add(CURVE25519_VERSION); - add(ASYMMETRIC_MASTER_SECRET_FIX_VERSION); - add(NO_V1_VERSION); - add(SIGNED_PREKEY_VERSION); - add(NO_DECRYPT_QUEUE_VERSION); - add(PUSH_DECRYPT_SERIAL_ID_VERSION); - add(MIGRATE_SESSION_PLAINTEXT); - add(MEDIA_DOWNLOAD_CONTROLS_VERSION); - add(REDPHONE_SUPPORT_VERSION); - add(NO_MORE_CANONICAL_DB_VERSION); - add(SCREENSHOTS); - add(INTERNALIZE_CONTACTS); - add(PERSISTENT_BLOBS); - add(SQLCIPHER); - add(SQLCIPHER_COMPLETE); - add(REMOVE_CACHE); - add(FULL_TEXT_SEARCH); - add(BAD_IMPORT_CLEANUP); - add(IMAGE_CACHE_CLEANUP); - add(WORKMANAGER_MIGRATION); - add(COLOR_MIGRATION); - add(UNIDENTIFIED_DELIVERY); - add(SIGNALING_KEY_DEPRECATION); - add(CONVERSATION_SEARCH); +// add(NO_MORE_KEY_EXCHANGE_PREFIX_VERSION); +// add(TOFU_IDENTITIES_VERSION); +// add(CURVE25519_VERSION); +// add(ASYMMETRIC_MASTER_SECRET_FIX_VERSION); +// add(NO_V1_VERSION); +// add(SIGNED_PREKEY_VERSION); +// add(NO_DECRYPT_QUEUE_VERSION); +// add(PUSH_DECRYPT_SERIAL_ID_VERSION); +// add(MIGRATE_SESSION_PLAINTEXT); +// add(MEDIA_DOWNLOAD_CONTROLS_VERSION); +// add(REDPHONE_SUPPORT_VERSION); +// add(NO_MORE_CANONICAL_DB_VERSION); +// add(SCREENSHOTS); +// add(INTERNALIZE_CONTACTS); +// add(PERSISTENT_BLOBS); +// add(SQLCIPHER); +// add(SQLCIPHER_COMPLETE); +// add(REMOVE_CACHE); +// add(FULL_TEXT_SEARCH); +// add(BAD_IMPORT_CLEANUP); +// add(IMAGE_CACHE_CLEANUP); +// add(WORKMANAGER_MIGRATION); +// add(COLOR_MIGRATION); +// add(UNIDENTIFIED_DELIVERY); +// add(SIGNALING_KEY_DEPRECATION); +// add(CONVERSATION_SEARCH); }}; - private MasterSecret masterSecret; +// private MasterSecret masterSecret; @Override public void onCreate(Bundle bundle) { super.onCreate(bundle); - this.masterSecret = KeyCachingService.getMasterSecret(this); +// this.masterSecret = KeyCachingService.getMasterSecret(this); if (needsUpgradeTask()) { Log.i("DatabaseUpgradeActivity", "Upgrading..."); @@ -202,160 +190,160 @@ public class DatabaseUpgradeActivity extends BaseActivity { Context context = DatabaseUpgradeActivity.this.getApplicationContext(); Log.i("DatabaseUpgradeActivity", "Running background upgrade.."); - DatabaseFactory.getInstance(DatabaseUpgradeActivity.this) - .onApplicationLevelUpgrade(context, masterSecret, params[0], this); +// DatabaseFactory.getInstance(DatabaseUpgradeActivity.this) +// .onApplicationLevelUpgrade(context, params[0], this); - if (params[0] < CURVE25519_VERSION) { - IdentityKeyUtil.migrateIdentityKeys(context, masterSecret); - } - - if (params[0] < NO_V1_VERSION) { - File v1sessions = new File(context.getFilesDir(), "sessions"); - - if (v1sessions.exists() && v1sessions.isDirectory()) { - File[] contents = v1sessions.listFiles(); - - if (contents != null) { - for (File session : contents) { - session.delete(); - } - } - - v1sessions.delete(); - } - } - - if (params[0] < SIGNED_PREKEY_VERSION) { -// ApplicationContext.getInstance(getApplicationContext()) -// .getJobManager() -// .add(new CreateSignedPreKeyJob(context)); - } - - if (params[0] < NO_DECRYPT_QUEUE_VERSION) { - scheduleMessagesInPushDatabase(context); - } - - if (params[0] < PUSH_DECRYPT_SERIAL_ID_VERSION) { - scheduleMessagesInPushDatabase(context); - } - - if (params[0] < MIGRATE_SESSION_PLAINTEXT) { -// new TextSecureSessionStore(context, masterSecret).migrateSessions(); -// new TextSecurePreKeyStore(context, masterSecret).migrateRecords(); - - IdentityKeyUtil.migrateIdentityKeys(context, masterSecret); - scheduleMessagesInPushDatabase(context);; - } - - if (params[0] < MEDIA_DOWNLOAD_CONTROLS_VERSION) { - schedulePendingIncomingParts(context); - } - - if (params[0] < REDPHONE_SUPPORT_VERSION) { - ApplicationContext.getInstance(getApplicationContext()) - .getJobManager() - .add(new RefreshAttributesJob()); - } - - if (params[0] < SCREENSHOTS) { - boolean screenSecurity = PreferenceManager.getDefaultSharedPreferences(context).getBoolean(TextSecurePreferences.SCREEN_SECURITY_PREF, true); - TextSecurePreferences.setScreenSecurityEnabled(getApplicationContext(), screenSecurity); - } - - if (params[0] < PERSISTENT_BLOBS) { - File externalDir = context.getExternalFilesDir(null); - - if (externalDir != null && externalDir.isDirectory() && externalDir.exists()) { - for (File blob : externalDir.listFiles()) { - if (blob.exists() && blob.isFile()) blob.delete(); - } - } - } - - if (params[0] < INTERNALIZE_CONTACTS) { - if (TextSecurePreferences.isPushRegistered(getApplicationContext())) { - TextSecurePreferences.setHasSuccessfullyRetrievedDirectory(getApplicationContext(), true); - } - } - - if (params[0] < SQLCIPHER) { - scheduleMessagesInPushDatabase(context); - } - - if (params[0] < SQLCIPHER_COMPLETE) { - File file = context.getDatabasePath("messages.db"); - if (file != null && file.exists()) file.delete(); - } - - if (params[0] < REMOVE_JOURNAL) { - File file = context.getDatabasePath("messages.db-journal"); - if (file != null && file.exists()) file.delete(); - } - - if (params[0] < REMOVE_CACHE) { - try { - FileUtils.deleteDirectoryContents(context.getCacheDir()); - } catch (IOException e) { - Log.w(TAG, e); - } - } - - if (params[0] < IMAGE_CACHE_CLEANUP) { - try { - FileUtils.deleteDirectoryContents(context.getExternalCacheDir()); - GlideApp.get(context).clearDiskCache(); - } catch (IOException e) { - Log.w(TAG, e); - } - } - - // This migration became unnecessary after switching away from WorkManager -// if (params[0] < WORKMANAGER_MIGRATION) { -// Log.i(TAG, "Beginning migration of existing jobs to WorkManager"); +// if (params[0] < CURVE25519_VERSION) { +// IdentityKeyUtil.migrateIdentityKeys(context, masterSecret); +// } // -// JobManager jobManager = ApplicationContext.getInstance(getApplicationContext()).getJobManager(); -// PersistentStorage storage = new PersistentStorage(getApplicationContext(), "TextSecureJobs", new JavaJobSerializer()); +// if (params[0] < NO_V1_VERSION) { +// File v1sessions = new File(context.getFilesDir(), "sessions"); // -// for (Job job : storage.getAllUnencrypted()) { -// jobManager.add(job); -// Log.i(TAG, "Migrated job with class '" + job.getClass().getSimpleName() + "' to run on new JobManager."); +// if (v1sessions.exists() && v1sessions.isDirectory()) { +// File[] contents = v1sessions.listFiles(); +// +// if (contents != null) { +// for (File session : contents) { +// session.delete(); +// } +// } +// +// v1sessions.delete(); // } // } - - if (params[0] < COLOR_MIGRATION) { - long startTime = System.currentTimeMillis(); - DatabaseFactory.getRecipientDatabase(context).updateSystemContactColors((name, color) -> { - if (color != null) { - try { - return MaterialColor.fromSerialized(color); - } catch (MaterialColor.UnknownColorException e) { - Log.w(TAG, "Encountered an unknown color during legacy color migration.", e); - return ContactColorsLegacy.generateFor(name); - } - } - return ContactColorsLegacy.generateFor(name); - }); - Log.i(TAG, "Color migration took " + (System.currentTimeMillis() - startTime) + " ms"); - } - - if (params[0] < UNIDENTIFIED_DELIVERY) { - if (TextSecurePreferences.isMultiDevice(context)) { - Log.i(TAG, "MultiDevice: Disabling UD (will be re-enabled if possible after pending refresh)."); - TextSecurePreferences.setIsUnidentifiedDeliveryEnabled(context, false); - } - - Log.i(TAG, "Scheduling UD attributes refresh."); - ApplicationContext.getInstance(context) - .getJobManager() - .add(new RefreshAttributesJob()); - } - - if (params[0] < SIGNALING_KEY_DEPRECATION) { - Log.i(TAG, "Scheduling a RefreshAttributesJob to remove the signaling key remotely."); - ApplicationContext.getInstance(context) - .getJobManager() - .add(new RefreshAttributesJob()); - } +// +// if (params[0] < SIGNED_PREKEY_VERSION) { +//// ApplicationContext.getInstance(getApplicationContext()) +//// .getJobManager() +//// .add(new CreateSignedPreKeyJob(context)); +// } +// +// if (params[0] < NO_DECRYPT_QUEUE_VERSION) { +// scheduleMessagesInPushDatabase(context); +// } +// +// if (params[0] < PUSH_DECRYPT_SERIAL_ID_VERSION) { +// scheduleMessagesInPushDatabase(context); +// } +// +// if (params[0] < MIGRATE_SESSION_PLAINTEXT) { +//// new TextSecureSessionStore(context, masterSecret).migrateSessions(); +//// new TextSecurePreKeyStore(context, masterSecret).migrateRecords(); +// +// IdentityKeyUtil.migrateIdentityKeys(context, masterSecret); +// scheduleMessagesInPushDatabase(context);; +// } +// +// if (params[0] < MEDIA_DOWNLOAD_CONTROLS_VERSION) { +// schedulePendingIncomingParts(context); +// } +// +// if (params[0] < REDPHONE_SUPPORT_VERSION) { +// ApplicationContext.getInstance(getApplicationContext()) +// .getJobManager() +// .add(new RefreshAttributesJob()); +// } +// +// if (params[0] < SCREENSHOTS) { +// boolean screenSecurity = PreferenceManager.getDefaultSharedPreferences(context).getBoolean(TextSecurePreferences.SCREEN_SECURITY_PREF, true); +// TextSecurePreferences.setScreenSecurityEnabled(getApplicationContext(), screenSecurity); +// } +// +// if (params[0] < PERSISTENT_BLOBS) { +// File externalDir = context.getExternalFilesDir(null); +// +// if (externalDir != null && externalDir.isDirectory() && externalDir.exists()) { +// for (File blob : externalDir.listFiles()) { +// if (blob.exists() && blob.isFile()) blob.delete(); +// } +// } +// } +// +// if (params[0] < INTERNALIZE_CONTACTS) { +// if (TextSecurePreferences.isPushRegistered(getApplicationContext())) { +// TextSecurePreferences.setHasSuccessfullyRetrievedDirectory(getApplicationContext(), true); +// } +// } +// +// if (params[0] < SQLCIPHER) { +// scheduleMessagesInPushDatabase(context); +// } +// +// if (params[0] < SQLCIPHER_COMPLETE) { +// File file = context.getDatabasePath("messages.db"); +// if (file != null && file.exists()) file.delete(); +// } +// +// if (params[0] < REMOVE_JOURNAL) { +// File file = context.getDatabasePath("messages.db-journal"); +// if (file != null && file.exists()) file.delete(); +// } +// +// if (params[0] < REMOVE_CACHE) { +// try { +// FileUtils.deleteDirectoryContents(context.getCacheDir()); +// } catch (IOException e) { +// Log.w(TAG, e); +// } +// } +// +// if (params[0] < IMAGE_CACHE_CLEANUP) { +// try { +// FileUtils.deleteDirectoryContents(context.getExternalCacheDir()); +// GlideApp.get(context).clearDiskCache(); +// } catch (IOException e) { +// Log.w(TAG, e); +// } +// } +// +// // This migration became unnecessary after switching away from WorkManager +//// if (params[0] < WORKMANAGER_MIGRATION) { +//// Log.i(TAG, "Beginning migration of existing jobs to WorkManager"); +//// +//// JobManager jobManager = ApplicationContext.getInstance(getApplicationContext()).getJobManager(); +//// PersistentStorage storage = new PersistentStorage(getApplicationContext(), "TextSecureJobs", new JavaJobSerializer()); +//// +//// for (Job job : storage.getAllUnencrypted()) { +//// jobManager.add(job); +//// Log.i(TAG, "Migrated job with class '" + job.getClass().getSimpleName() + "' to run on new JobManager."); +//// } +//// } +// +// if (params[0] < COLOR_MIGRATION) { +// long startTime = System.currentTimeMillis(); +// DatabaseFactory.getRecipientDatabase(context).updateSystemContactColors((name, color) -> { +// if (color != null) { +// try { +// return MaterialColor.fromSerialized(color); +// } catch (MaterialColor.UnknownColorException e) { +// Log.w(TAG, "Encountered an unknown color during legacy color migration.", e); +// return ContactColorsLegacy.generateFor(name); +// } +// } +// return ContactColorsLegacy.generateFor(name); +// }); +// Log.i(TAG, "Color migration took " + (System.currentTimeMillis() - startTime) + " ms"); +// } +// +// if (params[0] < UNIDENTIFIED_DELIVERY) { +// if (TextSecurePreferences.isMultiDevice(context)) { +// Log.i(TAG, "MultiDevice: Disabling UD (will be re-enabled if possible after pending refresh)."); +// TextSecurePreferences.setIsUnidentifiedDeliveryEnabled(context, false); +// } +// +// Log.i(TAG, "Scheduling UD attributes refresh."); +// ApplicationContext.getInstance(context) +// .getJobManager() +// .add(new RefreshAttributesJob()); +// } +// +// if (params[0] < SIGNALING_KEY_DEPRECATION) { +// Log.i(TAG, "Scheduling a RefreshAttributesJob to remove the signaling key remotely."); +// ApplicationContext.getInstance(context) +// .getJobManager() +// .add(new RefreshAttributesJob()); +// } return null; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java index 0b3da4a02e..8050784a08 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java @@ -62,7 +62,6 @@ import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.permissions.Permissions; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.util.AttachmentUtil; -import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.SaveAttachmentTask; import org.thoughtcrime.securesms.util.StickyHeaderDecoration; import org.thoughtcrime.securesms.util.Util; @@ -86,18 +85,11 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity { public static final String ADDRESS_EXTRA = "address"; - private final DynamicLanguage dynamicLanguage = new DynamicLanguage(); - private Toolbar toolbar; private TabLayout tabLayout; private ViewPager viewPager; private Recipient recipient; - @Override - protected void onPreCreate() { - dynamicLanguage.onCreate(this); - } - @Override protected void onCreate(Bundle bundle, boolean ready) { setContentView(R.layout.media_overview_activity); @@ -109,12 +101,6 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity { this.viewPager.setAdapter(new MediaOverviewPagerAdapter(getSupportFragmentManager())); } - @Override - public void onResume() { - super.onResume(); - dynamicLanguage.onResume(this); - } - @Override public boolean onOptionsItemSelected(MenuItem item) { super.onOptionsItemSelected(item); @@ -172,7 +158,7 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity { Bundle args = new Bundle(); args.putString(MediaOverviewGalleryFragment.ADDRESS_EXTRA, recipient.getAddress().serialize()); - args.putSerializable(MediaOverviewGalleryFragment.LOCALE_EXTRA, dynamicLanguage.getCurrentLocale()); + args.putSerializable(MediaOverviewGalleryFragment.LOCALE_EXTRA, Locale.getDefault()); fragment.setArguments(args); diff --git a/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java index d31a41c8e2..d7abc45105 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java @@ -68,12 +68,12 @@ import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientModifiedListener; import org.thoughtcrime.securesms.util.AttachmentUtil; import org.thoughtcrime.securesms.util.DateUtils; -import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.SaveAttachmentTask; import org.thoughtcrime.securesms.util.SaveAttachmentTask.Attachment; import org.thoughtcrime.securesms.util.Util; import java.io.IOException; +import java.util.Locale; import java.util.WeakHashMap; import network.loki.messenger.R; @@ -95,8 +95,6 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im public static final String OUTGOING_EXTRA = "outgoing"; public static final String LEFT_IS_RECENT_EXTRA = "left_is_recent"; - private final DynamicLanguage dynamicLanguage = new DynamicLanguage(); - private ViewPager mediaPager; private View detailsContainer; private TextView caption; @@ -120,8 +118,6 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im @SuppressWarnings("ConstantConditions") @Override protected void onCreate(Bundle bundle, boolean ready) { - dynamicLanguage.onCreate(this); - viewModel = new ViewModelProvider(this).get(MediaPreviewViewModel.class); setContentView(R.layout.media_preview_activity); @@ -172,7 +168,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im CharSequence relativeTimeSpan; if (mediaItem.date > 0) { - relativeTimeSpan = DateUtils.getExtendedRelativeTimeSpanString(this,dynamicLanguage.getCurrentLocale(), mediaItem.date); + relativeTimeSpan = DateUtils.getExtendedRelativeTimeSpanString(this, Locale.getDefault(), mediaItem.date); } else { relativeTimeSpan = getString(R.string.MediaPreviewActivity_draft); } @@ -189,7 +185,6 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im public void onResume() { super.onResume(); - dynamicLanguage.onResume(this); initializeMedia(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/MessageDetailsActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MessageDetailsActivity.java index 605794f0f3..041e92ea21 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MessageDetailsActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/MessageDetailsActivity.java @@ -55,7 +55,6 @@ import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientModifiedListener; import org.thoughtcrime.securesms.sms.MessageSender; import org.thoughtcrime.securesms.util.DateUtils; -import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.ExpirationUtil; import org.thoughtcrime.securesms.util.Util; import org.session.libsignal.libsignal.util.guava.Optional; @@ -102,15 +101,8 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity private ListView recipientsList; private LayoutInflater inflater; - private DynamicLanguage dynamicLanguage = new DynamicLanguage(); - private boolean running; - @Override - protected void onPreCreate() { - dynamicLanguage.onCreate(this); - } - @Override public void onCreate(Bundle bundle, boolean ready) { super.onCreate(bundle, ready); @@ -125,7 +117,6 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity @Override protected void onResume() { super.onResume(); - dynamicLanguage.onResume(this); assert getSupportActionBar() != null; getSupportActionBar().setTitle("Message Details"); @@ -211,7 +202,7 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity sentDate.setText("-"); receivedContainer.setVisibility(View.GONE); } else { - Locale dateLocale = dynamicLanguage.getCurrentLocale(); + Locale dateLocale = Locale.getDefault(); SimpleDateFormat dateFormatter = DateUtils.getDetailedDateFormatter(this, dateLocale); sentDate.setText(dateFormatter.format(new Date(messageRecord.getDateSent()))); sentDate.setOnLongClickListener(v -> { @@ -271,7 +262,7 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity toFrom.setVisibility(View.GONE); separator.setVisibility(View.GONE); } - conversationItem.bind(messageRecord, Optional.absent(), Optional.absent(), glideRequests, dynamicLanguage.getCurrentLocale(), new HashSet<>(), recipient, null, false); + conversationItem.bind(messageRecord, Optional.absent(), Optional.absent(), glideRequests, Locale.getDefault(), new HashSet<>(), recipient, null, false); recipientsList.setAdapter(new MessageDetailsRecipientAdapter(this, glideRequests, messageRecord, recipients, isPushGroup)); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/PassphraseActivity.java b/app/src/main/java/org/thoughtcrime/securesms/PassphraseActivity.java deleted file mode 100644 index 318cbf73be..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/PassphraseActivity.java +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright (C) 2011 Whisper Systems - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.thoughtcrime.securesms; - -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.os.IBinder; -import org.thoughtcrime.securesms.logging.Log; - -import org.thoughtcrime.securesms.crypto.MasterSecret; -import org.thoughtcrime.securesms.service.KeyCachingService; - - -/** - * Base Activity for changing/prompting local encryption passphrase. - * - * @author Moxie Marlinspike - */ -public abstract class PassphraseActivity extends BaseActionBarActivity { - - private static final String TAG = PassphraseActivity.class.getSimpleName(); - - private KeyCachingService keyCachingService; - private MasterSecret masterSecret; - - protected void setMasterSecret(MasterSecret masterSecret) { - this.masterSecret = masterSecret; - Intent bindIntent = new Intent(this, KeyCachingService.class); - startService(bindIntent); - bindService(bindIntent, serviceConnection, Context.BIND_AUTO_CREATE); - } - - protected abstract void cleanup(); - - private ServiceConnection serviceConnection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName className, IBinder service) { - keyCachingService = ((KeyCachingService.KeySetBinder)service).getService(); - keyCachingService.setMasterSecret(masterSecret); - - PassphraseActivity.this.unbindService(PassphraseActivity.this.serviceConnection); - - masterSecret = null; - cleanup(); - - Intent nextIntent = getIntent().getParcelableExtra("next_intent"); - if (nextIntent != null) { - try { - startActivity(nextIntent); - } catch (java.lang.SecurityException e) { - Log.w(TAG, "Access permission not passed from PassphraseActivity, retry sharing."); - } - } - finish(); - } - @Override - public void onServiceDisconnected(ComponentName name) { - keyCachingService = null; - } - }; -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/PassphraseChangeActivity.java b/app/src/main/java/org/thoughtcrime/securesms/PassphraseChangeActivity.java deleted file mode 100644 index 666f4c5d3f..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/PassphraseChangeActivity.java +++ /dev/null @@ -1,177 +0,0 @@ -/** - * Copyright (C) 2011 Whisper Systems - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.thoughtcrime.securesms; - -import android.content.Context; -import android.os.AsyncTask; -import android.os.Bundle; -import android.text.Editable; -import android.view.View; -import android.view.View.OnClickListener; -import android.widget.Button; -import android.widget.EditText; - -import org.thoughtcrime.securesms.crypto.InvalidPassphraseException; -import org.thoughtcrime.securesms.crypto.MasterSecret; -import org.thoughtcrime.securesms.crypto.MasterSecretUtil; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.util.DynamicLanguage; -import org.thoughtcrime.securesms.util.DynamicTheme; -import org.thoughtcrime.securesms.util.TextSecurePreferences; - -import network.loki.messenger.R; - -/** - * Activity for changing a user's local encryption passphrase. - * - * @author Moxie Marlinspike - */ - -public class PassphraseChangeActivity extends PassphraseActivity { - - private DynamicTheme dynamicTheme = new DynamicTheme(); - private DynamicLanguage dynamicLanguage = new DynamicLanguage(); - - private EditText originalPassphrase; - private EditText newPassphrase; - private EditText repeatPassphrase; - private Button okButton; - private Button cancelButton; - - @Override - public void onCreate(Bundle savedInstanceState) { - dynamicTheme.onCreate(this); - dynamicLanguage.onCreate(this); - super.onCreate(savedInstanceState); - - setContentView(R.layout.change_passphrase_activity); - - initializeResources(); - } - - @Override - public void onResume() { - super.onResume(); - dynamicTheme.onResume(this); - dynamicLanguage.onResume(this); - } - - private void initializeResources() { - this.originalPassphrase = (EditText) findViewById(R.id.old_passphrase ); - this.newPassphrase = (EditText) findViewById(R.id.new_passphrase ); - this.repeatPassphrase = (EditText) findViewById(R.id.repeat_passphrase ); - - this.okButton = (Button ) findViewById(R.id.ok_button ); - this.cancelButton = (Button ) findViewById(R.id.cancel_button ); - - this.okButton.setOnClickListener(new OkButtonClickListener()); - this.cancelButton.setOnClickListener(new CancelButtonClickListener()); - - if (TextSecurePreferences.isPasswordDisabled(this)) { - this.originalPassphrase.setVisibility(View.GONE); - } else { - this.originalPassphrase.setVisibility(View.VISIBLE); - } - } - - private void verifyAndSavePassphrases() { - Editable originalText = this.originalPassphrase.getText(); - Editable newText = this.newPassphrase.getText(); - Editable repeatText = this.repeatPassphrase.getText(); - - String original = (originalText == null ? "" : originalText.toString()); - String passphrase = (newText == null ? "" : newText.toString()); - String passphraseRepeat = (repeatText == null ? "" : repeatText.toString()); - - if (TextSecurePreferences.isPasswordDisabled(this)) { - original = MasterSecretUtil.UNENCRYPTED_PASSPHRASE; - } - - if (!passphrase.equals(passphraseRepeat)) { - this.newPassphrase.setText(""); - this.repeatPassphrase.setText(""); - this.newPassphrase.setError(getString(R.string.PassphraseChangeActivity_passphrases_dont_match_exclamation)); - this.newPassphrase.requestFocus(); - } else if (passphrase.equals("")) { - this.newPassphrase.setError(getString(R.string.PassphraseChangeActivity_enter_new_passphrase_exclamation)); - this.newPassphrase.requestFocus(); - } else { - new ChangePassphraseTask(this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, original, passphrase); - } - } - - private class CancelButtonClickListener implements OnClickListener { - public void onClick(View v) { - finish(); - } - } - - private class OkButtonClickListener implements OnClickListener { - public void onClick(View v) { - verifyAndSavePassphrases(); - } - } - - private class ChangePassphraseTask extends AsyncTask { - private final Context context; - - public ChangePassphraseTask(Context context) { - this.context = context; - } - - @Override - protected void onPreExecute() { - okButton.setEnabled(false); - } - - @Override - protected MasterSecret doInBackground(String... params) { - try { - MasterSecret masterSecret = MasterSecretUtil.changeMasterSecretPassphrase(context, params[0], params[1]); - TextSecurePreferences.setPasswordDisabled(context, false); - - return masterSecret; - - } catch (InvalidPassphraseException e) { - Log.w(PassphraseChangeActivity.class.getSimpleName(), e); - return null; - } - } - - @Override - protected void onPostExecute(MasterSecret masterSecret) { - okButton.setEnabled(true); - - if (masterSecret != null) { - setMasterSecret(masterSecret); - } else { - originalPassphrase.setText(""); - originalPassphrase.setError(getString(R.string.PassphraseChangeActivity_incorrect_old_passphrase_exclamation)); - originalPassphrase.requestFocus(); - } - } - } - - @Override - protected void cleanup() { - this.originalPassphrase = null; - this.newPassphrase = null; - this.repeatPassphrase = null; - - System.gc(); - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/PassphraseCreateActivity.java b/app/src/main/java/org/thoughtcrime/securesms/PassphraseCreateActivity.java deleted file mode 100644 index 8bdbedc4dc..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/PassphraseCreateActivity.java +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright (C) 2011 Whisper Systems - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.thoughtcrime.securesms; - -import android.os.AsyncTask; -import android.os.Bundle; - -import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; -import org.thoughtcrime.securesms.crypto.MasterSecret; -import org.thoughtcrime.securesms.crypto.MasterSecretUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; -import org.thoughtcrime.securesms.util.VersionTracker; - -import network.loki.messenger.R; - -/** - * Activity for creating a user's local encryption passphrase. - * - * @author Moxie Marlinspike - */ - -public class PassphraseCreateActivity extends PassphraseActivity { - - public PassphraseCreateActivity() { } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.create_passphrase_activity); - - initializeResources(); - } - - private void initializeResources() { - new SecretGenerator().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, MasterSecretUtil.UNENCRYPTED_PASSPHRASE); - } - - private class SecretGenerator extends AsyncTask { - private MasterSecret masterSecret; - - @Override - protected void onPreExecute() { - } - - @Override - protected Void doInBackground(String... params) { - String passphrase = params[0]; - masterSecret = MasterSecretUtil.generateMasterSecret(PassphraseCreateActivity.this, - passphrase); - - MasterSecretUtil.generateAsymmetricMasterSecret(PassphraseCreateActivity.this, masterSecret); - IdentityKeyUtil.generateIdentityKeyPair(PassphraseCreateActivity.this); - VersionTracker.updateLastSeenVersion(PassphraseCreateActivity.this); - - TextSecurePreferences.setLastExperienceVersionCode(PassphraseCreateActivity.this, Util.getCanonicalVersionCode()); - TextSecurePreferences.setPasswordDisabled(PassphraseCreateActivity.this, true); - TextSecurePreferences.setReadReceiptsEnabled(PassphraseCreateActivity.this, true); - TextSecurePreferences.setTypingIndicatorsEnabled(PassphraseCreateActivity.this, true); - TextSecurePreferences.setHasSeenWelcomeScreen(PassphraseCreateActivity.this, false); - - return null; - } - - @Override - protected void onPostExecute(Void param) { - setMasterSecret(masterSecret); - } - } - - @Override - protected void cleanup() { - System.gc(); - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/PassphrasePromptActivity.java b/app/src/main/java/org/thoughtcrime/securesms/PassphrasePromptActivity.java index 20d67b672a..2f9da6efe5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/PassphrasePromptActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/PassphrasePromptActivity.java @@ -18,66 +18,43 @@ package org.thoughtcrime.securesms; import android.animation.Animator; import android.app.KeyguardManager; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.ServiceConnection; import android.graphics.PorterDuff; -import android.os.Build; import android.os.Bundle; -import android.text.Editable; -import android.text.InputType; +import android.os.IBinder; import android.text.SpannableString; import android.text.Spanned; import android.text.style.RelativeSizeSpan; import android.text.style.TypefaceSpan; -import android.view.KeyEvent; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; import android.view.View; -import android.view.View.OnClickListener; -import android.view.WindowManager; import android.view.animation.Animation; import android.view.animation.BounceInterpolator; import android.view.animation.TranslateAnimation; -import android.view.inputmethod.EditorInfo; import android.widget.Button; -import android.widget.EditText; -import android.widget.ImageButton; import android.widget.ImageView; -import android.widget.TextView; import androidx.core.hardware.fingerprint.FingerprintManagerCompat; import androidx.core.os.CancellationSignal; import org.thoughtcrime.securesms.animation.AnimationCompleteListener; import org.thoughtcrime.securesms.components.AnimatingToggle; -import org.thoughtcrime.securesms.crypto.InvalidPassphraseException; -import org.thoughtcrime.securesms.crypto.MasterSecret; -import org.thoughtcrime.securesms.crypto.MasterSecretUtil; import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.util.DynamicLanguage; +import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.util.TextSecurePreferences; import network.loki.messenger.R; -/** - * Activity that prompts for a user's passphrase. - * - * @author Moxie Marlinspike - */ -public class PassphrasePromptActivity extends PassphraseActivity { +//TODO Rename to screen lock activity and refactor to Kotlin. +public class PassphrasePromptActivity extends BaseActionBarActivity { private static final String TAG = PassphrasePromptActivity.class.getSimpleName(); - private DynamicLanguage dynamicLanguage = new DynamicLanguage(); - - private View passphraseAuthContainer; private ImageView fingerprintPrompt; private Button lockScreenButton; - private EditText passphraseText; - private ImageButton showButton; - private ImageButton hideButton; private AnimatingToggle visibilityToggle; private FingerprintManagerCompat fingerprintManager; @@ -87,20 +64,34 @@ public class PassphrasePromptActivity extends PassphraseActivity { private boolean authenticated; private boolean failure; + private KeyCachingService keyCachingService; + @Override public void onCreate(Bundle savedInstanceState) { Log.i(TAG, "onCreate()"); - dynamicLanguage.onCreate(this); super.onCreate(savedInstanceState); setContentView(R.layout.prompt_passphrase_activity); initializeResources(); + + // Start and bind to the KeyCachingService instance. + Intent bindIntent = new Intent(this, KeyCachingService.class); + startService(bindIntent); + bindService(bindIntent, new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + keyCachingService = ((KeyCachingService.KeySetBinder)service).getService(); + } + @Override + public void onServiceDisconnected(ComponentName name) { + keyCachingService = null; + } + }, Context.BIND_AUTO_CREATE); } @Override public void onResume() { super.onResume(); - dynamicLanguage.onResume(this); setLockTypeVisibility(); @@ -126,26 +117,6 @@ public class PassphrasePromptActivity extends PassphraseActivity { setIntent(intent); } - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - MenuInflater inflater = this.getMenuInflater(); - menu.clear(); - - // inflater.inflate(R.menu.log_submit, menu); - super.onPrepareOptionsMenu(menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - super.onOptionsItemSelected(item); - switch (item.getItemId()) { - case R.id.menu_submit_debug_logs: handleLogSubmit(); return true; - } - - return false; - } - @Override public void onActivityResult(int requestCode, int resultcode, Intent data) { super.onActivityResult(requestCode, resultcode, data); @@ -159,57 +130,26 @@ public class PassphrasePromptActivity extends PassphraseActivity { } } - private void handleLogSubmit() { - Intent intent = new Intent(this, LogSubmitActivity.class); - startActivity(intent); - } - - private void handlePassphrase() { - try { - Editable text = passphraseText.getText(); - String passphrase = (text == null ? "" : text.toString()); - MasterSecret masterSecret = MasterSecretUtil.getMasterSecret(this, passphrase); - - setMasterSecret(masterSecret); - } catch (InvalidPassphraseException ipe) { - passphraseText.setText(""); - passphraseText.setError( - getString(R.string.PassphrasePromptActivity_invalid_passphrase_exclamation)); - } - } - private void handleAuthenticated() { - try { - authenticated = true; - - MasterSecret masterSecret = MasterSecretUtil.getMasterSecret(this, MasterSecretUtil.UNENCRYPTED_PASSPHRASE); - setMasterSecret(masterSecret); - } catch (InvalidPassphraseException e) { - throw new AssertionError(e); - } - } + authenticated = true; + //TODO Replace with a proper call. + keyCachingService.setMasterSecret(new Object()); - private void setPassphraseVisibility(boolean visibility) { - int cursorPosition = passphraseText.getSelectionStart(); - if (visibility) { - passphraseText.setInputType(InputType.TYPE_CLASS_TEXT | - InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD); - } else { - passphraseText.setInputType(InputType.TYPE_CLASS_TEXT | - InputType.TYPE_TEXT_VARIATION_PASSWORD); + // Finish and proceed with the next intent. + Intent nextIntent = getIntent().getParcelableExtra("next_intent"); + if (nextIntent != null) { + startActivity(nextIntent); +// try { +// startActivity(nextIntent); +// } catch (java.lang.SecurityException e) { +// Log.w(TAG, "Access permission not passed from PassphraseActivity, retry sharing."); +// } } - passphraseText.setSelection(cursorPosition); + finish(); } private void initializeResources() { - - ImageButton okButton = findViewById(R.id.ok_button); - - showButton = findViewById(R.id.passphrase_visibility); - hideButton = findViewById(R.id.passphrase_visibility_off); visibilityToggle = findViewById(R.id.button_toggle); - passphraseText = findViewById(R.id.passphrase_edit); - passphraseAuthContainer = findViewById(R.id.password_auth_container); fingerprintPrompt = findViewById(R.id.fingerprint_auth_container); lockScreenButton = findViewById(R.id.lock_screen_auth_container); fingerprintManager = FingerprintManagerCompat.from(this); @@ -220,13 +160,6 @@ public class PassphrasePromptActivity extends PassphraseActivity { hint.setSpan(new RelativeSizeSpan(0.9f), 0, hint.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); hint.setSpan(new TypefaceSpan("sans-serif"), 0, hint.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); - passphraseText.setHint(hint); - okButton.setOnClickListener(new OkButtonClickListener()); - showButton.setOnClickListener(new ShowButtonOnClickListener()); - hideButton.setOnClickListener(new HideButtonOnClickListener()); - passphraseText.setOnEditorActionListener(new PassphraseActionListener()); - passphraseText.setImeActionLabel(getString(R.string.prompt_passphrase_activity__unlock), EditorInfo.IME_ACTION_DONE); - fingerprintPrompt.setImageResource(R.drawable.ic_fingerprint_white_48dp); fingerprintPrompt.getBackground().setColorFilter(getResources().getColor(R.color.signal_primary), PorterDuff.Mode.SRC_IN); @@ -235,8 +168,6 @@ public class PassphrasePromptActivity extends PassphraseActivity { private void setLockTypeVisibility() { if (TextSecurePreferences.isScreenLockEnabled(this)) { - passphraseAuthContainer.setVisibility(View.GONE); - if (fingerprintManager.isHardwareDetected() && fingerprintManager.hasEnrolledFingerprints()) { fingerprintPrompt.setVisibility(View.VISIBLE); lockScreenButton.setVisibility(View.GONE); @@ -245,7 +176,6 @@ public class PassphrasePromptActivity extends PassphraseActivity { lockScreenButton.setVisibility(View.VISIBLE); } } else { - passphraseAuthContainer.setVisibility(View.VISIBLE); fingerprintPrompt.setVisibility(View.GONE); lockScreenButton.setVisibility(View.GONE); } @@ -266,13 +196,10 @@ public class PassphrasePromptActivity extends PassphraseActivity { Log.i(TAG, "Listening for fingerprints..."); fingerprintCancellationSignal = new CancellationSignal(); fingerprintManager.authenticate(null, 0, fingerprintCancellationSignal, fingerprintListener, null); - } else if (Build.VERSION.SDK_INT >= 21){ + } else { Log.i(TAG, "firing intent..."); Intent intent = keyguardManager.createConfirmDeviceCredentialIntent("Unlock Session", ""); startActivityForResult(intent, 1); - } else { - Log.w(TAG, "Not compatible..."); - handleAuthenticated(); } } @@ -282,54 +209,6 @@ public class PassphrasePromptActivity extends PassphraseActivity { } } - private class PassphraseActionListener implements TextView.OnEditorActionListener { - @Override - public boolean onEditorAction(TextView exampleView, int actionId, KeyEvent keyEvent) { - if ((keyEvent == null && actionId == EditorInfo.IME_ACTION_DONE) || - (keyEvent != null && keyEvent.getAction() == KeyEvent.ACTION_DOWN && - (actionId == EditorInfo.IME_NULL))) - { - handlePassphrase(); - return true; - } else if (keyEvent != null && keyEvent.getAction() == KeyEvent.ACTION_UP && - actionId == EditorInfo.IME_NULL) - { - return true; - } - - return false; - } - } - - private class OkButtonClickListener implements OnClickListener { - @Override - public void onClick(View v) { - handlePassphrase(); - } - } - - private class ShowButtonOnClickListener implements OnClickListener { - @Override - public void onClick(View v) { - visibilityToggle.display(hideButton); - setPassphraseVisibility(true); - } - } - - private class HideButtonOnClickListener implements OnClickListener { - @Override - public void onClick(View v) { - visibilityToggle.display(showButton); - setPassphraseVisibility(false); - } - } - - @Override - protected void cleanup() { - this.passphraseText.setText(""); - System.gc(); - } - private class FingerprintListener extends FingerprintManagerCompat.AuthenticationCallback { @Override public void onAuthenticationError(int errMsgId, CharSequence errString) { @@ -356,7 +235,6 @@ public class PassphrasePromptActivity extends PassphraseActivity { @Override public void onAuthenticationFailed() { Log.w(TAG, "onAuthenticatoinFailed()"); - FingerprintManagerCompat.AuthenticationCallback callback = this; fingerprintPrompt.setImageResource(R.drawable.ic_close_white_48dp); fingerprintPrompt.getBackground().setColorFilter(getResources().getColor(R.color.red_500), PorterDuff.Mode.SRC_IN); @@ -380,6 +258,5 @@ public class PassphrasePromptActivity extends PassphraseActivity { fingerprintPrompt.startAnimation(shake); } - } -} +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/PassphraseRequiredActionBarActivity.java b/app/src/main/java/org/thoughtcrime/securesms/PassphraseRequiredActionBarActivity.java index 720e4aa1fb..4b7960d50e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/PassphraseRequiredActionBarActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/PassphraseRequiredActionBarActivity.java @@ -10,8 +10,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; -import org.thoughtcrime.securesms.crypto.MasterSecretUtil; -import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob; import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.loki.activities.HomeActivity; import org.thoughtcrime.securesms.loki.activities.LandingActivity; @@ -27,7 +25,6 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA public static final String LOCALE_EXTRA = "locale_extra"; private static final int STATE_NORMAL = 0; - private static final int STATE_CREATE_PASSPHRASE = 1; private static final int STATE_PROMPT_PASSPHRASE = 2; private static final int STATE_UPGRADE_DATABASE = 3; private static final int STATE_PROMPT_PUSH_REGISTRATION = 4; @@ -118,7 +115,6 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA Log.i(TAG, "routeApplicationState(), state: " + state); switch (state) { - case STATE_CREATE_PASSPHRASE: return getCreatePassphraseIntent(); case STATE_PROMPT_PASSPHRASE: return getPromptPassphraseIntent(); case STATE_UPGRADE_DATABASE: return getUpgradeDatabaseIntent(); case STATE_WELCOME_SCREEN: return getWelcomeIntent(); @@ -129,9 +125,7 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA } private int getApplicationState(boolean locked) { - if (!MasterSecretUtil.isPassphraseInitialized(this)) { - return STATE_CREATE_PASSPHRASE; - } else if (locked) { + if (locked) { return STATE_PROMPT_PASSPHRASE; } else if (DatabaseUpgradeActivity.isUpdate(this)) { return STATE_UPGRADE_DATABASE; @@ -148,10 +142,6 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA } } - private Intent getCreatePassphraseIntent() { - return getRoutedIntent(PassphraseCreateActivity.class, getIntent()); - } - private Intent getPromptPassphraseIntent() { return getRoutedIntent(PassphrasePromptActivity.class, getIntent()); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/ShareActivity.java b/app/src/main/java/org/thoughtcrime/securesms/ShareActivity.java index acbb775f4e..45be4d26c7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ShareActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ShareActivity.java @@ -48,7 +48,6 @@ import org.thoughtcrime.securesms.mms.PartAuthority; import org.thoughtcrime.securesms.providers.BlobProvider; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.stickers.StickerLocator; -import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.ViewUtil; diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java index febd2cc5a4..624c029f88 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java @@ -48,8 +48,6 @@ import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.view.ActionMode; -import androidx.core.app.ActivityCompat; -import androidx.core.app.ActivityOptionsCompat; import androidx.fragment.app.Fragment; import androidx.loader.app.LoaderManager; import androidx.loader.content.Loader; @@ -66,8 +64,6 @@ import org.thoughtcrime.securesms.ShareActivity; import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.components.ConversationTypingView; import org.thoughtcrime.securesms.components.recyclerview.SmoothScrollingLinearLayoutManager; -import org.thoughtcrime.securesms.contactshare.Contact; -import org.thoughtcrime.securesms.contactshare.ContactUtil; import org.thoughtcrime.securesms.conversation.ConversationAdapter.HeaderViewHolder; import org.thoughtcrime.securesms.conversation.ConversationAdapter.ItemClickListener; import org.thoughtcrime.securesms.database.Address; diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/AsymmetricMasterCipher.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/AsymmetricMasterCipher.java deleted file mode 100644 index b625876729..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/AsymmetricMasterCipher.java +++ /dev/null @@ -1,138 +0,0 @@ -/** - * Copyright (C) 2011 Whisper Systems - * Copyright (C) 2013 Open Whisper Systems - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.thoughtcrime.securesms.crypto; - -import org.thoughtcrime.securesms.util.Base64; -import org.thoughtcrime.securesms.util.Util; -import org.session.libsignal.libsignal.InvalidKeyException; -import org.session.libsignal.libsignal.InvalidMessageException; -import org.session.libsignal.libsignal.ecc.Curve; -import org.session.libsignal.libsignal.ecc.ECKeyPair; -import org.session.libsignal.libsignal.ecc.ECPrivateKey; -import org.session.libsignal.libsignal.ecc.ECPublicKey; -import org.thoughtcrime.securesms.util.Conversions; - -import java.io.IOException; -import java.security.NoSuchAlgorithmException; - -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; - -/** - * This class is used to asymmetricly encrypt local data. This is used in the case - * where TextSecure receives an SMS, but the user's local encryption passphrase is - * not cached (either because of a timeout, or because it hasn't yet been entered). - * - * In this case, we have access to the public key of a local keypair. We encrypt - * the message with this, and put it into the DB. When the user enters their passphrase, - * we can get access to the private key of the local keypair, decrypt the message, and - * replace it into the DB with symmetric encryption. - * - * The encryption protocol is as follows: - * - * 1) Generate an ephemeral keypair. - * 2) Do ECDH with the public key of the local durable keypair. - * 3) Do KMF with the ECDH result to obtain a master secret. - * 4) Encrypt the message with that master secret. - * - * @author Moxie Marlinspike - * - */ -public class AsymmetricMasterCipher { - - private final AsymmetricMasterSecret asymmetricMasterSecret; - - public AsymmetricMasterCipher(AsymmetricMasterSecret asymmetricMasterSecret) { - this.asymmetricMasterSecret = asymmetricMasterSecret; - } - - public byte[] encryptBytes(byte[] body) { - try { - ECPublicKey theirPublic = asymmetricMasterSecret.getDjbPublicKey(); - ECKeyPair ourKeyPair = Curve.generateKeyPair(); - byte[] secret = Curve.calculateAgreement(theirPublic, ourKeyPair.getPrivateKey()); - MasterCipher masterCipher = getMasterCipherForSecret(secret); - byte[] encryptedBodyBytes = masterCipher.encryptBytes(body); - - PublicKey ourPublicKey = new PublicKey(31337, ourKeyPair.getPublicKey()); - byte[] publicKeyBytes = ourPublicKey.serialize(); - - return Util.combine(publicKeyBytes, encryptedBodyBytes); - } catch (InvalidKeyException e) { - throw new AssertionError(e); - } - } - - public byte[] decryptBytes(byte[] combined) throws IOException, InvalidMessageException { - try { - byte[][] parts = Util.split(combined, PublicKey.KEY_SIZE, combined.length - PublicKey.KEY_SIZE); - PublicKey theirPublicKey = new PublicKey(parts[0], 0); - - ECPrivateKey ourPrivateKey = asymmetricMasterSecret.getPrivateKey(); - byte[] secret = Curve.calculateAgreement(theirPublicKey.getKey(), ourPrivateKey); - MasterCipher masterCipher = getMasterCipherForSecret(secret); - - return masterCipher.decryptBytes(parts[1]); - } catch (InvalidKeyException e) { - throw new InvalidMessageException(e); - } - } - - public String decryptBody(String body) throws IOException, InvalidMessageException { - byte[] combined = Base64.decode(body); - return new String(decryptBytes(combined)); - } - - public String encryptBody(String body) { - return Base64.encodeBytes(encryptBytes(body.getBytes())); - } - - private MasterCipher getMasterCipherForSecret(byte[] secretBytes) { - SecretKeySpec cipherKey = deriveCipherKey(secretBytes); - SecretKeySpec macKey = deriveMacKey(secretBytes); - MasterSecret masterSecret = new MasterSecret(cipherKey, macKey); - - return new MasterCipher(masterSecret); - } - - private SecretKeySpec deriveMacKey(byte[] secretBytes) { - byte[] digestedBytes = getDigestedBytes(secretBytes, 1); - byte[] macKeyBytes = new byte[20]; - - System.arraycopy(digestedBytes, 0, macKeyBytes, 0, macKeyBytes.length); - return new SecretKeySpec(macKeyBytes, "HmacSHA1"); - } - - private SecretKeySpec deriveCipherKey(byte[] secretBytes) { - byte[] digestedBytes = getDigestedBytes(secretBytes, 0); - byte[] cipherKeyBytes = new byte[16]; - - System.arraycopy(digestedBytes, 0, cipherKeyBytes, 0, cipherKeyBytes.length); - return new SecretKeySpec(cipherKeyBytes, "AES"); - } - - private byte[] getDigestedBytes(byte[] secretBytes, int iteration) { - try { - Mac mac = Mac.getInstance("HmacSHA256"); - mac.init(new SecretKeySpec(secretBytes, "HmacSHA256")); - return mac.doFinal(Conversions.intToByteArray(iteration)); - } catch (NoSuchAlgorithmException | java.security.InvalidKeyException e) { - throw new AssertionError(e); - } - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java index 47cf0831ca..2c5ba26e4b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java @@ -40,15 +40,14 @@ import java.util.List; * * @author Moxie Marlinspike */ - +//TODO AC: Delete public class IdentityKeyUtil { + private static final String MASTER_SECRET_UTIL_PREFERENCES_NAME = "SecureSMS-Preferences"; + @SuppressWarnings("unused") private static final String TAG = IdentityKeyUtil.class.getSimpleName(); - private static final String IDENTITY_PUBLIC_KEY_CIPHERTEXT_LEGACY_PREF = "pref_identity_public_curve25519"; - private static final String IDENTITY_PRIVATE_KEY_CIPHERTEXT_LEGACY_PREF = "pref_identity_private_curve25519"; - public static final String IDENTITY_PUBLIC_KEY_PREF = "pref_identity_public_v3"; public static final String IDENTITY_PRIVATE_KEY_PREF = "pref_identity_private_v3"; public static final String ED25519_PUBLIC_KEY = "pref_ed25519_public_key"; @@ -56,7 +55,7 @@ public class IdentityKeyUtil { public static final String LOKI_SEED = "loki_seed"; public static boolean hasIdentityKey(Context context) { - SharedPreferences preferences = context.getSharedPreferences(MasterSecretUtil.PREFERENCES_NAME, 0); + SharedPreferences preferences = context.getSharedPreferences(MASTER_SECRET_UTIL_PREFERENCES_NAME, 0); return preferences.contains(IDENTITY_PUBLIC_KEY_PREF) && @@ -87,63 +86,38 @@ public class IdentityKeyUtil { } } - public static void generateIdentityKeyPair(Context context) { - ECKeyPair keyPair = Curve.generateKeyPair();; - IdentityKey publicKey = new IdentityKey(keyPair.getPublicKey()); - ECPrivateKey privateKey = keyPair.getPrivateKey(); - save(context, IDENTITY_PUBLIC_KEY_PREF, Base64.encodeBytes(publicKey.serialize())); - save(context, IDENTITY_PRIVATE_KEY_PREF, Base64.encodeBytes(privateKey.serialize())); - } - - public static void migrateIdentityKeys(@NonNull Context context, - @NonNull MasterSecret masterSecret) - { - if (!hasIdentityKey(context)) { - if (hasLegacyIdentityKeys(context)) { - IdentityKeyPair legacyPair = getLegacyIdentityKeyPair(context, masterSecret); - - save(context, IDENTITY_PUBLIC_KEY_PREF, Base64.encodeBytes(legacyPair.getPublicKey().serialize())); - save(context, IDENTITY_PRIVATE_KEY_PREF, Base64.encodeBytes(legacyPair.getPrivateKey().serialize())); - - delete(context, IDENTITY_PUBLIC_KEY_CIPHERTEXT_LEGACY_PREF); - delete(context, IDENTITY_PRIVATE_KEY_CIPHERTEXT_LEGACY_PREF); - } else { - generateIdentityKeyPair(context); - } - } - } - public static List getBackupRecords(@NonNull Context context) { - SharedPreferences preferences = context.getSharedPreferences(MasterSecretUtil.PREFERENCES_NAME, 0); + final String prefName = MASTER_SECRET_UTIL_PREFERENCES_NAME; + SharedPreferences preferences = context.getSharedPreferences(prefName, 0); LinkedList prefList = new LinkedList<>(); prefList.add(BackupProtos.SharedPreference.newBuilder() - .setFile(MasterSecretUtil.PREFERENCES_NAME) + .setFile(prefName) .setKey(IDENTITY_PUBLIC_KEY_PREF) .setValue(preferences.getString(IDENTITY_PUBLIC_KEY_PREF, null)) .build()); prefList.add(BackupProtos.SharedPreference.newBuilder() - .setFile(MasterSecretUtil.PREFERENCES_NAME) + .setFile(prefName) .setKey(IDENTITY_PRIVATE_KEY_PREF) .setValue(preferences.getString(IDENTITY_PRIVATE_KEY_PREF, null)) .build()); if (preferences.contains(ED25519_PUBLIC_KEY)) { prefList.add(BackupProtos.SharedPreference.newBuilder() - .setFile(MasterSecretUtil.PREFERENCES_NAME) + .setFile(prefName) .setKey(ED25519_PUBLIC_KEY) .setValue(preferences.getString(ED25519_PUBLIC_KEY, null)) .build()); } if (preferences.contains(ED25519_SECRET_KEY)) { prefList.add(BackupProtos.SharedPreference.newBuilder() - .setFile(MasterSecretUtil.PREFERENCES_NAME) + .setFile(prefName) .setKey(ED25519_SECRET_KEY) .setValue(preferences.getString(ED25519_SECRET_KEY, null)) .build()); } prefList.add(BackupProtos.SharedPreference.newBuilder() - .setFile(MasterSecretUtil.PREFERENCES_NAME) + .setFile(prefName) .setKey(LOKI_SEED) .setValue(preferences.getString(LOKI_SEED, null)) .build()); @@ -151,34 +125,13 @@ public class IdentityKeyUtil { return prefList; } - private static boolean hasLegacyIdentityKeys(Context context) { - return - retrieve(context, IDENTITY_PUBLIC_KEY_CIPHERTEXT_LEGACY_PREF) != null && - retrieve(context, IDENTITY_PRIVATE_KEY_CIPHERTEXT_LEGACY_PREF) != null; - } - - private static IdentityKeyPair getLegacyIdentityKeyPair(@NonNull Context context, - @NonNull MasterSecret masterSecret) - { - try { - MasterCipher masterCipher = new MasterCipher(masterSecret); - byte[] publicKeyBytes = Base64.decode(retrieve(context, IDENTITY_PUBLIC_KEY_CIPHERTEXT_LEGACY_PREF)); - IdentityKey identityKey = new IdentityKey(publicKeyBytes, 0); - ECPrivateKey privateKey = masterCipher.decryptKey(Base64.decode(retrieve(context, IDENTITY_PRIVATE_KEY_CIPHERTEXT_LEGACY_PREF))); - - return new IdentityKeyPair(identityKey, privateKey); - } catch (IOException | InvalidKeyException e) { - throw new AssertionError(e); - } - } - public static String retrieve(Context context, String key) { - SharedPreferences preferences = context.getSharedPreferences(MasterSecretUtil.PREFERENCES_NAME, 0); + SharedPreferences preferences = context.getSharedPreferences(MASTER_SECRET_UTIL_PREFERENCES_NAME, 0); return preferences.getString(key, null); } public static void save(Context context, String key, String value) { - SharedPreferences preferences = context.getSharedPreferences(MasterSecretUtil.PREFERENCES_NAME, 0); + SharedPreferences preferences = context.getSharedPreferences(MASTER_SECRET_UTIL_PREFERENCES_NAME, 0); Editor preferencesEditor = preferences.edit(); preferencesEditor.putString(key, value); @@ -186,6 +139,6 @@ public class IdentityKeyUtil { } public static void delete(Context context, String key) { - context.getSharedPreferences(MasterSecretUtil.PREFERENCES_NAME, 0).edit().remove(key).commit(); + context.getSharedPreferences(MASTER_SECRET_UTIL_PREFERENCES_NAME, 0).edit().remove(key).commit(); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/MasterCipher.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/MasterCipher.java deleted file mode 100644 index ca6bfca465..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/MasterCipher.java +++ /dev/null @@ -1,225 +0,0 @@ -/** - * Copyright (C) 2011 Whisper Systems - * Copyright (C) 2013 Open Whisper Systems - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.thoughtcrime.securesms.crypto; - -import androidx.annotation.NonNull; -import org.thoughtcrime.securesms.logging.Log; - -import org.thoughtcrime.securesms.util.Base64; -import org.thoughtcrime.securesms.util.Hex; -import org.session.libsignal.libsignal.InvalidMessageException; -import org.session.libsignal.libsignal.ecc.Curve; -import org.session.libsignal.libsignal.ecc.ECPrivateKey; - -import java.io.IOException; -import java.security.GeneralSecurityException; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.util.Arrays; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.Mac; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -/** - * Class that handles encryption for local storage. - * - * The protocol format is roughly: - * - * 1) 16 byte random IV. - * 2) AES-CBC(plaintext) - * 3) HMAC-SHA1 of 1 and 2 - * - * @author Moxie Marlinspike - */ - -public class MasterCipher { - - private static final String TAG = MasterCipher.class.getSimpleName(); - - private final MasterSecret masterSecret; - private final Cipher encryptingCipher; - private final Cipher decryptingCipher; - private final Mac hmac; - - public MasterCipher(MasterSecret masterSecret) { - try { - this.masterSecret = masterSecret; - this.encryptingCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - this.decryptingCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - this.hmac = Mac.getInstance("HmacSHA1"); - } catch (NoSuchPaddingException | NoSuchAlgorithmException nspe) { - throw new AssertionError(nspe); - } - } - - public byte[] encryptKey(ECPrivateKey privateKey) { - return encryptBytes(privateKey.serialize()); - } - - public String encryptBody(@NonNull String body) { - return encryptAndEncodeBytes(body.getBytes()); - } - - public String decryptBody(String body) throws InvalidMessageException { - return new String(decodeAndDecryptBytes(body)); - } - - public ECPrivateKey decryptKey(byte[] key) - throws org.session.libsignal.libsignal.InvalidKeyException - { - try { - return Curve.decodePrivatePoint(decryptBytes(key)); - } catch (InvalidMessageException ime) { - throw new org.session.libsignal.libsignal.InvalidKeyException(ime); - } - } - - public byte[] decryptBytes(@NonNull byte[] decodedBody) throws InvalidMessageException { - try { - Mac mac = getMac(masterSecret.getMacKey()); - byte[] encryptedBody = verifyMacBody(mac, decodedBody); - - Cipher cipher = getDecryptingCipher(masterSecret.getEncryptionKey(), encryptedBody); - byte[] encrypted = getDecryptedBody(cipher, encryptedBody); - - return encrypted; - } catch (GeneralSecurityException ge) { - throw new InvalidMessageException(ge); - } - } - - public byte[] encryptBytes(byte[] body) { - try { - Cipher cipher = getEncryptingCipher(masterSecret.getEncryptionKey()); - Mac mac = getMac(masterSecret.getMacKey()); - - byte[] encryptedBody = getEncryptedBody(cipher, body); - byte[] encryptedAndMacBody = getMacBody(mac, encryptedBody); - - return encryptedAndMacBody; - } catch (GeneralSecurityException ge) { - Log.w("bodycipher", ge); - return null; - } - - } - - public boolean verifyMacFor(String content, byte[] theirMac) { - byte[] ourMac = getMacFor(content); - Log.i(TAG, "Our Mac: " + Hex.toString(ourMac)); - Log.i(TAG, "Thr Mac: " + Hex.toString(theirMac)); - return Arrays.equals(ourMac, theirMac); - } - - public byte[] getMacFor(String content) { - Log.w(TAG, "Macing: " + content); - try { - Mac mac = getMac(masterSecret.getMacKey()); - return mac.doFinal(content.getBytes()); - } catch (GeneralSecurityException ike) { - throw new AssertionError(ike); - } - } - - private byte[] decodeAndDecryptBytes(String body) throws InvalidMessageException { - try { - byte[] decodedBody = Base64.decode(body); - return decryptBytes(decodedBody); - } catch (IOException e) { - throw new InvalidMessageException("Bad Base64 Encoding...", e); - } - } - - private String encryptAndEncodeBytes(@NonNull byte[] bytes) { - byte[] encryptedAndMacBody = encryptBytes(bytes); - return Base64.encodeBytes(encryptedAndMacBody); - } - - private byte[] verifyMacBody(@NonNull Mac hmac, @NonNull byte[] encryptedAndMac) throws InvalidMessageException { - if (encryptedAndMac.length < hmac.getMacLength()) { - throw new InvalidMessageException("length(encrypted body + MAC) < length(MAC)"); - } - - byte[] encrypted = new byte[encryptedAndMac.length - hmac.getMacLength()]; - System.arraycopy(encryptedAndMac, 0, encrypted, 0, encrypted.length); - - byte[] remoteMac = new byte[hmac.getMacLength()]; - System.arraycopy(encryptedAndMac, encryptedAndMac.length - remoteMac.length, remoteMac, 0, remoteMac.length); - - byte[] localMac = hmac.doFinal(encrypted); - - if (!Arrays.equals(remoteMac, localMac)) - throw new InvalidMessageException("MAC doesen't match."); - - return encrypted; - } - - private byte[] getDecryptedBody(Cipher cipher, byte[] encryptedBody) throws IllegalBlockSizeException, BadPaddingException { - return cipher.doFinal(encryptedBody, cipher.getBlockSize(), encryptedBody.length - cipher.getBlockSize()); - } - - private byte[] getEncryptedBody(Cipher cipher, byte[] body) throws IllegalBlockSizeException, BadPaddingException { - byte[] encrypted = cipher.doFinal(body); - byte[] iv = cipher.getIV(); - - byte[] ivAndBody = new byte[iv.length + encrypted.length]; - System.arraycopy(iv, 0, ivAndBody, 0, iv.length); - System.arraycopy(encrypted, 0, ivAndBody, iv.length, encrypted.length); - - return ivAndBody; - } - - private Mac getMac(SecretKeySpec key) throws NoSuchAlgorithmException, InvalidKeyException { - // Mac hmac = Mac.getInstance("HmacSHA1"); - hmac.init(key); - - return hmac; - } - - private byte[] getMacBody(Mac hmac, byte[] encryptedBody) { - byte[] mac = hmac.doFinal(encryptedBody); - byte[] encryptedAndMac = new byte[encryptedBody.length + mac.length]; - - System.arraycopy(encryptedBody, 0, encryptedAndMac, 0, encryptedBody.length); - System.arraycopy(mac, 0, encryptedAndMac, encryptedBody.length, mac.length); - - return encryptedAndMac; - } - - private Cipher getDecryptingCipher(SecretKeySpec key, byte[] encryptedBody) throws InvalidKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchPaddingException { - // Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - IvParameterSpec iv = new IvParameterSpec(encryptedBody, 0, decryptingCipher.getBlockSize()); - decryptingCipher.init(Cipher.DECRYPT_MODE, key, iv); - - return decryptingCipher; - } - - private Cipher getEncryptingCipher(SecretKeySpec key) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException { - // Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - encryptingCipher.init(Cipher.ENCRYPT_MODE, key); - - return encryptingCipher; - } - -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/MasterSecret.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/MasterSecret.java deleted file mode 100644 index e849aae617..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/MasterSecret.java +++ /dev/null @@ -1,119 +0,0 @@ -/** - * Copyright (C) 2011 Whisper Systems - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.thoughtcrime.securesms.crypto; - -import android.os.Parcel; -import android.os.Parcelable; - -import javax.crypto.spec.SecretKeySpec; -import java.util.Arrays; - -/** - * When a user first initializes TextSecure, a few secrets - * are generated. These are: - * - * 1) A 128bit symmetric encryption key. - * 2) A 160bit symmetric MAC key. - * 3) An ECC keypair. - * - * The first two, along with the ECC keypair's private key, are - * then encrypted on disk using PBE. - * - * This class represents 1 and 2. - * - * @author Moxie Marlinspike - */ - -public class MasterSecret implements Parcelable { - - private final SecretKeySpec encryptionKey; - private final SecretKeySpec macKey; - - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - @Override - public MasterSecret createFromParcel(Parcel in) { - return new MasterSecret(in); - } - - @Override - public MasterSecret[] newArray(int size) { - return new MasterSecret[size]; - } - }; - - public MasterSecret(SecretKeySpec encryptionKey, SecretKeySpec macKey) { - this.encryptionKey = encryptionKey; - this.macKey = macKey; - } - - private MasterSecret(Parcel in) { - byte[] encryptionKeyBytes = new byte[in.readInt()]; - in.readByteArray(encryptionKeyBytes); - - byte[] macKeyBytes = new byte[in.readInt()]; - in.readByteArray(macKeyBytes); - - this.encryptionKey = new SecretKeySpec(encryptionKeyBytes, "AES"); - this.macKey = new SecretKeySpec(macKeyBytes, "HmacSHA1"); - - // SecretKeySpec does an internal copy in its constructor. - Arrays.fill(encryptionKeyBytes, (byte) 0x00); - Arrays.fill(macKeyBytes, (byte)0x00); - } - - - public SecretKeySpec getEncryptionKey() { - return this.encryptionKey; - } - - public SecretKeySpec getMacKey() { - return this.macKey; - } - - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeInt(encryptionKey.getEncoded().length); - out.writeByteArray(encryptionKey.getEncoded()); - out.writeInt(macKey.getEncoded().length); - out.writeByteArray(macKey.getEncoded()); - } - - @Override - public int describeContents() { - return 0; - } - - public MasterSecret parcelClone() { - Parcel thisParcel = Parcel.obtain(); - Parcel thatParcel = Parcel.obtain(); - byte[] bytes = null; - - thisParcel.writeValue(this); - bytes = thisParcel.marshall(); - - thatParcel.unmarshall(bytes, 0, bytes.length); - thatParcel.setDataPosition(0); - - MasterSecret that = (MasterSecret)thatParcel.readValue(MasterSecret.class.getClassLoader()); - - thisParcel.recycle(); - thatParcel.recycle(); - - return that; - } - -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/MasterSecretUtil.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/MasterSecretUtil.java deleted file mode 100644 index 1797941ff1..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/MasterSecretUtil.java +++ /dev/null @@ -1,374 +0,0 @@ -/** - * Copyright (C) 2011 Whisper Systems - * Copyright (C) 2013 Open Whisper Systems - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.thoughtcrime.securesms.crypto; - -import android.content.Context; -import android.content.SharedPreferences; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import android.text.TextUtils; -import org.thoughtcrime.securesms.logging.Log; - -import org.thoughtcrime.securesms.util.Base64; -import org.thoughtcrime.securesms.util.Util; -import org.session.libsignal.libsignal.InvalidKeyException; -import org.session.libsignal.libsignal.ecc.Curve; -import org.session.libsignal.libsignal.ecc.ECKeyPair; -import org.session.libsignal.libsignal.ecc.ECPrivateKey; -import org.session.libsignal.libsignal.ecc.ECPublicKey; - -import java.io.IOException; -import java.security.GeneralSecurityException; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.security.spec.InvalidKeySpecException; -import java.util.Arrays; - -import javax.crypto.Cipher; -import javax.crypto.KeyGenerator; -import javax.crypto.Mac; -import javax.crypto.SecretKey; -import javax.crypto.SecretKeyFactory; -import javax.crypto.spec.PBEKeySpec; -import javax.crypto.spec.PBEParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -/** - * Helper class for generating and securely storing a MasterSecret. - * - * @author Moxie Marlinspike - */ - -public class MasterSecretUtil { - - public static final String UNENCRYPTED_PASSPHRASE = "unencrypted"; - public static final String PREFERENCES_NAME = "SecureSMS-Preferences"; - - private static final String ASYMMETRIC_LOCAL_PUBLIC_DJB = "asymmetric_master_secret_curve25519_public"; - private static final String ASYMMETRIC_LOCAL_PRIVATE_DJB = "asymmetric_master_secret_curve25519_private"; - - public static MasterSecret changeMasterSecretPassphrase(Context context, - MasterSecret masterSecret, - String newPassphrase) - { - try { - byte[] combinedSecrets = Util.combine(masterSecret.getEncryptionKey().getEncoded(), - masterSecret.getMacKey().getEncoded()); - - byte[] encryptionSalt = generateSalt(); - int iterations = generateIterationCount(newPassphrase, encryptionSalt); - byte[] encryptedMasterSecret = encryptWithPassphrase(encryptionSalt, iterations, combinedSecrets, newPassphrase); - byte[] macSalt = generateSalt(); - byte[] encryptedAndMacdMasterSecret = macWithPassphrase(macSalt, iterations, encryptedMasterSecret, newPassphrase); - - save(context, "encryption_salt", encryptionSalt); - save(context, "mac_salt", macSalt); - save(context, "passphrase_iterations", iterations); - save(context, "master_secret", encryptedAndMacdMasterSecret); - save(context, "passphrase_initialized", true); - - return masterSecret; - } catch (GeneralSecurityException gse) { - throw new AssertionError(gse); - } - } - - public static MasterSecret changeMasterSecretPassphrase(Context context, - String originalPassphrase, - String newPassphrase) - throws InvalidPassphraseException - { - MasterSecret masterSecret = getMasterSecret(context, originalPassphrase); - changeMasterSecretPassphrase(context, masterSecret, newPassphrase); - - return masterSecret; - } - - public static MasterSecret getMasterSecret(Context context, String passphrase) - throws InvalidPassphraseException - { - try { - byte[] encryptedAndMacdMasterSecret = retrieve(context, "master_secret"); - byte[] macSalt = retrieve(context, "mac_salt"); - int iterations = retrieve(context, "passphrase_iterations", 100); - byte[] encryptedMasterSecret = verifyMac(macSalt, iterations, encryptedAndMacdMasterSecret, passphrase); - byte[] encryptionSalt = retrieve(context, "encryption_salt"); - byte[] combinedSecrets = decryptWithPassphrase(encryptionSalt, iterations, encryptedMasterSecret, passphrase); - byte[] encryptionSecret = Util.split(combinedSecrets, 16, 20)[0]; - byte[] macSecret = Util.split(combinedSecrets, 16, 20)[1]; - - return new MasterSecret(new SecretKeySpec(encryptionSecret, "AES"), - new SecretKeySpec(macSecret, "HmacSHA1")); - } catch (GeneralSecurityException e) { - Log.w("keyutil", e); - return null; //XXX - } catch (IOException e) { - Log.w("keyutil", e); - return null; //XXX - } - } - - public static AsymmetricMasterSecret getAsymmetricMasterSecret(@NonNull Context context, - @Nullable MasterSecret masterSecret) - { - try { - byte[] djbPublicBytes = retrieve(context, ASYMMETRIC_LOCAL_PUBLIC_DJB); - byte[] djbPrivateBytes = retrieve(context, ASYMMETRIC_LOCAL_PRIVATE_DJB); - - ECPublicKey djbPublicKey = null; - ECPrivateKey djbPrivateKey = null; - - if (djbPublicBytes != null) { - djbPublicKey = Curve.decodePoint(djbPublicBytes, 0); - } - - if (masterSecret != null) { - MasterCipher masterCipher = new MasterCipher(masterSecret); - - if (djbPrivateBytes != null) { - djbPrivateKey = masterCipher.decryptKey(djbPrivateBytes); - } - } - - return new AsymmetricMasterSecret(djbPublicKey, djbPrivateKey); - } catch (InvalidKeyException | IOException ike) { - throw new AssertionError(ike); - } - } - - public static AsymmetricMasterSecret generateAsymmetricMasterSecret(Context context, - MasterSecret masterSecret) - { - MasterCipher masterCipher = new MasterCipher(masterSecret); - ECKeyPair keyPair = Curve.generateKeyPair(); - - save(context, ASYMMETRIC_LOCAL_PUBLIC_DJB, keyPair.getPublicKey().serialize()); - save(context, ASYMMETRIC_LOCAL_PRIVATE_DJB, masterCipher.encryptKey(keyPair.getPrivateKey())); - - return new AsymmetricMasterSecret(keyPair.getPublicKey(), keyPair.getPrivateKey()); - } - - public static MasterSecret generateMasterSecret(Context context, String passphrase) { - try { - byte[] encryptionSecret = generateEncryptionSecret(); - byte[] macSecret = generateMacSecret(); - byte[] masterSecret = Util.combine(encryptionSecret, macSecret); - byte[] encryptionSalt = generateSalt(); - int iterations = generateIterationCount(passphrase, encryptionSalt); - byte[] encryptedMasterSecret = encryptWithPassphrase(encryptionSalt, iterations, masterSecret, passphrase); - byte[] macSalt = generateSalt(); - byte[] encryptedAndMacdMasterSecret = macWithPassphrase(macSalt, iterations, encryptedMasterSecret, passphrase); - - save(context, "encryption_salt", encryptionSalt); - save(context, "mac_salt", macSalt); - save(context, "passphrase_iterations", iterations); - save(context, "master_secret", encryptedAndMacdMasterSecret); - save(context, "passphrase_initialized", true); - - return new MasterSecret(new SecretKeySpec(encryptionSecret, "AES"), - new SecretKeySpec(macSecret, "HmacSHA1")); - } catch (GeneralSecurityException e) { - Log.w("keyutil", e); - return null; - } - } - - public static boolean hasAsymmericMasterSecret(Context context) { - SharedPreferences settings = context.getSharedPreferences(PREFERENCES_NAME, 0); - return settings.contains(ASYMMETRIC_LOCAL_PUBLIC_DJB); - } - - public static boolean isPassphraseInitialized(Context context) { - SharedPreferences preferences = context.getSharedPreferences(PREFERENCES_NAME, 0); - return preferences.getBoolean("passphrase_initialized", false); - } - - public static void clear(Context context) { - context.getSharedPreferences(PREFERENCES_NAME, 0).edit().clear().commit(); - } - - private static void save(Context context, String key, int value) { - if (!context.getSharedPreferences(PREFERENCES_NAME, 0) - .edit() - .putInt(key, value) - .commit()) - { - throw new AssertionError("failed to save a shared pref in MasterSecretUtil"); - } - } - - private static void save(Context context, String key, byte[] value) { - if (!context.getSharedPreferences(PREFERENCES_NAME, 0) - .edit() - .putString(key, Base64.encodeBytes(value)) - .commit()) - { - throw new AssertionError("failed to save a shared pref in MasterSecretUtil"); - } - } - - private static void save(Context context, String key, boolean value) { - if (!context.getSharedPreferences(PREFERENCES_NAME, 0) - .edit() - .putBoolean(key, value) - .commit()) - { - throw new AssertionError("failed to save a shared pref in MasterSecretUtil"); - } - } - - private static byte[] retrieve(Context context, String key) throws IOException { - SharedPreferences settings = context.getSharedPreferences(PREFERENCES_NAME, 0); - String encodedValue = settings.getString(key, ""); - - if (TextUtils.isEmpty(encodedValue)) return null; - else return Base64.decode(encodedValue); - } - - private static int retrieve(Context context, String key, int defaultValue) throws IOException { - SharedPreferences settings = context.getSharedPreferences(PREFERENCES_NAME, 0); - return settings.getInt(key, defaultValue); - } - - private static byte[] generateEncryptionSecret() { - try { - KeyGenerator generator = KeyGenerator.getInstance("AES"); - generator.init(128); - - SecretKey key = generator.generateKey(); - return key.getEncoded(); - } catch (NoSuchAlgorithmException ex) { - Log.w("keyutil", ex); - return null; - } - } - - private static byte[] generateMacSecret() { - try { - KeyGenerator generator = KeyGenerator.getInstance("HmacSHA1"); - return generator.generateKey().getEncoded(); - } catch (NoSuchAlgorithmException e) { - Log.w("keyutil", e); - return null; - } - } - - private static byte[] generateSalt() { - SecureRandom random = new SecureRandom(); - byte[] salt = new byte[16]; - random.nextBytes(salt); - - return salt; - } - - private static int generateIterationCount(String passphrase, byte[] salt) { - int TARGET_ITERATION_TIME = 50; //ms - int MINIMUM_ITERATION_COUNT = 100; //default for low-end devices - int BENCHMARK_ITERATION_COUNT = 10000; //baseline starting iteration count - - try { - PBEKeySpec keyspec = new PBEKeySpec(passphrase.toCharArray(), salt, BENCHMARK_ITERATION_COUNT); - SecretKeyFactory skf = SecretKeyFactory.getInstance("PBEWITHSHA1AND128BITAES-CBC-BC"); - - long startTime = System.currentTimeMillis(); - skf.generateSecret(keyspec); - long finishTime = System.currentTimeMillis(); - - int scaledIterationTarget = (int) (((double)BENCHMARK_ITERATION_COUNT / (double)(finishTime - startTime)) * TARGET_ITERATION_TIME); - - if (scaledIterationTarget < MINIMUM_ITERATION_COUNT) return MINIMUM_ITERATION_COUNT; - else return scaledIterationTarget; - } catch (NoSuchAlgorithmException e) { - Log.w("MasterSecretUtil", e); - return MINIMUM_ITERATION_COUNT; - } catch (InvalidKeySpecException e) { - Log.w("MasterSecretUtil", e); - return MINIMUM_ITERATION_COUNT; - } - } - - private static SecretKey getKeyFromPassphrase(String passphrase, byte[] salt, int iterations) - throws GeneralSecurityException - { - PBEKeySpec keyspec = new PBEKeySpec(passphrase.toCharArray(), salt, iterations); - SecretKeyFactory skf = SecretKeyFactory.getInstance("PBEWITHSHA1AND128BITAES-CBC-BC"); - return skf.generateSecret(keyspec); - } - - private static Cipher getCipherFromPassphrase(String passphrase, byte[] salt, int iterations, int opMode) - throws GeneralSecurityException - { - SecretKey key = getKeyFromPassphrase(passphrase, salt, iterations); - Cipher cipher = Cipher.getInstance(key.getAlgorithm()); - cipher.init(opMode, key, new PBEParameterSpec(salt, iterations)); - - return cipher; - } - - private static byte[] encryptWithPassphrase(byte[] encryptionSalt, int iterations, byte[] data, String passphrase) - throws GeneralSecurityException - { - Cipher cipher = getCipherFromPassphrase(passphrase, encryptionSalt, iterations, Cipher.ENCRYPT_MODE); - return cipher.doFinal(data); - } - - private static byte[] decryptWithPassphrase(byte[] encryptionSalt, int iterations, byte[] data, String passphrase) - throws GeneralSecurityException, IOException - { - Cipher cipher = getCipherFromPassphrase(passphrase, encryptionSalt, iterations, Cipher.DECRYPT_MODE); - return cipher.doFinal(data); - } - - private static Mac getMacForPassphrase(String passphrase, byte[] salt, int iterations) - throws GeneralSecurityException - { - SecretKey key = getKeyFromPassphrase(passphrase, salt, iterations); - byte[] pbkdf2 = key.getEncoded(); - SecretKeySpec hmacKey = new SecretKeySpec(pbkdf2, "HmacSHA1"); - Mac hmac = Mac.getInstance("HmacSHA1"); - hmac.init(hmacKey); - - return hmac; - } - - private static byte[] verifyMac(byte[] macSalt, int iterations, byte[] encryptedAndMacdData, String passphrase) throws InvalidPassphraseException, GeneralSecurityException, IOException { - Mac hmac = getMacForPassphrase(passphrase, macSalt, iterations); - - byte[] encryptedData = new byte[encryptedAndMacdData.length - hmac.getMacLength()]; - System.arraycopy(encryptedAndMacdData, 0, encryptedData, 0, encryptedData.length); - - byte[] givenMac = new byte[hmac.getMacLength()]; - System.arraycopy(encryptedAndMacdData, encryptedAndMacdData.length-hmac.getMacLength(), givenMac, 0, givenMac.length); - - byte[] localMac = hmac.doFinal(encryptedData); - - if (Arrays.equals(givenMac, localMac)) return encryptedData; - else throw new InvalidPassphraseException("MAC Error"); - } - - private static byte[] macWithPassphrase(byte[] macSalt, int iterations, byte[] data, String passphrase) throws GeneralSecurityException { - Mac hmac = getMacForPassphrase(passphrase, macSalt, iterations); - byte[] mac = hmac.doFinal(data); - byte[] result = new byte[data.length + mac.length]; - - System.arraycopy(data, 0, result, 0, data.length); - System.arraycopy(mac, 0, result, data.length, mac.length); - - return result; - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/DatabaseFactory.java b/app/src/main/java/org/thoughtcrime/securesms/database/DatabaseFactory.java index f02e1d9c05..7289cc3d5d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/DatabaseFactory.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/DatabaseFactory.java @@ -17,18 +17,15 @@ package org.thoughtcrime.securesms.database; import android.content.Context; + import androidx.annotation.NonNull; import net.sqlcipher.database.SQLiteDatabase; -import org.thoughtcrime.securesms.DatabaseUpgradeActivity; import org.thoughtcrime.securesms.crypto.AttachmentSecret; import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider; import org.thoughtcrime.securesms.crypto.DatabaseSecret; import org.thoughtcrime.securesms.crypto.DatabaseSecretProvider; -import org.thoughtcrime.securesms.crypto.MasterSecret; -import org.thoughtcrime.securesms.database.helpers.ClassicOpenHelper; -import org.thoughtcrime.securesms.database.helpers.SQLCipherMigrationHelper; import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase; import org.thoughtcrime.securesms.loki.database.LokiBackupFilesDatabase; @@ -36,7 +33,6 @@ import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase; import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase; import org.thoughtcrime.securesms.loki.database.LokiUserDatabase; import org.thoughtcrime.securesms.loki.database.SharedSenderKeysDatabase; -import org.thoughtcrime.securesms.util.TextSecurePreferences; public class DatabaseFactory { @@ -227,28 +223,4 @@ public class DatabaseFactory { this.sskDatabase = new SharedSenderKeysDatabase(context, databaseHelper); } - public void onApplicationLevelUpgrade(@NonNull Context context, @NonNull MasterSecret masterSecret, - int fromVersion, DatabaseUpgradeActivity.DatabaseUpgradeListener listener) - { - databaseHelper.getWritableDatabase(); - - ClassicOpenHelper legacyOpenHelper = null; - - if (fromVersion < DatabaseUpgradeActivity.ASYMMETRIC_MASTER_SECRET_FIX_VERSION) { - legacyOpenHelper = new ClassicOpenHelper(context); - legacyOpenHelper.onApplicationLevelUpgrade(context, masterSecret, fromVersion, listener); - } - - if (fromVersion < DatabaseUpgradeActivity.SQLCIPHER && TextSecurePreferences.getNeedsSqlCipherMigration(context)) { - if (legacyOpenHelper == null) { - legacyOpenHelper = new ClassicOpenHelper(context); - } - - SQLCipherMigrationHelper.migrateCiphertext(context, masterSecret, - legacyOpenHelper.getWritableDatabase(), - databaseHelper.getWritableDatabase(), - listener); - } - } - } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/ClassicOpenHelper.java b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/ClassicOpenHelper.java deleted file mode 100644 index 1ad2418169..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/ClassicOpenHelper.java +++ /dev/null @@ -1,1448 +0,0 @@ -package org.thoughtcrime.securesms.database.helpers; - - -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.database.sqlite.SQLiteConstraintException; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import androidx.annotation.Nullable; -import android.text.TextUtils; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.DatabaseUpgradeActivity; -import org.thoughtcrime.securesms.crypto.AttachmentSecret; -import org.thoughtcrime.securesms.crypto.ClassicDecryptingPartInputStream; -import org.thoughtcrime.securesms.crypto.MasterCipher; -import org.thoughtcrime.securesms.crypto.MasterSecret; -import org.thoughtcrime.securesms.crypto.MasterSecretUtil; -import org.thoughtcrime.securesms.database.AttachmentDatabase; -import org.thoughtcrime.securesms.database.DraftDatabase; -import org.thoughtcrime.securesms.database.GroupDatabase; -import org.thoughtcrime.securesms.database.GroupReceiptDatabase; -import org.thoughtcrime.securesms.database.MmsDatabase; -import org.thoughtcrime.securesms.database.PushDatabase; -import org.thoughtcrime.securesms.database.RecipientDatabase; -import org.thoughtcrime.securesms.database.SmsDatabase; -import org.thoughtcrime.securesms.database.ThreadDatabase; -import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.util.Base64; -import org.thoughtcrime.securesms.util.DelimiterUtil; -import org.thoughtcrime.securesms.util.Hex; -import org.thoughtcrime.securesms.util.JsonUtils; -import org.thoughtcrime.securesms.util.MediaUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; -import org.session.libsignal.libsignal.IdentityKey; -import org.session.libsignal.libsignal.InvalidMessageException; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.security.SecureRandom; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import java.util.regex.Pattern; - -public class ClassicOpenHelper extends SQLiteOpenHelper { - - static final String NAME = "messages.db"; - - private static final int INTRODUCED_IDENTITIES_VERSION = 2; - private static final int INTRODUCED_INDEXES_VERSION = 3; - private static final int INTRODUCED_DATE_SENT_VERSION = 4; - private static final int INTRODUCED_DRAFTS_VERSION = 5; - private static final int INTRODUCED_NEW_TYPES_VERSION = 6; - private static final int INTRODUCED_MMS_BODY_VERSION = 7; - private static final int INTRODUCED_MMS_FROM_VERSION = 8; - private static final int INTRODUCED_TOFU_IDENTITY_VERSION = 9; - private static final int INTRODUCED_PUSH_DATABASE_VERSION = 10; - private static final int INTRODUCED_GROUP_DATABASE_VERSION = 11; - private static final int INTRODUCED_PUSH_FIX_VERSION = 12; - private static final int INTRODUCED_DELIVERY_RECEIPTS = 13; - private static final int INTRODUCED_PART_DATA_SIZE_VERSION = 14; - private static final int INTRODUCED_THUMBNAILS_VERSION = 15; - private static final int INTRODUCED_IDENTITY_COLUMN_VERSION = 16; - private static final int INTRODUCED_UNIQUE_PART_IDS_VERSION = 17; - private static final int INTRODUCED_RECIPIENT_PREFS_DB = 18; - private static final int INTRODUCED_ENVELOPE_CONTENT_VERSION = 19; - private static final int INTRODUCED_COLOR_PREFERENCE_VERSION = 20; - private static final int INTRODUCED_DB_OPTIMIZATIONS_VERSION = 21; - private static final int INTRODUCED_INVITE_REMINDERS_VERSION = 22; - private static final int INTRODUCED_CONVERSATION_LIST_THUMBNAILS_VERSION = 23; - private static final int INTRODUCED_ARCHIVE_VERSION = 24; - private static final int INTRODUCED_CONVERSATION_LIST_STATUS_VERSION = 25; - private static final int MIGRATED_CONVERSATION_LIST_STATUS_VERSION = 26; - private static final int INTRODUCED_SUBSCRIPTION_ID_VERSION = 27; - private static final int INTRODUCED_EXPIRE_MESSAGES_VERSION = 28; - private static final int INTRODUCED_LAST_SEEN = 29; - private static final int INTRODUCED_DIGEST = 30; - private static final int INTRODUCED_NOTIFIED = 31; - private static final int INTRODUCED_DOCUMENTS = 32; - private static final int INTRODUCED_FAST_PREFLIGHT = 33; - private static final int INTRODUCED_VOICE_NOTES = 34; - private static final int INTRODUCED_IDENTITY_TIMESTAMP = 35; - private static final int SANIFY_ATTACHMENT_DOWNLOAD = 36; - private static final int NO_MORE_CANONICAL_ADDRESS_DATABASE = 37; - private static final int NO_MORE_RECIPIENTS_PLURAL = 38; - private static final int INTERNAL_DIRECTORY = 39; - private static final int INTERNAL_SYSTEM_DISPLAY_NAME = 40; - private static final int PROFILES = 41; - private static final int PROFILE_SHARING_APPROVAL = 42; - private static final int UNSEEN_NUMBER_OFFER = 43; - private static final int READ_RECEIPTS = 44; - private static final int GROUP_RECEIPT_TRACKING = 45; - private static final int UNREAD_COUNT_VERSION = 46; - private static final int MORE_RECIPIENT_FIELDS = 47; - private static final int DATABASE_VERSION = 47; - - private static final String TAG = ClassicOpenHelper.class.getSimpleName(); - - private final Context context; - - public ClassicOpenHelper(Context context) { - super(context, NAME, null, DATABASE_VERSION); - this.context = context.getApplicationContext(); - } - - @Override - public void onCreate(SQLiteDatabase db) { - db.execSQL(SmsDatabase.CREATE_TABLE); - db.execSQL(MmsDatabase.CREATE_TABLE); - db.execSQL(AttachmentDatabase.CREATE_TABLE); - db.execSQL(ThreadDatabase.CREATE_TABLE); - db.execSQL(DraftDatabase.CREATE_TABLE); - db.execSQL(PushDatabase.CREATE_TABLE); - db.execSQL(GroupDatabase.CREATE_TABLE); - db.execSQL(RecipientDatabase.CREATE_TABLE); - db.execSQL(GroupReceiptDatabase.CREATE_TABLE); - - executeStatements(db, SmsDatabase.CREATE_INDEXS); - executeStatements(db, MmsDatabase.CREATE_INDEXS); - executeStatements(db, AttachmentDatabase.CREATE_INDEXS); - executeStatements(db, ThreadDatabase.CREATE_INDEXS); - executeStatements(db, DraftDatabase.CREATE_INDEXS); - executeStatements(db, GroupDatabase.CREATE_INDEXS); - executeStatements(db, GroupReceiptDatabase.CREATE_INDEXES); - } - - public void onApplicationLevelUpgrade(Context context, MasterSecret masterSecret, int fromVersion, - DatabaseUpgradeActivity.DatabaseUpgradeListener listener) - { - SQLiteDatabase db = getWritableDatabase(); - db.beginTransaction(); - - if (fromVersion < DatabaseUpgradeActivity.NO_MORE_KEY_EXCHANGE_PREFIX_VERSION) { - String KEY_EXCHANGE = "?TextSecureKeyExchange"; - String PROCESSED_KEY_EXCHANGE = "?TextSecureKeyExchangd"; - String STALE_KEY_EXCHANGE = "?TextSecureKeyExchangs"; - int ROW_LIMIT = 500; - - MasterCipher masterCipher = new MasterCipher(masterSecret); - int smsCount = 0; - int threadCount = 0; - int skip = 0; - - Cursor cursor = db.query("sms", new String[] {"COUNT(*)"}, "type & " + 0x80000000 + " != 0", - null, null, null, null); - - if (cursor != null && cursor.moveToFirst()) { - smsCount = cursor.getInt(0); - cursor.close(); - } - - cursor = db.query("thread", new String[] {"COUNT(*)"}, "snippet_type & " + 0x80000000 + " != 0", - null, null, null, null); - - if (cursor != null && cursor.moveToFirst()) { - threadCount = cursor.getInt(0); - cursor.close(); - } - - Cursor smsCursor = null; - - Log.i("DatabaseFactory", "Upgrade count: " + (smsCount + threadCount)); - - do { - Log.i("DatabaseFactory", "Looping SMS cursor..."); - if (smsCursor != null) - smsCursor.close(); - - smsCursor = db.query("sms", new String[] {"_id", "type", "body"}, - "type & " + 0x80000000 + " != 0", - null, null, null, "_id", skip + "," + ROW_LIMIT); - - while (smsCursor != null && smsCursor.moveToNext()) { - listener.setProgress(smsCursor.getPosition() + skip, smsCount + threadCount); - - try { - String body = masterCipher.decryptBody(smsCursor.getString(smsCursor.getColumnIndexOrThrow("body"))); - long type = smsCursor.getLong(smsCursor.getColumnIndexOrThrow("type")); - long id = smsCursor.getLong(smsCursor.getColumnIndexOrThrow("_id")); - - if (body.startsWith(KEY_EXCHANGE)) { - body = body.substring(KEY_EXCHANGE.length()); - body = masterCipher.encryptBody(body); - type |= 0x8000; - - db.execSQL("UPDATE sms SET body = ?, type = ? WHERE _id = ?", - new String[] {body, type+"", id+""}); - } else if (body.startsWith(PROCESSED_KEY_EXCHANGE)) { - body = body.substring(PROCESSED_KEY_EXCHANGE.length()); - body = masterCipher.encryptBody(body); - type |= (0x8000 | 0x2000); - - db.execSQL("UPDATE sms SET body = ?, type = ? WHERE _id = ?", - new String[] {body, type+"", id+""}); - } else if (body.startsWith(STALE_KEY_EXCHANGE)) { - body = body.substring(STALE_KEY_EXCHANGE.length()); - body = masterCipher.encryptBody(body); - type |= (0x8000 | 0x4000); - - db.execSQL("UPDATE sms SET body = ?, type = ? WHERE _id = ?", - new String[] {body, type+"", id+""}); - } - } catch (InvalidMessageException e) { - Log.w("DatabaseFactory", e); - } - } - - skip += ROW_LIMIT; - } while (smsCursor != null && smsCursor.getCount() > 0); - - - - Cursor threadCursor = null; - skip = 0; - - do { - Log.i("DatabaseFactory", "Looping thread cursor..."); - - if (threadCursor != null) - threadCursor.close(); - - threadCursor = db.query("thread", new String[] {"_id", "snippet_type", "snippet"}, - "snippet_type & " + 0x80000000 + " != 0", - null, null, null, "_id", skip + "," + ROW_LIMIT); - - while (threadCursor != null && threadCursor.moveToNext()) { - listener.setProgress(smsCount + threadCursor.getPosition(), smsCount + threadCount); - - try { - String snippet = threadCursor.getString(threadCursor.getColumnIndexOrThrow("snippet")); - long snippetType = threadCursor.getLong(threadCursor.getColumnIndexOrThrow("snippet_type")); - long id = threadCursor.getLong(threadCursor.getColumnIndexOrThrow("_id")); - - if (!TextUtils.isEmpty(snippet)) { - snippet = masterCipher.decryptBody(snippet); - } - - if (snippet.startsWith(KEY_EXCHANGE)) { - snippet = snippet.substring(KEY_EXCHANGE.length()); - snippet = masterCipher.encryptBody(snippet); - snippetType |= 0x8000; - - db.execSQL("UPDATE thread SET snippet = ?, snippet_type = ? WHERE _id = ?", - new String[] {snippet, snippetType+"", id+""}); - } else if (snippet.startsWith(PROCESSED_KEY_EXCHANGE)) { - snippet = snippet.substring(PROCESSED_KEY_EXCHANGE.length()); - snippet = masterCipher.encryptBody(snippet); - snippetType |= (0x8000 | 0x2000); - - db.execSQL("UPDATE thread SET snippet = ?, snippet_type = ? WHERE _id = ?", - new String[] {snippet, snippetType+"", id+""}); - } else if (snippet.startsWith(STALE_KEY_EXCHANGE)) { - snippet = snippet.substring(STALE_KEY_EXCHANGE.length()); - snippet = masterCipher.encryptBody(snippet); - snippetType |= (0x8000 | 0x4000); - - db.execSQL("UPDATE thread SET snippet = ?, snippet_type = ? WHERE _id = ?", - new String[] {snippet, snippetType+"", id+""}); - } - } catch (InvalidMessageException e) { - Log.w("DatabaseFactory", e); - } - } - - skip += ROW_LIMIT; - } while (threadCursor != null && threadCursor.getCount() > 0); - - if (smsCursor != null) - smsCursor.close(); - - if (threadCursor != null) - threadCursor.close(); - } - - if (fromVersion < DatabaseUpgradeActivity.MMS_BODY_VERSION) { - Log.i("DatabaseFactory", "Update MMS bodies..."); - MasterCipher masterCipher = new MasterCipher(masterSecret); - Cursor mmsCursor = db.query("mms", new String[] {"_id"}, - "msg_box & " + 0x80000000L + " != 0", - null, null, null, null); - - Log.i("DatabaseFactory", "Got MMS rows: " + (mmsCursor == null ? "null" : mmsCursor.getCount())); - - while (mmsCursor != null && mmsCursor.moveToNext()) { - listener.setProgress(mmsCursor.getPosition(), mmsCursor.getCount()); - - long mmsId = mmsCursor.getLong(mmsCursor.getColumnIndexOrThrow("_id")); - String body = null; - int partCount = 0; - Cursor partCursor = db.query("part", new String[] {"_id", "ct", "_data", "encrypted"}, - "mid = ?", new String[] {mmsId+""}, null, null, null); - - while (partCursor != null && partCursor.moveToNext()) { - String contentType = partCursor.getString(partCursor.getColumnIndexOrThrow("ct")); - - if (MediaUtil.isTextType(contentType)) { - try { - long partId = partCursor.getLong(partCursor.getColumnIndexOrThrow("_id")); - String dataLocation = partCursor.getString(partCursor.getColumnIndexOrThrow("_data")); - boolean encrypted = partCursor.getInt(partCursor.getColumnIndexOrThrow("encrypted")) == 1; - File dataFile = new File(dataLocation); - - InputStream is; - - AttachmentSecret attachmentSecret = new AttachmentSecret(masterSecret.getEncryptionKey().getEncoded(), - masterSecret.getMacKey().getEncoded(), null); - if (encrypted) is = ClassicDecryptingPartInputStream.createFor(attachmentSecret, dataFile); - else is = new FileInputStream(dataFile); - - body = (body == null) ? Util.readFullyAsString(is) : body + " " + Util.readFullyAsString(is); - - //noinspection ResultOfMethodCallIgnored - dataFile.delete(); - db.delete("part", "_id = ?", new String[] {partId+""}); - } catch (IOException e) { - Log.w("DatabaseFactory", e); - } - } else if (MediaUtil.isAudioType(contentType) || - MediaUtil.isImageType(contentType) || - MediaUtil.isVideoType(contentType)) - { - partCount++; - } - } - - if (!TextUtils.isEmpty(body)) { - body = masterCipher.encryptBody(body); - db.execSQL("UPDATE mms SET body = ?, part_count = ? WHERE _id = ?", - new String[] {body, partCount+"", mmsId+""}); - } else { - db.execSQL("UPDATE mms SET part_count = ? WHERE _id = ?", - new String[] {partCount+"", mmsId+""}); - } - - Log.i("DatabaseFactory", "Updated body: " + body + " and part_count: " + partCount); - } - } - - if (fromVersion < DatabaseUpgradeActivity.TOFU_IDENTITIES_VERSION) { - File sessionDirectory = new File(context.getFilesDir() + File.separator + "sessions"); - - if (sessionDirectory.exists() && sessionDirectory.isDirectory()) { - File[] sessions = sessionDirectory.listFiles(); - - if (sessions != null) { - for (File session : sessions) { - String name = session.getName(); - - if (name.matches("[0-9]+")) { - long recipientId = Long.parseLong(name); - IdentityKey identityKey = null; - // NOTE (4/21/14) -- At this moment in time, we're forgetting the ability to parse - // V1 session records. Despite our usual attempts to avoid using shared code in the - // upgrade path, this is too complex to put here directly. Thus, unfortunately - // this operation is now lost to the ages. From the git log, it seems to have been - // almost exactly a year since this went in, so hopefully the bulk of people have - // already upgraded. -// IdentityKey identityKey = Session.getRemoteIdentityKey(context, masterSecret, recipientId); - - if (identityKey != null) { - MasterCipher masterCipher = new MasterCipher(masterSecret); - String identityKeyString = Base64.encodeBytes(identityKey.serialize()); - String macString = Base64.encodeBytes(masterCipher.getMacFor(recipientId + - identityKeyString)); - - db.execSQL("REPLACE INTO identities (recipient, key, mac) VALUES (?, ?, ?)", - new String[] {recipientId+"", identityKeyString, macString}); - } - } - } - } - } - } - - if (fromVersion < DatabaseUpgradeActivity.ASYMMETRIC_MASTER_SECRET_FIX_VERSION) { - if (!MasterSecretUtil.hasAsymmericMasterSecret(context)) { - MasterSecretUtil.generateAsymmetricMasterSecret(context, masterSecret); - - MasterCipher masterCipher = new MasterCipher(masterSecret); - Cursor cursor = null; - - try { - cursor = db.query(SmsDatabase.TABLE_NAME, - new String[] {SmsDatabase.ID, SmsDatabase.BODY, SmsDatabase.TYPE}, - SmsDatabase.TYPE + " & ? == 0", - new String[] {String.valueOf(SmsDatabase.Types.ENCRYPTION_MASK)}, - null, null, null); - - while (cursor.moveToNext()) { - long id = cursor.getLong(0); - String body = cursor.getString(1); - long type = cursor.getLong(2); - - String encryptedBody = masterCipher.encryptBody(body); - - ContentValues update = new ContentValues(); - update.put(SmsDatabase.BODY, encryptedBody); - update.put(SmsDatabase.TYPE, type | 0x80000000); // Inline now deprecated symmetric encryption type - - db.update(SmsDatabase.TABLE_NAME, update, SmsDatabase.ID + " = ?", - new String[] {String.valueOf(id)}); - } - } finally { - if (cursor != null) - cursor.close(); - } - } - } - - db.setTransactionSuccessful(); - db.endTransaction(); - -// DecryptingQueue.schedulePendingDecrypts(context, masterSecret); - ApplicationContext.getInstance(context).messageNotifier.updateNotification(context); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - db.beginTransaction(); - - if (oldVersion < INTRODUCED_IDENTITIES_VERSION) { - db.execSQL("CREATE TABLE identities (_id INTEGER PRIMARY KEY, key TEXT UNIQUE, name TEXT UNIQUE, mac TEXT);"); - } - - if (oldVersion < INTRODUCED_INDEXES_VERSION) { - executeStatements(db, new String[] { - "CREATE INDEX IF NOT EXISTS sms_thread_id_index ON sms (thread_id);", - "CREATE INDEX IF NOT EXISTS sms_read_index ON sms (read);", - "CREATE INDEX IF NOT EXISTS sms_read_and_thread_id_index ON sms (read,thread_id);", - "CREATE INDEX IF NOT EXISTS sms_type_index ON sms (type);" - }); - executeStatements(db, new String[] { - "CREATE INDEX IF NOT EXISTS mms_thread_id_index ON mms (thread_id);", - "CREATE INDEX IF NOT EXISTS mms_read_index ON mms (read);", - "CREATE INDEX IF NOT EXISTS mms_read_and_thread_id_index ON mms (read,thread_id);", - "CREATE INDEX IF NOT EXISTS mms_message_box_index ON mms (msg_box);" - }); - executeStatements(db, new String[] { - "CREATE INDEX IF NOT EXISTS part_mms_id_index ON part (mid);" - }); - executeStatements(db, new String[] { - "CREATE INDEX IF NOT EXISTS thread_recipient_ids_index ON thread (recipient_ids);", - }); - executeStatements(db, new String[] { - "CREATE INDEX IF NOT EXISTS mms_addresses_mms_id_index ON mms_addresses (mms_id);", - }); - } - - if (oldVersion < INTRODUCED_DATE_SENT_VERSION) { - db.execSQL("ALTER TABLE sms ADD COLUMN date_sent INTEGER;"); - db.execSQL("UPDATE sms SET date_sent = date;"); - - db.execSQL("ALTER TABLE mms ADD COLUMN date_received INTEGER;"); - db.execSQL("UPDATE mms SET date_received = date;"); - } - - if (oldVersion < INTRODUCED_DRAFTS_VERSION) { - db.execSQL("CREATE TABLE drafts (_id INTEGER PRIMARY KEY, thread_id INTEGER, type TEXT, value TEXT);"); - executeStatements(db, new String[] { - "CREATE INDEX IF NOT EXISTS draft_thread_index ON drafts (thread_id);", - }); - } - - if (oldVersion < INTRODUCED_NEW_TYPES_VERSION) { - String KEY_EXCHANGE = "?TextSecureKeyExchange"; - String SYMMETRIC_ENCRYPT = "?TextSecureLocalEncrypt"; - String ASYMMETRIC_ENCRYPT = "?TextSecureAsymmetricEncrypt"; - String ASYMMETRIC_LOCAL_ENCRYPT = "?TextSecureAsymmetricLocalEncrypt"; - String PROCESSED_KEY_EXCHANGE = "?TextSecureKeyExchangd"; - String STALE_KEY_EXCHANGE = "?TextSecureKeyExchangs"; - - // SMS Updates - db.execSQL("UPDATE sms SET type = ? WHERE type = ?", new String[] {20L+"", 1L+""}); - db.execSQL("UPDATE sms SET type = ? WHERE type = ?", new String[] {21L+"", 43L+""}); - db.execSQL("UPDATE sms SET type = ? WHERE type = ?", new String[] {22L+"", 4L+""}); - db.execSQL("UPDATE sms SET type = ? WHERE type = ?", new String[] {23L+"", 2L+""}); - db.execSQL("UPDATE sms SET type = ? WHERE type = ?", new String[] {24L+"", 5L+""}); - - db.execSQL("UPDATE sms SET type = ? WHERE type = ?", new String[] {(21L | 0x800000L)+"", 42L+""}); - db.execSQL("UPDATE sms SET type = ? WHERE type = ?", new String[] {(23L | 0x800000L)+"", 44L+""}); - db.execSQL("UPDATE sms SET type = ? WHERE type = ?", new String[] {(20L | 0x800000L)+"", 45L+""}); - db.execSQL("UPDATE sms SET type = ? WHERE type = ?", new String[] {(20L | 0x800000L | 0x10000000L)+"", 46L+""}); - db.execSQL("UPDATE sms SET type = ? WHERE type = ?", new String[] {(20L)+"", 47L+""}); - db.execSQL("UPDATE sms SET type = ? WHERE type = ?", new String[] {(20L | 0x800000L | 0x08000000L)+"", 48L+""}); - - db.execSQL("UPDATE sms SET body = substr(body, ?), type = type | ? WHERE body LIKE ?", - new String[] {(SYMMETRIC_ENCRYPT.length()+1)+"", - 0x80000000L+"", - SYMMETRIC_ENCRYPT + "%"}); - - db.execSQL("UPDATE sms SET body = substr(body, ?), type = type | ? WHERE body LIKE ?", - new String[] {(ASYMMETRIC_LOCAL_ENCRYPT.length()+1)+"", - 0x40000000L+"", - ASYMMETRIC_LOCAL_ENCRYPT + "%"}); - - db.execSQL("UPDATE sms SET body = substr(body, ?), type = type | ? WHERE body LIKE ?", - new String[] {(ASYMMETRIC_ENCRYPT.length()+1)+"", - (0x800000L | 0x20000000L)+"", - ASYMMETRIC_ENCRYPT + "%"}); - - db.execSQL("UPDATE sms SET body = substr(body, ?), type = type | ? WHERE body LIKE ?", - new String[] {(KEY_EXCHANGE.length()+1)+"", - 0x8000L+"", - KEY_EXCHANGE + "%"}); - - db.execSQL("UPDATE sms SET body = substr(body, ?), type = type | ? WHERE body LIKE ?", - new String[] {(PROCESSED_KEY_EXCHANGE.length()+1)+"", - (0x8000L | 0x2000L)+"", - PROCESSED_KEY_EXCHANGE + "%"}); - - db.execSQL("UPDATE sms SET body = substr(body, ?), type = type | ? WHERE body LIKE ?", - new String[] {(STALE_KEY_EXCHANGE.length()+1)+"", - (0x8000L | 0x4000L)+"", - STALE_KEY_EXCHANGE + "%"}); - - // MMS Updates - - db.execSQL("UPDATE mms SET msg_box = ? WHERE msg_box = ?", new String[] {(20L | 0x80000000L)+"", 1+""}); - db.execSQL("UPDATE mms SET msg_box = ? WHERE msg_box = ?", new String[] {(23L | 0x80000000L)+"", 2+""}); - db.execSQL("UPDATE mms SET msg_box = ? WHERE msg_box = ?", new String[] {(21L | 0x80000000L)+"", 4+""}); - db.execSQL("UPDATE mms SET msg_box = ? WHERE msg_box = ?", new String[] {(24L | 0x80000000L)+"", 12+""}); - - db.execSQL("UPDATE mms SET msg_box = ? WHERE msg_box = ?", new String[] {(21L | 0x80000000L | 0x800000L) +"", 5+""}); - db.execSQL("UPDATE mms SET msg_box = ? WHERE msg_box = ?", new String[] {(23L | 0x80000000L | 0x800000L) +"", 6+""}); - db.execSQL("UPDATE mms SET msg_box = ? WHERE msg_box = ?", new String[] {(20L | 0x20000000L | 0x800000L) +"", 7+""}); - db.execSQL("UPDATE mms SET msg_box = ? WHERE msg_box = ?", new String[] {(20L | 0x80000000L | 0x800000L) +"", 8+""}); - db.execSQL("UPDATE mms SET msg_box = ? WHERE msg_box = ?", new String[] {(20L | 0x08000000L | 0x800000L) +"", 9+""}); - db.execSQL("UPDATE mms SET msg_box = ? WHERE msg_box = ?", new String[] {(20L | 0x10000000L | 0x800000L) +"", 10+""}); - - // Thread Updates - - db.execSQL("ALTER TABLE thread ADD COLUMN snippet_type INTEGER;"); - - db.execSQL("UPDATE thread SET snippet = substr(snippet, ?), " + - "snippet_type = ? WHERE snippet LIKE ?", - new String[] {(SYMMETRIC_ENCRYPT.length()+1)+"", - 0x80000000L+"", - SYMMETRIC_ENCRYPT + "%"}); - - db.execSQL("UPDATE thread SET snippet = substr(snippet, ?), " + - "snippet_type = ? WHERE snippet LIKE ?", - new String[] {(ASYMMETRIC_LOCAL_ENCRYPT.length()+1)+"", - 0x40000000L+"", - ASYMMETRIC_LOCAL_ENCRYPT + "%"}); - - db.execSQL("UPDATE thread SET snippet = substr(snippet, ?), " + - "snippet_type = ? WHERE snippet LIKE ?", - new String[] {(ASYMMETRIC_ENCRYPT.length()+1)+"", - (0x800000L | 0x20000000L)+"", - ASYMMETRIC_ENCRYPT + "%"}); - - db.execSQL("UPDATE thread SET snippet = substr(snippet, ?), " + - "snippet_type = ? WHERE snippet LIKE ?", - new String[] {(KEY_EXCHANGE.length()+1)+"", - 0x8000L+"", - KEY_EXCHANGE + "%"}); - - db.execSQL("UPDATE thread SET snippet = substr(snippet, ?), " + - "snippet_type = ? WHERE snippet LIKE ?", - new String[] {(STALE_KEY_EXCHANGE.length()+1)+"", - (0x8000L | 0x4000L)+"", - STALE_KEY_EXCHANGE + "%"}); - - db.execSQL("UPDATE thread SET snippet = substr(snippet, ?), " + - "snippet_type = ? WHERE snippet LIKE ?", - new String[] {(PROCESSED_KEY_EXCHANGE.length()+1)+"", - (0x8000L | 0x2000L)+"", - PROCESSED_KEY_EXCHANGE + "%"}); - } - - if (oldVersion < INTRODUCED_MMS_BODY_VERSION) { - db.execSQL("ALTER TABLE mms ADD COLUMN body TEXT"); - db.execSQL("ALTER TABLE mms ADD COLUMN part_count INTEGER"); - } - - if (oldVersion < INTRODUCED_MMS_FROM_VERSION) { - db.execSQL("ALTER TABLE mms ADD COLUMN address TEXT"); - - Cursor cursor = db.query("mms_addresses", null, "type = ?", new String[] {0x89+""}, - null, null, null); - - while (cursor != null && cursor.moveToNext()) { - long mmsId = cursor.getLong(cursor.getColumnIndexOrThrow("mms_id")); - String address = cursor.getString(cursor.getColumnIndexOrThrow("address")); - - if (!TextUtils.isEmpty(address)) { - db.execSQL("UPDATE mms SET address = ? WHERE _id = ?", new String[]{address, mmsId+""}); - } - } - - if (cursor != null) - cursor.close(); - } - - if (oldVersion < INTRODUCED_TOFU_IDENTITY_VERSION) { - db.execSQL("DROP TABLE identities"); - db.execSQL("CREATE TABLE identities (_id INTEGER PRIMARY KEY, recipient INTEGER UNIQUE, key TEXT, mac TEXT);"); - } - - if (oldVersion < INTRODUCED_PUSH_DATABASE_VERSION) { - db.execSQL("CREATE TABLE push (_id INTEGER PRIMARY KEY, type INTEGER, source TEXT, destinations TEXT, body TEXT, TIMESTAMP INTEGER);"); - db.execSQL("ALTER TABLE part ADD COLUMN pending_push INTEGER;"); - db.execSQL("CREATE INDEX IF NOT EXISTS pending_push_index ON part (pending_push);"); - } - - if (oldVersion < INTRODUCED_GROUP_DATABASE_VERSION) { - db.execSQL("CREATE TABLE groups (_id INTEGER PRIMARY KEY, group_id TEXT, title TEXT, members TEXT, avatar BLOB, avatar_id INTEGER, avatar_key BLOB, avatar_content_type TEXT, avatar_relay TEXT, timestamp INTEGER, active INTEGER DEFAULT 1);"); - db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS group_id_index ON groups (GROUP_ID);"); - db.execSQL("ALTER TABLE push ADD COLUMN device_id INTEGER DEFAULT 1;"); - db.execSQL("ALTER TABLE sms ADD COLUMN address_device_id INTEGER DEFAULT 1;"); - db.execSQL("ALTER TABLE mms ADD COLUMN address_device_id INTEGER DEFAULT 1;"); - } - - if (oldVersion < INTRODUCED_PUSH_FIX_VERSION) { - db.execSQL("CREATE TEMPORARY table push_backup (_id INTEGER PRIMARY KEY, type INTEGER, source, TEXT, destinations TEXT, body TEXT, timestamp INTEGER, device_id INTEGER DEFAULT 1);"); - db.execSQL("INSERT INTO push_backup(_id, type, source, body, timestamp, device_id) SELECT _id, type, source, body, timestamp, device_id FROM push;"); - db.execSQL("DROP TABLE push"); - db.execSQL("CREATE TABLE push (_id INTEGER PRIMARY KEY, type INTEGER, source TEXT, body TEXT, timestamp INTEGER, device_id INTEGER DEFAULT 1);"); - db.execSQL("INSERT INTO push (_id, type, source, body, timestamp, device_id) SELECT _id, type, source, body, timestamp, device_id FROM push_backup;"); - db.execSQL("DROP TABLE push_backup;"); - } - - if (oldVersion < INTRODUCED_DELIVERY_RECEIPTS) { - db.execSQL("ALTER TABLE sms ADD COLUMN delivery_receipt_count INTEGER DEFAULT 0;"); - db.execSQL("ALTER TABLE mms ADD COLUMN delivery_receipt_count INTEGER DEFAULT 0;"); - db.execSQL("CREATE INDEX IF NOT EXISTS sms_date_sent_index ON sms (date_sent);"); - db.execSQL("CREATE INDEX IF NOT EXISTS mms_date_sent_index ON mms (date);"); - } - - if (oldVersion < INTRODUCED_PART_DATA_SIZE_VERSION) { - db.execSQL("ALTER TABLE part ADD COLUMN data_size INTEGER DEFAULT 0;"); - } - - if (oldVersion < INTRODUCED_THUMBNAILS_VERSION) { - db.execSQL("ALTER TABLE part ADD COLUMN thumbnail TEXT;"); - db.execSQL("ALTER TABLE part ADD COLUMN aspect_ratio REAL;"); - } - - if (oldVersion < INTRODUCED_IDENTITY_COLUMN_VERSION) { - db.execSQL("ALTER TABLE sms ADD COLUMN mismatched_identities TEXT"); - db.execSQL("ALTER TABLE mms ADD COLUMN mismatched_identities TEXT"); - db.execSQL("ALTER TABLE mms ADD COLUMN network_failures TEXT"); - } - - if (oldVersion < INTRODUCED_UNIQUE_PART_IDS_VERSION) { - db.execSQL("ALTER TABLE part ADD COLUMN unique_id INTEGER NOT NULL DEFAULT 0"); - } - - if (oldVersion < INTRODUCED_RECIPIENT_PREFS_DB) { - db.execSQL("CREATE TABLE recipient_preferences " + - "(_id INTEGER PRIMARY KEY, recipient_ids TEXT UNIQUE, block INTEGER DEFAULT 0, " + - "notification TEXT DEFAULT NULL, vibrate INTEGER DEFAULT 0, mute_until INTEGER DEFAULT 0)"); - } - - if (oldVersion < INTRODUCED_ENVELOPE_CONTENT_VERSION) { - db.execSQL("ALTER TABLE push ADD COLUMN content TEXT"); - } - - if (oldVersion < INTRODUCED_COLOR_PREFERENCE_VERSION) { - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN color TEXT DEFAULT NULL"); - } - - if (oldVersion < INTRODUCED_DB_OPTIMIZATIONS_VERSION) { - db.execSQL("UPDATE mms SET date_received = (date_received * 1000), date = (date * 1000);"); - db.execSQL("CREATE INDEX IF NOT EXISTS sms_thread_date_index ON sms (thread_id, date);"); - db.execSQL("CREATE INDEX IF NOT EXISTS mms_thread_date_index ON mms (thread_id, date_received);"); - } - - if (oldVersion < INTRODUCED_INVITE_REMINDERS_VERSION) { - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN seen_invite_reminder INTEGER DEFAULT 0"); - } - - if (oldVersion < INTRODUCED_CONVERSATION_LIST_THUMBNAILS_VERSION) { - db.execSQL("ALTER TABLE thread ADD COLUMN snippet_uri TEXT DEFAULT NULL"); - } - - if (oldVersion < INTRODUCED_ARCHIVE_VERSION) { - db.execSQL("ALTER TABLE thread ADD COLUMN archived INTEGER DEFAULT 0"); - db.execSQL("CREATE INDEX IF NOT EXISTS archived_index ON thread (archived)"); - } - - if (oldVersion < INTRODUCED_CONVERSATION_LIST_STATUS_VERSION) { - db.execSQL("ALTER TABLE thread ADD COLUMN status INTEGER DEFAULT -1"); - db.execSQL("ALTER TABLE thread ADD COLUMN delivery_receipt_count INTEGER DEFAULT 0"); - } - - if (oldVersion < MIGRATED_CONVERSATION_LIST_STATUS_VERSION) { - Cursor threadCursor = db.query("thread", new String[] {"_id"}, null, null, null, null, null); - - while (threadCursor != null && threadCursor.moveToNext()) { - long threadId = threadCursor.getLong(threadCursor.getColumnIndexOrThrow("_id")); - - Cursor cursor = db.rawQuery("SELECT DISTINCT date AS date_received, status, " + - "delivery_receipt_count FROM sms WHERE (thread_id = ?1) " + - "UNION ALL SELECT DISTINCT date_received, -1 AS status, " + - "delivery_receipt_count FROM mms WHERE (thread_id = ?1) " + - "ORDER BY date_received DESC LIMIT 1", new String[]{threadId + ""}); - - if (cursor != null && cursor.moveToNext()) { - int status = cursor.getInt(cursor.getColumnIndexOrThrow("status")); - int receiptCount = cursor.getInt(cursor.getColumnIndexOrThrow("delivery_receipt_count")); - - db.execSQL("UPDATE thread SET status = ?, delivery_receipt_count = ? WHERE _id = ?", - new String[]{status + "", receiptCount + "", threadId + ""}); - } - } - } - - if (oldVersion < INTRODUCED_SUBSCRIPTION_ID_VERSION) { - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN default_subscription_id INTEGER DEFAULT -1"); - db.execSQL("ALTER TABLE sms ADD COLUMN subscription_id INTEGER DEFAULT -1"); - db.execSQL("ALTER TABLE mms ADD COLUMN subscription_id INTEGER DEFAULT -1"); - } - - if (oldVersion < INTRODUCED_EXPIRE_MESSAGES_VERSION) { - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN expire_messages INTEGER DEFAULT 0"); - db.execSQL("ALTER TABLE sms ADD COLUMN expires_in INTEGER DEFAULT 0"); - db.execSQL("ALTER TABLE mms ADD COLUMN expires_in INTEGER DEFAULT 0"); - db.execSQL("ALTER TABLE sms ADD COLUMN expire_started INTEGER DEFAULT 0"); - db.execSQL("ALTER TABLE mms ADD COLUMN expire_started INTEGER DEFAULT 0"); - db.execSQL("ALTER TABLE thread ADD COLUMN expires_in INTEGER DEFAULT 0"); - } - - if (oldVersion < INTRODUCED_LAST_SEEN) { - db.execSQL("ALTER TABLE thread ADD COLUMN last_seen INTEGER DEFAULT 0"); - } - - if (oldVersion < INTRODUCED_DIGEST) { - db.execSQL("ALTER TABLE part ADD COLUMN digest BLOB"); - db.execSQL("ALTER TABLE groups ADD COLUMN avatar_digest BLOB"); - } - - if (oldVersion < INTRODUCED_NOTIFIED) { - db.execSQL("ALTER TABLE sms ADD COLUMN notified INTEGER DEFAULT 0"); - db.execSQL("ALTER TABLE mms ADD COLUMN notified INTEGER DEFAULT 0"); - - db.execSQL("DROP INDEX sms_read_and_thread_id_index"); - db.execSQL("CREATE INDEX IF NOT EXISTS sms_read_and_notified_and_thread_id_index ON sms(read,notified,thread_id)"); - - db.execSQL("DROP INDEX mms_read_and_thread_id_index"); - db.execSQL("CREATE INDEX IF NOT EXISTS mms_read_and_notified_and_thread_id_index ON mms(read,notified,thread_id)"); - } - - if (oldVersion < INTRODUCED_DOCUMENTS) { - db.execSQL("ALTER TABLE part ADD COLUMN file_name TEXT"); - } - - if (oldVersion < INTRODUCED_FAST_PREFLIGHT) { - db.execSQL("ALTER TABLE part ADD COLUMN fast_preflight_id TEXT"); - } - - if (oldVersion < INTRODUCED_VOICE_NOTES) { - db.execSQL("ALTER TABLE part ADD COLUMN voice_note INTEGER DEFAULT 0"); - } - - if (oldVersion < INTRODUCED_IDENTITY_TIMESTAMP) { - db.execSQL("ALTER TABLE identities ADD COLUMN timestamp INTEGER DEFAULT 0"); - db.execSQL("ALTER TABLE identities ADD COLUMN first_use INTEGER DEFAULT 0"); - db.execSQL("ALTER TABLE identities ADD COLUMN nonblocking_approval INTEGER DEFAULT 0"); - db.execSQL("ALTER TABLE identities ADD COLUMN verified INTEGER DEFAULT 0"); - - db.execSQL("DROP INDEX archived_index"); - db.execSQL("CREATE INDEX IF NOT EXISTS archived_count_index ON thread (archived, message_count)"); - } - - if (oldVersion < SANIFY_ATTACHMENT_DOWNLOAD) { - db.execSQL("UPDATE part SET pending_push = '2' WHERE pending_push = '1'"); - } - - if (oldVersion < NO_MORE_CANONICAL_ADDRESS_DATABASE) { - SQLiteOpenHelper canonicalAddressDatabaseHelper = new SQLiteOpenHelper(context, "canonical_address.db", null, 1) { - @Override - public void onCreate(SQLiteDatabase db) { - throw new AssertionError("No canonical address DB?"); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {} - }; - - SQLiteDatabase canonicalAddressDatabase = canonicalAddressDatabaseHelper.getReadableDatabase(); - NumberMigrator numberMigrator = new NumberMigrator(TextSecurePreferences.getLocalNumber(context)); - - // Migrate Thread Database - Cursor cursor = db.query("thread", new String[] {"_id", "recipient_ids"}, null, null, null, null, null); - - while (cursor != null && cursor.moveToNext()) { - long threadId = cursor.getLong(0); - String recipientIdsList = cursor.getString(1); - String[] recipientIds = recipientIdsList.split(" "); - String[] addresses = new String[recipientIds.length]; - - for (int i=0;i newDocumentList = new LinkedList<>(); - - for (PreCanonicalAddressIdentityMismatchDocument oldDocument : oldDocumentList.list) { - Cursor resolved = canonicalAddressDatabase.query("canonical_addresses", new String[] {"address"}, "_id = ?", new String[] {String.valueOf(oldDocument.recipientId)}, null, null, null); - - if (resolved != null && resolved.moveToFirst()) { - String address = resolved.getString(0); - newDocumentList.add(new PostCanonicalAddressIdentityMismatchDocument(numberMigrator.migrate(address), oldDocument.identityKey)); - } else { - throw new AssertionError("Unable to resolve: " + oldDocument.recipientId); - } - - if (resolved != null) resolved.close(); - } - - ContentValues values = new ContentValues(1); - values.put("mismatched_identities", JsonUtils.toJson(new PostCanonicalAddressIdentityMismatchList(newDocumentList))); - db.update("sms", values, "_id = ?", new String[] {String.valueOf(id)}); - } catch (IOException e) { - Log.w(TAG, e); - } - } - } - - if (cursor != null) cursor.close(); - - // Migrate MMS mismatched identities - cursor = db.query("mms", new String[] {"_id", "mismatched_identities"}, "mismatched_identities IS NOT NULL", null, null, null, null); - - while (cursor != null && cursor.moveToNext()) { - long id = cursor.getLong(0); - String document = cursor.getString(1); - - if (!TextUtils.isEmpty(document)) { - try { - PreCanonicalAddressIdentityMismatchList oldDocumentList = JsonUtils.fromJson(document, PreCanonicalAddressIdentityMismatchList.class); - List newDocumentList = new LinkedList<>(); - - for (PreCanonicalAddressIdentityMismatchDocument oldDocument : oldDocumentList.list) { - Cursor resolved = canonicalAddressDatabase.query("canonical_addresses", new String[] {"address"}, "_id = ?", new String[] {String.valueOf(oldDocument.recipientId)}, null, null, null); - - if (resolved != null && resolved.moveToFirst()) { - String address = resolved.getString(0); - newDocumentList.add(new PostCanonicalAddressIdentityMismatchDocument(numberMigrator.migrate(address), oldDocument.identityKey)); - } else { - throw new AssertionError("Unable to resolve: " + oldDocument.recipientId); - } - - if (resolved != null) resolved.close(); - } - - ContentValues values = new ContentValues(1); - values.put("mismatched_identities", JsonUtils.toJson(new PostCanonicalAddressIdentityMismatchList(newDocumentList))); - db.update("mms", values, "_id = ?", new String[] {String.valueOf(id)}); - } catch (IOException e) { - Log.w(TAG, e); - } - } - } - - if (cursor != null) cursor.close(); - - // Migrate MMS network failures - cursor = db.query("mms", new String[] {"_id", "network_failures"}, "network_failures IS NOT NULL", null, null, null, null); - - while (cursor != null && cursor.moveToNext()) { - long id = cursor.getLong(0); - String document = cursor.getString(1); - - if (!TextUtils.isEmpty(document)) { - try { - PreCanonicalAddressNetworkFailureList oldDocumentList = JsonUtils.fromJson(document, PreCanonicalAddressNetworkFailureList.class); - List newDocumentList = new LinkedList<>(); - - for (PreCanonicalAddressNetworkFailureDocument oldDocument : oldDocumentList.list) { - Cursor resolved = canonicalAddressDatabase.query("canonical_addresses", new String[] {"address"}, "_id = ?", new String[] {String.valueOf(oldDocument.recipientId)}, null, null, null); - - if (resolved != null && resolved.moveToFirst()) { - String address = resolved.getString(0); - newDocumentList.add(new PostCanonicalAddressNetworkFailureDocument(numberMigrator.migrate(address))); - } else { - throw new AssertionError("Unable to resolve: " + oldDocument.recipientId); - } - - if (resolved != null) resolved.close(); - } - - ContentValues values = new ContentValues(1); - values.put("network_failures", JsonUtils.toJson(new PostCanonicalAddressNetworkFailureList(newDocumentList))); - db.update("mms", values, "_id = ?", new String[] {String.valueOf(id)}); - } catch (IOException e) { - Log.w(TAG, e); - } - } - } - - // Migrate sessions - File sessionsDirectory = new File(context.getFilesDir(), "sessions-v2"); - - if (sessionsDirectory.exists() && sessionsDirectory.isDirectory()) { - File[] sessions = sessionsDirectory.listFiles(); - - for (File session : sessions) { - try { - String[] sessionParts = session.getName().split("[.]"); - long recipientId = Long.parseLong(sessionParts[0]); - - int deviceId; - - if (sessionParts.length > 1) deviceId = Integer.parseInt(sessionParts[1]); - else deviceId = 1; - - Cursor resolved = canonicalAddressDatabase.query("canonical_addresses", new String[] {"address"}, "_id = ?", new String[] {String.valueOf(recipientId)}, null, null, null); - - if (resolved != null && resolved.moveToNext()) { - String address = resolved.getString(0); - File destination = new File(session.getParentFile(), address + (deviceId != 1 ? "." + deviceId : "")); - - if (!session.renameTo(destination)) { - Log.w(TAG, "Session rename failed: " + destination); - } - } - - if (resolved != null) resolved.close(); - } catch (NumberFormatException e) { - Log.w(TAG, e); - } - } - } - - } - - if (oldVersion < NO_MORE_RECIPIENTS_PLURAL) { - db.execSQL("ALTER TABLE groups ADD COLUMN mms INTEGER DEFAULT 0"); - - Cursor cursor = db.query("thread", new String[] {"_id", "recipient_ids"}, null, null, null, null, null); - - while (cursor != null && cursor.moveToNext()) { - long threadId = cursor.getLong(0); - String addressListString = cursor.getString(1); - String[] addressList = DelimiterUtil.split(addressListString, ' '); - - if (addressList.length == 1) { - ContentValues contentValues = new ContentValues(); - contentValues.put("recipient_ids", DelimiterUtil.unescape(addressListString, ' ')); - db.update("thread", contentValues, "_id = ?", new String[] {String.valueOf(threadId)}); - } else { - byte[] groupId = new byte[16]; - List members = new LinkedList<>(); - - new SecureRandom().nextBytes(groupId); - - for (String address : addressList) { - members.add(DelimiterUtil.escape(DelimiterUtil.unescape(address, ' '), ',')); - } - - members.add(DelimiterUtil.escape(TextSecurePreferences.getLocalNumber(context), ',')); - - Collections.sort(members); - - String encodedGroupId = "__signal_mms_group__!" + Hex.toStringCondensed(groupId); - ContentValues groupValues = new ContentValues(); - ContentValues threadValues = new ContentValues(); - - groupValues.put("group_id", encodedGroupId); - groupValues.put("members", Util.join(members, ",")); - groupValues.put("mms", 1); - - threadValues.put("recipient_ids", encodedGroupId); - - db.insert("groups", null, groupValues); - db.update("thread", threadValues, "_id = ?", new String[] {String.valueOf(threadId)}); - db.update("recipient_preferences", threadValues, "recipient_ids = ?", new String[] {addressListString}); - } - } - - if (cursor != null) cursor.close(); - - cursor = db.query("recipient_preferences", new String[] {"_id", "recipient_ids"}, null, null, null, null, null); - - while (cursor != null && cursor.moveToNext()) { - long id = cursor.getLong(0); - String addressListString = cursor.getString(1); - String[] addressList = DelimiterUtil.split(addressListString, ' '); - - if (addressList.length == 1) { - ContentValues contentValues = new ContentValues(); - contentValues.put("recipient_ids", DelimiterUtil.unescape(addressListString, ' ')); - db.update("recipient_preferences", contentValues, "_id = ?", new String[] {String.valueOf(id)}); - } else { - Log.w(TAG, "Found preferences for MMS thread that appears to be gone: " + addressListString); - db.delete("recipient_preferences", "_id = ?", new String[] {String.valueOf(id)}); - } - } - - if (cursor != null) cursor.close(); - - cursor = db.rawQuery("SELECT mms._id, thread.recipient_ids FROM mms, thread WHERE mms.address IS NULL AND mms.thread_id = thread._id", null); - - while (cursor != null && cursor.moveToNext()) { - long id = cursor.getLong(0); - ContentValues contentValues = new ContentValues(1); - - contentValues.put("address", cursor.getString(1)); - db.update("mms", contentValues, "_id = ?", new String[] {String.valueOf(id)}); - } - - if (cursor != null) cursor.close(); - } - - if (oldVersion < INTERNAL_DIRECTORY) { - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN registered INTEGER DEFAULT 0"); - - OldDirectoryDatabaseHelper directoryDatabaseHelper = new OldDirectoryDatabaseHelper(context); - SQLiteDatabase directoryDatabase = directoryDatabaseHelper.getWritableDatabase(); - - Cursor cursor = directoryDatabase.query("directory", new String[] {"number", "registered"}, null, null, null, null, null); - - while (cursor != null && cursor.moveToNext()) { - String address = new NumberMigrator(TextSecurePreferences.getLocalNumber(context)).migrate(cursor.getString(0)); - ContentValues contentValues = new ContentValues(1); - - contentValues.put("registered", cursor.getInt(1) == 1 ? 1 : 2); - - if (db.update("recipient_preferences", contentValues, "recipient_ids = ?", new String[] {address}) < 1) { - contentValues.put("recipient_ids", address); - db.insert("recipient_preferences", null, contentValues); - } - } - - if (cursor != null) cursor.close(); - } - - if (oldVersion < INTERNAL_SYSTEM_DISPLAY_NAME) { - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN system_display_name TEXT DEFAULT NULL"); - } - - if (oldVersion < PROFILES) { - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN profile_key TEXT DEFAULT NULL"); - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN signal_profile_name TEXT DEFAULT NULL"); - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN signal_profile_avatar TEXT DEFAULT NULL"); - } - - if (oldVersion < PROFILE_SHARING_APPROVAL) { - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN profile_sharing_approval INTEGER DEFAULT 0"); - } - - if (oldVersion < UNSEEN_NUMBER_OFFER) { - db.execSQL("ALTER TABLE thread ADD COLUMN has_sent INTEGER DEFAULT 0"); - } - - if (oldVersion < READ_RECEIPTS) { - db.execSQL("ALTER TABLE sms ADD COLUMN read_receipt_count INTEGER DEFAULT 0"); - db.execSQL("ALTER TABLE mms ADD COLUMN read_receipt_count INTEGER DEFAULT 0"); - db.execSQL("ALTER TABLE thread ADD COLUMN read_receipt_count INTEGER DEFAULT 0"); - } - - if (oldVersion < GROUP_RECEIPT_TRACKING) { - db.execSQL("CREATE TABLE group_receipts (_id INTEGER PRIMARY KEY, mms_id INTEGER, address TEXT, status INTEGER, timestamp INTEGER)"); - db.execSQL("CREATE INDEX IF NOT EXISTS group_receipt_mms_id_index ON group_receipts (mms_id)"); - } - - if (oldVersion < UNREAD_COUNT_VERSION) { - db.execSQL("ALTER TABLE thread ADD COLUMN unread_count INTEGER DEFAULT 0"); - - try (Cursor cursor = db.query("thread", new String[] {"_id"}, "read = 0", null, null, null, null)) { - while (cursor != null && cursor.moveToNext()) { - long threadId = cursor.getLong(0); - int unreadCount = 0; - - try (Cursor smsCursor = db.rawQuery("SELECT COUNT(*) FROM sms WHERE thread_id = ? AND read = '0'", new String[] {String.valueOf(threadId)})) { - if (smsCursor != null && smsCursor.moveToFirst()) { - unreadCount += smsCursor.getInt(0); - } - } - - try (Cursor mmsCursor = db.rawQuery("SELECT COUNT(*) FROM mms WHERE thread_id = ? AND read = '0'", new String[] {String.valueOf(threadId)})) { - if (mmsCursor != null && mmsCursor.moveToFirst()) { - unreadCount += mmsCursor.getInt(0); - } - } - - db.execSQL("UPDATE thread SET unread_count = ? WHERE _id = ?", - new String[] {String.valueOf(unreadCount), - String.valueOf(threadId)}); - } - } - } - - if (oldVersion < MORE_RECIPIENT_FIELDS) { - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN system_contact_photo TEXT DEFAULT NULL"); - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN system_phone_label TEXT DEFAULT NULL"); - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN system_contact_uri TEXT DEFAULT NULL"); - /* - if (Permissions.hasAny(context, Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS)) { - try (Cursor cursor = db.query("recipient_preferences", null, null, null, null, null, null)) { - while (cursor != null && cursor.moveToNext()) { - Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow("recipient_ids"))); - - if (address.isPhone() && !TextUtils.isEmpty(address.toPhoneString())) { - Uri lookup = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(address.toPhoneString())); - - try (Cursor contactCursor = context.getContentResolver().query(lookup, new String[] {ContactsContract.PhoneLookup.DISPLAY_NAME, - ContactsContract.PhoneLookup.LOOKUP_KEY, - ContactsContract.PhoneLookup._ID, - ContactsContract.PhoneLookup.NUMBER, - ContactsContract.PhoneLookup.LABEL, - ContactsContract.PhoneLookup.PHOTO_URI}, - null, null, null)) - { - if (contactCursor != null && contactCursor.moveToFirst()) { - ContentValues contentValues = new ContentValues(3); - contentValues.put("system_contact_photo", contactCursor.getString(5)); - contentValues.put("system_phone_label", contactCursor.getString(4)); - contentValues.put("system_contact_uri", ContactsContract.Contacts.getLookupUri(contactCursor.getLong(2), contactCursor.getString(1)).toString()); - - db.update("recipient_preferences", contentValues, "recipient_ids = ?", new String[] {address.toPhoneString()}); - } - } - } - } - } - } - */ - } - - db.setTransactionSuccessful(); - db.endTransaction(); - } - - private void executeStatements(SQLiteDatabase db, String[] statements) { - for (String statement : statements) - db.execSQL(statement); - } - - private static class PreCanonicalAddressIdentityMismatchList { - @JsonProperty(value = "m") - private List list; - } - - private static class PostCanonicalAddressIdentityMismatchList { - @JsonProperty(value = "m") - private List list; - - public PostCanonicalAddressIdentityMismatchList(List list) { - this.list = list; - } - } - - private static class PreCanonicalAddressIdentityMismatchDocument { - @JsonProperty(value = "r") - private long recipientId; - - @JsonProperty(value = "k") - private String identityKey; - } - - private static class PostCanonicalAddressIdentityMismatchDocument { - @JsonProperty(value = "a") - private String address; - - @JsonProperty(value = "k") - private String identityKey; - - public PostCanonicalAddressIdentityMismatchDocument() {} - - public PostCanonicalAddressIdentityMismatchDocument(String address, String identityKey) { - this.address = address; - this.identityKey = identityKey; - } - } - - private static class PreCanonicalAddressNetworkFailureList { - @JsonProperty(value = "l") - private List list; - } - - private static class PostCanonicalAddressNetworkFailureList { - @JsonProperty(value = "l") - private List list; - - public PostCanonicalAddressNetworkFailureList(List list) { - this.list = list; - } - } - - private static class PreCanonicalAddressNetworkFailureDocument { - @JsonProperty(value = "r") - private long recipientId; - } - - private static class PostCanonicalAddressNetworkFailureDocument { - @JsonProperty(value = "a") - private String address; - - public PostCanonicalAddressNetworkFailureDocument() {} - - public PostCanonicalAddressNetworkFailureDocument(String address) { - this.address = address; - } - } - - private static class NumberMigrator { - - private static final String TAG = NumberMigrator.class.getSimpleName(); - - private static final Set SHORT_COUNTRIES = new HashSet() {{ - add("NU"); - add("TK"); - add("NC"); - add("AC"); - }}; - - private final String localNumberString; - - private final Pattern ALPHA_PATTERN = Pattern.compile("[a-zA-Z]"); - - - public NumberMigrator(String localNumber) { - this.localNumberString = localNumber; - } - - public String migrate(@Nullable String number) { - if (number == null) return "Unknown"; - return number; - } - } - - private static class OldDirectoryDatabaseHelper extends SQLiteOpenHelper { - - private static final int INTRODUCED_CHANGE_FROM_TOKEN_TO_E164_NUMBER = 2; - private static final int INTRODUCED_VOICE_COLUMN = 4; - private static final int INTRODUCED_VIDEO_COLUMN = 5; - - private static final String DATABASE_NAME = "whisper_directory.db"; - private static final int DATABASE_VERSION = 5; - - private static final String TABLE_NAME = "directory"; - private static final String ID = "_id"; - private static final String NUMBER = "number"; - private static final String REGISTERED = "registered"; - private static final String RELAY = "relay"; - private static final String TIMESTAMP = "timestamp"; - private static final String VOICE = "voice"; - private static final String VIDEO = "video"; - - private static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + "(" + ID + " INTEGER PRIMARY KEY, " + - NUMBER + " TEXT UNIQUE, " + - REGISTERED + " INTEGER, " + - RELAY + " TEXT, " + - TIMESTAMP + " INTEGER, " + - VOICE + " INTEGER, " + - VIDEO + " INTEGER);"; - - public OldDirectoryDatabaseHelper(Context context) { - super(context, DATABASE_NAME, null, DATABASE_VERSION); - } - - @Override - public void onCreate(SQLiteDatabase db) { - db.execSQL(CREATE_TABLE); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - if (oldVersion < INTRODUCED_CHANGE_FROM_TOKEN_TO_E164_NUMBER) { - db.execSQL("DROP TABLE directory;"); - db.execSQL("CREATE TABLE directory ( _id INTEGER PRIMARY KEY, " + - "number TEXT UNIQUE, " + - "registered INTEGER, " + - "relay TEXT, " + - "supports_sms INTEGER, " + - "timestamp INTEGER);"); - } - - if (oldVersion < INTRODUCED_VOICE_COLUMN) { - db.execSQL("ALTER TABLE directory ADD COLUMN voice INTEGER;"); - } - - if (oldVersion < INTRODUCED_VIDEO_COLUMN) { - db.execSQL("ALTER TABLE directory ADD COLUMN video INTEGER;"); - } - } - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherMigrationHelper.java b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherMigrationHelper.java deleted file mode 100644 index dde6634801..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherMigrationHelper.java +++ /dev/null @@ -1,254 +0,0 @@ -package org.thoughtcrime.securesms.database.helpers; - - -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import android.text.TextUtils; -import org.thoughtcrime.securesms.logging.Log; -import android.util.Pair; - -import com.annimon.stream.function.BiFunction; - -import org.thoughtcrime.securesms.DatabaseUpgradeActivity; -import network.loki.messenger.R; -import org.thoughtcrime.securesms.crypto.AsymmetricMasterCipher; -import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider; -import org.thoughtcrime.securesms.crypto.MasterCipher; -import org.thoughtcrime.securesms.crypto.MasterSecret; -import org.thoughtcrime.securesms.crypto.MasterSecretUtil; -import org.thoughtcrime.securesms.service.GenericForegroundService; -import org.thoughtcrime.securesms.util.Base64; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.session.libsignal.libsignal.InvalidMessageException; - -import java.io.IOException; -import java.util.HashSet; -import java.util.Set; - -public class SQLCipherMigrationHelper { - - private static final String TAG = SQLCipherMigrationHelper.class.getSimpleName(); - - private static final long ENCRYPTION_SYMMETRIC_BIT = 0x80000000; - private static final long ENCRYPTION_ASYMMETRIC_BIT = 0x40000000; - - static void migratePlaintext(@NonNull Context context, - @NonNull android.database.sqlite.SQLiteDatabase legacyDb, - @NonNull net.sqlcipher.database.SQLiteDatabase modernDb) - { - modernDb.beginTransaction(); - try { - GenericForegroundService.startForegroundTask(context, context.getString(R.string.SQLCipherMigrationHelper_migrating_signal_database)); - copyTable("identities", legacyDb, modernDb, null); - copyTable("push", legacyDb, modernDb, null); - copyTable("groups", legacyDb, modernDb, null); - copyTable("recipient_preferences", legacyDb, modernDb, null); - copyTable("group_receipts", legacyDb, modernDb, null); - modernDb.setTransactionSuccessful(); - } finally { - modernDb.endTransaction(); - GenericForegroundService.stopForegroundTask(context); - } - } - - public static void migrateCiphertext(@NonNull Context context, - @NonNull MasterSecret masterSecret, - @NonNull android.database.sqlite.SQLiteDatabase legacyDb, - @NonNull net.sqlcipher.database.SQLiteDatabase modernDb, - @Nullable DatabaseUpgradeActivity.DatabaseUpgradeListener listener) - { - MasterCipher legacyCipher = new MasterCipher(masterSecret); - AsymmetricMasterCipher legacyAsymmetricCipher = new AsymmetricMasterCipher(MasterSecretUtil.getAsymmetricMasterSecret(context, masterSecret)); - - modernDb.beginTransaction(); - - try { - GenericForegroundService.startForegroundTask(context, context.getString(R.string.SQLCipherMigrationHelper_migrating_signal_database)); - int total = 5000; - - copyTable("sms", legacyDb, modernDb, (row, progress) -> { - Pair plaintext = getPlaintextBody(legacyCipher, legacyAsymmetricCipher, - row.getAsLong("type"), - row.getAsString("body")); - - row.put("body", plaintext.second); - row.put("type", plaintext.first); - - if (listener != null && (progress.first % 1000 == 0)) { - listener.setProgress(getTotalProgress(0, progress.first, progress.second), total); - } - - return row; - }); - - copyTable("mms", legacyDb, modernDb, (row, progress) -> { - Pair plaintext = getPlaintextBody(legacyCipher, legacyAsymmetricCipher, - row.getAsLong("msg_box"), - row.getAsString("body")); - - row.put("body", plaintext.second); - row.put("msg_box", plaintext.first); - - if (listener != null && (progress.first % 1000 == 0)) { - listener.setProgress(getTotalProgress(1000, progress.first, progress.second), total); - } - - return row; - }); - - copyTable("part", legacyDb, modernDb, (row, progress) -> { - String fileName = row.getAsString("file_name"); - String mediaKey = row.getAsString("cd"); - - try { - if (!TextUtils.isEmpty(fileName)) { - row.put("file_name", legacyCipher.decryptBody(fileName)); - } - } catch (InvalidMessageException e) { - Log.w(TAG, e); - } - - try { - if (!TextUtils.isEmpty(mediaKey)) { - byte[] plaintext; - - if (mediaKey.startsWith("?ASYNC-")) { - plaintext = legacyAsymmetricCipher.decryptBytes(Base64.decode(mediaKey.substring("?ASYNC-".length()))); - } else { - plaintext = legacyCipher.decryptBytes(Base64.decode(mediaKey)); - } - - row.put("cd", Base64.encodeBytes(plaintext)); - } - } catch (IOException | InvalidMessageException e) { - Log.w(TAG, e); - } - - if (listener != null && (progress.first % 1000 == 0)) { - listener.setProgress(getTotalProgress(2000, progress.first, progress.second), total); - } - - return row; - }); - - copyTable("thread", legacyDb, modernDb, (row, progress) -> { - Long snippetType = row.getAsLong("snippet_type"); - if (snippetType == null) snippetType = 0L; - - Pair plaintext = getPlaintextBody(legacyCipher, legacyAsymmetricCipher, - snippetType, row.getAsString("snippet")); - - row.put("snippet", plaintext.second); - row.put("snippet_type", plaintext.first); - - if (listener != null && (progress.first % 1000 == 0)) { - listener.setProgress(getTotalProgress(3000, progress.first, progress.second), total); - } - - return row; - }); - - - copyTable("drafts", legacyDb, modernDb, (row, progress) -> { - String draftType = row.getAsString("type"); - String draft = row.getAsString("value"); - - try { - if (!TextUtils.isEmpty(draftType)) row.put("type", legacyCipher.decryptBody(draftType)); - if (!TextUtils.isEmpty(draft)) row.put("value", legacyCipher.decryptBody(draft)); - } catch (InvalidMessageException e) { - Log.w(TAG, e); - } - - if (listener != null && (progress.first % 1000 == 0)) { - listener.setProgress(getTotalProgress(4000, progress.first, progress.second), total); - } - - return row; - }); - - AttachmentSecretProvider.getInstance(context).setClassicKey(context, masterSecret.getEncryptionKey().getEncoded(), masterSecret.getMacKey().getEncoded()); - TextSecurePreferences.setNeedsSqlCipherMigration(context, false); - modernDb.setTransactionSuccessful(); - } finally { - modernDb.endTransaction(); - GenericForegroundService.stopForegroundTask(context); - } - } - - private static void copyTable(@NonNull String tableName, - @NonNull android.database.sqlite.SQLiteDatabase legacyDb, - @NonNull net.sqlcipher.database.SQLiteDatabase modernDb, - @Nullable BiFunction, ContentValues> transformer) - { - Set destinationColumns = getTableColumns(tableName, modernDb); - - try (Cursor cursor = legacyDb.query(tableName, null, null, null, null, null, null)) { - int count = (cursor != null) ? cursor.getCount() : 0; - int progress = 1; - - while (cursor != null && cursor.moveToNext()) { - ContentValues row = new ContentValues(); - - for (int i=0;i(progress++, count)); - } - - modernDb.insert(tableName, null, row); - } - } - } - - private static Pair getPlaintextBody(@NonNull MasterCipher legacyCipher, - @NonNull AsymmetricMasterCipher legacyAsymmetricCipher, - long type, - @Nullable String body) - { - try { - if (!TextUtils.isEmpty(body)) { - if ((type & ENCRYPTION_SYMMETRIC_BIT) != 0) body = legacyCipher.decryptBody(body); - else if ((type & ENCRYPTION_ASYMMETRIC_BIT) != 0) body = legacyAsymmetricCipher.decryptBody(body); - } - } catch (InvalidMessageException | IOException e) { - Log.w(TAG, e); - } - - type &= ~(ENCRYPTION_SYMMETRIC_BIT); - type &= ~(ENCRYPTION_ASYMMETRIC_BIT); - - return new Pair<>(type, body); - } - - private static Set getTableColumns(String tableName, net.sqlcipher.database.SQLiteDatabase database) { - Set results = new HashSet<>(); - - try (Cursor cursor = database.rawQuery("PRAGMA table_info(" + tableName + ")", null)) { - while (cursor != null && cursor.moveToNext()) { - results.add(cursor.getString(1)); - } - } - - return results; - } - - private static int getTotalProgress(int sectionOffset, int sectionProgress, int sectionTotal) { - double percentOfSectionComplete = ((double)sectionProgress) / ((double)sectionTotal); - return sectionOffset + (int)(((double)1000) * percentOfSectionComplete); - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java index 19facfe0e5..1fa0ccedd2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java @@ -1,12 +1,8 @@ package org.thoughtcrime.securesms.database.helpers; -import android.content.ContentValues; import android.content.Context; import android.database.Cursor; -import android.net.Uri; -import android.os.SystemClock; -import android.text.TextUtils; import androidx.annotation.NonNull; @@ -14,10 +10,7 @@ import net.sqlcipher.database.SQLiteDatabase; import net.sqlcipher.database.SQLiteDatabaseHook; import net.sqlcipher.database.SQLiteOpenHelper; -import org.session.libsignal.service.loki.api.opengroups.PublicChat; import org.thoughtcrime.securesms.crypto.DatabaseSecret; -import org.thoughtcrime.securesms.crypto.MasterSecret; -import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.AttachmentDatabase; import org.thoughtcrime.securesms.database.DraftDatabase; import org.thoughtcrime.securesms.database.GroupDatabase; @@ -40,44 +33,14 @@ import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase; import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase; import org.thoughtcrime.securesms.loki.database.LokiUserDatabase; import org.thoughtcrime.securesms.loki.database.SharedSenderKeysDatabase; -import org.thoughtcrime.securesms.notifications.NotificationChannels; -import org.thoughtcrime.securesms.service.KeyCachingService; -import org.thoughtcrime.securesms.util.GroupUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; - -import java.io.File; public class SQLCipherOpenHelper extends SQLiteOpenHelper { @SuppressWarnings("unused") private static final String TAG = SQLCipherOpenHelper.class.getSimpleName(); - private static final int RECIPIENT_CALL_RINGTONE_VERSION = 2; - private static final int MIGRATE_PREKEYS_VERSION = 3; - private static final int MIGRATE_SESSIONS_VERSION = 4; - private static final int NO_MORE_IMAGE_THUMBNAILS_VERSION = 5; - private static final int ATTACHMENT_DIMENSIONS = 6; - private static final int QUOTED_REPLIES = 7; - private static final int SHARED_CONTACTS = 8; - private static final int FULL_TEXT_SEARCH = 9; - private static final int BAD_IMPORT_CLEANUP = 10; - private static final int QUOTE_MISSING = 11; - private static final int NOTIFICATION_CHANNELS = 12; - private static final int SECRET_SENDER = 13; - private static final int ATTACHMENT_CAPTIONS = 14; - private static final int ATTACHMENT_CAPTIONS_FIX = 15; - private static final int PREVIEWS = 16; - private static final int CONVERSATION_SEARCH = 17; - private static final int SELF_ATTACHMENT_CLEANUP = 18; - private static final int RECIPIENT_FORCE_SMS_SELECTION = 19; - private static final int JOBMANAGER_STRIKES_BACK = 20; - private static final int STICKERS = 21; - private static final int lokiV1 = 22; - private static final int lokiV2 = 23; - private static final int lokiV3 = 24; - private static final int lokiV4 = 25; - private static final int lokiV5 = 26; - private static final int lokiV6 = 27; + // First public release (1.0.0) DB version was 27. + // So we have to keep the migrations onwards. private static final int lokiV7 = 28; private static final int lokiV8 = 29; private static final int lokiV9 = 30; @@ -177,20 +140,6 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { executeStatements(db, GroupDatabase.CREATE_INDEXS); executeStatements(db, GroupReceiptDatabase.CREATE_INDEXES); executeStatements(db, StickerDatabase.CREATE_INDEXES); - - if (context.getDatabasePath(ClassicOpenHelper.NAME).exists()) { - ClassicOpenHelper legacyHelper = new ClassicOpenHelper(context); - android.database.sqlite.SQLiteDatabase legacyDb = legacyHelper.getWritableDatabase(); - - SQLCipherMigrationHelper.migratePlaintext(context, legacyDb, db); - - MasterSecret masterSecret = KeyCachingService.getMasterSecret(context); - - if (masterSecret != null) SQLCipherMigrationHelper.migrateCiphertext(context, masterSecret, legacyDb, db, null); - else TextSecurePreferences.setNeedsSqlCipherMigration(context, true); - - SessionStoreMigrationHelper.migrateSessions(context, db); - } } @Override @@ -210,379 +159,6 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { try { - if (oldVersion < RECIPIENT_CALL_RINGTONE_VERSION) { - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN call_ringtone TEXT DEFAULT NULL"); - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN call_vibrate INTEGER DEFAULT " + RecipientDatabase.VibrateState.DEFAULT.getId()); - } - - if (oldVersion < MIGRATE_PREKEYS_VERSION) { - db.execSQL("CREATE TABLE signed_prekeys (_id INTEGER PRIMARY KEY, key_id INTEGER UNIQUE, public_key TEXT NOT NULL, private_key TEXT NOT NULL, signature TEXT NOT NULL, timestamp INTEGER DEFAULT 0)"); - db.execSQL("CREATE TABLE one_time_prekeys (_id INTEGER PRIMARY KEY, key_id INTEGER UNIQUE, public_key TEXT NOT NULL, private_key TEXT NOT NULL)"); - } - - if (oldVersion < MIGRATE_SESSIONS_VERSION) { - db.execSQL("CREATE TABLE sessions (_id INTEGER PRIMARY KEY, address TEXT NOT NULL, device INTEGER NOT NULL, record BLOB NOT NULL, UNIQUE(address, device) ON CONFLICT REPLACE)"); - SessionStoreMigrationHelper.migrateSessions(context, db); - } - - if (oldVersion < NO_MORE_IMAGE_THUMBNAILS_VERSION) { - ContentValues update = new ContentValues(); - update.put("thumbnail", (String)null); - update.put("aspect_ratio", (String)null); - update.put("thumbnail_random", (String)null); - - try (Cursor cursor = db.query("part", new String[] {"_id", "ct", "thumbnail"}, "thumbnail IS NOT NULL", null, null, null, null)) { - while (cursor != null && cursor.moveToNext()) { - long id = cursor.getLong(cursor.getColumnIndexOrThrow("_id")); - String contentType = cursor.getString(cursor.getColumnIndexOrThrow("ct")); - - if (contentType != null && !contentType.startsWith("video")) { - String thumbnailPath = cursor.getString(cursor.getColumnIndexOrThrow("thumbnail")); - File thumbnailFile = new File(thumbnailPath); - thumbnailFile.delete(); - - db.update("part", update, "_id = ?", new String[] {String.valueOf(id)}); - } - } - } - } - - if (oldVersion < ATTACHMENT_DIMENSIONS) { - db.execSQL("ALTER TABLE part ADD COLUMN width INTEGER DEFAULT 0"); - db.execSQL("ALTER TABLE part ADD COLUMN height INTEGER DEFAULT 0"); - } - - if (oldVersion < QUOTED_REPLIES) { - db.execSQL("ALTER TABLE mms ADD COLUMN quote_id INTEGER DEFAULT 0"); - db.execSQL("ALTER TABLE mms ADD COLUMN quote_author TEXT"); - db.execSQL("ALTER TABLE mms ADD COLUMN quote_body TEXT"); - db.execSQL("ALTER TABLE mms ADD COLUMN quote_attachment INTEGER DEFAULT -1"); - - db.execSQL("ALTER TABLE part ADD COLUMN quote INTEGER DEFAULT 0"); - } - - if (oldVersion < SHARED_CONTACTS) { - db.execSQL("ALTER TABLE mms ADD COLUMN shared_contacts TEXT"); - } - - if (oldVersion < FULL_TEXT_SEARCH) { - db.execSQL("CREATE VIRTUAL TABLE sms_fts USING fts5(body, content=sms, content_rowid=_id)"); - db.execSQL("CREATE TRIGGER sms_ai AFTER INSERT ON sms BEGIN\n" + - " INSERT INTO sms_fts(rowid, body) VALUES (new._id, new.body);\n" + - "END;"); - db.execSQL("CREATE TRIGGER sms_ad AFTER DELETE ON sms BEGIN\n" + - " INSERT INTO sms_fts(sms_fts, rowid, body) VALUES('delete', old._id, old.body);\n" + - "END;\n"); - db.execSQL("CREATE TRIGGER sms_au AFTER UPDATE ON sms BEGIN\n" + - " INSERT INTO sms_fts(sms_fts, rowid, body) VALUES('delete', old._id, old.body);\n" + - " INSERT INTO sms_fts(rowid, body) VALUES(new._id, new.body);\n" + - "END;"); - - db.execSQL("CREATE VIRTUAL TABLE mms_fts USING fts5(body, content=mms, content_rowid=_id)"); - db.execSQL("CREATE TRIGGER mms_ai AFTER INSERT ON mms BEGIN\n" + - " INSERT INTO mms_fts(rowid, body) VALUES (new._id, new.body);\n" + - "END;"); - db.execSQL("CREATE TRIGGER mms_ad AFTER DELETE ON mms BEGIN\n" + - " INSERT INTO mms_fts(mms_fts, rowid, body) VALUES('delete', old._id, old.body);\n" + - "END;\n"); - db.execSQL("CREATE TRIGGER mms_au AFTER UPDATE ON mms BEGIN\n" + - " INSERT INTO mms_fts(mms_fts, rowid, body) VALUES('delete', old._id, old.body);\n" + - " INSERT INTO mms_fts(rowid, body) VALUES(new._id, new.body);\n" + - "END;"); - - Log.i(TAG, "Beginning to build search index."); - long start = SystemClock.elapsedRealtime(); - - db.execSQL("INSERT INTO sms_fts (rowid, body) SELECT _id, body FROM sms"); - - long smsFinished = SystemClock.elapsedRealtime(); - Log.i(TAG, "Indexing SMS completed in " + (smsFinished - start) + " ms"); - - db.execSQL("INSERT INTO mms_fts (rowid, body) SELECT _id, body FROM mms"); - - long mmsFinished = SystemClock.elapsedRealtime(); - Log.i(TAG, "Indexing MMS completed in " + (mmsFinished - smsFinished) + " ms"); - Log.i(TAG, "Indexing finished. Total time: " + (mmsFinished - start) + " ms"); - } - - if (oldVersion < BAD_IMPORT_CLEANUP) { - String trimmedCondition = " NOT IN (SELECT _id FROM mms)"; - - db.delete("group_receipts", "mms_id" + trimmedCondition, null); - - String[] columns = new String[] { "_id", "unique_id", "_data", "thumbnail"}; - - try (Cursor cursor = db.query("part", columns, "mid" + trimmedCondition, null, null, null, null)) { - while (cursor != null && cursor.moveToNext()) { - db.delete("part", "_id = ? AND unique_id = ?", new String[] { String.valueOf(cursor.getLong(0)), String.valueOf(cursor.getLong(1)) }); - - String data = cursor.getString(2); - String thumbnail = cursor.getString(3); - - if (!TextUtils.isEmpty(data)) { - new File(data).delete(); - } - - if (!TextUtils.isEmpty(thumbnail)) { - new File(thumbnail).delete(); - } - } - } - } - - // Note: This column only being checked due to upgrade issues as described in #8184 - if (oldVersion < QUOTE_MISSING && !columnExists(db, "mms", "quote_missing")) { - db.execSQL("ALTER TABLE mms ADD COLUMN quote_missing INTEGER DEFAULT 0"); - } - - // Note: The column only being checked due to upgrade issues as described in #8184 - if (oldVersion < NOTIFICATION_CHANNELS && !columnExists(db, "recipient_preferences", "notification_channel")) { - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN notification_channel TEXT DEFAULT NULL"); - NotificationChannels.create(context); - - try (Cursor cursor = db.rawQuery("SELECT recipient_ids, system_display_name, signal_profile_name, notification, vibrate FROM recipient_preferences WHERE notification NOT NULL OR vibrate != 0", null)) { - while (cursor != null && cursor.moveToNext()) { - String addressString = cursor.getString(cursor.getColumnIndexOrThrow("recipient_ids")); - Address address = Address.fromExternal(context, addressString); - String systemName = cursor.getString(cursor.getColumnIndexOrThrow("system_display_name")); - String profileName = cursor.getString(cursor.getColumnIndexOrThrow("signal_profile_name")); - String messageSound = cursor.getString(cursor.getColumnIndexOrThrow("notification")); - Uri messageSoundUri = messageSound != null ? Uri.parse(messageSound) : null; - int vibrateState = cursor.getInt(cursor.getColumnIndexOrThrow("vibrate")); - String displayName = NotificationChannels.getChannelDisplayNameFor(context, systemName, profileName, address); - boolean vibrateEnabled = vibrateState == 0 ? TextSecurePreferences.isNotificationVibrateEnabled(context) : vibrateState == 1; - - if (address.isGroup()) { - try(Cursor groupCursor = db.rawQuery("SELECT title FROM groups WHERE group_id = ?", new String[] { address.toGroupString() })) { - if (groupCursor != null && groupCursor.moveToFirst()) { - String title = groupCursor.getString(groupCursor.getColumnIndexOrThrow("title")); - - if (!TextUtils.isEmpty(title)) { - displayName = title; - } - } - } - } - - String channelId = NotificationChannels.createChannelFor(context, address, displayName, messageSoundUri, vibrateEnabled); - - ContentValues values = new ContentValues(1); - values.put("notification_channel", channelId); - db.update("recipient_preferences", values, "recipient_ids = ?", new String[] { addressString }); - } - } - } - - if (oldVersion < SECRET_SENDER) { - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN unidentified_access_mode INTEGER DEFAULT 0"); - db.execSQL("ALTER TABLE push ADD COLUMN server_timestamp INTEGER DEFAULT 0"); - db.execSQL("ALTER TABLE push ADD COLUMN server_guid TEXT DEFAULT NULL"); - db.execSQL("ALTER TABLE group_receipts ADD COLUMN unidentified INTEGER DEFAULT 0"); - db.execSQL("ALTER TABLE mms ADD COLUMN unidentified INTEGER DEFAULT 0"); - db.execSQL("ALTER TABLE sms ADD COLUMN unidentified INTEGER DEFAULT 0"); - } - - if (oldVersion < ATTACHMENT_CAPTIONS) { - db.execSQL("ALTER TABLE part ADD COLUMN caption TEXT DEFAULT NULL"); - } - - // 4.30.8 included a migration, but not a correct CREATE_TABLE statement, so we need to add - // this column if it isn't present. - if (oldVersion < ATTACHMENT_CAPTIONS_FIX) { - if (!columnExists(db, "part", "caption")) { - db.execSQL("ALTER TABLE part ADD COLUMN caption TEXT DEFAULT NULL"); - } - } - - if (oldVersion < PREVIEWS) { - db.execSQL("ALTER TABLE mms ADD COLUMN previews TEXT"); - } - - if (oldVersion < CONVERSATION_SEARCH) { - db.execSQL("DROP TABLE sms_fts"); - db.execSQL("DROP TABLE mms_fts"); - db.execSQL("DROP TRIGGER sms_ai"); - db.execSQL("DROP TRIGGER sms_au"); - db.execSQL("DROP TRIGGER sms_ad"); - db.execSQL("DROP TRIGGER mms_ai"); - db.execSQL("DROP TRIGGER mms_au"); - db.execSQL("DROP TRIGGER mms_ad"); - - db.execSQL("CREATE VIRTUAL TABLE sms_fts USING fts5(body, thread_id UNINDEXED, content=sms, content_rowid=_id)"); - db.execSQL("CREATE TRIGGER sms_ai AFTER INSERT ON sms BEGIN\n" + - " INSERT INTO sms_fts(rowid, body, thread_id) VALUES (new._id, new.body, new.thread_id);\n" + - "END;"); - db.execSQL("CREATE TRIGGER sms_ad AFTER DELETE ON sms BEGIN\n" + - " INSERT INTO sms_fts(sms_fts, rowid, body, thread_id) VALUES('delete', old._id, old.body, old.thread_id);\n" + - "END;\n"); - db.execSQL("CREATE TRIGGER sms_au AFTER UPDATE ON sms BEGIN\n" + - " INSERT INTO sms_fts(sms_fts, rowid, body, thread_id) VALUES('delete', old._id, old.body, old.thread_id);\n" + - " INSERT INTO sms_fts(rowid, body, thread_id) VALUES(new._id, new.body, new.thread_id);\n" + - "END;"); - - db.execSQL("CREATE VIRTUAL TABLE mms_fts USING fts5(body, thread_id UNINDEXED, content=mms, content_rowid=_id)"); - db.execSQL("CREATE TRIGGER mms_ai AFTER INSERT ON mms BEGIN\n" + - " INSERT INTO mms_fts(rowid, body, thread_id) VALUES (new._id, new.body, new.thread_id);\n" + - "END;"); - db.execSQL("CREATE TRIGGER mms_ad AFTER DELETE ON mms BEGIN\n" + - " INSERT INTO mms_fts(mms_fts, rowid, body, thread_id) VALUES('delete', old._id, old.body, old.thread_id);\n" + - "END;\n"); - db.execSQL("CREATE TRIGGER mms_au AFTER UPDATE ON mms BEGIN\n" + - " INSERT INTO mms_fts(mms_fts, rowid, body, thread_id) VALUES('delete', old._id, old.body, old.thread_id);\n" + - " INSERT INTO mms_fts(rowid, body, thread_id) VALUES(new._id, new.body, new.thread_id);\n" + - "END;"); - - Log.i(TAG, "Beginning to build search index."); - long start = SystemClock.elapsedRealtime(); - - db.execSQL("INSERT INTO sms_fts (rowid, body, thread_id) SELECT _id, body, thread_id FROM sms"); - - long smsFinished = SystemClock.elapsedRealtime(); - Log.i(TAG, "Indexing SMS completed in " + (smsFinished - start) + " ms"); - - db.execSQL("INSERT INTO mms_fts (rowid, body, thread_id) SELECT _id, body, thread_id FROM mms"); - - long mmsFinished = SystemClock.elapsedRealtime(); - Log.i(TAG, "Indexing MMS completed in " + (mmsFinished - smsFinished) + " ms"); - Log.i(TAG, "Indexing finished. Total time: " + (mmsFinished - start) + " ms"); - } - - if (oldVersion < SELF_ATTACHMENT_CLEANUP) { - String localNumber = TextSecurePreferences.getLocalNumber(context); - - if (!TextUtils.isEmpty(localNumber)) { - try (Cursor threadCursor = db.rawQuery("SELECT _id FROM thread WHERE recipient_ids = ?", new String[]{ localNumber })) { - if (threadCursor != null && threadCursor.moveToFirst()) { - long threadId = threadCursor.getLong(0); - ContentValues updateValues = new ContentValues(1); - - updateValues.put("pending_push", 0); - - int count = db.update("part", updateValues, "mid IN (SELECT _id FROM mms WHERE thread_id = ?)", new String[]{ String.valueOf(threadId) }); - Log.i(TAG, "Updated " + count + " self-sent attachments."); - } - } - } - } - - if (oldVersion < RECIPIENT_FORCE_SMS_SELECTION) { - db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN force_sms_selection INTEGER DEFAULT 0"); - } - - if (oldVersion < JOBMANAGER_STRIKES_BACK) { - db.execSQL("CREATE TABLE job_spec(_id INTEGER PRIMARY KEY AUTOINCREMENT, " + - "job_spec_id TEXT UNIQUE, " + - "factory_key TEXT, " + - "queue_key TEXT, " + - "create_time INTEGER, " + - "next_run_attempt_time INTEGER, " + - "run_attempt INTEGER, " + - "max_attempts INTEGER, " + - "max_backoff INTEGER, " + - "max_instances INTEGER, " + - "lifespan INTEGER, " + - "serialized_data TEXT, " + - "is_running INTEGER)"); - - db.execSQL("CREATE TABLE constraint_spec(_id INTEGER PRIMARY KEY AUTOINCREMENT, " + - "job_spec_id TEXT, " + - "factory_key TEXT, " + - "UNIQUE(job_spec_id, factory_key))"); - - db.execSQL("CREATE TABLE dependency_spec(_id INTEGER PRIMARY KEY AUTOINCREMENT, " + - "job_spec_id TEXT, " + - "depends_on_job_spec_id TEXT, " + - "UNIQUE(job_spec_id, depends_on_job_spec_id))"); - } - - if (oldVersion < STICKERS) { - db.execSQL("CREATE TABLE sticker (_id INTEGER PRIMARY KEY AUTOINCREMENT, " + - "pack_id TEXT NOT NULL, " + - "pack_key TEXT NOT NULL, " + - "pack_title TEXT NOT NULL, " + - "pack_author TEXT NOT NULL, " + - "sticker_id INTEGER, " + - "cover INTEGER, " + - "emoji TEXT NOT NULL, " + - "last_used INTEGER, " + - "installed INTEGER," + - "file_path TEXT NOT NULL, " + - "file_length INTEGER, " + - "file_random BLOB, " + - "UNIQUE(pack_id, sticker_id, cover) ON CONFLICT IGNORE)"); - - db.execSQL("CREATE INDEX IF NOT EXISTS sticker_pack_id_index ON sticker (pack_id);"); - db.execSQL("CREATE INDEX IF NOT EXISTS sticker_sticker_id_index ON sticker (sticker_id);"); - - db.execSQL("ALTER TABLE part ADD COLUMN sticker_pack_id TEXT"); - db.execSQL("ALTER TABLE part ADD COLUMN sticker_pack_key TEXT"); - db.execSQL("ALTER TABLE part ADD COLUMN sticker_id INTEGER DEFAULT -1"); - db.execSQL("CREATE INDEX IF NOT EXISTS part_sticker_pack_id_index ON part (sticker_pack_id)"); - } - - if (oldVersion < lokiV1) { - db.execSQL(LokiAPIDatabase.getCreateOpenGroupAuthTokenTableCommand()); - db.execSQL(LokiAPIDatabase.getCreateLastMessageServerIDTableCommand()); - db.execSQL(LokiAPIDatabase.getCreateLastDeletionServerIDTableCommand()); - } - - if (oldVersion < lokiV2) { - db.execSQL(LokiUserDatabase.getCreateServerDisplayNameTableCommand()); - } - - if (oldVersion < lokiV3) { - db.execSQL(LokiAPIDatabase.getCreateDeviceLinkCacheCommand()); - db.execSQL(LokiThreadDatabase.getCreatePublicChatTableCommand()); - - db.execSQL("ALTER TABLE groups ADD COLUMN avatar_url TEXT"); - db.execSQL("ALTER TABLE part ADD COLUMN url TEXT"); - } - - if (oldVersion < lokiV4) { - db.execSQL(LokiMessageDatabase.getCreateMessageToThreadMappingTableCommand()); - } - - if (oldVersion < lokiV5) { - db.execSQL(LokiAPIDatabase.getCreateUserCountTableCommand()); - } - - if (oldVersion < lokiV6) { - // Migrate public chats from __textsecure_group__ to __loki_public_chat_group__ - try (Cursor lokiPublicChatCursor = db.rawQuery("SELECT public_chat FROM loki_public_chat_database", null)) { - while (lokiPublicChatCursor != null && lokiPublicChatCursor.moveToNext()) { - String chatString = lokiPublicChatCursor.getString(0); - PublicChat publicChat = PublicChat.fromJSON(chatString); - if (publicChat != null) { - byte[] groupId = publicChat.getId().getBytes(); - String oldId = GroupUtil.getEncodedId(groupId, false); - String newId = GroupUtil.getEncodedOpenGroupId(groupId); - ContentValues threadUpdate = new ContentValues(); - threadUpdate.put("recipient_ids", newId); - db.update("thread", threadUpdate, "recipient_ids = ?", new String[]{ oldId }); - ContentValues groupUpdate = new ContentValues(); - groupUpdate.put("group_id", newId); - db.update("groups", groupUpdate,"group_id = ?", new String[] { oldId }); - } - } - } - - // Migrate RSS feeds from __textsecure_group__ to __loki_rss_feed_group__ - String[] rssFeedIds = new String[] { "loki.network.feed", "loki.network.messenger-updates.feed" }; - for (String groupId : rssFeedIds) { - String oldId = GroupUtil.getEncodedId(groupId.getBytes(), false); - String newId = GroupUtil.getEncodedRSSFeedId(groupId.getBytes()); - ContentValues threadUpdate = new ContentValues(); - threadUpdate.put("recipient_ids", newId); - db.update("thread", threadUpdate, "recipient_ids = ?", new String[]{ oldId }); - ContentValues groupUpdate = new ContentValues(); - groupUpdate.put("group_id", newId); - db.update("groups", groupUpdate,"group_id = ?", new String[] { oldId }); - } - - // Add admin field in groups - db.execSQL("ALTER TABLE groups ADD COLUMN admins TEXT"); - } - if (oldVersion < lokiV7) { db.execSQL(LokiMessageDatabase.getCreateErrorMessageTableCommand()); } @@ -652,10 +228,6 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { } finally { db.endTransaction(); } - - if (oldVersion < MIGRATE_PREKEYS_VERSION) { -// PreKeyMigrationHelper.cleanUpPreKeys(context); - } } public SQLiteDatabase getReadableDatabase() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SessionStoreMigrationHelper.java b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SessionStoreMigrationHelper.java deleted file mode 100644 index ce74ce7e75..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SessionStoreMigrationHelper.java +++ /dev/null @@ -1,109 +0,0 @@ -package org.thoughtcrime.securesms.database.helpers; - - -import android.content.ContentValues; -import android.content.Context; -import org.thoughtcrime.securesms.logging.Log; - -import net.sqlcipher.database.SQLiteDatabase; - -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.SessionDatabase; -import org.thoughtcrime.securesms.util.Conversions; -import org.session.libsignal.libsignal.state.SessionRecord; -import org.session.libsignal.libsignal.state.SessionState; -import org.session.libsignal.libsignal.state.StorageProtos.SessionStructure; -import org.session.libsignal.service.api.push.SignalServiceAddress; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; - -class SessionStoreMigrationHelper { - - private static final String TAG = SessionStoreMigrationHelper.class.getSimpleName(); - - private static final String SESSIONS_DIRECTORY_V2 = "sessions-v2"; - private static final Object FILE_LOCK = new Object(); - - private static final int SINGLE_STATE_VERSION = 1; - private static final int ARCHIVE_STATES_VERSION = 2; - private static final int PLAINTEXT_VERSION = 3; - private static final int CURRENT_VERSION = 3; - - static void migrateSessions(Context context, SQLiteDatabase database) { - File directory = new File(context.getFilesDir(), SESSIONS_DIRECTORY_V2); - - if (directory.exists()) { - File[] sessionFiles = directory.listFiles(); - - if (sessionFiles != null) { - for (File sessionFile : sessionFiles) { - try { - String[] parts = sessionFile.getName().split("[.]"); - Address address = Address.fromSerialized(parts[0]); - - int deviceId; - - if (parts.length > 1) deviceId = Integer.parseInt(parts[1]); - else deviceId = SignalServiceAddress.DEFAULT_DEVICE_ID; - - FileInputStream in = new FileInputStream(sessionFile); - int versionMarker = readInteger(in); - - if (versionMarker > CURRENT_VERSION) { - throw new AssertionError("Unknown version: " + versionMarker + ", " + sessionFile.getAbsolutePath()); - } - - byte[] serialized = readBlob(in); - in.close(); - - if (versionMarker < PLAINTEXT_VERSION) { - throw new AssertionError("Not plaintext: " + versionMarker + ", " + sessionFile.getAbsolutePath()); - } - - SessionRecord sessionRecord; - - if (versionMarker == SINGLE_STATE_VERSION) { - Log.i(TAG, "Migrating single state version: " + sessionFile.getAbsolutePath()); - SessionStructure sessionStructure = SessionStructure.parseFrom(serialized); - SessionState sessionState = new SessionState(sessionStructure); - - sessionRecord = new SessionRecord(sessionState); - } else if (versionMarker >= ARCHIVE_STATES_VERSION) { - Log.i(TAG, "Migrating session: " + sessionFile.getAbsolutePath()); - sessionRecord = new SessionRecord(serialized); - } else { - throw new AssertionError("Unknown version: " + versionMarker + ", " + sessionFile.getAbsolutePath()); - } - - - ContentValues contentValues = new ContentValues(); - contentValues.put(SessionDatabase.ADDRESS, address.serialize()); - contentValues.put(SessionDatabase.DEVICE, deviceId); - contentValues.put(SessionDatabase.RECORD, sessionRecord.serialize()); - - database.insert(SessionDatabase.TABLE_NAME, null, contentValues); - } catch (NumberFormatException | IOException e) { - Log.w(TAG, e); - } - } - } - } - } - - private static byte[] readBlob(FileInputStream in) throws IOException { - int length = readInteger(in); - byte[] blobBytes = new byte[length]; - - in.read(blobBytes, 0, blobBytes.length); - return blobBytes; - } - - private static int readInteger(FileInputStream in) throws IOException { - byte[] integer = new byte[4]; - in.read(integer, 0, integer.length); - return Conversions.byteArrayToInt(integer); - } - -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyActivity.java b/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyActivity.java index ac1215f6fd..6b2e83b735 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyActivity.java @@ -21,7 +21,6 @@ import com.google.android.material.tabs.TabLayout; import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity; import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.providers.BlobProvider; -import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.ViewUtil; @@ -42,19 +41,12 @@ public class GiphyActivity extends PassphraseRequiredActionBarActivity public static final String EXTRA_WIDTH = "extra_width"; public static final String EXTRA_HEIGHT = "extra_height"; - private final DynamicLanguage dynamicLanguage = new DynamicLanguage(); - private GiphyGifFragment gifFragment; private GiphyStickerFragment stickerFragment; private boolean forMms; private GiphyAdapter.GiphyViewHolder finishingImage; - @Override - public void onPreCreate() { - dynamicLanguage.onCreate(this); - } - @Override public void onCreate(Bundle bundle, boolean ready) { setContentView(R.layout.giphy_activity); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshAttributesJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshAttributesJob.java index e2af735dd5..ecafc01fff 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshAttributesJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshAttributesJob.java @@ -19,6 +19,7 @@ import java.io.IOException; import javax.inject.Inject; +//TODO AC: Delete public class RefreshAttributesJob extends BaseJob implements InjectableType { public static final String KEY = "RefreshAttributesJob"; diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/AppProtectionPreferenceFragment.java b/app/src/main/java/org/thoughtcrime/securesms/preferences/AppProtectionPreferenceFragment.java index 2ebc0a9ad2..fbef184b4a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/AppProtectionPreferenceFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/AppProtectionPreferenceFragment.java @@ -5,17 +5,13 @@ import android.app.KeyguardManager; import android.content.Context; import android.content.Intent; import android.os.Bundle; -import android.widget.Toast; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; -import androidx.preference.CheckBoxPreference; import androidx.preference.Preference; import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.PassphraseChangeActivity; import org.thoughtcrime.securesms.components.SwitchPreferenceCompat; -import org.thoughtcrime.securesms.crypto.MasterSecretUtil; import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.util.TextSecurePreferences; @@ -27,8 +23,6 @@ import network.loki.messenger.R; public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment implements InjectableType { - private CheckBoxPreference disablePassphrase; - @Override public void onAttach(Activity activity) { super.onAttach(activity); @@ -39,17 +33,12 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment public void onCreate(Bundle paramBundle) { super.onCreate(paramBundle); - disablePassphrase = (CheckBoxPreference) this.findPreference("pref_enable_passphrase_temporary"); - this.findPreference(TextSecurePreferences.SCREEN_LOCK).setOnPreferenceChangeListener(new ScreenLockListener()); this.findPreference(TextSecurePreferences.SCREEN_LOCK_TIMEOUT).setOnPreferenceClickListener(new ScreenLockTimeoutListener()); - this.findPreference(TextSecurePreferences.CHANGE_PASSPHRASE_PREF).setOnPreferenceClickListener(new ChangePassphraseClickListener()); - this.findPreference(TextSecurePreferences.PASSPHRASE_TIMEOUT_INTERVAL_PREF).setOnPreferenceClickListener(new PassphraseIntervalClickListener()); this.findPreference(TextSecurePreferences.READ_RECEIPTS_PREF).setOnPreferenceChangeListener(new ReadReceiptToggleListener()); this.findPreference(TextSecurePreferences.TYPING_INDICATORS).setOnPreferenceChangeListener(new TypingIndicatorsToggleListener()); this.findPreference(TextSecurePreferences.LINK_PREVIEWS).setOnPreferenceChangeListener(new LinkPreviewToggleListener()); - disablePassphrase.setOnPreferenceChangeListener(new DisablePassphraseClickListener()); initializeVisibility(); } @@ -62,16 +51,9 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment @Override public void onResume() { super.onResume(); - if (!TextSecurePreferences.isPasswordDisabled(getContext())) initializePassphraseTimeoutSummary(); - else initializeScreenLockTimeoutSummary(); - - disablePassphrase.setChecked(!TextSecurePreferences.isPasswordDisabled(getActivity())); - } - - private void initializePassphraseTimeoutSummary() { - int timeoutMinutes = TextSecurePreferences.getPassphraseTimeoutInterval(getActivity()); - this.findPreference(TextSecurePreferences.PASSPHRASE_TIMEOUT_INTERVAL_PREF) - .setSummary(getResources().getQuantityString(R.plurals.AppProtectionPreferenceFragment_minutes, timeoutMinutes, timeoutMinutes)); + if (TextSecurePreferences.isPasswordDisabled(getContext())) { + initializeScreenLockTimeoutSummary(); + } } private void initializeScreenLockTimeoutSummary() { @@ -87,11 +69,6 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment private void initializeVisibility() { if (TextSecurePreferences.isPasswordDisabled(getContext())) { - findPreference("pref_enable_passphrase_temporary").setVisible(false); - findPreference(TextSecurePreferences.CHANGE_PASSPHRASE_PREF).setVisible(false); - findPreference(TextSecurePreferences.PASSPHRASE_TIMEOUT_INTERVAL_PREF).setVisible(false); - findPreference(TextSecurePreferences.PASSPHRASE_TIMEOUT_PREF).setVisible(false); - KeyguardManager keyguardManager = (KeyguardManager)getContext().getSystemService(Context.KEYGUARD_SERVICE); if (!keyguardManager.isKeyguardSecure()) { ((SwitchPreferenceCompat)findPreference(TextSecurePreferences.SCREEN_LOCK)).setChecked(false); @@ -178,90 +155,4 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment return true; } } - - public static CharSequence getSummary(Context context) { - final int privacySummaryResId = R.string.ApplicationPreferencesActivity_privacy_summary; - final String onRes = context.getString(R.string.ApplicationPreferencesActivity_on); - final String offRes = context.getString(R.string.ApplicationPreferencesActivity_off); - - if (TextSecurePreferences.isPasswordDisabled(context) && !TextSecurePreferences.isScreenLockEnabled(context)) { -// if (TextSecurePreferences.isRegistrationtLockEnabled(context)) { -// return context.getString(privacySummaryResId, offRes, onRes); -// } else { - return context.getString(privacySummaryResId, offRes, offRes); -// } - } else { -// if (TextSecurePreferences.isRegistrationtLockEnabled(context)) { -// return context.getString(privacySummaryResId, onRes, onRes); -// } else { - return context.getString(privacySummaryResId, onRes, offRes); -// } - } - } - - private class ChangePassphraseClickListener implements Preference.OnPreferenceClickListener { - @Override - public boolean onPreferenceClick(Preference preference) { - if (MasterSecretUtil.isPassphraseInitialized(getActivity())) { - startActivity(new Intent(getActivity(), PassphraseChangeActivity.class)); - } else { - Toast.makeText(getActivity(), - R.string.ApplicationPreferenceActivity_you_havent_set_a_passphrase_yet, - Toast.LENGTH_LONG).show(); - } - - return true; - } - } - - private class PassphraseIntervalClickListener implements Preference.OnPreferenceClickListener { - - @Override - public boolean onPreferenceClick(Preference preference) { - new TimeDurationPickerDialog(getContext(), (view, duration) -> { - int timeoutMinutes = Math.max((int)TimeUnit.MILLISECONDS.toMinutes(duration), 1); - - TextSecurePreferences.setPassphraseTimeoutInterval(getActivity(), timeoutMinutes); - - initializePassphraseTimeoutSummary(); - - }, 0).show(); - - return true; - } - } - - private class DisablePassphraseClickListener implements Preference.OnPreferenceChangeListener { - - @Override - public boolean onPreferenceChange(final Preference preference, Object newValue) { - if (((CheckBoxPreference)preference).isChecked()) { - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - builder.setTitle(R.string.ApplicationPreferencesActivity_disable_passphrase); - builder.setMessage(R.string.ApplicationPreferencesActivity_this_will_permanently_unlock_signal_and_message_notifications); - builder.setIconAttribute(R.attr.dialog_alert_icon); - builder.setPositiveButton(R.string.ApplicationPreferencesActivity_disable, (dialog, which) -> { - MasterSecretUtil.changeMasterSecretPassphrase(getActivity(), - KeyCachingService.getMasterSecret(getContext()), - MasterSecretUtil.UNENCRYPTED_PASSPHRASE); - - TextSecurePreferences.setPasswordDisabled(getActivity(), true); - ((CheckBoxPreference)preference).setChecked(false); - - Intent intent = new Intent(getActivity(), KeyCachingService.class); - intent.setAction(KeyCachingService.DISABLE_ACTION); - getActivity().startService(intent); - - initializeVisibility(); - }); - builder.setNegativeButton(android.R.string.cancel, null); - builder.show(); - } else { - Intent intent = new Intent(getActivity(), PassphraseChangeActivity.class); - startActivity(intent); - } - - return false; - } - } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/KeyCachingService.java b/app/src/main/java/org/thoughtcrime/securesms/service/KeyCachingService.java index aae8b49377..911028cd32 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/KeyCachingService.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/KeyCachingService.java @@ -34,13 +34,9 @@ import androidx.core.app.NotificationCompat; import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.DatabaseUpgradeActivity; import org.thoughtcrime.securesms.DummyActivity; -import org.thoughtcrime.securesms.crypto.InvalidPassphraseException; -import org.thoughtcrime.securesms.crypto.MasterSecret; -import org.thoughtcrime.securesms.crypto.MasterSecretUtil; import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.loki.activities.HomeActivity; import org.thoughtcrime.securesms.notifications.NotificationChannels; -import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.ServiceUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; @@ -67,13 +63,12 @@ public class KeyCachingService extends Service { private static final String PASSPHRASE_EXPIRED_EVENT = "org.thoughtcrime.securesms.service.action.PASSPHRASE_EXPIRED_EVENT"; public static final String CLEAR_KEY_ACTION = "org.thoughtcrime.securesms.service.action.CLEAR_KEY"; public static final String DISABLE_ACTION = "org.thoughtcrime.securesms.service.action.DISABLE"; - public static final String LOCALE_CHANGE_EVENT = "org.thoughtcrime.securesms.service.action.LOCALE_CHANGE_EVENT"; - - private DynamicLanguage dynamicLanguage = new DynamicLanguage(); private final IBinder binder = new KeySetBinder(); - private static MasterSecret masterSecret; + // AC: This is a temporal drop off replacement for the refactoring time being. + // This field only indicates if the app was unlocked or not (null means locked). + private static Object masterSecret; public KeyCachingService() {} @@ -81,18 +76,6 @@ public class KeyCachingService extends Service { return getMasterSecret(context) == null; } - public static synchronized @Nullable MasterSecret getMasterSecret(Context context) { - if (masterSecret == null && (TextSecurePreferences.isPasswordDisabled(context) && !TextSecurePreferences.isScreenLockEnabled(context))) { - try { - return MasterSecretUtil.getMasterSecret(context, MasterSecretUtil.UNENCRYPTED_PASSPHRASE); - } catch (InvalidPassphraseException e) { - Log.w("KeyCachingService", e); - } - } - - return masterSecret; - } - public static void onAppForegrounded(@NonNull Context context) { ServiceUtil.getAlarmManager(context).cancel(buildExpirationPendingIntent(context)); } @@ -101,8 +84,22 @@ public class KeyCachingService extends Service { startTimeoutIfAppropriate(context); } + //TODO AC: Delete + public static synchronized @Nullable Object getMasterSecret(Context context) { +// if (masterSecret == null && (TextSecurePreferences.isPasswordDisabled(context) && !TextSecurePreferences.isScreenLockEnabled(context))) { +// try { +// return MasterSecretUtil.getMasterSecret(context, MasterSecretUtil.UNENCRYPTED_PASSPHRASE); +// } catch (InvalidPassphraseException e) { +// Log.w("KeyCachingService", e); +// } +// } + + return masterSecret; + } + + //TODO AC: Delete @SuppressLint("StaticFieldLeak") - public void setMasterSecret(final MasterSecret masterSecret) { + public void setMasterSecret(final Object masterSecret) { synchronized (KeyCachingService.class) { KeyCachingService.masterSecret = masterSecret; @@ -132,7 +129,6 @@ public class KeyCachingService extends Service { case CLEAR_KEY_ACTION: handleClearKey(); break; case PASSPHRASE_EXPIRED_EVENT: handleClearKey(); break; case DISABLE_ACTION: handleDisableService(); break; - case LOCALE_CHANGE_EVENT: handleLocaleChanged(); break; case LOCK_TOGGLED_EVENT: handleLockToggled(); break; } } @@ -146,12 +142,12 @@ public class KeyCachingService extends Service { super.onCreate(); if (TextSecurePreferences.isPasswordDisabled(this) && !TextSecurePreferences.isScreenLockEnabled(this)) { - try { - MasterSecret masterSecret = MasterSecretUtil.getMasterSecret(this, MasterSecretUtil.UNENCRYPTED_PASSPHRASE); - setMasterSecret(masterSecret); - } catch (InvalidPassphraseException e) { - Log.w("KeyCachingService", e); - } +// try { +// MasterSecret masterSecret = MasterSecretUtil.getMasterSecret(this, MasterSecretUtil.UNENCRYPTED_PASSPHRASE); + setMasterSecret(new Object()); +// } catch (InvalidPassphraseException e) { +// Log.w("KeyCachingService", e); +// } } } @@ -196,12 +192,12 @@ public class KeyCachingService extends Service { private void handleLockToggled() { stopForeground(true); - try { - MasterSecret masterSecret = MasterSecretUtil.getMasterSecret(this, MasterSecretUtil.UNENCRYPTED_PASSPHRASE); +// try { +// MasterSecret masterSecret = MasterSecretUtil.getMasterSecret(this, MasterSecretUtil.UNENCRYPTED_PASSPHRASE); setMasterSecret(masterSecret); - } catch (InvalidPassphraseException e) { - Log.w(TAG, e); - } +// } catch (InvalidPassphraseException e) { +// Log.w(TAG, e); +// } } private void handleDisableService() { @@ -212,11 +208,6 @@ public class KeyCachingService extends Service { } } - private void handleLocaleChanged() { - dynamicLanguage.updateServiceLocale(this); - foregroundService(); - } - private static void startTimeoutIfAppropriate(@NonNull Context context) { boolean appVisible = ApplicationContext.getInstance(context).isAppVisible(); boolean secretSet = KeyCachingService.masterSecret != null; diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java b/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java index ff1834e22e..202d901b15 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java @@ -136,7 +136,7 @@ public class TextSecurePreferences { private static final String DATABASE_UNENCRYPTED_SECRET = "pref_database_unencrypted_secret"; private static final String ATTACHMENT_ENCRYPTED_SECRET = "pref_attachment_encrypted_secret"; private static final String ATTACHMENT_UNENCRYPTED_SECRET = "pref_attachment_unencrypted_secret"; - private static final String NEEDS_SQLCIPHER_MIGRATION = "pref_needs_sql_cipher_migration"; + private static final String NEEDS_SQLCIPHER_MIGRATION = "pref_needs_sql_cipher_migration"; //TODO AC: Delete private static final String NEXT_PRE_KEY_ID = "pref_next_pre_key_id"; private static final String ACTIVE_SIGNED_PRE_KEY_ID = "pref_active_signed_pre_key_id"; diff --git a/app/src/main/res/layout/change_passphrase_activity.xml b/app/src/main/res/layout/change_passphrase_activity.xml deleted file mode 100644 index 4dfe1626f0..0000000000 --- a/app/src/main/res/layout/change_passphrase_activity.xml +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - -