Master secret removed.

Screen lock related classes refactoring.
Legacy database util classes and migrations removed.
This commit is contained in:
Anton Chekulaev 2020-12-14 18:16:16 +11:00
parent 2aa179585f
commit e294199ea3
33 changed files with 323 additions and 4333 deletions

View File

@ -312,10 +312,6 @@
android:screenOrientation="portrait" android:screenOrientation="portrait"
android:theme="@style/Theme.TextSecure.DayNight.NoActionBar" android:theme="@style/Theme.TextSecure.DayNight.NoActionBar"
android:windowSoftInputMode="stateHidden" /> android:windowSoftInputMode="stateHidden" />
<activity
android:name="org.thoughtcrime.securesms.PassphraseChangeActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
android:label="@string/AndroidManifest__change_passphrase" />
<activity <activity
android:name="org.thoughtcrime.securesms.stickers.StickerManagementActivity" android:name="org.thoughtcrime.securesms.stickers.StickerManagementActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"

View File

@ -38,7 +38,6 @@ import org.signal.aesgcmprovider.AesGcmProvider;
import org.thoughtcrime.securesms.components.TypingStatusRepository; import org.thoughtcrime.securesms.components.TypingStatusRepository;
import org.thoughtcrime.securesms.components.TypingStatusSender; import org.thoughtcrime.securesms.components.TypingStatusSender;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil; import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore; import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
@ -112,7 +111,6 @@ import org.session.libsignal.service.loki.protocol.closedgroups.SharedSenderKeys
import org.session.libsignal.service.loki.protocol.closedgroups.SharedSenderKeysImplementationDelegate; import org.session.libsignal.service.loki.protocol.closedgroups.SharedSenderKeysImplementationDelegate;
import org.session.libsignal.service.loki.protocol.mentions.MentionsManager; import org.session.libsignal.service.loki.protocol.mentions.MentionsManager;
import org.session.libsignal.service.loki.protocol.meta.SessionMetaProtocol; import org.session.libsignal.service.loki.protocol.meta.SessionMetaProtocol;
import org.session.libsignal.service.loki.protocol.meta.TTLUtilities;
import org.session.libsignal.service.loki.protocol.sessionmanagement.SessionManagementProtocol; import org.session.libsignal.service.loki.protocol.sessionmanagement.SessionManagementProtocol;
import org.session.libsignal.service.loki.protocol.sessionmanagement.SessionManagementProtocolDelegate; import org.session.libsignal.service.loki.protocol.sessionmanagement.SessionManagementProtocolDelegate;
import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLink; import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLink;
@ -582,7 +580,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
boolean wasUnlinked = TextSecurePreferences.getWasUnlinked(this); boolean wasUnlinked = TextSecurePreferences.getWasUnlinked(this);
TextSecurePreferences.clearAll(this); TextSecurePreferences.clearAll(this);
TextSecurePreferences.setWasUnlinked(this, wasUnlinked); TextSecurePreferences.setWasUnlinked(this, wasUnlinked);
MasterSecretUtil.clear(this); // MasterSecretUtil.clear(this);
if (!deleteDatabase("signal.db")) { if (!deleteDatabase("signal.db")) {
Log.d("Loki", "Failed to delete database."); Log.d("Loki", "Failed to delete database.");
} }

View File

@ -23,15 +23,10 @@ import android.content.Intent;
import android.database.Cursor; import android.database.Cursor;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import androidx.preference.PreferenceManager;
import android.view.View; import android.view.View;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import org.thoughtcrime.securesms.attachments.DatabaseAttachment; import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
import org.thoughtcrime.securesms.color.MaterialColor;
import org.thoughtcrime.securesms.contacts.avatars.ContactColorsLegacy;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.AttachmentDatabase; import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.MmsDatabase;
@ -40,17 +35,10 @@ import org.thoughtcrime.securesms.database.PushDatabase;
import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob; import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob;
import org.thoughtcrime.securesms.jobs.PushDecryptJob; import org.thoughtcrime.securesms.jobs.PushDecryptJob;
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.FileUtils;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.VersionTracker; import org.thoughtcrime.securesms.util.VersionTracker;
import java.io.File;
import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.TreeSet; import java.util.TreeSet;
@ -60,72 +48,72 @@ import network.loki.messenger.R;
public class DatabaseUpgradeActivity extends BaseActivity { public class DatabaseUpgradeActivity extends BaseActivity {
private static final String TAG = DatabaseUpgradeActivity.class.getSimpleName(); private static final String TAG = DatabaseUpgradeActivity.class.getSimpleName();
public static final int NO_MORE_KEY_EXCHANGE_PREFIX_VERSION = 0; // 46 // public static final int NO_MORE_KEY_EXCHANGE_PREFIX_VERSION = 0; // 46
public static final int MMS_BODY_VERSION = 0; // 46 // public static final int MMS_BODY_VERSION = 0; // 46
public static final int TOFU_IDENTITIES_VERSION = 1; // 50 // public static final int TOFU_IDENTITIES_VERSION = 1; // 50
public static final int CURVE25519_VERSION = 2; // 63 // public static final int CURVE25519_VERSION = 2; // 63
public static final int ASYMMETRIC_MASTER_SECRET_FIX_VERSION = 3; // 73 // public static final int ASYMMETRIC_MASTER_SECRET_FIX_VERSION = 3; // 73
public static final int NO_V1_VERSION = 4; // 83 // public static final int NO_V1_VERSION = 4; // 83
public static final int SIGNED_PREKEY_VERSION = 4; // 83 // public static final int SIGNED_PREKEY_VERSION = 4; // 83
public static final int NO_DECRYPT_QUEUE_VERSION = 5; // 113 // public static final int NO_DECRYPT_QUEUE_VERSION = 5; // 113
public static final int PUSH_DECRYPT_SERIAL_ID_VERSION = 6; // 131 // public static final int PUSH_DECRYPT_SERIAL_ID_VERSION = 6; // 131
public static final int MIGRATE_SESSION_PLAINTEXT = 7; // 136 // public static final int MIGRATE_SESSION_PLAINTEXT = 7; // 136
public static final int CONTACTS_ACCOUNT_VERSION = 7; // 136 // public static final int CONTACTS_ACCOUNT_VERSION = 7; // 136
public static final int MEDIA_DOWNLOAD_CONTROLS_VERSION = 8; // 151 // public static final int MEDIA_DOWNLOAD_CONTROLS_VERSION = 8; // 151
public static final int REDPHONE_SUPPORT_VERSION = 9; // 157 // public static final int REDPHONE_SUPPORT_VERSION = 9; // 157
public static final int NO_MORE_CANONICAL_DB_VERSION = 10; // 276 // public static final int NO_MORE_CANONICAL_DB_VERSION = 10; // 276
public static final int PROFILES = 11; // 289 // public static final int PROFILES = 11; // 289
public static final int SCREENSHOTS = 12; // 300 // public static final int SCREENSHOTS = 12; // 300
public static final int PERSISTENT_BLOBS = 13; // 317 // public static final int PERSISTENT_BLOBS = 13; // 317
public static final int INTERNALIZE_CONTACTS = 13; // 317 // public static final int INTERNALIZE_CONTACTS = 13; // 317
public static final int SQLCIPHER = 14; // 334 // public static final int SQLCIPHER = 14; // 334
public static final int SQLCIPHER_COMPLETE = 15; // 352 // public static final int SQLCIPHER_COMPLETE = 15; // 352
public static final int REMOVE_JOURNAL = 16; // 353 // public static final int REMOVE_JOURNAL = 16; // 353
public static final int REMOVE_CACHE = 17; // 354 // public static final int REMOVE_CACHE = 17; // 354
public static final int FULL_TEXT_SEARCH = 18; // 358 // public static final int FULL_TEXT_SEARCH = 18; // 358
public static final int BAD_IMPORT_CLEANUP = 19; // 373 // public static final int BAD_IMPORT_CLEANUP = 19; // 373
public static final int IMAGE_CACHE_CLEANUP = 20; // 406 // public static final int IMAGE_CACHE_CLEANUP = 20; // 406
public static final int WORKMANAGER_MIGRATION = 21; // 408 // public static final int WORKMANAGER_MIGRATION = 21; // 408
public static final int COLOR_MIGRATION = 22; // 412 // public static final int COLOR_MIGRATION = 22; // 412
public static final int UNIDENTIFIED_DELIVERY = 23; // 422 // public static final int UNIDENTIFIED_DELIVERY = 23; // 422
public static final int SIGNALING_KEY_DEPRECATION = 24; // 447 // public static final int SIGNALING_KEY_DEPRECATION = 24; // 447
public static final int CONVERSATION_SEARCH = 25; // 455 // public static final int CONVERSATION_SEARCH = 25; // 455
private static final SortedSet<Integer> UPGRADE_VERSIONS = new TreeSet<Integer>() {{ private static final SortedSet<Integer> UPGRADE_VERSIONS = new TreeSet<Integer>() {{
add(NO_MORE_KEY_EXCHANGE_PREFIX_VERSION); // add(NO_MORE_KEY_EXCHANGE_PREFIX_VERSION);
add(TOFU_IDENTITIES_VERSION); // add(TOFU_IDENTITIES_VERSION);
add(CURVE25519_VERSION); // add(CURVE25519_VERSION);
add(ASYMMETRIC_MASTER_SECRET_FIX_VERSION); // add(ASYMMETRIC_MASTER_SECRET_FIX_VERSION);
add(NO_V1_VERSION); // add(NO_V1_VERSION);
add(SIGNED_PREKEY_VERSION); // add(SIGNED_PREKEY_VERSION);
add(NO_DECRYPT_QUEUE_VERSION); // add(NO_DECRYPT_QUEUE_VERSION);
add(PUSH_DECRYPT_SERIAL_ID_VERSION); // add(PUSH_DECRYPT_SERIAL_ID_VERSION);
add(MIGRATE_SESSION_PLAINTEXT); // add(MIGRATE_SESSION_PLAINTEXT);
add(MEDIA_DOWNLOAD_CONTROLS_VERSION); // add(MEDIA_DOWNLOAD_CONTROLS_VERSION);
add(REDPHONE_SUPPORT_VERSION); // add(REDPHONE_SUPPORT_VERSION);
add(NO_MORE_CANONICAL_DB_VERSION); // add(NO_MORE_CANONICAL_DB_VERSION);
add(SCREENSHOTS); // add(SCREENSHOTS);
add(INTERNALIZE_CONTACTS); // add(INTERNALIZE_CONTACTS);
add(PERSISTENT_BLOBS); // add(PERSISTENT_BLOBS);
add(SQLCIPHER); // add(SQLCIPHER);
add(SQLCIPHER_COMPLETE); // add(SQLCIPHER_COMPLETE);
add(REMOVE_CACHE); // add(REMOVE_CACHE);
add(FULL_TEXT_SEARCH); // add(FULL_TEXT_SEARCH);
add(BAD_IMPORT_CLEANUP); // add(BAD_IMPORT_CLEANUP);
add(IMAGE_CACHE_CLEANUP); // add(IMAGE_CACHE_CLEANUP);
add(WORKMANAGER_MIGRATION); // add(WORKMANAGER_MIGRATION);
add(COLOR_MIGRATION); // add(COLOR_MIGRATION);
add(UNIDENTIFIED_DELIVERY); // add(UNIDENTIFIED_DELIVERY);
add(SIGNALING_KEY_DEPRECATION); // add(SIGNALING_KEY_DEPRECATION);
add(CONVERSATION_SEARCH); // add(CONVERSATION_SEARCH);
}}; }};
private MasterSecret masterSecret; // private MasterSecret masterSecret;
@Override @Override
public void onCreate(Bundle bundle) { public void onCreate(Bundle bundle) {
super.onCreate(bundle); super.onCreate(bundle);
this.masterSecret = KeyCachingService.getMasterSecret(this); // this.masterSecret = KeyCachingService.getMasterSecret(this);
if (needsUpgradeTask()) { if (needsUpgradeTask()) {
Log.i("DatabaseUpgradeActivity", "Upgrading..."); Log.i("DatabaseUpgradeActivity", "Upgrading...");
@ -202,160 +190,160 @@ public class DatabaseUpgradeActivity extends BaseActivity {
Context context = DatabaseUpgradeActivity.this.getApplicationContext(); Context context = DatabaseUpgradeActivity.this.getApplicationContext();
Log.i("DatabaseUpgradeActivity", "Running background upgrade.."); Log.i("DatabaseUpgradeActivity", "Running background upgrade..");
DatabaseFactory.getInstance(DatabaseUpgradeActivity.this) // DatabaseFactory.getInstance(DatabaseUpgradeActivity.this)
.onApplicationLevelUpgrade(context, masterSecret, params[0], this); // .onApplicationLevelUpgrade(context, params[0], this);
if (params[0] < CURVE25519_VERSION) { // if (params[0] < CURVE25519_VERSION) {
IdentityKeyUtil.migrateIdentityKeys(context, masterSecret); // 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");
// //
// JobManager jobManager = ApplicationContext.getInstance(getApplicationContext()).getJobManager(); // if (params[0] < NO_V1_VERSION) {
// PersistentStorage storage = new PersistentStorage(getApplicationContext(), "TextSecureJobs", new JavaJobSerializer()); // File v1sessions = new File(context.getFilesDir(), "sessions");
// //
// for (Job job : storage.getAllUnencrypted()) { // if (v1sessions.exists() && v1sessions.isDirectory()) {
// jobManager.add(job); // File[] contents = v1sessions.listFiles();
// Log.i(TAG, "Migrated job with class '" + job.getClass().getSimpleName() + "' to run on new JobManager."); //
// if (contents != null) {
// for (File session : contents) {
// session.delete();
// }
// }
//
// v1sessions.delete();
// } // }
// } // }
//
if (params[0] < COLOR_MIGRATION) { // if (params[0] < SIGNED_PREKEY_VERSION) {
long startTime = System.currentTimeMillis(); //// ApplicationContext.getInstance(getApplicationContext())
DatabaseFactory.getRecipientDatabase(context).updateSystemContactColors((name, color) -> { //// .getJobManager()
if (color != null) { //// .add(new CreateSignedPreKeyJob(context));
try { // }
return MaterialColor.fromSerialized(color); //
} catch (MaterialColor.UnknownColorException e) { // if (params[0] < NO_DECRYPT_QUEUE_VERSION) {
Log.w(TAG, "Encountered an unknown color during legacy color migration.", e); // scheduleMessagesInPushDatabase(context);
return ContactColorsLegacy.generateFor(name); // }
} //
} // if (params[0] < PUSH_DECRYPT_SERIAL_ID_VERSION) {
return ContactColorsLegacy.generateFor(name); // scheduleMessagesInPushDatabase(context);
}); // }
Log.i(TAG, "Color migration took " + (System.currentTimeMillis() - startTime) + " ms"); //
} // if (params[0] < MIGRATE_SESSION_PLAINTEXT) {
//// new TextSecureSessionStore(context, masterSecret).migrateSessions();
if (params[0] < UNIDENTIFIED_DELIVERY) { //// new TextSecurePreKeyStore(context, masterSecret).migrateRecords();
if (TextSecurePreferences.isMultiDevice(context)) { //
Log.i(TAG, "MultiDevice: Disabling UD (will be re-enabled if possible after pending refresh)."); // IdentityKeyUtil.migrateIdentityKeys(context, masterSecret);
TextSecurePreferences.setIsUnidentifiedDeliveryEnabled(context, false); // scheduleMessagesInPushDatabase(context);;
} // }
//
Log.i(TAG, "Scheduling UD attributes refresh."); // if (params[0] < MEDIA_DOWNLOAD_CONTROLS_VERSION) {
ApplicationContext.getInstance(context) // schedulePendingIncomingParts(context);
.getJobManager() // }
.add(new RefreshAttributesJob()); //
} // if (params[0] < REDPHONE_SUPPORT_VERSION) {
// ApplicationContext.getInstance(getApplicationContext())
if (params[0] < SIGNALING_KEY_DEPRECATION) { // .getJobManager()
Log.i(TAG, "Scheduling a RefreshAttributesJob to remove the signaling key remotely."); // .add(new RefreshAttributesJob());
ApplicationContext.getInstance(context) // }
.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; return null;
} }

View File

@ -62,7 +62,6 @@ import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.permissions.Permissions; import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.AttachmentUtil; import org.thoughtcrime.securesms.util.AttachmentUtil;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.SaveAttachmentTask; import org.thoughtcrime.securesms.util.SaveAttachmentTask;
import org.thoughtcrime.securesms.util.StickyHeaderDecoration; import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
@ -86,18 +85,11 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
public static final String ADDRESS_EXTRA = "address"; public static final String ADDRESS_EXTRA = "address";
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
private Toolbar toolbar; private Toolbar toolbar;
private TabLayout tabLayout; private TabLayout tabLayout;
private ViewPager viewPager; private ViewPager viewPager;
private Recipient recipient; private Recipient recipient;
@Override
protected void onPreCreate() {
dynamicLanguage.onCreate(this);
}
@Override @Override
protected void onCreate(Bundle bundle, boolean ready) { protected void onCreate(Bundle bundle, boolean ready) {
setContentView(R.layout.media_overview_activity); setContentView(R.layout.media_overview_activity);
@ -109,12 +101,6 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
this.viewPager.setAdapter(new MediaOverviewPagerAdapter(getSupportFragmentManager())); this.viewPager.setAdapter(new MediaOverviewPagerAdapter(getSupportFragmentManager()));
} }
@Override
public void onResume() {
super.onResume();
dynamicLanguage.onResume(this);
}
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item); super.onOptionsItemSelected(item);
@ -172,7 +158,7 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putString(MediaOverviewGalleryFragment.ADDRESS_EXTRA, recipient.getAddress().serialize()); 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); fragment.setArguments(args);

View File

@ -68,12 +68,12 @@ import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener; import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.util.AttachmentUtil; import org.thoughtcrime.securesms.util.AttachmentUtil;
import org.thoughtcrime.securesms.util.DateUtils; import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.SaveAttachmentTask; import org.thoughtcrime.securesms.util.SaveAttachmentTask;
import org.thoughtcrime.securesms.util.SaveAttachmentTask.Attachment; import org.thoughtcrime.securesms.util.SaveAttachmentTask.Attachment;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import java.io.IOException; import java.io.IOException;
import java.util.Locale;
import java.util.WeakHashMap; import java.util.WeakHashMap;
import network.loki.messenger.R; 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 OUTGOING_EXTRA = "outgoing";
public static final String LEFT_IS_RECENT_EXTRA = "left_is_recent"; public static final String LEFT_IS_RECENT_EXTRA = "left_is_recent";
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
private ViewPager mediaPager; private ViewPager mediaPager;
private View detailsContainer; private View detailsContainer;
private TextView caption; private TextView caption;
@ -120,8 +118,6 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
@SuppressWarnings("ConstantConditions") @SuppressWarnings("ConstantConditions")
@Override @Override
protected void onCreate(Bundle bundle, boolean ready) { protected void onCreate(Bundle bundle, boolean ready) {
dynamicLanguage.onCreate(this);
viewModel = new ViewModelProvider(this).get(MediaPreviewViewModel.class); viewModel = new ViewModelProvider(this).get(MediaPreviewViewModel.class);
setContentView(R.layout.media_preview_activity); setContentView(R.layout.media_preview_activity);
@ -172,7 +168,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
CharSequence relativeTimeSpan; CharSequence relativeTimeSpan;
if (mediaItem.date > 0) { if (mediaItem.date > 0) {
relativeTimeSpan = DateUtils.getExtendedRelativeTimeSpanString(this,dynamicLanguage.getCurrentLocale(), mediaItem.date); relativeTimeSpan = DateUtils.getExtendedRelativeTimeSpanString(this, Locale.getDefault(), mediaItem.date);
} else { } else {
relativeTimeSpan = getString(R.string.MediaPreviewActivity_draft); relativeTimeSpan = getString(R.string.MediaPreviewActivity_draft);
} }
@ -189,7 +185,6 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
dynamicLanguage.onResume(this);
initializeMedia(); initializeMedia();
} }

View File

@ -55,7 +55,6 @@ import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener; import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.sms.MessageSender; import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.DateUtils; import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.ExpirationUtil; import org.thoughtcrime.securesms.util.ExpirationUtil;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.session.libsignal.libsignal.util.guava.Optional; import org.session.libsignal.libsignal.util.guava.Optional;
@ -102,15 +101,8 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
private ListView recipientsList; private ListView recipientsList;
private LayoutInflater inflater; private LayoutInflater inflater;
private DynamicLanguage dynamicLanguage = new DynamicLanguage();
private boolean running; private boolean running;
@Override
protected void onPreCreate() {
dynamicLanguage.onCreate(this);
}
@Override @Override
public void onCreate(Bundle bundle, boolean ready) { public void onCreate(Bundle bundle, boolean ready) {
super.onCreate(bundle, ready); super.onCreate(bundle, ready);
@ -125,7 +117,6 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
@Override @Override
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
dynamicLanguage.onResume(this);
assert getSupportActionBar() != null; assert getSupportActionBar() != null;
getSupportActionBar().setTitle("Message Details"); getSupportActionBar().setTitle("Message Details");
@ -211,7 +202,7 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
sentDate.setText("-"); sentDate.setText("-");
receivedContainer.setVisibility(View.GONE); receivedContainer.setVisibility(View.GONE);
} else { } else {
Locale dateLocale = dynamicLanguage.getCurrentLocale(); Locale dateLocale = Locale.getDefault();
SimpleDateFormat dateFormatter = DateUtils.getDetailedDateFormatter(this, dateLocale); SimpleDateFormat dateFormatter = DateUtils.getDetailedDateFormatter(this, dateLocale);
sentDate.setText(dateFormatter.format(new Date(messageRecord.getDateSent()))); sentDate.setText(dateFormatter.format(new Date(messageRecord.getDateSent())));
sentDate.setOnLongClickListener(v -> { sentDate.setOnLongClickListener(v -> {
@ -271,7 +262,7 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
toFrom.setVisibility(View.GONE); toFrom.setVisibility(View.GONE);
separator.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)); recipientsList.setAdapter(new MessageDetailsRecipientAdapter(this, glideRequests, messageRecord, recipients, isPushGroup));
} }

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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;
}
};
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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<String, Void, MasterSecret> {
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();
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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<String, Void, Void> {
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();
}
}

View File

@ -18,66 +18,43 @@ package org.thoughtcrime.securesms;
import android.animation.Animator; import android.animation.Animator;
import android.app.KeyguardManager; import android.app.KeyguardManager;
import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.PorterDuff; import android.graphics.PorterDuff;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.text.Editable; import android.os.IBinder;
import android.text.InputType;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.Spanned; import android.text.Spanned;
import android.text.style.RelativeSizeSpan; import android.text.style.RelativeSizeSpan;
import android.text.style.TypefaceSpan; 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;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.view.animation.Animation; import android.view.animation.Animation;
import android.view.animation.BounceInterpolator; import android.view.animation.BounceInterpolator;
import android.view.animation.TranslateAnimation; import android.view.animation.TranslateAnimation;
import android.view.inputmethod.EditorInfo;
import android.widget.Button; import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView;
import androidx.core.hardware.fingerprint.FingerprintManagerCompat; import androidx.core.hardware.fingerprint.FingerprintManagerCompat;
import androidx.core.os.CancellationSignal; import androidx.core.os.CancellationSignal;
import org.thoughtcrime.securesms.animation.AnimationCompleteListener; import org.thoughtcrime.securesms.animation.AnimationCompleteListener;
import org.thoughtcrime.securesms.components.AnimatingToggle; 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.logging.Log;
import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import network.loki.messenger.R; import network.loki.messenger.R;
/** //TODO Rename to screen lock activity and refactor to Kotlin.
* Activity that prompts for a user's passphrase. public class PassphrasePromptActivity extends BaseActionBarActivity {
*
* @author Moxie Marlinspike
*/
public class PassphrasePromptActivity extends PassphraseActivity {
private static final String TAG = PassphrasePromptActivity.class.getSimpleName(); private static final String TAG = PassphrasePromptActivity.class.getSimpleName();
private DynamicLanguage dynamicLanguage = new DynamicLanguage();
private View passphraseAuthContainer;
private ImageView fingerprintPrompt; private ImageView fingerprintPrompt;
private Button lockScreenButton; private Button lockScreenButton;
private EditText passphraseText;
private ImageButton showButton;
private ImageButton hideButton;
private AnimatingToggle visibilityToggle; private AnimatingToggle visibilityToggle;
private FingerprintManagerCompat fingerprintManager; private FingerprintManagerCompat fingerprintManager;
@ -87,20 +64,34 @@ public class PassphrasePromptActivity extends PassphraseActivity {
private boolean authenticated; private boolean authenticated;
private boolean failure; private boolean failure;
private KeyCachingService keyCachingService;
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "onCreate()"); Log.i(TAG, "onCreate()");
dynamicLanguage.onCreate(this);
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.prompt_passphrase_activity); setContentView(R.layout.prompt_passphrase_activity);
initializeResources(); 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 @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
dynamicLanguage.onResume(this);
setLockTypeVisibility(); setLockTypeVisibility();
@ -126,26 +117,6 @@ public class PassphrasePromptActivity extends PassphraseActivity {
setIntent(intent); 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 @Override
public void onActivityResult(int requestCode, int resultcode, Intent data) { public void onActivityResult(int requestCode, int resultcode, Intent data) {
super.onActivityResult(requestCode, resultcode, 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() { private void handleAuthenticated() {
try { authenticated = true;
authenticated = true; //TODO Replace with a proper call.
keyCachingService.setMasterSecret(new Object());
MasterSecret masterSecret = MasterSecretUtil.getMasterSecret(this, MasterSecretUtil.UNENCRYPTED_PASSPHRASE);
setMasterSecret(masterSecret);
} catch (InvalidPassphraseException e) {
throw new AssertionError(e);
}
}
private void setPassphraseVisibility(boolean visibility) { // Finish and proceed with the next intent.
int cursorPosition = passphraseText.getSelectionStart(); Intent nextIntent = getIntent().getParcelableExtra("next_intent");
if (visibility) { if (nextIntent != null) {
passphraseText.setInputType(InputType.TYPE_CLASS_TEXT | startActivity(nextIntent);
InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD); // try {
} else { // startActivity(nextIntent);
passphraseText.setInputType(InputType.TYPE_CLASS_TEXT | // } catch (java.lang.SecurityException e) {
InputType.TYPE_TEXT_VARIATION_PASSWORD); // Log.w(TAG, "Access permission not passed from PassphraseActivity, retry sharing.");
// }
} }
passphraseText.setSelection(cursorPosition); finish();
} }
private void initializeResources() { 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); 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); fingerprintPrompt = findViewById(R.id.fingerprint_auth_container);
lockScreenButton = findViewById(R.id.lock_screen_auth_container); lockScreenButton = findViewById(R.id.lock_screen_auth_container);
fingerprintManager = FingerprintManagerCompat.from(this); 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 RelativeSizeSpan(0.9f), 0, hint.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
hint.setSpan(new TypefaceSpan("sans-serif"), 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.setImageResource(R.drawable.ic_fingerprint_white_48dp);
fingerprintPrompt.getBackground().setColorFilter(getResources().getColor(R.color.signal_primary), PorterDuff.Mode.SRC_IN); 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() { private void setLockTypeVisibility() {
if (TextSecurePreferences.isScreenLockEnabled(this)) { if (TextSecurePreferences.isScreenLockEnabled(this)) {
passphraseAuthContainer.setVisibility(View.GONE);
if (fingerprintManager.isHardwareDetected() && fingerprintManager.hasEnrolledFingerprints()) { if (fingerprintManager.isHardwareDetected() && fingerprintManager.hasEnrolledFingerprints()) {
fingerprintPrompt.setVisibility(View.VISIBLE); fingerprintPrompt.setVisibility(View.VISIBLE);
lockScreenButton.setVisibility(View.GONE); lockScreenButton.setVisibility(View.GONE);
@ -245,7 +176,6 @@ public class PassphrasePromptActivity extends PassphraseActivity {
lockScreenButton.setVisibility(View.VISIBLE); lockScreenButton.setVisibility(View.VISIBLE);
} }
} else { } else {
passphraseAuthContainer.setVisibility(View.VISIBLE);
fingerprintPrompt.setVisibility(View.GONE); fingerprintPrompt.setVisibility(View.GONE);
lockScreenButton.setVisibility(View.GONE); lockScreenButton.setVisibility(View.GONE);
} }
@ -266,13 +196,10 @@ public class PassphrasePromptActivity extends PassphraseActivity {
Log.i(TAG, "Listening for fingerprints..."); Log.i(TAG, "Listening for fingerprints...");
fingerprintCancellationSignal = new CancellationSignal(); fingerprintCancellationSignal = new CancellationSignal();
fingerprintManager.authenticate(null, 0, fingerprintCancellationSignal, fingerprintListener, null); fingerprintManager.authenticate(null, 0, fingerprintCancellationSignal, fingerprintListener, null);
} else if (Build.VERSION.SDK_INT >= 21){ } else {
Log.i(TAG, "firing intent..."); Log.i(TAG, "firing intent...");
Intent intent = keyguardManager.createConfirmDeviceCredentialIntent("Unlock Session", ""); Intent intent = keyguardManager.createConfirmDeviceCredentialIntent("Unlock Session", "");
startActivityForResult(intent, 1); 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 { private class FingerprintListener extends FingerprintManagerCompat.AuthenticationCallback {
@Override @Override
public void onAuthenticationError(int errMsgId, CharSequence errString) { public void onAuthenticationError(int errMsgId, CharSequence errString) {
@ -356,7 +235,6 @@ public class PassphrasePromptActivity extends PassphraseActivity {
@Override @Override
public void onAuthenticationFailed() { public void onAuthenticationFailed() {
Log.w(TAG, "onAuthenticatoinFailed()"); Log.w(TAG, "onAuthenticatoinFailed()");
FingerprintManagerCompat.AuthenticationCallback callback = this;
fingerprintPrompt.setImageResource(R.drawable.ic_close_white_48dp); fingerprintPrompt.setImageResource(R.drawable.ic_close_white_48dp);
fingerprintPrompt.getBackground().setColorFilter(getResources().getColor(R.color.red_500), PorterDuff.Mode.SRC_IN); 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); fingerprintPrompt.startAnimation(shake);
} }
} }
} }

View File

@ -10,8 +10,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; 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.logging.Log;
import org.thoughtcrime.securesms.loki.activities.HomeActivity; import org.thoughtcrime.securesms.loki.activities.HomeActivity;
import org.thoughtcrime.securesms.loki.activities.LandingActivity; 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"; public static final String LOCALE_EXTRA = "locale_extra";
private static final int STATE_NORMAL = 0; 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_PROMPT_PASSPHRASE = 2;
private static final int STATE_UPGRADE_DATABASE = 3; private static final int STATE_UPGRADE_DATABASE = 3;
private static final int STATE_PROMPT_PUSH_REGISTRATION = 4; 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); Log.i(TAG, "routeApplicationState(), state: " + state);
switch (state) { switch (state) {
case STATE_CREATE_PASSPHRASE: return getCreatePassphraseIntent();
case STATE_PROMPT_PASSPHRASE: return getPromptPassphraseIntent(); case STATE_PROMPT_PASSPHRASE: return getPromptPassphraseIntent();
case STATE_UPGRADE_DATABASE: return getUpgradeDatabaseIntent(); case STATE_UPGRADE_DATABASE: return getUpgradeDatabaseIntent();
case STATE_WELCOME_SCREEN: return getWelcomeIntent(); case STATE_WELCOME_SCREEN: return getWelcomeIntent();
@ -129,9 +125,7 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA
} }
private int getApplicationState(boolean locked) { private int getApplicationState(boolean locked) {
if (!MasterSecretUtil.isPassphraseInitialized(this)) { if (locked) {
return STATE_CREATE_PASSPHRASE;
} else if (locked) {
return STATE_PROMPT_PASSPHRASE; return STATE_PROMPT_PASSPHRASE;
} else if (DatabaseUpgradeActivity.isUpdate(this)) { } else if (DatabaseUpgradeActivity.isUpdate(this)) {
return STATE_UPGRADE_DATABASE; 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() { private Intent getPromptPassphraseIntent() {
return getRoutedIntent(PassphrasePromptActivity.class, getIntent()); return getRoutedIntent(PassphrasePromptActivity.class, getIntent());
} }

View File

@ -48,7 +48,6 @@ import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.providers.BlobProvider; import org.thoughtcrime.securesms.providers.BlobProvider;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.stickers.StickerLocator; import org.thoughtcrime.securesms.stickers.StickerLocator;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.ViewUtil;

View File

@ -48,8 +48,6 @@ import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.view.ActionMode; import androidx.appcompat.view.ActionMode;
import androidx.core.app.ActivityCompat;
import androidx.core.app.ActivityOptionsCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.loader.app.LoaderManager; import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader; import androidx.loader.content.Loader;
@ -66,8 +64,6 @@ import org.thoughtcrime.securesms.ShareActivity;
import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.components.ConversationTypingView; import org.thoughtcrime.securesms.components.ConversationTypingView;
import org.thoughtcrime.securesms.components.recyclerview.SmoothScrollingLinearLayoutManager; 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.HeaderViewHolder;
import org.thoughtcrime.securesms.conversation.ConversationAdapter.ItemClickListener; import org.thoughtcrime.securesms.conversation.ConversationAdapter.ItemClickListener;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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);
}
}
}

View File

@ -40,15 +40,14 @@ import java.util.List;
* *
* @author Moxie Marlinspike * @author Moxie Marlinspike
*/ */
//TODO AC: Delete
public class IdentityKeyUtil { public class IdentityKeyUtil {
private static final String MASTER_SECRET_UTIL_PREFERENCES_NAME = "SecureSMS-Preferences";
@SuppressWarnings("unused") @SuppressWarnings("unused")
private static final String TAG = IdentityKeyUtil.class.getSimpleName(); 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_PUBLIC_KEY_PREF = "pref_identity_public_v3";
public static final String IDENTITY_PRIVATE_KEY_PREF = "pref_identity_private_v3"; public static final String IDENTITY_PRIVATE_KEY_PREF = "pref_identity_private_v3";
public static final String ED25519_PUBLIC_KEY = "pref_ed25519_public_key"; 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 final String LOKI_SEED = "loki_seed";
public static boolean hasIdentityKey(Context context) { 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 return
preferences.contains(IDENTITY_PUBLIC_KEY_PREF) && 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<BackupProtos.SharedPreference> getBackupRecords(@NonNull Context context) { public static List<BackupProtos.SharedPreference> 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<BackupProtos.SharedPreference> prefList = new LinkedList<>(); LinkedList<BackupProtos.SharedPreference> prefList = new LinkedList<>();
prefList.add(BackupProtos.SharedPreference.newBuilder() prefList.add(BackupProtos.SharedPreference.newBuilder()
.setFile(MasterSecretUtil.PREFERENCES_NAME) .setFile(prefName)
.setKey(IDENTITY_PUBLIC_KEY_PREF) .setKey(IDENTITY_PUBLIC_KEY_PREF)
.setValue(preferences.getString(IDENTITY_PUBLIC_KEY_PREF, null)) .setValue(preferences.getString(IDENTITY_PUBLIC_KEY_PREF, null))
.build()); .build());
prefList.add(BackupProtos.SharedPreference.newBuilder() prefList.add(BackupProtos.SharedPreference.newBuilder()
.setFile(MasterSecretUtil.PREFERENCES_NAME) .setFile(prefName)
.setKey(IDENTITY_PRIVATE_KEY_PREF) .setKey(IDENTITY_PRIVATE_KEY_PREF)
.setValue(preferences.getString(IDENTITY_PRIVATE_KEY_PREF, null)) .setValue(preferences.getString(IDENTITY_PRIVATE_KEY_PREF, null))
.build()); .build());
if (preferences.contains(ED25519_PUBLIC_KEY)) { if (preferences.contains(ED25519_PUBLIC_KEY)) {
prefList.add(BackupProtos.SharedPreference.newBuilder() prefList.add(BackupProtos.SharedPreference.newBuilder()
.setFile(MasterSecretUtil.PREFERENCES_NAME) .setFile(prefName)
.setKey(ED25519_PUBLIC_KEY) .setKey(ED25519_PUBLIC_KEY)
.setValue(preferences.getString(ED25519_PUBLIC_KEY, null)) .setValue(preferences.getString(ED25519_PUBLIC_KEY, null))
.build()); .build());
} }
if (preferences.contains(ED25519_SECRET_KEY)) { if (preferences.contains(ED25519_SECRET_KEY)) {
prefList.add(BackupProtos.SharedPreference.newBuilder() prefList.add(BackupProtos.SharedPreference.newBuilder()
.setFile(MasterSecretUtil.PREFERENCES_NAME) .setFile(prefName)
.setKey(ED25519_SECRET_KEY) .setKey(ED25519_SECRET_KEY)
.setValue(preferences.getString(ED25519_SECRET_KEY, null)) .setValue(preferences.getString(ED25519_SECRET_KEY, null))
.build()); .build());
} }
prefList.add(BackupProtos.SharedPreference.newBuilder() prefList.add(BackupProtos.SharedPreference.newBuilder()
.setFile(MasterSecretUtil.PREFERENCES_NAME) .setFile(prefName)
.setKey(LOKI_SEED) .setKey(LOKI_SEED)
.setValue(preferences.getString(LOKI_SEED, null)) .setValue(preferences.getString(LOKI_SEED, null))
.build()); .build());
@ -151,34 +125,13 @@ public class IdentityKeyUtil {
return prefList; 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) { 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); return preferences.getString(key, null);
} }
public static void save(Context context, String key, String value) { 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(); Editor preferencesEditor = preferences.edit();
preferencesEditor.putString(key, value); preferencesEditor.putString(key, value);
@ -186,6 +139,6 @@ public class IdentityKeyUtil {
} }
public static void delete(Context context, String key) { 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();
} }
} }

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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;
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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<MasterSecret> CREATOR = new Parcelable.Creator<MasterSecret>() {
@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;
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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;
}
}

View File

@ -17,18 +17,15 @@
package org.thoughtcrime.securesms.database; package org.thoughtcrime.securesms.database;
import android.content.Context; import android.content.Context;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import net.sqlcipher.database.SQLiteDatabase; import net.sqlcipher.database.SQLiteDatabase;
import org.thoughtcrime.securesms.DatabaseUpgradeActivity;
import org.thoughtcrime.securesms.crypto.AttachmentSecret; import org.thoughtcrime.securesms.crypto.AttachmentSecret;
import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider; import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider;
import org.thoughtcrime.securesms.crypto.DatabaseSecret; import org.thoughtcrime.securesms.crypto.DatabaseSecret;
import org.thoughtcrime.securesms.crypto.DatabaseSecretProvider; 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.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase; import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase;
import org.thoughtcrime.securesms.loki.database.LokiBackupFilesDatabase; 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.LokiThreadDatabase;
import org.thoughtcrime.securesms.loki.database.LokiUserDatabase; import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
import org.thoughtcrime.securesms.loki.database.SharedSenderKeysDatabase; import org.thoughtcrime.securesms.loki.database.SharedSenderKeysDatabase;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
public class DatabaseFactory { public class DatabaseFactory {
@ -227,28 +223,4 @@ public class DatabaseFactory {
this.sskDatabase = new SharedSenderKeysDatabase(context, databaseHelper); 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);
}
}
} }

View File

@ -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<Long, String> 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<Long, String> 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<Long, String> 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, Pair<Integer, Integer>, ContentValues> transformer)
{
Set<String> 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<cursor.getColumnCount();i++) {
String columnName = cursor.getColumnName(i);
if (destinationColumns.contains(columnName)) {
switch (cursor.getType(i)) {
case Cursor.FIELD_TYPE_STRING: row.put(columnName, cursor.getString(i)); break;
case Cursor.FIELD_TYPE_FLOAT: row.put(columnName, cursor.getFloat(i)); break;
case Cursor.FIELD_TYPE_INTEGER: row.put(columnName, cursor.getLong(i)); break;
case Cursor.FIELD_TYPE_BLOB: row.put(columnName, cursor.getBlob(i)); break;
}
}
}
if (transformer != null) {
row = transformer.apply(row, new Pair<>(progress++, count));
}
modernDb.insert(tableName, null, row);
}
}
}
private static Pair<Long, String> 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<String> getTableColumns(String tableName, net.sqlcipher.database.SQLiteDatabase database) {
Set<String> 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);
}
}

View File

@ -1,12 +1,8 @@
package org.thoughtcrime.securesms.database.helpers; package org.thoughtcrime.securesms.database.helpers;
import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.net.Uri;
import android.os.SystemClock;
import android.text.TextUtils;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -14,10 +10,7 @@ import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteDatabaseHook; import net.sqlcipher.database.SQLiteDatabaseHook;
import net.sqlcipher.database.SQLiteOpenHelper; 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.DatabaseSecret;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.AttachmentDatabase; import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.DraftDatabase; import org.thoughtcrime.securesms.database.DraftDatabase;
import org.thoughtcrime.securesms.database.GroupDatabase; 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.LokiThreadDatabase;
import org.thoughtcrime.securesms.loki.database.LokiUserDatabase; import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
import org.thoughtcrime.securesms.loki.database.SharedSenderKeysDatabase; 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 { public class SQLCipherOpenHelper extends SQLiteOpenHelper {
@SuppressWarnings("unused") @SuppressWarnings("unused")
private static final String TAG = SQLCipherOpenHelper.class.getSimpleName(); private static final String TAG = SQLCipherOpenHelper.class.getSimpleName();
private static final int RECIPIENT_CALL_RINGTONE_VERSION = 2; // First public release (1.0.0) DB version was 27.
private static final int MIGRATE_PREKEYS_VERSION = 3; // So we have to keep the migrations onwards.
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;
private static final int lokiV7 = 28; private static final int lokiV7 = 28;
private static final int lokiV8 = 29; private static final int lokiV8 = 29;
private static final int lokiV9 = 30; private static final int lokiV9 = 30;
@ -177,20 +140,6 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
executeStatements(db, GroupDatabase.CREATE_INDEXS); executeStatements(db, GroupDatabase.CREATE_INDEXS);
executeStatements(db, GroupReceiptDatabase.CREATE_INDEXES); executeStatements(db, GroupReceiptDatabase.CREATE_INDEXES);
executeStatements(db, StickerDatabase.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 @Override
@ -210,379 +159,6 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
try { 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) { if (oldVersion < lokiV7) {
db.execSQL(LokiMessageDatabase.getCreateErrorMessageTableCommand()); db.execSQL(LokiMessageDatabase.getCreateErrorMessageTableCommand());
} }
@ -652,10 +228,6 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
} finally { } finally {
db.endTransaction(); db.endTransaction();
} }
if (oldVersion < MIGRATE_PREKEYS_VERSION) {
// PreKeyMigrationHelper.cleanUpPreKeys(context);
}
} }
public SQLiteDatabase getReadableDatabase() { public SQLiteDatabase getReadableDatabase() {

View File

@ -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);
}
}

View File

@ -21,7 +21,6 @@ import com.google.android.material.tabs.TabLayout;
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity; import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.providers.BlobProvider; import org.thoughtcrime.securesms.providers.BlobProvider;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.ViewUtil; 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_WIDTH = "extra_width";
public static final String EXTRA_HEIGHT = "extra_height"; public static final String EXTRA_HEIGHT = "extra_height";
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
private GiphyGifFragment gifFragment; private GiphyGifFragment gifFragment;
private GiphyStickerFragment stickerFragment; private GiphyStickerFragment stickerFragment;
private boolean forMms; private boolean forMms;
private GiphyAdapter.GiphyViewHolder finishingImage; private GiphyAdapter.GiphyViewHolder finishingImage;
@Override
public void onPreCreate() {
dynamicLanguage.onCreate(this);
}
@Override @Override
public void onCreate(Bundle bundle, boolean ready) { public void onCreate(Bundle bundle, boolean ready) {
setContentView(R.layout.giphy_activity); setContentView(R.layout.giphy_activity);

View File

@ -19,6 +19,7 @@ import java.io.IOException;
import javax.inject.Inject; import javax.inject.Inject;
//TODO AC: Delete
public class RefreshAttributesJob extends BaseJob implements InjectableType { public class RefreshAttributesJob extends BaseJob implements InjectableType {
public static final String KEY = "RefreshAttributesJob"; public static final String KEY = "RefreshAttributesJob";

View File

@ -5,17 +5,13 @@ import android.app.KeyguardManager;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.widget.Toast;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.preference.CheckBoxPreference;
import androidx.preference.Preference; import androidx.preference.Preference;
import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.PassphraseChangeActivity;
import org.thoughtcrime.securesms.components.SwitchPreferenceCompat; import org.thoughtcrime.securesms.components.SwitchPreferenceCompat;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
@ -27,8 +23,6 @@ import network.loki.messenger.R;
public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment implements InjectableType { public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment implements InjectableType {
private CheckBoxPreference disablePassphrase;
@Override @Override
public void onAttach(Activity activity) { public void onAttach(Activity activity) {
super.onAttach(activity); super.onAttach(activity);
@ -39,17 +33,12 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
public void onCreate(Bundle paramBundle) { public void onCreate(Bundle paramBundle) {
super.onCreate(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).setOnPreferenceChangeListener(new ScreenLockListener());
this.findPreference(TextSecurePreferences.SCREEN_LOCK_TIMEOUT).setOnPreferenceClickListener(new ScreenLockTimeoutListener()); 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.READ_RECEIPTS_PREF).setOnPreferenceChangeListener(new ReadReceiptToggleListener());
this.findPreference(TextSecurePreferences.TYPING_INDICATORS).setOnPreferenceChangeListener(new TypingIndicatorsToggleListener()); this.findPreference(TextSecurePreferences.TYPING_INDICATORS).setOnPreferenceChangeListener(new TypingIndicatorsToggleListener());
this.findPreference(TextSecurePreferences.LINK_PREVIEWS).setOnPreferenceChangeListener(new LinkPreviewToggleListener()); this.findPreference(TextSecurePreferences.LINK_PREVIEWS).setOnPreferenceChangeListener(new LinkPreviewToggleListener());
disablePassphrase.setOnPreferenceChangeListener(new DisablePassphraseClickListener());
initializeVisibility(); initializeVisibility();
} }
@ -62,16 +51,9 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
if (!TextSecurePreferences.isPasswordDisabled(getContext())) initializePassphraseTimeoutSummary(); if (TextSecurePreferences.isPasswordDisabled(getContext())) {
else initializeScreenLockTimeoutSummary(); 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));
} }
private void initializeScreenLockTimeoutSummary() { private void initializeScreenLockTimeoutSummary() {
@ -87,11 +69,6 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
private void initializeVisibility() { private void initializeVisibility() {
if (TextSecurePreferences.isPasswordDisabled(getContext())) { 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); KeyguardManager keyguardManager = (KeyguardManager)getContext().getSystemService(Context.KEYGUARD_SERVICE);
if (!keyguardManager.isKeyguardSecure()) { if (!keyguardManager.isKeyguardSecure()) {
((SwitchPreferenceCompat)findPreference(TextSecurePreferences.SCREEN_LOCK)).setChecked(false); ((SwitchPreferenceCompat)findPreference(TextSecurePreferences.SCREEN_LOCK)).setChecked(false);
@ -178,90 +155,4 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
return true; 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;
}
}
} }

View File

@ -34,13 +34,9 @@ import androidx.core.app.NotificationCompat;
import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.DatabaseUpgradeActivity; import org.thoughtcrime.securesms.DatabaseUpgradeActivity;
import org.thoughtcrime.securesms.DummyActivity; 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.logging.Log;
import org.thoughtcrime.securesms.loki.activities.HomeActivity; import org.thoughtcrime.securesms.loki.activities.HomeActivity;
import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.ServiceUtil; import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences; 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"; 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 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 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 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() {} public KeyCachingService() {}
@ -81,18 +76,6 @@ public class KeyCachingService extends Service {
return getMasterSecret(context) == null; 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) { public static void onAppForegrounded(@NonNull Context context) {
ServiceUtil.getAlarmManager(context).cancel(buildExpirationPendingIntent(context)); ServiceUtil.getAlarmManager(context).cancel(buildExpirationPendingIntent(context));
} }
@ -101,8 +84,22 @@ public class KeyCachingService extends Service {
startTimeoutIfAppropriate(context); 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") @SuppressLint("StaticFieldLeak")
public void setMasterSecret(final MasterSecret masterSecret) { public void setMasterSecret(final Object masterSecret) {
synchronized (KeyCachingService.class) { synchronized (KeyCachingService.class) {
KeyCachingService.masterSecret = masterSecret; KeyCachingService.masterSecret = masterSecret;
@ -132,7 +129,6 @@ public class KeyCachingService extends Service {
case CLEAR_KEY_ACTION: handleClearKey(); break; case CLEAR_KEY_ACTION: handleClearKey(); break;
case PASSPHRASE_EXPIRED_EVENT: handleClearKey(); break; case PASSPHRASE_EXPIRED_EVENT: handleClearKey(); break;
case DISABLE_ACTION: handleDisableService(); break; case DISABLE_ACTION: handleDisableService(); break;
case LOCALE_CHANGE_EVENT: handleLocaleChanged(); break;
case LOCK_TOGGLED_EVENT: handleLockToggled(); break; case LOCK_TOGGLED_EVENT: handleLockToggled(); break;
} }
} }
@ -146,12 +142,12 @@ public class KeyCachingService extends Service {
super.onCreate(); super.onCreate();
if (TextSecurePreferences.isPasswordDisabled(this) && !TextSecurePreferences.isScreenLockEnabled(this)) { if (TextSecurePreferences.isPasswordDisabled(this) && !TextSecurePreferences.isScreenLockEnabled(this)) {
try { // try {
MasterSecret masterSecret = MasterSecretUtil.getMasterSecret(this, MasterSecretUtil.UNENCRYPTED_PASSPHRASE); // MasterSecret masterSecret = MasterSecretUtil.getMasterSecret(this, MasterSecretUtil.UNENCRYPTED_PASSPHRASE);
setMasterSecret(masterSecret); setMasterSecret(new Object());
} catch (InvalidPassphraseException e) { // } catch (InvalidPassphraseException e) {
Log.w("KeyCachingService", e); // Log.w("KeyCachingService", e);
} // }
} }
} }
@ -196,12 +192,12 @@ public class KeyCachingService extends Service {
private void handleLockToggled() { private void handleLockToggled() {
stopForeground(true); stopForeground(true);
try { // try {
MasterSecret masterSecret = MasterSecretUtil.getMasterSecret(this, MasterSecretUtil.UNENCRYPTED_PASSPHRASE); // MasterSecret masterSecret = MasterSecretUtil.getMasterSecret(this, MasterSecretUtil.UNENCRYPTED_PASSPHRASE);
setMasterSecret(masterSecret); setMasterSecret(masterSecret);
} catch (InvalidPassphraseException e) { // } catch (InvalidPassphraseException e) {
Log.w(TAG, e); // Log.w(TAG, e);
} // }
} }
private void handleDisableService() { 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) { private static void startTimeoutIfAppropriate(@NonNull Context context) {
boolean appVisible = ApplicationContext.getInstance(context).isAppVisible(); boolean appVisible = ApplicationContext.getInstance(context).isAppVisible();
boolean secretSet = KeyCachingService.masterSecret != null; boolean secretSet = KeyCachingService.masterSecret != null;

View File

@ -136,7 +136,7 @@ public class TextSecurePreferences {
private static final String DATABASE_UNENCRYPTED_SECRET = "pref_database_unencrypted_secret"; 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_ENCRYPTED_SECRET = "pref_attachment_encrypted_secret";
private static final String ATTACHMENT_UNENCRYPTED_SECRET = "pref_attachment_unencrypted_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 NEXT_PRE_KEY_ID = "pref_next_pre_key_id";
private static final String ACTIVE_SIGNED_PRE_KEY_ID = "pref_active_signed_pre_key_id"; private static final String ACTIVE_SIGNED_PRE_KEY_ID = "pref_active_signed_pre_key_id";

View File

@ -1,83 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fillViewport="true">
<FrameLayout android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center">
<LinearLayout android:paddingEnd="16dip"
android:paddingStart="16dip"
android:paddingTop="10dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="visible"
android:orientation="vertical">
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText android:id="@+id/old_passphrase"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"
android:layout_marginBottom="10dip"
android:hint="@string/change_passphrase_activity__old_passphrase"
android:singleLine="true"/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText android:id="@+id/new_passphrase"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"
android:hint="@string/change_passphrase_activity__new_passphrase"
android:singleLine="true"/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText android:id="@+id/repeat_passphrase"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"
android:hint="@string/change_passphrase_activity__repeat_new_passphrase"
android:singleLine="true" />
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp">
<Button android:id="@+id/cancel_button"
android:text="@android:string/cancel"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="7dp"
android:textAppearance="?android:attr/textAppearanceMedium"/>
<Button android:id="@+id/ok_button"
android:text="@android:string/ok"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"/>
</LinearLayout>
</LinearLayout>
</FrameLayout>
</ScrollView>

View File

@ -1,31 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/prompt_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:id="@+id/passphrase_edit"
style="?android:attr/progressBarStyleLargeInverse"
android:layout_width="75dp"
android:layout_height="75dp"
android:layout_centerInParent="true"
android:indeterminate="true"
android:padding="10dp"
android:visibility="gone"
tools:visibility="visible"/>
<ImageView
android:id="@+id/watermark"
android:layout_width="wrap_content"
android:layout_height="128dp"
android:layout_above="@id/passphrase_edit"
android:layout_centerInParent="true"
android:layout_marginBottom="20dp"
android:contentDescription="@string/PassphrasePromptActivity_watermark_content_description"
android:src="@drawable/ic_launcher_foreground" />
</RelativeLayout>

View File

@ -73,73 +73,7 @@
android:id="@+id/lock_screen_auth_container" android:id="@+id/lock_screen_auth_container"
android:layout_width="196dp" android:layout_width="196dp"
android:layout_height="@dimen/medium_button_height" android:layout_height="@dimen/medium_button_height"
android:text="Tap to Unlock" /> android:text="Tap to Unlock"/>
<RelativeLayout android:id="@+id/password_auth_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="60dp"
tools:visibility="visible">
<EditText android:id="@+id/passphrase_edit"
android:layout_width="match_parent"
android:layout_height="45sp"
android:inputType="textPassword"
android:layout_marginStart="50dp"
android:layout_marginEnd="50dp"
android:singleLine="true"
android:paddingStart="10dp"
android:paddingEnd="40dp"
tools:text="password"/>
<org.thoughtcrime.securesms.components.AnimatingToggle
android:id="@+id/button_toggle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignRight="@+id/passphrase_edit"
android:layout_centerVertical="true"
android:layout_gravity="center"
android:gravity="center">
<ImageButton android:id="@+id/passphrase_visibility"
android:src="?ic_visibility_on"
android:background="@drawable/touch_highlight_background"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:paddingTop="3dp"
android:paddingBottom="3dp"
android:layout_centerVertical="true" />
<ImageButton android:id="@+id/passphrase_visibility_off"
android:src="?ic_visibility_off"
android:background="@drawable/touch_highlight_background"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:paddingTop="3dp"
android:paddingBottom="3dp"
android:layout_centerVertical="true" />
</org.thoughtcrime.securesms.components.AnimatingToggle>
<ImageButton android:id="@+id/ok_button"
android:src="?ic_arrow_forward"
android:contentDescription="@string/PassphrasePromptActivity_ok_button_content_description"
android:background="@null"
android:text="@string/prompt_passphrase_activity__unlock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:textAppearance="?android:attr/textAppearanceMedium"
android:paddingStart="5dp"
android:paddingTop="5dp"
android:paddingEnd="10dp"
android:paddingBottom="5dp"/>
</RelativeLayout>
</LinearLayout> </LinearLayout>
</RelativeLayout> </RelativeLayout>

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:title="@string/preferences__submit_debug_log"
android:id="@+id/menu_submit_debug_logs"
android:icon="@android:drawable/ic_menu_upload" />
</menu>

View File

@ -15,29 +15,29 @@
android:key="pref_android_screen_lock_timeout" android:key="pref_android_screen_lock_timeout"
android:dependency="pref_android_screen_lock" /> android:dependency="pref_android_screen_lock" />
<org.thoughtcrime.securesms.components.SwitchPreferenceCompat <!-- <org.thoughtcrime.securesms.components.SwitchPreferenceCompat-->
android:key="pref_enable_passphrase_temporary" <!-- android:key="pref_enable_passphrase_temporary"-->
android:defaultValue="true" <!-- android:defaultValue="true"-->
android:title="@string/preferences__enable_passphrase" <!-- android:title="@string/preferences__enable_passphrase"-->
android:summary="@string/preferences__lock_signal_and_message_notifications_with_a_passphrase" /> <!-- android:summary="@string/preferences__lock_signal_and_message_notifications_with_a_passphrase" />-->
<Preference <!-- <Preference-->
android:key="pref_change_passphrase" <!-- android:key="pref_change_passphrase"-->
android:title="@string/preferences__change_passphrase" <!-- android:title="@string/preferences__change_passphrase"-->
android:summary="@string/preferences__change_your_passphrase" <!-- android:summary="@string/preferences__change_your_passphrase"-->
android:dependency="pref_enable_passphrase_temporary" /> <!-- android:dependency="pref_enable_passphrase_temporary" />-->
<org.thoughtcrime.securesms.components.SwitchPreferenceCompat <!-- <org.thoughtcrime.securesms.components.SwitchPreferenceCompat-->
android:defaultValue="false" <!-- android:defaultValue="false"-->
android:key="pref_timeout_passphrase" <!-- android:key="pref_timeout_passphrase"-->
android:title="@string/preferences__inactivity_timeout_passphrase" <!-- android:title="@string/preferences__inactivity_timeout_passphrase"-->
android:summary="@string/preferences__auto_lock_signal_after_a_specified_time_interval_of_inactivity" <!-- android:summary="@string/preferences__auto_lock_signal_after_a_specified_time_interval_of_inactivity"-->
android:dependency="pref_enable_passphrase_temporary" /> <!-- android:dependency="pref_enable_passphrase_temporary" />-->
<Preference <!-- <Preference-->
android:title="@string/preferences__inactivity_timeout_interval" <!-- android:title="@string/preferences__inactivity_timeout_interval"-->
android:key="pref_timeout_interval" <!-- android:key="pref_timeout_interval"-->
android:dependency="pref_timeout_passphrase" /> <!-- android:dependency="pref_timeout_passphrase" />-->
<org.thoughtcrime.securesms.components.SwitchPreferenceCompat <org.thoughtcrime.securesms.components.SwitchPreferenceCompat
android:defaultValue="true" android:defaultValue="true"