mirror of
https://github.com/oxen-io/session-android.git
synced 2025-10-24 07:18:43 +00:00
Migrate from SQLite and ciphertext blobs to SQLCipher + KeyStore
This commit is contained in:
@@ -11,7 +11,6 @@ import android.net.Uri;
|
||||
import android.os.RemoteException;
|
||||
import android.provider.ContactsContract;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
@@ -21,7 +20,6 @@ import com.annimon.stream.Stream;
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.crypto.SessionUtil;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
@@ -50,7 +48,7 @@ public class DirectoryHelper {
|
||||
|
||||
private static final String TAG = DirectoryHelper.class.getSimpleName();
|
||||
|
||||
public static void refreshDirectory(@NonNull Context context, @Nullable MasterSecret masterSecret, boolean notifyOfNewUsers)
|
||||
public static void refreshDirectory(@NonNull Context context, boolean notifyOfNewUsers)
|
||||
throws IOException
|
||||
{
|
||||
if (TextUtils.isEmpty(TextSecurePreferences.getLocalNumber(context))) return;
|
||||
@@ -64,7 +62,7 @@ public class DirectoryHelper {
|
||||
.add(new MultiDeviceContactUpdateJob(context));
|
||||
}
|
||||
|
||||
if (notifyOfNewUsers) notifyNewUsers(context, masterSecret, newlyActiveUsers);
|
||||
if (notifyOfNewUsers) notifyNewUsers(context, newlyActiveUsers);
|
||||
}
|
||||
|
||||
private static @NonNull List<Address> refreshDirectory(@NonNull Context context, @NonNull SignalServiceAccountManager accountManager)
|
||||
@@ -122,7 +120,6 @@ public class DirectoryHelper {
|
||||
}
|
||||
|
||||
public static RegisteredState refreshDirectoryFor(@NonNull Context context,
|
||||
@Nullable MasterSecret masterSecret,
|
||||
@NonNull Recipient recipient)
|
||||
throws IOException
|
||||
{
|
||||
@@ -145,7 +142,7 @@ public class DirectoryHelper {
|
||||
}
|
||||
|
||||
if (!activeUser && systemContact) {
|
||||
notifyNewUsers(context, masterSecret, Collections.singletonList(recipient.getAddress()));
|
||||
notifyNewUsers(context, Collections.singletonList(recipient.getAddress()));
|
||||
}
|
||||
|
||||
return RegisteredState.REGISTERED;
|
||||
@@ -191,22 +188,21 @@ public class DirectoryHelper {
|
||||
}
|
||||
|
||||
private static void notifyNewUsers(@NonNull Context context,
|
||||
@Nullable MasterSecret masterSecret,
|
||||
@NonNull List<Address> newUsers)
|
||||
{
|
||||
if (!TextSecurePreferences.isNewContactsNotificationEnabled(context)) return;
|
||||
|
||||
for (Address newUser: newUsers) {
|
||||
if (!SessionUtil.hasSession(context, masterSecret, newUser) && !Util.isOwnNumber(context, newUser)) {
|
||||
if (!SessionUtil.hasSession(context, newUser) && !Util.isOwnNumber(context, newUser)) {
|
||||
IncomingJoinedMessage message = new IncomingJoinedMessage(newUser);
|
||||
Optional<InsertResult> insertResult = DatabaseFactory.getSmsDatabase(context).insertMessageInbox(message);
|
||||
|
||||
if (insertResult.isPresent()) {
|
||||
int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
|
||||
if (hour >= 9 && hour < 23) {
|
||||
MessageNotifier.updateNotification(context, masterSecret, insertResult.get().getThreadId(), true);
|
||||
MessageNotifier.updateNotification(context, insertResult.get().getThreadId(), true);
|
||||
} else {
|
||||
MessageNotifier.updateNotification(context, masterSecret, insertResult.get().getThreadId(), false);
|
||||
MessageNotifier.updateNotification(context, insertResult.get().getThreadId(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.thoughtcrime.securesms.util;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.support.annotation.NonNull;
|
||||
@@ -9,7 +10,6 @@ import android.support.annotation.UiThread;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecretUnion;
|
||||
import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore;
|
||||
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
@@ -47,6 +47,7 @@ public class IdentityUtil {
|
||||
|
||||
private static final String TAG = IdentityUtil.class.getSimpleName();
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
@UiThread
|
||||
public static ListenableFuture<Optional<IdentityRecord>> getRemoteIdentityKey(final Context context, final Recipient recipient) {
|
||||
final SettableFuture<Optional<IdentityRecord>> future = new SettableFuture<>();
|
||||
@@ -67,8 +68,7 @@ public class IdentityUtil {
|
||||
return future;
|
||||
}
|
||||
|
||||
public static void markIdentityVerified(Context context, MasterSecretUnion masterSecret,
|
||||
Recipient recipient, boolean verified, boolean remote)
|
||||
public static void markIdentityVerified(Context context, Recipient recipient, boolean verified, boolean remote)
|
||||
{
|
||||
long time = System.currentTimeMillis();
|
||||
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
|
||||
@@ -96,13 +96,13 @@ public class IdentityUtil {
|
||||
if (verified) outgoing = new OutgoingIdentityVerifiedMessage(recipient);
|
||||
else outgoing = new OutgoingIdentityDefaultMessage(recipient);
|
||||
|
||||
DatabaseFactory.getEncryptingSmsDatabase(context).insertMessageOutbox(masterSecret, threadId, outgoing, false, time, null);
|
||||
DatabaseFactory.getSmsDatabase(context).insertMessageOutbox(threadId, outgoing, false, time, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (remote) {
|
||||
IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.<SignalServiceGroup>absent(), 0);
|
||||
IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.absent(), 0);
|
||||
|
||||
if (verified) incoming = new IncomingIdentityVerifiedMessage(incoming);
|
||||
else incoming = new IncomingIdentityDefaultMessage(incoming);
|
||||
@@ -117,8 +117,7 @@ public class IdentityUtil {
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
|
||||
|
||||
Log.w(TAG, "Inserting verified outbox...");
|
||||
DatabaseFactory.getEncryptingSmsDatabase(context)
|
||||
.insertMessageOutbox(masterSecret, threadId, outgoing, false, time, null);
|
||||
DatabaseFactory.getSmsDatabase(context).insertMessageOutbox(threadId, outgoing, false, time, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,12 +139,12 @@ public class IdentityUtil {
|
||||
}
|
||||
}
|
||||
|
||||
IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.<SignalServiceGroup>absent(), 0);
|
||||
IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.absent(), 0);
|
||||
IncomingIdentityUpdateMessage individualUpdate = new IncomingIdentityUpdateMessage(incoming);
|
||||
Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(individualUpdate);
|
||||
|
||||
if (insertResult.isPresent()) {
|
||||
MessageNotifier.updateNotification(context, null, insertResult.get().getThreadId());
|
||||
MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,7 +165,7 @@ public class IdentityUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static void processVerifiedMessage(Context context, MasterSecretUnion masterSecret, VerifiedMessage verifiedMessage) {
|
||||
public static void processVerifiedMessage(Context context, VerifiedMessage verifiedMessage) {
|
||||
synchronized (SESSION_LOCK) {
|
||||
IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context);
|
||||
Recipient recipient = Recipient.from(context, Address.fromExternal(context, verifiedMessage.getDestination()), true);
|
||||
@@ -183,7 +182,7 @@ public class IdentityUtil {
|
||||
identityRecord.get().getVerifiedStatus() != IdentityDatabase.VerifiedStatus.DEFAULT)
|
||||
{
|
||||
identityDatabase.setVerified(recipient.getAddress(), identityRecord.get().getIdentityKey(), IdentityDatabase.VerifiedStatus.DEFAULT);
|
||||
markIdentityVerified(context, masterSecret, recipient, false, true);
|
||||
markIdentityVerified(context, recipient, false, true);
|
||||
}
|
||||
|
||||
if (verifiedMessage.getVerified() == VerifiedMessage.VerifiedState.VERIFIED &&
|
||||
@@ -193,7 +192,7 @@ public class IdentityUtil {
|
||||
{
|
||||
saveIdentity(context, verifiedMessage.getDestination(), verifiedMessage.getIdentityKey());
|
||||
identityDatabase.setVerified(recipient.getAddress(), verifiedMessage.getIdentityKey(), IdentityDatabase.VerifiedStatus.VERIFIED);
|
||||
markIdentityVerified(context, masterSecret, recipient, true, true);
|
||||
markIdentityVerified(context, recipient, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,14 +42,14 @@ public class MediaUtil {
|
||||
public static final String VIDEO_UNSPECIFIED = "video/*";
|
||||
|
||||
|
||||
public static @Nullable ThumbnailData generateThumbnail(Context context, MasterSecret masterSecret, String contentType, Uri uri)
|
||||
public static @Nullable ThumbnailData generateThumbnail(Context context, String contentType, Uri uri)
|
||||
throws BitmapDecodingException
|
||||
{
|
||||
long startMillis = System.currentTimeMillis();
|
||||
ThumbnailData data = null;
|
||||
|
||||
if (isImageType(contentType)) {
|
||||
data = new ThumbnailData(generateImageThumbnail(context, masterSecret, uri));
|
||||
data = new ThumbnailData(generateImageThumbnail(context, uri));
|
||||
}
|
||||
|
||||
if (data != null) {
|
||||
@@ -61,14 +61,14 @@ public class MediaUtil {
|
||||
return data;
|
||||
}
|
||||
|
||||
private static Bitmap generateImageThumbnail(Context context, MasterSecret masterSecret, Uri uri)
|
||||
private static Bitmap generateImageThumbnail(Context context, Uri uri)
|
||||
throws BitmapDecodingException
|
||||
{
|
||||
try {
|
||||
int maxSize = context.getResources().getDimensionPixelSize(R.dimen.media_bubble_height);
|
||||
return GlideApp.with(context.getApplicationContext())
|
||||
.asBitmap()
|
||||
.load(new DecryptableUri(masterSecret, uri))
|
||||
.load(new DecryptableUri(uri))
|
||||
.centerCrop()
|
||||
.into(maxSize, maxSize)
|
||||
.get();
|
||||
@@ -125,8 +125,8 @@ public class MediaUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static long getMediaSize(Context context, MasterSecret masterSecret, Uri uri) throws IOException {
|
||||
InputStream in = PartAuthority.getAttachmentStream(context, masterSecret, uri);
|
||||
public static long getMediaSize(Context context, Uri uri) throws IOException {
|
||||
InputStream in = PartAuthority.getAttachmentStream(context, uri);
|
||||
if (in == null) throw new IOException("Couldn't obtain input stream.");
|
||||
|
||||
long size = 0;
|
||||
|
||||
@@ -12,7 +12,6 @@ import android.webkit.MimeTypeMap;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.database.NoExternalStorageException;
|
||||
import org.thoughtcrime.securesms.mms.PartAuthority;
|
||||
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
|
||||
@@ -34,20 +33,18 @@ public class SaveAttachmentTask extends ProgressDialogAsyncTask<SaveAttachmentTa
|
||||
private static final int WRITE_ACCESS_FAILURE = 2;
|
||||
|
||||
private final WeakReference<Context> contextReference;
|
||||
private final WeakReference<MasterSecret> masterSecretReference;
|
||||
|
||||
private final int attachmentCount;
|
||||
|
||||
public SaveAttachmentTask(Context context, MasterSecret masterSecret) {
|
||||
this(context, masterSecret, 1);
|
||||
public SaveAttachmentTask(Context context) {
|
||||
this(context, 1);
|
||||
}
|
||||
|
||||
public SaveAttachmentTask(Context context, MasterSecret masterSecret, int count) {
|
||||
public SaveAttachmentTask(Context context, int count) {
|
||||
super(context,
|
||||
context.getResources().getQuantityString(R.plurals.ConversationFragment_saving_n_attachments, count, count),
|
||||
context.getResources().getQuantityString(R.plurals.ConversationFragment_saving_n_attachments_to_sd_card, count, count));
|
||||
this.contextReference = new WeakReference<>(context);
|
||||
this.masterSecretReference = new WeakReference<>(masterSecret);
|
||||
this.attachmentCount = count;
|
||||
}
|
||||
|
||||
@@ -59,7 +56,6 @@ public class SaveAttachmentTask extends ProgressDialogAsyncTask<SaveAttachmentTa
|
||||
|
||||
try {
|
||||
Context context = contextReference.get();
|
||||
MasterSecret masterSecret = masterSecretReference.get();
|
||||
String directory = null;
|
||||
|
||||
if (!StorageUtil.canWriteInSignalStorageDir()) {
|
||||
@@ -72,7 +68,7 @@ public class SaveAttachmentTask extends ProgressDialogAsyncTask<SaveAttachmentTa
|
||||
|
||||
for (Attachment attachment : attachments) {
|
||||
if (attachment != null) {
|
||||
directory = saveAttachment(context, masterSecret, attachment);
|
||||
directory = saveAttachment(context, attachment);
|
||||
if (directory == null) return new Pair<>(FAILURE, null);
|
||||
}
|
||||
}
|
||||
@@ -85,7 +81,7 @@ public class SaveAttachmentTask extends ProgressDialogAsyncTask<SaveAttachmentTa
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable String saveAttachment(Context context, MasterSecret masterSecret, Attachment attachment)
|
||||
private @Nullable String saveAttachment(Context context, Attachment attachment)
|
||||
throws NoExternalStorageException, IOException
|
||||
{
|
||||
String contentType = MediaUtil.getCorrectedMimeType(attachment.contentType);
|
||||
@@ -96,7 +92,7 @@ public class SaveAttachmentTask extends ProgressDialogAsyncTask<SaveAttachmentTa
|
||||
|
||||
File outputDirectory = createOutputDirectoryFromContentType(contentType);
|
||||
File mediaFile = createOutputFile(outputDirectory, fileName);
|
||||
InputStream inputStream = PartAuthority.getAttachmentStream(context, masterSecret, attachment.uri);
|
||||
InputStream inputStream = PartAuthority.getAttachmentStream(context, attachment.uri);
|
||||
|
||||
if (inputStream == null) {
|
||||
return null;
|
||||
|
||||
@@ -12,6 +12,7 @@ import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.preferences.widgets.NotificationPrivacyPreference;
|
||||
@@ -118,6 +119,52 @@ public class TextSecurePreferences {
|
||||
private static final String UNAUTHORIZED_RECEIVED = "pref_unauthorized_received";
|
||||
private static final String SUCCESSFUL_DIRECTORY_PREF = "pref_successful_directory";
|
||||
|
||||
private static final String DATABASE_ENCRYPTED_SECRET = "pref_database_encrypted_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_UNENCRYPTED_SECRET = "pref_attachment_unencrypted_secret";
|
||||
private static final String NEEDS_SQLCIPHER_MIGRATION = "pref_needs_sql_cipher_migration";
|
||||
|
||||
public static void setNeedsSqlCipherMigration(@NonNull Context context, boolean value) {
|
||||
setBooleanPreference(context, NEEDS_SQLCIPHER_MIGRATION, value);
|
||||
}
|
||||
|
||||
public static boolean getNeedsSqlCipherMigration(@NonNull Context context) {
|
||||
return getBooleanPreference(context, NEEDS_SQLCIPHER_MIGRATION, false);
|
||||
}
|
||||
|
||||
public static void setAttachmentEncryptedSecret(@NonNull Context context, @NonNull String secret) {
|
||||
setStringPreference(context, ATTACHMENT_ENCRYPTED_SECRET, secret);
|
||||
}
|
||||
|
||||
public static void setAttachmentUnencryptedSecret(@NonNull Context context, @Nullable String secret) {
|
||||
setStringPreference(context, ATTACHMENT_UNENCRYPTED_SECRET, secret);
|
||||
}
|
||||
|
||||
public static @Nullable String getAttachmentEncryptedSecret(@NonNull Context context) {
|
||||
return getStringPreference(context, ATTACHMENT_ENCRYPTED_SECRET, null);
|
||||
}
|
||||
|
||||
public static @Nullable String getAttachmentUnencryptedSecret(@NonNull Context context) {
|
||||
return getStringPreference(context, ATTACHMENT_UNENCRYPTED_SECRET, null);
|
||||
}
|
||||
|
||||
public static void setDatabaseEncryptedSecret(@NonNull Context context, @NonNull String secret) {
|
||||
setStringPreference(context, DATABASE_ENCRYPTED_SECRET, secret);
|
||||
}
|
||||
|
||||
public static void setDatabaseUnencryptedSecret(@NonNull Context context, @Nullable String secret) {
|
||||
setStringPreference(context, DATABASE_UNENCRYPTED_SECRET, secret);
|
||||
}
|
||||
|
||||
public static @Nullable String getDatabaseUnencryptedSecret(@NonNull Context context) {
|
||||
return getStringPreference(context, DATABASE_UNENCRYPTED_SECRET, null);
|
||||
}
|
||||
|
||||
public static @Nullable String getDatabaseEncryptedSecret(@NonNull Context context) {
|
||||
return getStringPreference(context, DATABASE_ENCRYPTED_SECRET, null);
|
||||
}
|
||||
|
||||
public static void setHasSuccessfullyRetrievedDirectory(Context context, boolean value) {
|
||||
setBooleanPreference(context, SUCCESSFUL_DIRECTORY_PREF, value);
|
||||
}
|
||||
|
||||
@@ -226,7 +226,7 @@ public class Util {
|
||||
}
|
||||
|
||||
public static long copy(InputStream in, OutputStream out) throws IOException {
|
||||
byte[] buffer = new byte[4096];
|
||||
byte[] buffer = new byte[8192];
|
||||
int read;
|
||||
long total = 0;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user