diff --git a/res/values/strings.xml b/res/values/strings.xml
index 0df6e3941e..3474bc0f32 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -467,7 +467,7 @@
%1$d messages in %2$d conversations
Most recent from: %1$s
- Encrypted message...
+ Locked message...
Media message: %s
(No subject)
Message delivery failed.
@@ -779,7 +779,7 @@
Change my passphrase
Enable passphrase
Passphrase %s
- Enable local encryption of messages and keys
+ Enable lock screen for messages
Screen security
Screen security %s
Block screenshots in the recents list and inside the app
diff --git a/res/xml/preferences_app_protection.xml b/res/xml/preferences_app_protection.xml
index 5ec51ecd6f..6a263f80ce 100644
--- a/res/xml/preferences_app_protection.xml
+++ b/res/xml/preferences_app_protection.xml
@@ -5,7 +5,7 @@
android:key="pref_enable_passphrase_temporary"
android:defaultValue="true"
android:title="@string/preferences__enable_passphrase"
- android:summary="@string/preferences__enable_local_encryption_of_messages_and_keys"/>
+ android:summary="@string/preferences__enable_lock_screen_for_messages"/>
UPGRADE_VERSIONS = new TreeSet() {{
add(NO_MORE_KEY_EXCHANGE_PREFIX_VERSION);
@@ -70,6 +65,7 @@ public class DatabaseUpgradeActivity extends BaseActivity {
add(SIGNED_PREKEY_VERSION);
add(NO_DECRYPT_QUEUE_VERSION);
add(PUSH_DECRYPT_SERIAL_ID_VERSION);
+ add(MIGRATE_SESSION_PLAINTEXT);
}};
private MasterSecret masterSecret;
@@ -90,10 +86,6 @@ public class DatabaseUpgradeActivity extends BaseActivity {
.execute(VersionTracker.getLastSeenVersion(this));
} else {
VersionTracker.updateLastSeenVersion(this);
- ApplicationContext.getInstance(this)
- .getJobManager()
- .setEncryptionKeys(new EncryptionKeys(ParcelUtil.serialize(masterSecret)));
-// DecryptingQueue.schedulePendingDecrypts(DatabaseUpgradeActivity.this, masterSecret);
MessageNotifier.updateNotification(DatabaseUpgradeActivity.this, masterSecret);
startActivity((Intent)getIntent().getParcelableExtra("next_intent"));
finish();
@@ -154,9 +146,7 @@ public class DatabaseUpgradeActivity extends BaseActivity {
.onApplicationLevelUpgrade(context, masterSecret, params[0], this);
if (params[0] < CURVE25519_VERSION) {
- if (!IdentityKeyUtil.hasCurve25519IdentityKeys(context)) {
- IdentityKeyUtil.generateCurve25519IdentityKeys(context, masterSecret);
- }
+ IdentityKeyUtil.migrateIdentityKeys(context, masterSecret);
}
if (params[0] < NO_V1_VERSION) {
@@ -178,70 +168,48 @@ public class DatabaseUpgradeActivity extends BaseActivity {
if (params[0] < SIGNED_PREKEY_VERSION) {
ApplicationContext.getInstance(getApplicationContext())
.getJobManager()
- .add(new CreateSignedPreKeyJob(context, masterSecret));
+ .add(new CreateSignedPreKeyJob(context));
}
if (params[0] < NO_DECRYPT_QUEUE_VERSION) {
- EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(getApplicationContext());
- PushDatabase pushDatabase = DatabaseFactory.getPushDatabase(getApplicationContext());
-
- SmsDatabase.Reader smsReader = null;
- Cursor pushReader = null;
-
- SmsMessageRecord record;
-
- try {
- smsReader = smsDatabase.getDecryptInProgressMessages(masterSecret);
-
- while ((record = smsReader.getNext()) != null) {
- ApplicationContext.getInstance(getApplicationContext())
- .getJobManager()
- .add(new SmsDecryptJob(getApplicationContext(), record.getId()));
- }
- } finally {
- if (smsReader != null)
- smsReader.close();
- }
-
- try {
- pushReader = pushDatabase.getPending();
-
- while ((pushReader != null && pushReader.moveToNext())) {
- ApplicationContext.getInstance(getApplicationContext())
- .getJobManager()
- .add(new PushDecryptJob(getApplicationContext(),
- pushReader.getLong(pushReader.getColumnIndexOrThrow(PushDatabase.ID)),
- pushReader.getString(pushReader.getColumnIndexOrThrow(PushDatabase.SOURCE))));
- }
- } finally {
- if (pushReader != null)
- pushReader.close();
- }
+ scheduleMessagesInPushDatabase(context);
}
if (params[0] < PUSH_DECRYPT_SERIAL_ID_VERSION) {
- PushDatabase pushDatabase = DatabaseFactory.getPushDatabase(context);
- Cursor pushReader = null;
+ scheduleMessagesInPushDatabase(context);
+ }
- try {
- pushReader = pushDatabase.getPending();
+ if (params[0] < MIGRATE_SESSION_PLAINTEXT) {
+ new TextSecureSessionStore(context, masterSecret).migrateSessions();
+ new TextSecurePreKeyStore(context, masterSecret).migrateRecords();
- while (pushReader != null && pushReader.moveToNext()) {
- ApplicationContext.getInstance(getApplicationContext())
- .getJobManager()
- .add(new PushDecryptJob(getApplicationContext(),
- pushReader.getLong(pushReader.getColumnIndexOrThrow(PushDatabase.ID)),
- pushReader.getString(pushReader.getColumnIndexOrThrow(PushDatabase.SOURCE))));
- }
- } finally {
- if (pushReader != null)
- pushReader.close();
- }
+ IdentityKeyUtil.migrateIdentityKeys(context, masterSecret);
+ scheduleMessagesInPushDatabase(context);;
}
return null;
}
+ private void scheduleMessagesInPushDatabase(Context context) {
+ PushDatabase pushDatabase = DatabaseFactory.getPushDatabase(context);
+ Cursor pushReader = null;
+
+ try {
+ pushReader = pushDatabase.getPending();
+
+ while (pushReader != null && pushReader.moveToNext()) {
+ ApplicationContext.getInstance(getApplicationContext())
+ .getJobManager()
+ .add(new PushDecryptJob(getApplicationContext(),
+ pushReader.getLong(pushReader.getColumnIndexOrThrow(PushDatabase.ID)),
+ pushReader.getString(pushReader.getColumnIndexOrThrow(PushDatabase.SOURCE))));
+ }
+ } finally {
+ if (pushReader != null)
+ pushReader.close();
+ }
+ }
+
@Override
protected void onProgressUpdate(Double... update) {
indeterminateProgress.setVisibility(View.GONE);
@@ -254,11 +222,6 @@ public class DatabaseUpgradeActivity extends BaseActivity {
@Override
protected void onPostExecute(Void result) {
VersionTracker.updateLastSeenVersion(DatabaseUpgradeActivity.this);
-// DecryptingQueue.schedulePendingDecrypts(DatabaseUpgradeActivity.this, masterSecret);
- ApplicationContext.getInstance(DatabaseUpgradeActivity.this)
- .getJobManager()
- .setEncryptionKeys(new EncryptionKeys(ParcelUtil.serialize(masterSecret)));
-
MessageNotifier.updateNotification(DatabaseUpgradeActivity.this, masterSecret);
startActivity((Intent)getIntent().getParcelableExtra("next_intent"));
diff --git a/src/org/thoughtcrime/securesms/DeviceProvisioningActivity.java b/src/org/thoughtcrime/securesms/DeviceProvisioningActivity.java
index 4f6149a3f5..39e0ea33ac 100644
--- a/src/org/thoughtcrime/securesms/DeviceProvisioningActivity.java
+++ b/src/org/thoughtcrime/securesms/DeviceProvisioningActivity.java
@@ -108,7 +108,7 @@ public class DeviceProvisioningActivity extends PassphraseRequiredActionBarActiv
String ephemeralId = uri.getQueryParameter("uuid");
String publicKeyEncoded = uri.getQueryParameter("pub_key");
ECPublicKey publicKey = Curve.decodePoint(Base64.decode(publicKeyEncoded), 0);
- IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret);
+ IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context);
accountManager.addDevice(ephemeralId, publicKey, identityKeyPair, verificationCode);
return SUCCESS;
diff --git a/src/org/thoughtcrime/securesms/PassphraseCreateActivity.java b/src/org/thoughtcrime/securesms/PassphraseCreateActivity.java
index 79e0149113..ac636a8d39 100644
--- a/src/org/thoughtcrime/securesms/PassphraseCreateActivity.java
+++ b/src/org/thoughtcrime/securesms/PassphraseCreateActivity.java
@@ -66,7 +66,7 @@ public class PassphraseCreateActivity extends PassphraseActivity {
passphrase);
MasterSecretUtil.generateAsymmetricMasterSecret(PassphraseCreateActivity.this, masterSecret);
- IdentityKeyUtil.generateIdentityKeys(PassphraseCreateActivity.this, masterSecret);
+ IdentityKeyUtil.generateIdentityKeys(PassphraseCreateActivity.this);
VersionTracker.updateLastSeenVersion(PassphraseCreateActivity.this);
TextSecurePreferences.setPasswordDisabled(PassphraseCreateActivity.this, true);
diff --git a/src/org/thoughtcrime/securesms/crypto/AsymmetricMasterCipher.java b/src/org/thoughtcrime/securesms/crypto/AsymmetricMasterCipher.java
index 0563b0ad73..23255c5206 100644
--- a/src/org/thoughtcrime/securesms/crypto/AsymmetricMasterCipher.java
+++ b/src/org/thoughtcrime/securesms/crypto/AsymmetricMasterCipher.java
@@ -61,39 +61,45 @@ public class AsymmetricMasterCipher {
this.asymmetricMasterSecret = asymmetricMasterSecret;
}
- public String decryptBody(String body) throws IOException, InvalidMessageException {
+ 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[] combined = Base64.decode(body);
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);
- byte[] decryptedBody = masterCipher.decryptBytes(parts[1]);
- return new String(decryptedBody);
- } catch (InvalidKeyException | InvalidMessageException ike) {
- throw new InvalidMessageException(ike);
+ 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) {
- 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.getBytes());
-
- PublicKey ourPublicKey = new PublicKey(31337, ourKeyPair.getPublicKey());
- byte[] publicKeyBytes = ourPublicKey.serialize();
- byte[] combined = Util.combine(publicKeyBytes, encryptedBodyBytes);
-
- return Base64.encodeBytes(combined);
- } catch (InvalidKeyException e) {
- throw new AssertionError(e);
- }
+ return Base64.encodeBytes(encryptBytes(body.getBytes()));
}
private MasterCipher getMasterCipherForSecret(byte[] secretBytes) {
diff --git a/src/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java b/src/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java
index dca0d09a3e..f0a4690fa0 100644
--- a/src/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java
+++ b/src/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java
@@ -20,7 +20,7 @@ package org.thoughtcrime.securesms.crypto;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
-import android.util.Log;
+import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.util.Base64;
import org.whispersystems.libaxolotl.IdentityKey;
@@ -40,86 +40,109 @@ import java.io.IOException;
public class IdentityKeyUtil {
- private static final String IDENTITY_PUBLIC_KEY_DJB_PREF = "pref_identity_public_curve25519";
- private static final String IDENTITY_PRIVATE_KEY_DJB_PREF = "pref_identity_private_curve25519";
+ 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";
+
+ private static final String IDENTITY_PUBLIC_KEY_PREF = "pref_identity_public_v3";
+ private static final String IDENTITY_PRIVATE_KEY_PREF = "pref_identity_private_v3";
public static boolean hasIdentityKey(Context context) {
SharedPreferences preferences = context.getSharedPreferences(MasterSecretUtil.PREFERENCES_NAME, 0);
return
- preferences.contains(IDENTITY_PUBLIC_KEY_DJB_PREF) &&
- preferences.contains(IDENTITY_PRIVATE_KEY_DJB_PREF);
+ preferences.contains(IDENTITY_PUBLIC_KEY_PREF) &&
+ preferences.contains(IDENTITY_PRIVATE_KEY_PREF);
}
- public static IdentityKey getIdentityKey(Context context) {
- if (!hasIdentityKey(context)) return null;
+ public static @NonNull IdentityKey getIdentityKey(@NonNull Context context) {
+ if (!hasIdentityKey(context)) throw new AssertionError("There isn't one!");
try {
- byte[] publicKeyBytes = Base64.decode(retrieve(context, IDENTITY_PUBLIC_KEY_DJB_PREF));
+ byte[] publicKeyBytes = Base64.decode(retrieve(context, IDENTITY_PUBLIC_KEY_PREF));
return new IdentityKey(publicKeyBytes, 0);
- } catch (IOException ioe) {
- Log.w("IdentityKeyUtil", ioe);
- return null;
- } catch (InvalidKeyException e) {
- Log.w("IdentityKeyUtil", e);
- return null;
- }
- }
-
- public static IdentityKeyPair getIdentityKeyPair(Context context,
- MasterSecret masterSecret)
- {
- if (!hasIdentityKey(context))
- return null;
-
- try {
- MasterCipher masterCipher = new MasterCipher(masterSecret);
- IdentityKey publicKey = getIdentityKey(context);
- ECPrivateKey privateKey = masterCipher.decryptKey(Base64.decode(retrieve(context, IDENTITY_PRIVATE_KEY_DJB_PREF)));
-
- return new IdentityKeyPair(publicKey, privateKey);
} catch (IOException | InvalidKeyException e) {
throw new AssertionError(e);
}
}
- public static void generateIdentityKeys(Context context, MasterSecret masterSecret) {
+ public static @NonNull IdentityKeyPair getIdentityKeyPair(@NonNull Context context) {
+ if (!hasIdentityKey(context)) throw new AssertionError("There isn't one!");
+
+ try {
+ IdentityKey publicKey = getIdentityKey(context);
+ ECPrivateKey privateKey = Curve.decodePrivatePoint(Base64.decode(retrieve(context, IDENTITY_PRIVATE_KEY_PREF)));
+
+ return new IdentityKeyPair(publicKey, privateKey);
+ } catch (IOException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ public static void generateIdentityKeys(Context context) {
ECKeyPair djbKeyPair = Curve.generateKeyPair();
-
- MasterCipher masterCipher = new MasterCipher(masterSecret);
IdentityKey djbIdentityKey = new IdentityKey(djbKeyPair.getPublicKey());
- byte[] djbPrivateKey = masterCipher.encryptKey(djbKeyPair.getPrivateKey());
+ ECPrivateKey djbPrivateKey = djbKeyPair.getPrivateKey();
- save(context, IDENTITY_PUBLIC_KEY_DJB_PREF, Base64.encodeBytes(djbIdentityKey.serialize()));
- save(context, IDENTITY_PRIVATE_KEY_DJB_PREF, Base64.encodeBytes(djbPrivateKey));
+ save(context, IDENTITY_PUBLIC_KEY_PREF, Base64.encodeBytes(djbIdentityKey.serialize()));
+ save(context, IDENTITY_PRIVATE_KEY_PREF, Base64.encodeBytes(djbPrivateKey.serialize()));
}
- public static boolean hasCurve25519IdentityKeys(Context context) {
+ 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.serialize()));
+ save(context, IDENTITY_PRIVATE_KEY_PREF, Base64.encodeBytes(legacyPair.serialize()));
+
+ delete(context, IDENTITY_PUBLIC_KEY_CIPHERTEXT_LEGACY_PREF);
+ delete(context, IDENTITY_PRIVATE_KEY_CIPHERTEXT_LEGACY_PREF);
+ } else {
+ generateIdentityKeys(context);
+ }
+ }
+ }
+
+ private static boolean hasLegacyIdentityKeys(Context context) {
return
- retrieve(context, IDENTITY_PUBLIC_KEY_DJB_PREF) != null &&
- retrieve(context, IDENTITY_PRIVATE_KEY_DJB_PREF) != null;
+ retrieve(context, IDENTITY_PUBLIC_KEY_CIPHERTEXT_LEGACY_PREF) != null &&
+ retrieve(context, IDENTITY_PRIVATE_KEY_CIPHERTEXT_LEGACY_PREF) != null;
}
- public static void generateCurve25519IdentityKeys(Context context, MasterSecret masterSecret) {
- MasterCipher masterCipher = new MasterCipher(masterSecret);
- ECKeyPair djbKeyPair = Curve.generateKeyPair();
- IdentityKey djbIdentityKey = new IdentityKey(djbKeyPair.getPublicKey());
- byte[] djbPrivateKey = masterCipher.encryptKey(djbKeyPair.getPrivateKey());
+ 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)));
- save(context, IDENTITY_PUBLIC_KEY_DJB_PREF, Base64.encodeBytes(djbIdentityKey.serialize()));
- save(context, IDENTITY_PRIVATE_KEY_DJB_PREF, Base64.encodeBytes(djbPrivateKey));
+ return new IdentityKeyPair(identityKey, privateKey);
+ } catch (IOException | InvalidKeyException e) {
+ throw new AssertionError(e);
+ }
}
- public static String retrieve(Context context, String key) {
+ private static String retrieve(Context context, String key) {
SharedPreferences preferences = context.getSharedPreferences(MasterSecretUtil.PREFERENCES_NAME, 0);
return preferences.getString(key, null);
}
- public static void save(Context context, String key, String value) {
+ private static void save(Context context, String key, String value) {
SharedPreferences preferences = context.getSharedPreferences(MasterSecretUtil.PREFERENCES_NAME, 0);
Editor preferencesEditor = preferences.edit();
preferencesEditor.putString(key, value);
if (!preferencesEditor.commit()) throw new AssertionError("failed to save identity key/value to shared preferences");
}
+
+ private static void delete(Context context, String key) {
+ context.getSharedPreferences(MasterSecretUtil.PREFERENCES_NAME, 0).edit().remove(key).commit();
+ }
+
}
diff --git a/src/org/thoughtcrime/securesms/crypto/MasterSecretUnion.java b/src/org/thoughtcrime/securesms/crypto/MasterSecretUnion.java
new file mode 100644
index 0000000000..121562630f
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/crypto/MasterSecretUnion.java
@@ -0,0 +1,29 @@
+package org.thoughtcrime.securesms.crypto;
+
+import android.support.annotation.NonNull;
+
+import org.whispersystems.libaxolotl.util.guava.Optional;
+
+public class MasterSecretUnion {
+
+ private final Optional masterSecret;
+ private final Optional asymmetricMasterSecret;
+
+ public MasterSecretUnion(@NonNull MasterSecret masterSecret) {
+ this.masterSecret = Optional.of(masterSecret);
+ this.asymmetricMasterSecret = Optional.absent();
+ }
+
+ public MasterSecretUnion(@NonNull AsymmetricMasterSecret asymmetricMasterSecret) {
+ this.masterSecret = Optional.absent();
+ this.asymmetricMasterSecret = Optional.of(asymmetricMasterSecret);
+ }
+
+ public Optional getMasterSecret() {
+ return masterSecret;
+ }
+
+ public Optional getAsymmetricMasterSecret() {
+ return asymmetricMasterSecret;
+ }
+}
diff --git a/src/org/thoughtcrime/securesms/crypto/MasterSecretUtil.java b/src/org/thoughtcrime/securesms/crypto/MasterSecretUtil.java
index 45f00ac538..598fdfc28a 100644
--- a/src/org/thoughtcrime/securesms/crypto/MasterSecretUtil.java
+++ b/src/org/thoughtcrime/securesms/crypto/MasterSecretUtil.java
@@ -19,6 +19,8 @@ package org.thoughtcrime.securesms.crypto;
import android.content.Context;
import android.content.SharedPreferences;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
@@ -121,8 +123,8 @@ public class MasterSecretUtil {
}
}
- public static AsymmetricMasterSecret getAsymmetricMasterSecret(Context context,
- MasterSecret masterSecret)
+ public static AsymmetricMasterSecret getAsymmetricMasterSecret(@NonNull Context context,
+ @Nullable MasterSecret masterSecret)
{
try {
byte[] djbPublicBytes = retrieve(context, ASYMMETRIC_LOCAL_PUBLIC_DJB);
diff --git a/src/org/thoughtcrime/securesms/crypto/MediaKey.java b/src/org/thoughtcrime/securesms/crypto/MediaKey.java
new file mode 100644
index 0000000000..058b5448fe
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/crypto/MediaKey.java
@@ -0,0 +1,31 @@
+package org.thoughtcrime.securesms.crypto;
+
+import android.support.annotation.NonNull;
+
+import org.thoughtcrime.securesms.util.Base64;
+import org.whispersystems.libaxolotl.InvalidMessageException;
+
+import java.io.IOException;
+
+public class MediaKey {
+
+ public static String getEncrypted(@NonNull MasterSecretUnion masterSecret, @NonNull byte[] key) {
+ if (masterSecret.getMasterSecret().isPresent()) {
+ return Base64.encodeBytes(new MasterCipher(masterSecret.getMasterSecret().get()).encryptBytes(key));
+ } else {
+ return "?ASYNC-" + Base64.encodeBytes(new AsymmetricMasterCipher(masterSecret.getAsymmetricMasterSecret().get()).encryptBytes(key));
+ }
+ }
+
+ public static byte[] getDecrypted(@NonNull MasterSecret masterSecret,
+ @NonNull AsymmetricMasterSecret asymmetricMasterSecret,
+ @NonNull String encodedKey)
+ throws IOException, InvalidMessageException
+ {
+ if (encodedKey.startsWith("?ASYNC-")) {
+ return new AsymmetricMasterCipher(asymmetricMasterSecret).decryptBytes(Base64.decode(encodedKey.substring("?ASYNC-".length())));
+ } else {
+ return new MasterCipher(masterSecret).decryptBytes(Base64.decode(encodedKey));
+ }
+ }
+}
diff --git a/src/org/thoughtcrime/securesms/crypto/PreKeyUtil.java b/src/org/thoughtcrime/securesms/crypto/PreKeyUtil.java
index 691bb85d42..b857946886 100644
--- a/src/org/thoughtcrime/securesms/crypto/PreKeyUtil.java
+++ b/src/org/thoughtcrime/securesms/crypto/PreKeyUtil.java
@@ -48,8 +48,8 @@ public class PreKeyUtil {
public static final int BATCH_SIZE = 100;
- public static List generatePreKeys(Context context, MasterSecret masterSecret) {
- PreKeyStore preKeyStore = new TextSecurePreKeyStore(context, masterSecret);
+ public static List generatePreKeys(Context context) {
+ PreKeyStore preKeyStore = new TextSecurePreKeyStore(context);
List records = new LinkedList<>();
int preKeyIdOffset = getNextPreKeyId(context);
@@ -66,11 +66,10 @@ public class PreKeyUtil {
return records;
}
- public static SignedPreKeyRecord generateSignedPreKey(Context context, MasterSecret masterSecret,
- IdentityKeyPair identityKeyPair)
+ public static SignedPreKeyRecord generateSignedPreKey(Context context, IdentityKeyPair identityKeyPair)
{
try {
- SignedPreKeyStore signedPreKeyStore = new TextSecurePreKeyStore(context, masterSecret);
+ SignedPreKeyStore signedPreKeyStore = new TextSecurePreKeyStore(context);
int signedPreKeyId = getNextSignedPreKeyId(context);
ECKeyPair keyPair = Curve.generateKeyPair();
byte[] signature = Curve.calculateSignature(identityKeyPair.getPrivateKey(), keyPair.getPublicKey().serialize());
@@ -85,8 +84,8 @@ public class PreKeyUtil {
}
}
- public static PreKeyRecord generateLastResortKey(Context context, MasterSecret masterSecret) {
- PreKeyStore preKeyStore = new TextSecurePreKeyStore(context, masterSecret);
+ public static PreKeyRecord generateLastResortKey(Context context) {
+ PreKeyStore preKeyStore = new TextSecurePreKeyStore(context);
if (preKeyStore.containsPreKey(Medium.MAX_VALUE)) {
try {
diff --git a/src/org/thoughtcrime/securesms/crypto/storage/TextSecureAxolotlStore.java b/src/org/thoughtcrime/securesms/crypto/storage/TextSecureAxolotlStore.java
index 8da6dcf1a7..51d35aeed2 100644
--- a/src/org/thoughtcrime/securesms/crypto/storage/TextSecureAxolotlStore.java
+++ b/src/org/thoughtcrime/securesms/crypto/storage/TextSecureAxolotlStore.java
@@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.crypto.storage;
import android.content.Context;
-import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.whispersystems.libaxolotl.AxolotlAddress;
import org.whispersystems.libaxolotl.IdentityKey;
import org.whispersystems.libaxolotl.IdentityKeyPair;
@@ -25,11 +24,11 @@ public class TextSecureAxolotlStore implements AxolotlStore {
private final IdentityKeyStore identityKeyStore;
private final SessionStore sessionStore;
- public TextSecureAxolotlStore(Context context, MasterSecret masterSecret) {
- this.preKeyStore = new TextSecurePreKeyStore(context, masterSecret);
- this.signedPreKeyStore = new TextSecurePreKeyStore(context, masterSecret);
- this.identityKeyStore = new TextSecureIdentityKeyStore(context, masterSecret);
- this.sessionStore = new TextSecureSessionStore(context, masterSecret);
+ public TextSecureAxolotlStore(Context context) {
+ this.preKeyStore = new TextSecurePreKeyStore(context);
+ this.signedPreKeyStore = new TextSecurePreKeyStore(context);
+ this.identityKeyStore = new TextSecureIdentityKeyStore(context);
+ this.sessionStore = new TextSecureSessionStore(context);
}
@Override
diff --git a/src/org/thoughtcrime/securesms/crypto/storage/TextSecureIdentityKeyStore.java b/src/org/thoughtcrime/securesms/crypto/storage/TextSecureIdentityKeyStore.java
index 48150934c8..f3a6de3d7c 100644
--- a/src/org/thoughtcrime/securesms/crypto/storage/TextSecureIdentityKeyStore.java
+++ b/src/org/thoughtcrime/securesms/crypto/storage/TextSecureIdentityKeyStore.java
@@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.crypto.storage;
import android.content.Context;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
-import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
@@ -13,17 +12,15 @@ import org.whispersystems.libaxolotl.state.IdentityKeyStore;
public class TextSecureIdentityKeyStore implements IdentityKeyStore {
- private final Context context;
- private final MasterSecret masterSecret;
+ private final Context context;
- public TextSecureIdentityKeyStore(Context context, MasterSecret masterSecret) {
- this.context = context;
- this.masterSecret = masterSecret;
+ public TextSecureIdentityKeyStore(Context context) {
+ this.context = context;
}
@Override
public IdentityKeyPair getIdentityKeyPair() {
- return IdentityKeyUtil.getIdentityKeyPair(context, masterSecret);
+ return IdentityKeyUtil.getIdentityKeyPair(context);
}
@Override
@@ -34,13 +31,13 @@ public class TextSecureIdentityKeyStore implements IdentityKeyStore {
@Override
public void saveIdentity(String name, IdentityKey identityKey) {
long recipientId = RecipientFactory.getRecipientsFromString(context, name, true).getPrimaryRecipient().getRecipientId();
- DatabaseFactory.getIdentityDatabase(context).saveIdentity(masterSecret, recipientId, identityKey);
+ DatabaseFactory.getIdentityDatabase(context).saveIdentity(recipientId, identityKey);
}
@Override
public boolean isTrustedIdentity(String name, IdentityKey identityKey) {
long recipientId = RecipientFactory.getRecipientsFromString(context, name, true).getPrimaryRecipient().getRecipientId();
return DatabaseFactory.getIdentityDatabase(context)
- .isValidIdentity(masterSecret, recipientId, identityKey);
+ .isValidIdentity(recipientId, identityKey);
}
}
diff --git a/src/org/thoughtcrime/securesms/crypto/storage/TextSecurePreKeyStore.java b/src/org/thoughtcrime/securesms/crypto/storage/TextSecurePreKeyStore.java
index effc2c893a..46858fdf5f 100644
--- a/src/org/thoughtcrime/securesms/crypto/storage/TextSecurePreKeyStore.java
+++ b/src/org/thoughtcrime/securesms/crypto/storage/TextSecurePreKeyStore.java
@@ -1,6 +1,8 @@
package org.thoughtcrime.securesms.crypto.storage;
import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.util.Log;
import org.thoughtcrime.securesms.crypto.MasterCipher;
@@ -28,14 +30,19 @@ public class TextSecurePreKeyStore implements PreKeyStore, SignedPreKeyStore {
public static final String SIGNED_PREKEY_DIRECTORY = "signed_prekeys";
- private static final int CURRENT_VERSION_MARKER = 1;
+ private static final int PLAINTEXT_VERSION = 2;
+ private static final int CURRENT_VERSION_MARKER = 2;
private static final Object FILE_LOCK = new Object();
private static final String TAG = TextSecurePreKeyStore.class.getSimpleName();
- private final Context context;
- private final MasterSecret masterSecret;
+ @NonNull private final Context context;
+ @Nullable private final MasterSecret masterSecret;
- public TextSecurePreKeyStore(Context context, MasterSecret masterSecret) {
+ public TextSecurePreKeyStore(@NonNull Context context) {
+ this(context, null);
+ }
+
+ public TextSecurePreKeyStore(@NonNull Context context, @Nullable MasterSecret masterSecret) {
this.context = context;
this.masterSecret = masterSecret;
}
@@ -129,28 +136,65 @@ public class TextSecurePreKeyStore implements PreKeyStore, SignedPreKeyStore {
record.delete();
}
+ public void migrateRecords() {
+ synchronized (FILE_LOCK) {
+ File preKeyRecords = getPreKeyDirectory();
+
+ for (File preKeyRecord : preKeyRecords.listFiles()) {
+ try {
+ int preKeyId = Integer.parseInt(preKeyRecord.getName());
+ PreKeyRecord record = loadPreKey(preKeyId);
+
+ storePreKey(preKeyId, record);
+ } catch (InvalidKeyIdException | NumberFormatException e) {
+ Log.w(TAG, e);
+ }
+ }
+
+ File signedPreKeyRecords = getSignedPreKeyDirectory();
+
+ for (File signedPreKeyRecord : signedPreKeyRecords.listFiles()) {
+ try {
+ int signedPreKeyId = Integer.parseInt(signedPreKeyRecord.getName());
+ SignedPreKeyRecord record = loadSignedPreKey(signedPreKeyId);
+
+ storeSignedPreKey(signedPreKeyId, record);
+ } catch (InvalidKeyIdException | NumberFormatException e) {
+ Log.w(TAG, e);
+ }
+ }
+ }
+ }
+
private byte[] loadSerializedRecord(File recordFile)
throws IOException, InvalidMessageException
{
- MasterCipher masterCipher = new MasterCipher(masterSecret);
FileInputStream fin = new FileInputStream(recordFile);
int recordVersion = readInteger(fin);
- if (recordVersion != CURRENT_VERSION_MARKER) {
+ if (recordVersion > CURRENT_VERSION_MARKER) {
throw new AssertionError("Invalid version: " + recordVersion);
}
- return masterCipher.decryptBytes(readBlob(fin));
+ byte[] serializedRecord = readBlob(fin);
+
+ if (recordVersion < PLAINTEXT_VERSION && masterSecret != null) {
+ MasterCipher masterCipher = new MasterCipher(masterSecret);
+ serializedRecord = masterCipher.decryptBytes(serializedRecord);
+ } else if (recordVersion < PLAINTEXT_VERSION) {
+ throw new AssertionError("Migration didn't happen!");
+ }
+
+ return serializedRecord;
}
private void storeSerializedRecord(File file, byte[] serialized) throws IOException {
- MasterCipher masterCipher = new MasterCipher(masterSecret);
- RandomAccessFile recordFile = new RandomAccessFile(file, "rw");
- FileChannel out = recordFile.getChannel();
+ RandomAccessFile recordFile = new RandomAccessFile(file, "rw");
+ FileChannel out = recordFile.getChannel();
out.position(0);
writeInteger(CURRENT_VERSION_MARKER, out);
- writeBlob(masterCipher.encryptBytes(serialized), out);
+ writeBlob(serialized, out);
out.truncate(out.position());
recordFile.close();
}
diff --git a/src/org/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore.java b/src/org/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore.java
index 30e62e5310..05b36c65d0 100644
--- a/src/org/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore.java
+++ b/src/org/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore.java
@@ -1,6 +1,8 @@
package org.thoughtcrime.securesms.crypto.storage;
import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.util.Log;
import org.thoughtcrime.securesms.crypto.MasterCipher;
@@ -34,37 +36,46 @@ public class TextSecureSessionStore implements SessionStore {
private static final int SINGLE_STATE_VERSION = 1;
private static final int ARCHIVE_STATES_VERSION = 2;
- private static final int CURRENT_VERSION = 2;
+ private static final int PLAINTEXT_VERSION = 3;
+ private static final int CURRENT_VERSION = 3;
- private final Context context;
- private final MasterSecret masterSecret;
+ @NonNull private final Context context;
+ @Nullable private final MasterSecret masterSecret;
- public TextSecureSessionStore(Context context, MasterSecret masterSecret) {
+ public TextSecureSessionStore(@NonNull Context context) {
+ this(context, null);
+ }
+
+ public TextSecureSessionStore(@NonNull Context context, @Nullable MasterSecret masterSecret) {
this.context = context.getApplicationContext();
this.masterSecret = masterSecret;
}
@Override
- public SessionRecord loadSession(AxolotlAddress address) {
+ public SessionRecord loadSession(@NonNull AxolotlAddress address) {
synchronized (FILE_LOCK) {
try {
- MasterCipher cipher = new MasterCipher(masterSecret);
- FileInputStream in = new FileInputStream(getSessionFile(address));
-
- int versionMarker = readInteger(in);
+ FileInputStream in = new FileInputStream(getSessionFile(address));
+ int versionMarker = readInteger(in);
if (versionMarker > CURRENT_VERSION) {
throw new AssertionError("Unknown version: " + versionMarker);
}
- byte[] serialized = cipher.decryptBytes(readBlob(in));
+ byte[] serialized = readBlob(in);
in.close();
+ if (versionMarker < PLAINTEXT_VERSION && masterSecret != null) {
+ serialized = new MasterCipher(masterSecret).decryptBytes(serialized);
+ } else if (versionMarker < PLAINTEXT_VERSION) {
+ throw new AssertionError("Session didn't get migrated: (" + versionMarker + "," + address + ")");
+ }
+
if (versionMarker == SINGLE_STATE_VERSION) {
SessionStructure sessionStructure = SessionStructure.parseFrom(serialized);
SessionState sessionState = new SessionState(sessionStructure);
return new SessionRecord(sessionState);
- } else if (versionMarker == ARCHIVE_STATES_VERSION) {
+ } else if (versionMarker >= ARCHIVE_STATES_VERSION) {
return new SessionRecord(serialized);
} else {
throw new AssertionError("Unknown version: " + versionMarker);
@@ -77,16 +88,15 @@ public class TextSecureSessionStore implements SessionStore {
}
@Override
- public void storeSession(AxolotlAddress address, SessionRecord record) {
+ public void storeSession(@NonNull AxolotlAddress address, @NonNull SessionRecord record) {
synchronized (FILE_LOCK) {
try {
- MasterCipher masterCipher = new MasterCipher(masterSecret);
RandomAccessFile sessionFile = new RandomAccessFile(getSessionFile(address), "rw");
FileChannel out = sessionFile.getChannel();
out.position(0);
writeInteger(CURRENT_VERSION, out);
- writeBlob(masterCipher.encryptBytes(record.serialize()), out);
+ writeBlob(record.serialize(), out);
out.truncate(out.position());
sessionFile.close();
@@ -143,6 +153,23 @@ public class TextSecureSessionStore implements SessionStore {
return results;
}
+ public void migrateSessions() {
+ synchronized (FILE_LOCK) {
+ File directory = getSessionDirectory();
+
+ for (File session : directory.listFiles()) {
+ if (session.isFile()) {
+ AxolotlAddress address = getAddressName(session);
+
+ if (address != null) {
+ SessionRecord sessionRecord = loadSession(address);
+ storeSession(address, sessionRecord);
+ }
+ }
+ }
+ }
+ }
+
private File getSessionFile(AxolotlAddress address) {
return new File(getSessionDirectory(), getSessionName(address));
}
@@ -168,6 +195,23 @@ public class TextSecureSessionStore implements SessionStore {
return recipientId + (deviceId == TextSecureAddress.DEFAULT_DEVICE_ID ? "" : "." + deviceId);
}
+ private @Nullable AxolotlAddress getAddressName(File sessionFile) {
+ try {
+ String[] parts = sessionFile.getName().split("[.]");
+ Recipient recipient = RecipientFactory.getRecipientForId(context, Integer.valueOf(parts[0]), true);
+
+ int deviceId;
+
+ if (parts.length > 1) deviceId = Integer.parseInt(parts[1]);
+ else deviceId = TextSecureAddress.DEFAULT_DEVICE_ID;
+
+ return new AxolotlAddress(recipient.getNumber(), deviceId);
+ } catch (NumberFormatException e) {
+ Log.w(TAG, e);
+ return null;
+ }
+ }
+
private byte[] readBlob(FileInputStream in) throws IOException {
int length = readInteger(in);
byte[] blobBytes = new byte[length];
diff --git a/src/org/thoughtcrime/securesms/database/EncryptingSmsDatabase.java b/src/org/thoughtcrime/securesms/database/EncryptingSmsDatabase.java
index c8878edf79..0d8acec5c2 100644
--- a/src/org/thoughtcrime/securesms/database/EncryptingSmsDatabase.java
+++ b/src/org/thoughtcrime/securesms/database/EncryptingSmsDatabase.java
@@ -19,12 +19,16 @@ package org.thoughtcrime.securesms.database;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteOpenHelper;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.util.Log;
import android.util.Pair;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.crypto.AsymmetricMasterCipher;
import org.thoughtcrime.securesms.crypto.AsymmetricMasterSecret;
+import org.thoughtcrime.securesms.crypto.MasterSecretUnion;
+import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.database.model.DisplayRecord;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
@@ -60,57 +64,80 @@ public class EncryptingSmsDatabase extends SmsDatabase {
return ciphertext;
}
- public long insertMessageOutbox(MasterSecret masterSecret, long threadId,
+ public long insertMessageOutbox(MasterSecretUnion masterSecret, long threadId,
OutgoingTextMessage message, boolean forceSms,
long timestamp)
{
long type = Types.BASE_OUTBOX_TYPE;
- message = message.withBody(getEncryptedBody(masterSecret, message.getMessageBody()));
- type |= Types.ENCRYPTION_SYMMETRIC_BIT;
+
+ if (masterSecret.getMasterSecret().isPresent()) {
+ message = message.withBody(getEncryptedBody(masterSecret.getMasterSecret().get(), message.getMessageBody()));
+ type |= Types.ENCRYPTION_SYMMETRIC_BIT;
+ } else {
+ message = message.withBody(getAsymmetricEncryptedBody(masterSecret.getAsymmetricMasterSecret().get(), message.getMessageBody()));
+ type |= Types.ENCRYPTION_ASYMMETRIC_BIT;
+ }
return insertMessageOutbox(threadId, message, type, forceSms, timestamp);
}
- public Pair insertMessageInbox(MasterSecret masterSecret,
- IncomingTextMessage message)
+ public Pair insertMessageInbox(@NonNull MasterSecretUnion masterSecret,
+ @NonNull IncomingTextMessage message)
{
- long type = Types.BASE_INBOX_TYPE;
-
- if (masterSecret == null && message.isSecureMessage()) {
- type |= Types.ENCRYPTION_REMOTE_BIT;
+ if (masterSecret.getMasterSecret().isPresent()) {
+ return insertMessageInbox(masterSecret.getMasterSecret().get(), message);
} else {
- type |= Types.ENCRYPTION_SYMMETRIC_BIT;
- message = message.withMessageBody(getEncryptedBody(masterSecret, message.getMessageBody()));
+ return insertMessageInbox(masterSecret.getAsymmetricMasterSecret().get(), message);
}
+ }
+
+ private Pair insertMessageInbox(@NonNull MasterSecret masterSecret,
+ @NonNull IncomingTextMessage message)
+ {
+ long type = Types.BASE_INBOX_TYPE | Types.ENCRYPTION_SYMMETRIC_BIT;
+
+ message = message.withMessageBody(getEncryptedBody(masterSecret, message.getMessageBody()));
return insertMessageInbox(message, type);
}
- public Pair insertMessageInbox(AsymmetricMasterSecret masterSecret,
- IncomingTextMessage message)
+ private Pair insertMessageInbox(@NonNull AsymmetricMasterSecret masterSecret,
+ @NonNull IncomingTextMessage message)
{
- long type = Types.BASE_INBOX_TYPE;
+ long type = Types.BASE_INBOX_TYPE | Types.ENCRYPTION_ASYMMETRIC_BIT;
- if (message.isSecureMessage()) {
- type |= Types.ENCRYPTION_REMOTE_BIT;
- } else {
- message = message.withMessageBody(getAsymmetricEncryptedBody(masterSecret, message.getMessageBody()));
- type |= Types.ENCRYPTION_ASYMMETRIC_BIT;
- }
+ message = message.withMessageBody(getAsymmetricEncryptedBody(masterSecret, message.getMessageBody()));
return insertMessageInbox(message, type);
}
- public Pair updateBundleMessageBody(MasterSecret masterSecret, long messageId, String body) {
- String encryptedBody = getEncryptedBody(masterSecret, body);
- return updateMessageBodyAndType(messageId, encryptedBody, Types.TOTAL_MASK,
- Types.BASE_INBOX_TYPE | Types.ENCRYPTION_SYMMETRIC_BIT | Types.SECURE_MESSAGE_BIT);
+ public Pair updateBundleMessageBody(MasterSecretUnion masterSecret, long messageId, String body) {
+ long type = Types.BASE_INBOX_TYPE | Types.SECURE_MESSAGE_BIT;
+ String encryptedBody;
+
+ if (masterSecret.getMasterSecret().isPresent()) {
+ encryptedBody = getEncryptedBody(masterSecret.getMasterSecret().get(), body);
+ type |= Types.ENCRYPTION_SYMMETRIC_BIT;
+ } else {
+ encryptedBody = getAsymmetricEncryptedBody(masterSecret.getAsymmetricMasterSecret().get(), body);
+ type |= Types.ENCRYPTION_ASYMMETRIC_BIT;
+ }
+
+ return updateMessageBodyAndType(messageId, encryptedBody, Types.TOTAL_MASK, type);
}
- public void updateMessageBody(MasterSecret masterSecret, long messageId, String body) {
- String encryptedBody = getEncryptedBody(masterSecret, body);
- updateMessageBodyAndType(messageId, encryptedBody, Types.ENCRYPTION_MASK,
- Types.ENCRYPTION_SYMMETRIC_BIT);
+ public void updateMessageBody(MasterSecretUnion masterSecret, long messageId, String body) {
+ long type;
+
+ if (masterSecret.getMasterSecret().isPresent()) {
+ body = getEncryptedBody(masterSecret.getMasterSecret().get(), body);
+ type = Types.ENCRYPTION_SYMMETRIC_BIT;
+ } else {
+ body = getAsymmetricEncryptedBody(masterSecret.getAsymmetricMasterSecret().get(), body);
+ type = Types.ENCRYPTION_ASYMMETRIC_BIT;
+ }
+
+ updateMessageBodyAndType(messageId, body, Types.ENCRYPTION_MASK, type);
}
public Reader getMessages(MasterSecret masterSecret, int skip, int limit) {
diff --git a/src/org/thoughtcrime/securesms/database/IdentityDatabase.java b/src/org/thoughtcrime/securesms/database/IdentityDatabase.java
index ea039a9c87..111cf5bb01 100644
--- a/src/org/thoughtcrime/securesms/database/IdentityDatabase.java
+++ b/src/org/thoughtcrime/securesms/database/IdentityDatabase.java
@@ -29,8 +29,6 @@ import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.Base64;
import org.whispersystems.libaxolotl.IdentityKey;
import org.whispersystems.libaxolotl.InvalidKeyException;
-import org.thoughtcrime.securesms.crypto.MasterCipher;
-import org.thoughtcrime.securesms.crypto.MasterSecret;
import java.io.IOException;
@@ -42,13 +40,11 @@ public class IdentityDatabase extends Database {
private static final String ID = "_id";
public static final String RECIPIENT = "recipient";
public static final String IDENTITY_KEY = "key";
- public static final String MAC = "mac";
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME +
" (" + ID + " INTEGER PRIMARY KEY, " +
RECIPIENT + " INTEGER UNIQUE, " +
- IDENTITY_KEY + " TEXT, " +
- MAC + " TEXT);";
+ IDENTITY_KEY + " TEXT);";
public IdentityDatabase(Context context, SQLiteOpenHelper databaseHelper) {
super(context, databaseHelper);
@@ -64,28 +60,19 @@ public class IdentityDatabase extends Database {
return cursor;
}
- public boolean isValidIdentity(MasterSecret masterSecret,
- long recipientId,
+ public boolean isValidIdentity(long recipientId,
IdentityKey theirIdentity)
{
- SQLiteDatabase database = databaseHelper.getReadableDatabase();
- MasterCipher masterCipher = new MasterCipher(masterSecret);
- Cursor cursor = null;
+ SQLiteDatabase database = databaseHelper.getReadableDatabase();
+ Cursor cursor = null;
try {
cursor = database.query(TABLE_NAME, null, RECIPIENT + " = ?",
new String[] {recipientId+""}, null, null,null);
if (cursor != null && cursor.moveToFirst()) {
- String serializedIdentity = cursor.getString(cursor.getColumnIndexOrThrow(IDENTITY_KEY));
- String mac = cursor.getString(cursor.getColumnIndexOrThrow(MAC));
-
- if (!masterCipher.verifyMacFor(recipientId + serializedIdentity, Base64.decode(mac))) {
- Log.w("IdentityDatabase", "MAC failed");
- return false;
- }
-
- IdentityKey ourIdentity = new IdentityKey(Base64.decode(serializedIdentity), 0);
+ String serializedIdentity = cursor.getString(cursor.getColumnIndexOrThrow(IDENTITY_KEY));
+ IdentityKey ourIdentity = new IdentityKey(Base64.decode(serializedIdentity), 0);
return ourIdentity.equals(theirIdentity);
} else {
@@ -104,18 +91,14 @@ public class IdentityDatabase extends Database {
}
}
- public void saveIdentity(MasterSecret masterSecret, long recipientId, IdentityKey identityKey)
+ public void saveIdentity(long recipientId, IdentityKey identityKey)
{
- SQLiteDatabase database = databaseHelper.getWritableDatabase();
- MasterCipher masterCipher = new MasterCipher(masterSecret);
- String identityKeyString = Base64.encodeBytes(identityKey.serialize());
- String macString = Base64.encodeBytes(masterCipher.getMacFor(recipientId +
- identityKeyString));
+ SQLiteDatabase database = databaseHelper.getWritableDatabase();
+ String identityKeyString = Base64.encodeBytes(identityKey.serialize());
ContentValues contentValues = new ContentValues();
contentValues.put(RECIPIENT, recipientId);
contentValues.put(IDENTITY_KEY, identityKeyString);
- contentValues.put(MAC, macString);
database.replace(TABLE_NAME, null, contentValues);
@@ -129,17 +112,15 @@ public class IdentityDatabase extends Database {
context.getContentResolver().notifyChange(CHANGE_URI, null);
}
- public Reader readerFor(MasterSecret masterSecret, Cursor cursor) {
- return new Reader(masterSecret, cursor);
+ public Reader readerFor(Cursor cursor) {
+ return new Reader(cursor);
}
public class Reader {
private final Cursor cursor;
- private final MasterCipher cipher;
- public Reader(MasterSecret masterSecret, Cursor cursor) {
+ public Reader(Cursor cursor) {
this.cursor = cursor;
- this.cipher = new MasterCipher(masterSecret);
}
public Identity getCurrent() {
@@ -147,14 +128,9 @@ public class IdentityDatabase extends Database {
Recipients recipients = RecipientFactory.getRecipientsForIds(context, new long[]{recipientId}, true);
try {
- String identityKeyString = cursor.getString(cursor.getColumnIndexOrThrow(IDENTITY_KEY));
- String mac = cursor.getString(cursor.getColumnIndexOrThrow(MAC));
+ String identityKeyString = cursor.getString(cursor.getColumnIndexOrThrow(IDENTITY_KEY));
+ IdentityKey identityKey = new IdentityKey(Base64.decode(identityKeyString), 0);
- if (!cipher.verifyMacFor(recipientId + identityKeyString, Base64.decode(mac))) {
- return new Identity(recipients, null);
- }
-
- IdentityKey identityKey = new IdentityKey(Base64.decode(identityKeyString), 0);
return new Identity(recipients, identityKey);
} catch (IOException e) {
Log.w("IdentityDatabase", e);
diff --git a/src/org/thoughtcrime/securesms/database/MmsDatabase.java b/src/org/thoughtcrime/securesms/database/MmsDatabase.java
index 8ec2419a63..6c434bf1d8 100644
--- a/src/org/thoughtcrime/securesms/database/MmsDatabase.java
+++ b/src/org/thoughtcrime/securesms/database/MmsDatabase.java
@@ -32,8 +32,10 @@ import com.google.i18n.phonenumbers.PhoneNumberUtil;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.R;
+import org.thoughtcrime.securesms.crypto.AsymmetricMasterCipher;
import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.crypto.MasterSecret;
+import org.thoughtcrime.securesms.crypto.MasterSecretUnion;
import org.thoughtcrime.securesms.database.documents.NetworkFailure;
import org.thoughtcrime.securesms.database.documents.NetworkFailureList;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
@@ -357,12 +359,11 @@ public class MmsDatabase extends MessagingDatabase {
return cursor;
}
- public void updateResponseStatus(long messageId, int status) {
- SQLiteDatabase database = databaseHelper.getWritableDatabase();
- ContentValues contentValues = new ContentValues();
- contentValues.put(RESPONSE_STATUS, status);
+ public Reader getDecryptInProgressMessages(MasterSecret masterSecret) {
+ SQLiteDatabase db = databaseHelper.getReadableDatabase();
+ String where = MESSAGE_BOX + " & " + (Types.ENCRYPTION_ASYMMETRIC_BIT) + " != 0";
- database.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {messageId + ""});
+ return readerFor(masterSecret, db.query(TABLE_NAME, MMS_PROJECTION, where, null, null, null, null));
}
private void updateMailboxBitmask(long id, long maskOff, long maskOn) {
@@ -417,15 +418,6 @@ public class MmsDatabase extends MessagingDatabase {
notifyConversationListeners(getThreadIdForMessage(messageId));
}
- public void markDeliveryStatus(long messageId, int status) {
- SQLiteDatabase database = databaseHelper.getWritableDatabase();
- ContentValues contentValues = new ContentValues();
- contentValues.put(STATUS, status);
-
- database.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {messageId + ""});
- notifyConversationListeners(getThreadIdForMessage(messageId));
- }
-
public void markAsNoSession(long messageId, long threadId) {
updateMailboxBitmask(messageId, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_NO_SESSION_BIT);
notifyConversationListeners(threadId);
@@ -474,6 +466,36 @@ public class MmsDatabase extends MessagingDatabase {
database.update(TABLE_NAME, contentValues, null, null);
}
+ public void updateMessageBody(MasterSecretUnion masterSecret, long messageId, String body) {
+ body = getEncryptedBody(masterSecret, body);
+
+ long type;
+
+ if (masterSecret.getMasterSecret().isPresent()) {
+ type = Types.ENCRYPTION_SYMMETRIC_BIT;
+ } else {
+ type = Types.ENCRYPTION_ASYMMETRIC_BIT;
+ }
+
+ updateMessageBodyAndType(messageId, body, Types.ENCRYPTION_MASK, type);
+ }
+
+ private Pair updateMessageBodyAndType(long messageId, String body, long maskOff, long maskOn) {
+ SQLiteDatabase db = databaseHelper.getWritableDatabase();
+ db.execSQL("UPDATE " + TABLE_NAME + " SET " + BODY + " = ?, " +
+ MESSAGE_BOX + " = (" + MESSAGE_BOX + " & " + (Types.TOTAL_MASK - maskOff) + " | " + maskOn + ") " +
+ "WHERE " + ID + " = ?",
+ new String[] {body, messageId + ""});
+
+ long threadId = getThreadIdForMessage(messageId);
+
+ DatabaseFactory.getThreadDatabase(context).update(threadId);
+ notifyConversationListeners(threadId);
+ notifyConversationListListeners();
+
+ return new Pair<>(messageId, threadId);
+ }
+
public Optional getNotification(long messageId) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
MmsAddressDatabase addressDatabase = DatabaseFactory.getMmsAddressDatabase(context);
@@ -544,15 +566,6 @@ public class MmsDatabase extends MessagingDatabase {
}
}
- public Reader getNotificationsWithDownloadState(MasterSecret masterSecret, long state) {
- SQLiteDatabase database = databaseHelper.getReadableDatabase();
- String selection = STATUS + " = ?";
- String[] selectionArgs = new String[]{state + ""};
-
- Cursor cursor = database.query(TABLE_NAME, MMS_PROJECTION, selection, selectionArgs, null, null, null);
- return new Reader(masterSecret, cursor);
- }
-
public long copyMessageInbox(MasterSecret masterSecret, long messageId) throws MmsException {
try {
SendReq request = getOutgoingMessage(masterSecret, messageId);
@@ -563,15 +576,17 @@ public class MmsDatabase extends MessagingDatabase {
contentValues.put(READ, 1);
contentValues.put(DATE_RECEIVED, contentValues.getAsLong(DATE_SENT));
- return insertMediaMessage(masterSecret, request.getPduHeaders(),
+ return insertMediaMessage(new MasterSecretUnion(masterSecret), request.getPduHeaders(),
request.getBody(), contentValues);
} catch (NoSuchMessageException e) {
throw new MmsException(e);
}
}
- private Pair insertMessageInbox(MasterSecret masterSecret, IncomingMediaMessage retrieved,
- String contentLocation, long threadId, long mailbox)
+ private Pair insertMessageInbox(MasterSecretUnion masterSecret,
+ IncomingMediaMessage retrieved,
+ String contentLocation,
+ long threadId, long mailbox)
throws MmsException
{
PduHeaders headers = retrieved.getPduHeaders();
@@ -614,35 +629,44 @@ public class MmsDatabase extends MessagingDatabase {
return new Pair<>(messageId, threadId);
}
- public Pair insertMessageInbox(MasterSecret masterSecret,
+ public Pair insertMessageInbox(MasterSecretUnion masterSecret,
IncomingMediaMessage retrieved,
String contentLocation, long threadId)
throws MmsException
{
- return insertMessageInbox(masterSecret, retrieved, contentLocation, threadId,
- Types.BASE_INBOX_TYPE | Types.ENCRYPTION_SYMMETRIC_BIT |
- (retrieved.isPushMessage() ? Types.PUSH_MESSAGE_BIT : 0));
+ long type = Types.BASE_INBOX_TYPE;
+
+ if (masterSecret.getMasterSecret().isPresent()) {
+ type |= Types.ENCRYPTION_SYMMETRIC_BIT;
+ } else {
+ type |= Types.ENCRYPTION_ASYMMETRIC_BIT;
+ }
+
+ if (retrieved.isPushMessage()) {
+ type |= Types.PUSH_MESSAGE_BIT;
+ }
+
+ return insertMessageInbox(masterSecret, retrieved, contentLocation, threadId, type);
}
- public Pair insertSecureMessageInbox(MasterSecret masterSecret,
- IncomingMediaMessage retrieved,
- String contentLocation, long threadId)
- throws MmsException
- {
- return insertMessageInbox(masterSecret, retrieved, contentLocation, threadId,
- Types.BASE_INBOX_TYPE | Types.SECURE_MESSAGE_BIT |
- Types.ENCRYPTION_REMOTE_BIT);
- }
-
- public Pair insertSecureDecryptedMessageInbox(MasterSecret masterSecret,
+ public Pair insertSecureDecryptedMessageInbox(MasterSecretUnion masterSecret,
IncomingMediaMessage retrieved,
long threadId)
throws MmsException
{
- return insertMessageInbox(masterSecret, retrieved, "", threadId,
- Types.BASE_INBOX_TYPE | Types.SECURE_MESSAGE_BIT |
- Types.ENCRYPTION_SYMMETRIC_BIT |
- (retrieved.isPushMessage() ? Types.PUSH_MESSAGE_BIT : 0));
+ long type = Types.BASE_INBOX_TYPE | Types.SECURE_MESSAGE_BIT;
+
+ if (masterSecret.getMasterSecret().isPresent()) {
+ type |= Types.ENCRYPTION_SYMMETRIC_BIT;
+ } else {
+ type |= Types.ENCRYPTION_ASYMMETRIC_BIT;
+ }
+
+ if (retrieved.isPushMessage()) {
+ type |= Types.PUSH_MESSAGE_BIT;
+ }
+
+ return insertMessageInbox(masterSecret, retrieved, "", threadId, type);
}
public Pair insertMessageInbox(@NonNull NotificationInd notification) {
@@ -680,11 +704,15 @@ public class MmsDatabase extends MessagingDatabase {
jobManager.add(new TrimThreadJob(context, threadId));
}
- public long insertMessageOutbox(MasterSecret masterSecret, OutgoingMediaMessage message,
+ public long insertMessageOutbox(@NonNull MasterSecretUnion masterSecret,
+ @NonNull OutgoingMediaMessage message,
long threadId, boolean forceSms, long timestamp)
throws MmsException
{
- long type = Types.BASE_OUTBOX_TYPE | Types.ENCRYPTION_SYMMETRIC_BIT;
+ long type = Types.BASE_OUTBOX_TYPE;
+
+ if (masterSecret.getMasterSecret().isPresent()) type |= Types.ENCRYPTION_SYMMETRIC_BIT;
+ else type |= Types.ENCRYPTION_ASYMMETRIC_BIT;
if (message.isSecure()) type |= Types.SECURE_MESSAGE_BIT;
if (forceSms) type |= Types.MESSAGE_FORCE_SMS_BIT;
@@ -727,14 +755,23 @@ public class MmsDatabase extends MessagingDatabase {
}
}
- long messageId = insertMediaMessage(masterSecret, sendRequest.getPduHeaders(),
+ long messageId = insertMediaMessage(masterSecret,
+ sendRequest.getPduHeaders(),
sendRequest.getBody(), contentValues);
jobManager.add(new TrimThreadJob(context, threadId));
return messageId;
}
- private long insertMediaMessage(MasterSecret masterSecret,
+ private String getEncryptedBody(MasterSecretUnion masterSecret, String body) {
+ if (masterSecret.getMasterSecret().isPresent()) {
+ return new MasterCipher(masterSecret.getMasterSecret().get()).encryptBody(body);
+ } else {
+ return new AsymmetricMasterCipher(masterSecret.getAsymmetricMasterSecret().get()).encryptBody(body);
+ }
+ }
+
+ private long insertMediaMessage(MasterSecretUnion masterSecret,
PduHeaders headers,
PduBody body,
ContentValues contentValues)
@@ -744,12 +781,14 @@ public class MmsDatabase extends MessagingDatabase {
PartDatabase partsDatabase = DatabaseFactory.getPartDatabase(context);
MmsAddressDatabase addressDatabase = DatabaseFactory.getMmsAddressDatabase(context);
- if (Types.isSymmetricEncryption(contentValues.getAsLong(MESSAGE_BOX))) {
+ if (Types.isSymmetricEncryption(contentValues.getAsLong(MESSAGE_BOX)) ||
+ Types.isAsymmetricEncryption(contentValues.getAsLong(MESSAGE_BOX)))
+ {
String messageText = PartParser.getMessageText(body);
body = PartParser.getSupportedMediaParts(body);
if (!TextUtils.isEmpty(messageText)) {
- contentValues.put(BODY, new MasterCipher(masterSecret).encryptBody(messageText));
+ contentValues.put(BODY, getEncryptedBody(masterSecret, messageText));
}
}
@@ -1105,6 +1144,8 @@ public class MmsDatabase extends MessagingDatabase {
return new DisplayRecord.Body(masterCipher.decryptBody(body), true);
} else if (!TextUtils.isEmpty(body) && masterCipher == null && Types.isSymmetricEncryption(box)) {
return new DisplayRecord.Body(body, false);
+ } else if (!TextUtils.isEmpty(body) && Types.isAsymmetricEncryption(box)) {
+ return new DisplayRecord.Body(body, false);
} else {
return new DisplayRecord.Body(body == null ? "" : body, true);
}
diff --git a/src/org/thoughtcrime/securesms/database/MmsSmsColumns.java b/src/org/thoughtcrime/securesms/database/MmsSmsColumns.java
index 882b05b7ce..9eaceb0bac 100644
--- a/src/org/thoughtcrime/securesms/database/MmsSmsColumns.java
+++ b/src/org/thoughtcrime/securesms/database/MmsSmsColumns.java
@@ -175,9 +175,7 @@ public interface MmsSmsColumns {
}
public static boolean isDecryptInProgressType(long type) {
- return
- (type & ENCRYPTION_REMOTE_BIT) != 0 ||
- (type & ENCRYPTION_ASYMMETRIC_BIT) != 0;
+ return (type & ENCRYPTION_ASYMMETRIC_BIT) != 0;
}
public static boolean isNoRemoteSessionType(long type) {
@@ -185,7 +183,8 @@ public interface MmsSmsColumns {
}
public static boolean isLegacyType(long type) {
- return (type & ENCRYPTION_REMOTE_LEGACY_BIT) != 0;
+ return (type & ENCRYPTION_REMOTE_LEGACY_BIT) != 0 ||
+ (type & ENCRYPTION_REMOTE_BIT) != 0;
}
public static long translateFromSystemBaseType(long theirType) {
diff --git a/src/org/thoughtcrime/securesms/database/PartDatabase.java b/src/org/thoughtcrime/securesms/database/PartDatabase.java
index 5207ed8eb2..f87fb6cec0 100644
--- a/src/org/thoughtcrime/securesms/database/PartDatabase.java
+++ b/src/org/thoughtcrime/securesms/database/PartDatabase.java
@@ -30,6 +30,7 @@ import android.util.Pair;
import org.thoughtcrime.securesms.crypto.DecryptingPartInputStream;
import org.thoughtcrime.securesms.crypto.EncryptingPartOutputStream;
import org.thoughtcrime.securesms.crypto.MasterSecret;
+import org.thoughtcrime.securesms.crypto.MasterSecretUnion;
import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.util.BitmapDecodingException;
import org.thoughtcrime.securesms.util.MediaUtil;
@@ -222,7 +223,7 @@ public class PartDatabase extends Database {
}
}
- void insertParts(MasterSecret masterSecret, long mmsId, PduBody body) throws MmsException {
+ void insertParts(MasterSecretUnion masterSecret, long mmsId, PduBody body) throws MmsException {
for (int i=0;i partData = null;
- if (part.getData() != null || part.getDataUri() != null) {
- partData = writePartData(masterSecret, part);
+ if ((part.getData() != null || part.getDataUri() != null) && masterSecret.getMasterSecret().isPresent()) {
+ partData = writePartData(masterSecret.getMasterSecret().get(), part);
Log.w(TAG, "Wrote part to file: " + partData.first.getAbsolutePath());
}
@@ -453,12 +454,12 @@ public class PartDatabase extends Database {
long partRowId = database.insert(TABLE_NAME, null, contentValues);
PartId partId = new PartId(partRowId, part.getUniqueId());
- if (thumbnail != null) {
+ if (thumbnail != null && masterSecret.getMasterSecret().isPresent()) {
Log.w(TAG, "inserting pre-generated thumbnail");
ThumbnailData data = new ThumbnailData(thumbnail);
- updatePartThumbnail(masterSecret, partId, part, data.toDataStream(), data.getAspectRatio());
+ updatePartThumbnail(masterSecret.getMasterSecret().get(), partId, part, data.toDataStream(), data.getAspectRatio());
} else if (!part.isInProgress()) {
- thumbnailExecutor.submit(new ThumbnailFetchCallable(masterSecret, partId));
+ thumbnailExecutor.submit(new ThumbnailFetchCallable(masterSecret.getMasterSecret().get(), partId));
}
return partId;
diff --git a/src/org/thoughtcrime/securesms/database/SmsDatabase.java b/src/org/thoughtcrime/securesms/database/SmsDatabase.java
index eef506f11b..716b38cd88 100644
--- a/src/org/thoughtcrime/securesms/database/SmsDatabase.java
+++ b/src/org/thoughtcrime/securesms/database/SmsDatabase.java
@@ -213,10 +213,6 @@ public class SmsDatabase extends MessagingDatabase {
updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_NO_SESSION_BIT);
}
- public void markAsDecrypting(long id) {
- updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_BIT);
- }
-
public void markAsLegacyVersion(long id) {
updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_LEGACY_BIT);
}
@@ -461,7 +457,7 @@ public class SmsDatabase extends MessagingDatabase {
}
public Cursor getDecryptInProgressMessages() {
- String where = TYPE + " & " + (Types.ENCRYPTION_REMOTE_BIT | Types.ENCRYPTION_ASYMMETRIC_BIT) + " != 0";
+ String where = TYPE + " & " + (Types.ENCRYPTION_ASYMMETRIC_BIT) + " != 0";
SQLiteDatabase db = databaseHelper.getReadableDatabase();
return db.query(TABLE_NAME, MESSAGE_PROJECTION, where, null, null, null, null);
}
diff --git a/src/org/thoughtcrime/securesms/dependencies/AxolotlStorageModule.java b/src/org/thoughtcrime/securesms/dependencies/AxolotlStorageModule.java
index 63bc496f3b..5e5d51301a 100644
--- a/src/org/thoughtcrime/securesms/dependencies/AxolotlStorageModule.java
+++ b/src/org/thoughtcrime/securesms/dependencies/AxolotlStorageModule.java
@@ -22,13 +22,13 @@ public class AxolotlStorageModule {
@Provides SignedPreKeyStoreFactory provideSignedPreKeyStoreFactory() {
return new SignedPreKeyStoreFactory() {
@Override
- public SignedPreKeyStore create(MasterSecret masterSecret) {
- return new TextSecureAxolotlStore(context, masterSecret);
+ public SignedPreKeyStore create() {
+ return new TextSecureAxolotlStore(context);
}
};
}
public static interface SignedPreKeyStoreFactory {
- public SignedPreKeyStore create(MasterSecret masterSecret);
+ public SignedPreKeyStore create();
}
}
diff --git a/src/org/thoughtcrime/securesms/dependencies/TextSecureCommunicationModule.java b/src/org/thoughtcrime/securesms/dependencies/TextSecureCommunicationModule.java
index f333229eec..7845aea745 100644
--- a/src/org/thoughtcrime/securesms/dependencies/TextSecureCommunicationModule.java
+++ b/src/org/thoughtcrime/securesms/dependencies/TextSecureCommunicationModule.java
@@ -4,7 +4,6 @@ import android.content.Context;
import org.thoughtcrime.securesms.BuildConfig;
import org.thoughtcrime.securesms.DeviceListActivity;
-import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.storage.TextSecureAxolotlStore;
import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob;
import org.thoughtcrime.securesms.jobs.CleanPreKeysJob;
@@ -61,12 +60,12 @@ public class TextSecureCommunicationModule {
@Provides TextSecureMessageSenderFactory provideTextSecureMessageSenderFactory() {
return new TextSecureMessageSenderFactory() {
@Override
- public TextSecureMessageSender create(MasterSecret masterSecret) {
+ public TextSecureMessageSender create() {
return new TextSecureMessageSender(BuildConfig.PUSH_URL,
new TextSecurePushTrustStore(context),
TextSecurePreferences.getLocalNumber(context),
TextSecurePreferences.getPushServerPassword(context),
- new TextSecureAxolotlStore(context, masterSecret),
+ new TextSecureAxolotlStore(context),
Optional.of((TextSecureMessageSender.EventListener)
new SecurityEventListener(context)));
}
@@ -80,7 +79,7 @@ public class TextSecureCommunicationModule {
}
public static interface TextSecureMessageSenderFactory {
- public TextSecureMessageSender create(MasterSecret masterSecret);
+ public TextSecureMessageSender create();
}
private static class DynamicCredentialsProvider implements CredentialsProvider {
diff --git a/src/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java b/src/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java
index 41a0d59d3c..f36191ed11 100644
--- a/src/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java
+++ b/src/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java
@@ -2,6 +2,8 @@ package org.thoughtcrime.securesms.groups;
import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.util.Log;
import android.util.Pair;
@@ -9,6 +11,8 @@ import com.google.protobuf.ByteString;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.crypto.MasterSecret;
+import org.thoughtcrime.securesms.crypto.MasterSecretUnion;
+import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
import org.thoughtcrime.securesms.database.GroupDatabase;
@@ -36,10 +40,10 @@ public class GroupMessageProcessor {
private static final String TAG = GroupMessageProcessor.class.getSimpleName();
- public static void process(Context context,
- MasterSecret masterSecret,
- TextSecureEnvelope envelope,
- TextSecureDataMessage message)
+ public static void process(@NonNull Context context,
+ @NonNull MasterSecretUnion masterSecret,
+ @NonNull TextSecureEnvelope envelope,
+ @NonNull TextSecureDataMessage message)
{
if (!message.getGroupInfo().isPresent() || message.getGroupInfo().get().getGroupId() == null) {
Log.w(TAG, "Received group message with no id! Ignoring...");
@@ -62,10 +66,10 @@ public class GroupMessageProcessor {
}
}
- private static void handleGroupCreate(Context context,
- MasterSecret masterSecret,
- TextSecureEnvelope envelope,
- TextSecureGroup group)
+ private static void handleGroupCreate(@NonNull Context context,
+ @NonNull MasterSecretUnion masterSecret,
+ @NonNull TextSecureEnvelope envelope,
+ @NonNull TextSecureGroup group)
{
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
byte[] id = group.getGroupId();
@@ -81,11 +85,11 @@ public class GroupMessageProcessor {
storeMessage(context, masterSecret, envelope, group, builder.build());
}
- private static void handleGroupUpdate(Context context,
- MasterSecret masterSecret,
- TextSecureEnvelope envelope,
- TextSecureGroup group,
- GroupRecord groupRecord)
+ private static void handleGroupUpdate(@NonNull Context context,
+ @NonNull MasterSecretUnion masterSecret,
+ @NonNull TextSecureEnvelope envelope,
+ @NonNull TextSecureGroup group,
+ @NonNull GroupRecord groupRecord)
{
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
@@ -131,11 +135,11 @@ public class GroupMessageProcessor {
storeMessage(context, masterSecret, envelope, group, builder.build());
}
- private static void handleGroupLeave(Context context,
- MasterSecret masterSecret,
- TextSecureEnvelope envelope,
- TextSecureGroup group,
- GroupRecord record)
+ private static void handleGroupLeave(@NonNull Context context,
+ @NonNull MasterSecretUnion masterSecret,
+ @NonNull TextSecureEnvelope envelope,
+ @NonNull TextSecureGroup group,
+ @NonNull GroupRecord record)
{
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
byte[] id = group.getGroupId();
@@ -152,9 +156,11 @@ public class GroupMessageProcessor {
}
- private static void storeMessage(Context context, MasterSecret masterSecret,
- TextSecureEnvelope envelope, TextSecureGroup group,
- GroupContext storage)
+ private static void storeMessage(@NonNull Context context,
+ @NonNull MasterSecretUnion masterSecret,
+ @NonNull TextSecureEnvelope envelope,
+ @NonNull TextSecureGroup group,
+ @NonNull GroupContext storage)
{
if (group.getAvatar().isPresent()) {
ApplicationContext.getInstance(context).getJobManager()
@@ -167,7 +173,7 @@ public class GroupMessageProcessor {
IncomingGroupMessage groupMessage = new IncomingGroupMessage(incoming, storage, body);
Pair messageAndThreadId = smsDatabase.insertMessageInbox(masterSecret, groupMessage);
- MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second);
+ MessageNotifier.updateNotification(context, masterSecret.getMasterSecret().orNull(), messageAndThreadId.second);
}
private static GroupContext.Builder createGroupContext(TextSecureGroup group) {
diff --git a/src/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java b/src/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java
index 85bce1f142..dc02db32b2 100644
--- a/src/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java
@@ -3,15 +3,15 @@ package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.util.Log;
-
-import org.thoughtcrime.securesms.crypto.MasterCipher;
+import org.thoughtcrime.securesms.crypto.AsymmetricMasterSecret;
import org.thoughtcrime.securesms.crypto.MasterSecret;
+import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
+import org.thoughtcrime.securesms.crypto.MediaKey;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.PartDatabase;
import org.thoughtcrime.securesms.database.PartDatabase.PartId;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
-import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.jobqueue.requirements.NetworkRequirement;
@@ -114,10 +114,10 @@ public class AttachmentDownloadJob extends MasterSecretJob implements Injectable
throws InvalidPartException
{
try {
- MasterCipher masterCipher = new MasterCipher(masterSecret);
- long id = Long.parseLong(Util.toIsoString(part.getContentLocation()));
- byte[] key = masterCipher.decryptBytes(Base64.decode(Util.toIsoString(part.getContentDisposition())));
- String relay = null;
+ AsymmetricMasterSecret asymmetricMasterSecret = MasterSecretUtil.getAsymmetricMasterSecret(context, masterSecret);
+ long id = Long.parseLong(Util.toIsoString(part.getContentLocation()));
+ byte[] key = MediaKey.getDecrypted(masterSecret, asymmetricMasterSecret, Util.toIsoString(part.getContentDisposition()));
+ String relay = null;
if (part.getName() != null) {
relay = Util.toIsoString(part.getName());
diff --git a/src/org/thoughtcrime/securesms/jobs/CleanPreKeysJob.java b/src/org/thoughtcrime/securesms/jobs/CleanPreKeysJob.java
index d3229b6ed1..a00e1a7117 100644
--- a/src/org/thoughtcrime/securesms/jobs/CleanPreKeysJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/CleanPreKeysJob.java
@@ -51,7 +51,7 @@ public class CleanPreKeysJob extends MasterSecretJob implements InjectableType {
@Override
public void onRun(MasterSecret masterSecret) throws IOException {
try {
- SignedPreKeyStore signedPreKeyStore = signedPreKeyStoreFactory.create(masterSecret);
+ SignedPreKeyStore signedPreKeyStore = signedPreKeyStoreFactory.create();
SignedPreKeyEntity currentSignedPreKey = accountManager.getSignedPreKey();
if (currentSignedPreKey == null) return;
diff --git a/src/org/thoughtcrime/securesms/jobs/CreateSignedPreKeyJob.java b/src/org/thoughtcrime/securesms/jobs/CreateSignedPreKeyJob.java
index c596ded6c9..b0828ef9fe 100644
--- a/src/org/thoughtcrime/securesms/jobs/CreateSignedPreKeyJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/CreateSignedPreKeyJob.java
@@ -7,9 +7,8 @@ import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.PreKeyUtil;
import org.thoughtcrime.securesms.dependencies.InjectableType;
-import org.thoughtcrime.securesms.util.ParcelUtil;
+import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
-import org.whispersystems.jobqueue.EncryptionKeys;
import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.jobqueue.requirements.NetworkRequirement;
import org.whispersystems.libaxolotl.IdentityKeyPair;
@@ -21,17 +20,19 @@ import java.io.IOException;
import javax.inject.Inject;
-public class CreateSignedPreKeyJob extends ContextJob implements InjectableType {
+public class CreateSignedPreKeyJob extends MasterSecretJob implements InjectableType {
+
+ private static final long serialVersionUID = 1L;
private static final String TAG = CreateSignedPreKeyJob.class.getSimpleName();
@Inject transient TextSecureAccountManager accountManager;
- public CreateSignedPreKeyJob(Context context, MasterSecret masterSecret) {
+ public CreateSignedPreKeyJob(Context context) {
super(context, JobParameters.newBuilder()
.withPersistence()
.withRequirement(new NetworkRequirement(context))
- .withEncryption(new EncryptionKeys(ParcelUtil.serialize(masterSecret)))
+ .withRequirement(new MasterSecretRequirement(context))
.withGroupId(CreateSignedPreKeyJob.class.getSimpleName())
.create());
}
@@ -40,16 +41,14 @@ public class CreateSignedPreKeyJob extends ContextJob implements InjectableType
public void onAdded() {}
@Override
- public void onRun() throws IOException {
- MasterSecret masterSecret = ParcelUtil.deserialize(getEncryptionKeys().getEncoded(), MasterSecret.CREATOR);
-
+ public void onRun(MasterSecret masterSecret) throws IOException {
if (TextSecurePreferences.isSignedPreKeyRegistered(context)) {
Log.w(TAG, "Signed prekey already registered...");
return;
}
- IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret);
- SignedPreKeyRecord signedPreKeyRecord = PreKeyUtil.generateSignedPreKey(context, masterSecret, identityKeyPair);
+ IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context);
+ SignedPreKeyRecord signedPreKeyRecord = PreKeyUtil.generateSignedPreKey(context, identityKeyPair);
accountManager.setSignedPreKey(signedPreKeyRecord);
TextSecurePreferences.setSignedPreKeyRegistered(context, true);
@@ -59,7 +58,7 @@ public class CreateSignedPreKeyJob extends ContextJob implements InjectableType
public void onCanceled() {}
@Override
- public boolean onShouldRetry(Exception exception) {
+ public boolean onShouldRetryThrowable(Exception exception) {
if (exception instanceof PushNetworkException) return true;
return false;
}
diff --git a/src/org/thoughtcrime/securesms/jobs/DeliveryReceiptJob.java b/src/org/thoughtcrime/securesms/jobs/DeliveryReceiptJob.java
index b9c260c989..4e4f04ac85 100644
--- a/src/org/thoughtcrime/securesms/jobs/DeliveryReceiptJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/DeliveryReceiptJob.java
@@ -47,7 +47,7 @@ public class DeliveryReceiptJob extends ContextJob implements InjectableType {
@Override
public void onRun() throws IOException {
Log.w("DeliveryReceiptJob", "Sending delivery receipt...");
- TextSecureMessageSender messageSender = messageSenderFactory.create(null);
+ TextSecureMessageSender messageSender = messageSenderFactory.create();
TextSecureAddress textSecureAddress = new TextSecureAddress(destination, Optional.fromNullable(relay));
messageSender.sendDeliveryReceipt(textSecureAddress, timestamp);
diff --git a/src/org/thoughtcrime/securesms/jobs/MasterSecretDecryptJob.java b/src/org/thoughtcrime/securesms/jobs/MasterSecretDecryptJob.java
new file mode 100644
index 0000000000..161a82711d
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/jobs/MasterSecretDecryptJob.java
@@ -0,0 +1,103 @@
+package org.thoughtcrime.securesms.jobs;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.Log;
+
+import org.thoughtcrime.securesms.crypto.AsymmetricMasterCipher;
+import org.thoughtcrime.securesms.crypto.AsymmetricMasterSecret;
+import org.thoughtcrime.securesms.crypto.MasterSecret;
+import org.thoughtcrime.securesms.crypto.MasterSecretUnion;
+import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
+import org.thoughtcrime.securesms.database.DatabaseFactory;
+import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
+import org.thoughtcrime.securesms.database.MmsDatabase;
+import org.thoughtcrime.securesms.database.SmsDatabase;
+import org.thoughtcrime.securesms.database.model.MessageRecord;
+import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
+import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
+import org.thoughtcrime.securesms.notifications.MessageNotifier;
+import org.whispersystems.jobqueue.JobParameters;
+import org.whispersystems.libaxolotl.InvalidMessageException;
+
+import java.io.IOException;
+
+public class MasterSecretDecryptJob extends MasterSecretJob {
+
+ private static final long serialVersionUID = 1L;
+ private static final String TAG = MasterSecretDecryptJob.class.getSimpleName();
+
+ public MasterSecretDecryptJob(Context context) {
+ super(context, JobParameters.newBuilder()
+ .withRequirement(new MasterSecretRequirement(context))
+ .create());
+ }
+
+ @Override
+ public void onRun(MasterSecret masterSecret) {
+ EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context);
+ SmsDatabase.Reader smsReader = smsDatabase.getDecryptInProgressMessages(masterSecret);
+
+ SmsMessageRecord smsRecord;
+
+ while ((smsRecord = smsReader.getNext()) != null) {
+ try {
+ String body = getAsymmetricDecryptedBody(masterSecret, smsRecord.getBody().getBody());
+ smsDatabase.updateMessageBody(new MasterSecretUnion(masterSecret), smsRecord.getId(), body);
+ } catch (InvalidMessageException e) {
+ Log.w(TAG, e);
+ }
+ }
+
+ MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context);
+ MmsDatabase.Reader mmsReader = mmsDatabase.getDecryptInProgressMessages(masterSecret);
+
+ MessageRecord mmsRecord;
+
+ while ((mmsRecord = mmsReader.getNext()) != null) {
+ try {
+ String body = getAsymmetricDecryptedBody(masterSecret, mmsRecord.getBody().getBody());
+ mmsDatabase.updateMessageBody(new MasterSecretUnion(masterSecret), mmsRecord.getId(), body);
+ } catch (InvalidMessageException e) {
+ Log.w(TAG, e);
+ }
+ }
+
+ smsReader.close();
+ mmsReader.close();
+
+ MessageNotifier.updateNotification(context, masterSecret);
+ }
+
+ @Override
+ public boolean onShouldRetryThrowable(Exception exception) {
+ return false;
+ }
+
+ @Override
+ public void onAdded() {
+
+ }
+
+ @Override
+ public void onCanceled() {
+
+ }
+
+ private String getAsymmetricDecryptedBody(MasterSecret masterSecret, String body)
+ throws InvalidMessageException
+ {
+ try {
+ AsymmetricMasterSecret asymmetricMasterSecret = MasterSecretUtil.getAsymmetricMasterSecret(context, masterSecret);
+ AsymmetricMasterCipher asymmetricMasterCipher = new AsymmetricMasterCipher(asymmetricMasterSecret);
+
+ if (TextUtils.isEmpty(body)) return "";
+ else return asymmetricMasterCipher.decryptBody(body);
+
+ } catch (IOException e) {
+ throw new InvalidMessageException(e);
+ }
+ }
+
+
+}
diff --git a/src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java b/src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java
index f5d9b397a2..53922d86b4 100644
--- a/src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java
@@ -2,21 +2,17 @@ package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.net.Uri;
-import android.os.Build.VERSION;
-import android.os.Build.VERSION_CODES;
import android.util.Log;
import android.util.Pair;
import org.thoughtcrime.securesms.crypto.MasterSecret;
+import org.thoughtcrime.securesms.crypto.MasterSecretUnion;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.mms.ApnUnavailableException;
import org.thoughtcrime.securesms.mms.CompatMmsConnection;
-import org.thoughtcrime.securesms.mms.IncomingLollipopMmsConnection;
import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
-import org.thoughtcrime.securesms.mms.IncomingLegacyMmsConnection;
-import org.thoughtcrime.securesms.mms.IncomingMmsConnection;
import org.thoughtcrime.securesms.mms.MmsRadioException;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.protocol.WirePrefix;
@@ -154,8 +150,8 @@ public class MmsDownloadJob extends MasterSecretJob {
database.markAsLegacyVersion(messageId, threadId);
messageAndThreadId = new Pair<>(messageId, threadId);
} else {
- messageAndThreadId = database.insertMessageInbox(masterSecret, message,
- contentLocation, threadId);
+ messageAndThreadId = database.insertMessageInbox(new MasterSecretUnion(masterSecret),
+ message, contentLocation, threadId);
database.delete(messageId);
}
diff --git a/src/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java b/src/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java
index 23358a64bf..35011f7879 100644
--- a/src/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java
@@ -59,7 +59,7 @@ public class MultiDeviceContactUpdateJob extends MasterSecretJob implements Inje
public void onRun(MasterSecret masterSecret)
throws IOException, UntrustedIdentityException, NetworkException
{
- TextSecureMessageSender messageSender = messageSenderFactory.create(masterSecret);
+ TextSecureMessageSender messageSender = messageSenderFactory.create();
File contactDataFile = createTempFile("multidevice-contact-update");
try {
diff --git a/src/org/thoughtcrime/securesms/jobs/MultiDeviceGroupUpdateJob.java b/src/org/thoughtcrime/securesms/jobs/MultiDeviceGroupUpdateJob.java
index 60c937f4fe..388be9dcda 100644
--- a/src/org/thoughtcrime/securesms/jobs/MultiDeviceGroupUpdateJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/MultiDeviceGroupUpdateJob.java
@@ -47,7 +47,7 @@ public class MultiDeviceGroupUpdateJob extends MasterSecretJob implements Inject
@Override
public void onRun(MasterSecret masterSecret) throws Exception {
- TextSecureMessageSender messageSender = messageSenderFactory.create(masterSecret);
+ TextSecureMessageSender messageSender = messageSenderFactory.create();
File contactDataFile = createTempFile("multidevice-contact-update");
GroupDatabase.Reader reader = null;
diff --git a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java
index 3da69d071a..17993d0c4b 100644
--- a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java
@@ -1,11 +1,15 @@
package org.thoughtcrime.securesms.jobs;
import android.content.Context;
+import android.support.annotation.NonNull;
import android.util.Log;
import android.util.Pair;
import org.thoughtcrime.securesms.ApplicationContext;
+import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.crypto.MasterSecret;
+import org.thoughtcrime.securesms.crypto.MasterSecretUnion;
+import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.crypto.SecurityEvent;
import org.thoughtcrime.securesms.crypto.storage.TextSecureAxolotlStore;
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore;
@@ -15,7 +19,6 @@ import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.database.PushDatabase;
import org.thoughtcrime.securesms.groups.GroupMessageProcessor;
-import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
import org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage;
@@ -47,9 +50,9 @@ import org.whispersystems.libaxolotl.state.SessionStore;
import org.whispersystems.libaxolotl.util.guava.Optional;
import org.whispersystems.textsecure.api.crypto.TextSecureCipher;
import org.whispersystems.textsecure.api.messages.TextSecureContent;
+import org.whispersystems.textsecure.api.messages.TextSecureDataMessage;
import org.whispersystems.textsecure.api.messages.TextSecureEnvelope;
import org.whispersystems.textsecure.api.messages.TextSecureGroup;
-import org.whispersystems.textsecure.api.messages.TextSecureDataMessage;
import org.whispersystems.textsecure.api.messages.multidevice.RequestMessage;
import org.whispersystems.textsecure.api.messages.multidevice.SentTranscriptMessage;
import org.whispersystems.textsecure.api.messages.multidevice.TextSecureSyncMessage;
@@ -59,9 +62,9 @@ import java.util.concurrent.TimeUnit;
import ws.com.google.android.mms.MmsException;
-public class PushDecryptJob extends MasterSecretJob {
+public class PushDecryptJob extends ContextJob {
- private static final long serialVersionUID = 1L;
+ private static final long serialVersionUID = 2L;
public static final String TAG = PushDecryptJob.class.getSimpleName();
@@ -75,7 +78,6 @@ public class PushDecryptJob extends MasterSecretJob {
public PushDecryptJob(Context context, long pushMessageId, long smsMessageId, String sender) {
super(context, JobParameters.newBuilder()
.withPersistence()
- .withRequirement(new MasterSecretRequirement(context))
.withGroupId(sender)
.withWakeLock(true, 5, TimeUnit.SECONDS)
.create());
@@ -91,18 +93,29 @@ public class PushDecryptJob extends MasterSecretJob {
}
@Override
- public void onRun(MasterSecret masterSecret) throws NoSuchMessageException {
+ public void onRun() throws NoSuchMessageException {
+ if (!IdentityKeyUtil.hasIdentityKey(context)) {
+ Log.w(TAG, "Skipping job, waiting for migration...");
+ return;
+ }
+
+ MasterSecret masterSecret = KeyCachingService.getMasterSecret(context);
PushDatabase database = DatabaseFactory.getPushDatabase(context);
TextSecureEnvelope envelope = database.get(messageId);
Optional optionalSmsMessageId = smsMessageId > 0 ? Optional.of(smsMessageId) :
Optional.absent();
- handleMessage(masterSecret, envelope, optionalSmsMessageId);
+ MasterSecretUnion masterSecretUnion;
+
+ if (masterSecret == null) masterSecretUnion = new MasterSecretUnion(MasterSecretUtil.getAsymmetricMasterSecret(context, null));
+ else masterSecretUnion = new MasterSecretUnion(masterSecret);
+
+ handleMessage(masterSecretUnion, envelope, optionalSmsMessageId);
database.delete(messageId);
}
@Override
- public boolean onShouldRetryThrowable(Exception exception) {
+ public boolean onShouldRetry(Exception exception) {
return false;
}
@@ -111,9 +124,9 @@ public class PushDecryptJob extends MasterSecretJob {
}
- private void handleMessage(MasterSecret masterSecret, TextSecureEnvelope envelope, Optional smsMessageId) {
+ private void handleMessage(MasterSecretUnion masterSecret, TextSecureEnvelope envelope, Optional smsMessageId) {
try {
- AxolotlStore axolotlStore = new TextSecureAxolotlStore(context, masterSecret);
+ AxolotlStore axolotlStore = new TextSecureAxolotlStore(context);
TextSecureAddress localAddress = new TextSecureAddress(TextSecurePreferences.getLocalNumber(context));
TextSecureCipher cipher = new TextSecureCipher(localAddress, axolotlStore);
@@ -157,8 +170,10 @@ public class PushDecryptJob extends MasterSecretJob {
}
}
- private void handleEndSessionMessage(MasterSecret masterSecret, TextSecureEnvelope envelope,
- TextSecureDataMessage message, Optional smsMessageId)
+ private void handleEndSessionMessage(@NonNull MasterSecretUnion masterSecret,
+ @NonNull TextSecureEnvelope envelope,
+ @NonNull TextSecureDataMessage message,
+ @NonNull Optional smsMessageId)
{
EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context);
IncomingTextMessage incomingTextMessage = new IncomingTextMessage(envelope.getSource(),
@@ -171,20 +186,25 @@ public class PushDecryptJob extends MasterSecretJob {
if (!smsMessageId.isPresent()) {
IncomingEndSessionMessage incomingEndSessionMessage = new IncomingEndSessionMessage(incomingTextMessage);
Pair messageAndThreadId = smsDatabase.insertMessageInbox(masterSecret, incomingEndSessionMessage);
+
threadId = messageAndThreadId.second;
} else {
smsDatabase.markAsEndSession(smsMessageId.get());
threadId = smsDatabase.getThreadIdForMessage(smsMessageId.get());
}
- SessionStore sessionStore = new TextSecureSessionStore(context, masterSecret);
+ SessionStore sessionStore = new TextSecureSessionStore(context);
sessionStore.deleteAllSessions(envelope.getSource());
SecurityEvent.broadcastSecurityUpdateEvent(context, threadId);
- MessageNotifier.updateNotification(context, masterSecret, threadId);
+ MessageNotifier.updateNotification(context, masterSecret.getMasterSecret().orNull(), threadId);
}
- private void handleGroupMessage(MasterSecret masterSecret, TextSecureEnvelope envelope, TextSecureDataMessage message, Optional smsMessageId) {
+ private void handleGroupMessage(@NonNull MasterSecretUnion masterSecret,
+ @NonNull TextSecureEnvelope envelope,
+ @NonNull TextSecureDataMessage message,
+ @NonNull Optional smsMessageId)
+ {
GroupMessageProcessor.process(context, masterSecret, envelope, message);
if (smsMessageId.isPresent()) {
@@ -192,7 +212,9 @@ public class PushDecryptJob extends MasterSecretJob {
}
}
- private void handleSynchronizeSentMessage(MasterSecret masterSecret, SentTranscriptMessage message, Optional smsMessageId)
+ private void handleSynchronizeSentMessage(@NonNull MasterSecretUnion masterSecret,
+ @NonNull SentTranscriptMessage message,
+ @NonNull Optional smsMessageId)
throws MmsException
{
if (message.getMessage().getAttachments().isPresent()) {
@@ -202,7 +224,9 @@ public class PushDecryptJob extends MasterSecretJob {
}
}
- private void handleSynchronizeRequestMessage(MasterSecret masterSecret, RequestMessage message) {
+ private void handleSynchronizeRequestMessage(@NonNull MasterSecretUnion masterSecret,
+ @NonNull RequestMessage message)
+ {
if (message.isContactsRequest()) {
ApplicationContext.getInstance(context)
.getJobManager()
@@ -216,8 +240,10 @@ public class PushDecryptJob extends MasterSecretJob {
}
}
- private void handleMediaMessage(MasterSecret masterSecret, TextSecureEnvelope envelope,
- TextSecureDataMessage message, Optional smsMessageId)
+ private void handleMediaMessage(@NonNull MasterSecretUnion masterSecret,
+ @NonNull TextSecureEnvelope envelope,
+ @NonNull TextSecureDataMessage message,
+ @NonNull Optional smsMessageId)
throws MmsException
{
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
@@ -239,12 +265,12 @@ public class PushDecryptJob extends MasterSecretJob {
DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get());
}
- MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second);
+ MessageNotifier.updateNotification(context, masterSecret.getMasterSecret().orNull(), messageAndThreadId.second);
}
- private void handleSynchronizeSentMediaMessage(MasterSecret masterSecret,
- SentTranscriptMessage message,
- Optional smsMessageId)
+ private void handleSynchronizeSentMediaMessage(@NonNull MasterSecretUnion masterSecret,
+ @NonNull SentTranscriptMessage message,
+ @NonNull Optional smsMessageId)
throws MmsException
{
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
@@ -270,8 +296,10 @@ public class PushDecryptJob extends MasterSecretJob {
}
}
- private void handleTextMessage(MasterSecret masterSecret, TextSecureEnvelope envelope,
- TextSecureDataMessage message, Optional smsMessageId)
+ private void handleTextMessage(@NonNull MasterSecretUnion masterSecret,
+ @NonNull TextSecureEnvelope envelope,
+ @NonNull TextSecureDataMessage message,
+ @NonNull Optional smsMessageId)
{
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
String body = message.getBody().isPresent() ? message.getBody().get() : "";
@@ -287,16 +315,15 @@ public class PushDecryptJob extends MasterSecretJob {
message.getGroupInfo());
textMessage = new IncomingEncryptedMessage(textMessage, body);
-
messageAndThreadId = database.insertMessageInbox(masterSecret, textMessage);
}
- MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second);
+ MessageNotifier.updateNotification(context, masterSecret.getMasterSecret().orNull(), messageAndThreadId.second);
}
- private void handleSynchronizeSentTextMessage(MasterSecret masterSecret,
- SentTranscriptMessage message,
- Optional smsMessageId)
+ private void handleSynchronizeSentTextMessage(@NonNull MasterSecretUnion masterSecret,
+ @NonNull SentTranscriptMessage message,
+ @NonNull Optional smsMessageId)
{
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
Recipients recipients = getSyncMessageDestination(message);
@@ -315,55 +342,70 @@ public class PushDecryptJob extends MasterSecretJob {
}
}
- private void handleInvalidVersionMessage(MasterSecret masterSecret, TextSecureEnvelope envelope, Optional smsMessageId) {
+ private void handleInvalidVersionMessage(@NonNull MasterSecretUnion masterSecret,
+ @NonNull TextSecureEnvelope envelope,
+ @NonNull Optional smsMessageId)
+ {
EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context);
if (!smsMessageId.isPresent()) {
- Pair messageAndThreadId = insertPlaceholder(masterSecret, envelope);
+ Pair messageAndThreadId = insertPlaceholder(envelope);
smsDatabase.markAsInvalidVersionKeyExchange(messageAndThreadId.first);
- MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second);
+ MessageNotifier.updateNotification(context, masterSecret.getMasterSecret().orNull(), messageAndThreadId.second);
} else {
smsDatabase.markAsInvalidVersionKeyExchange(smsMessageId.get());
}
}
- private void handleCorruptMessage(MasterSecret masterSecret, TextSecureEnvelope envelope, Optional smsMessageId) {
+ private void handleCorruptMessage(@NonNull MasterSecretUnion masterSecret,
+ @NonNull TextSecureEnvelope envelope,
+ @NonNull Optional smsMessageId)
+ {
EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context);
if (!smsMessageId.isPresent()) {
- Pair messageAndThreadId = insertPlaceholder(masterSecret, envelope);
+ Pair messageAndThreadId = insertPlaceholder(envelope);
smsDatabase.markAsDecryptFailed(messageAndThreadId.first);
- MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second);
+ MessageNotifier.updateNotification(context, masterSecret.getMasterSecret().orNull(), messageAndThreadId.second);
} else {
smsDatabase.markAsDecryptFailed(smsMessageId.get());
}
}
- private void handleNoSessionMessage(MasterSecret masterSecret, TextSecureEnvelope envelope, Optional smsMessageId) {
+ private void handleNoSessionMessage(@NonNull MasterSecretUnion masterSecret,
+ @NonNull TextSecureEnvelope envelope,
+ @NonNull Optional smsMessageId)
+ {
EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context);
if (!smsMessageId.isPresent()) {
- Pair messageAndThreadId = insertPlaceholder(masterSecret, envelope);
+ Pair messageAndThreadId = insertPlaceholder(envelope);
smsDatabase.markAsNoSession(messageAndThreadId.first);
- MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second);
+ MessageNotifier.updateNotification(context, masterSecret.getMasterSecret().orNull(), messageAndThreadId.second);
} else {
smsDatabase.markAsNoSession(smsMessageId.get());
}
}
- private void handleLegacyMessage(MasterSecret masterSecret, TextSecureEnvelope envelope, Optional smsMessageId) {
+ private void handleLegacyMessage(@NonNull MasterSecretUnion masterSecret,
+ @NonNull TextSecureEnvelope envelope,
+ @NonNull Optional smsMessageId)
+ {
EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context);
if (!smsMessageId.isPresent()) {
- Pair messageAndThreadId = insertPlaceholder(masterSecret, envelope);
+ Pair messageAndThreadId = insertPlaceholder(envelope);
smsDatabase.markAsLegacyVersion(messageAndThreadId.first);
- MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second);
+ MessageNotifier.updateNotification(context, masterSecret.getMasterSecret().orNull(), messageAndThreadId.second);
} else {
smsDatabase.markAsLegacyVersion(smsMessageId.get());
}
}
- private void handleDuplicateMessage(MasterSecret masterSecret, TextSecureEnvelope envelope, Optional smsMessageId) {
+ private void handleDuplicateMessage(@NonNull MasterSecretUnion masterSecret,
+ @NonNull TextSecureEnvelope envelope,
+ @NonNull Optional smsMessageId)
+ {
// Let's start ignoring these now
// SmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context);
//
@@ -376,7 +418,10 @@ public class PushDecryptJob extends MasterSecretJob {
// }
}
- private void handleUntrustedIdentityMessage(MasterSecret masterSecret, TextSecureEnvelope envelope, Optional smsMessageId) {
+ private void handleUntrustedIdentityMessage(@NonNull MasterSecretUnion masterSecret,
+ @NonNull TextSecureEnvelope envelope,
+ @NonNull Optional smsMessageId)
+ {
try {
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
Recipients recipients = RecipientFactory.getRecipientsFromString(context, envelope.getSource(), false);
@@ -393,7 +438,7 @@ public class PushDecryptJob extends MasterSecretJob {
Pair messageAndThreadId = database.insertMessageInbox(masterSecret, bundleMessage);
database.setMismatchedIdentity(messageAndThreadId.first, recipientId, identityKey);
- MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second);
+ MessageNotifier.updateNotification(context, masterSecret.getMasterSecret().orNull(), messageAndThreadId.second);
} else {
database.updateMessageBody(masterSecret, smsMessageId.get(), encoded);
database.markAsPreKeyBundle(smsMessageId.get());
@@ -404,16 +449,14 @@ public class PushDecryptJob extends MasterSecretJob {
}
}
- private Pair insertPlaceholder(MasterSecret masterSecret, TextSecureEnvelope envelope) {
- EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
-
- IncomingTextMessage textMessage = new IncomingTextMessage(envelope.getSource(), envelope.getSourceDevice(),
- envelope.getTimestamp(), "",
- Optional.absent());
+ private Pair insertPlaceholder(@NonNull TextSecureEnvelope envelope) {
+ EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
+ IncomingTextMessage textMessage = new IncomingTextMessage(envelope.getSource(), envelope.getSourceDevice(),
+ envelope.getTimestamp(), "",
+ Optional.absent());
textMessage = new IncomingEncryptedMessage(textMessage, "");
-
- return database.insertMessageInbox(masterSecret, textMessage);
+ return database.insertMessageInbox(textMessage);
}
private Recipients getSyncMessageDestination(SentTranscriptMessage message) {
diff --git a/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java
index 4362e2f307..2be79eba48 100644
--- a/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java
@@ -135,7 +135,7 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
{
message = getResolvedMessage(masterSecret, message, MediaConstraints.PUSH_CONSTRAINTS, false);
- TextSecureMessageSender messageSender = messageSenderFactory.create(masterSecret);
+ TextSecureMessageSender messageSender = messageSenderFactory.create();
byte[] groupId = GroupUtil.getDecodedId(message.getTo()[0].getString());
Recipients recipients = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupId, false);
List attachments = getAttachments(masterSecret, message);
diff --git a/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java
index 6f7c9ec8d9..5630a593ff 100644
--- a/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java
@@ -104,7 +104,7 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
throws RetryLaterException, InsecureFallbackApprovalException, UntrustedIdentityException,
UndeliverableMessageException
{
- TextSecureMessageSender messageSender = messageSenderFactory.create(masterSecret);
+ TextSecureMessageSender messageSender = messageSenderFactory.create();
String destination = message.getTo()[0].getString();
try {
diff --git a/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java
index 391e9d766b..2d3730c5a7 100644
--- a/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java
@@ -59,7 +59,7 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
try {
Log.w(TAG, "Sending message: " + messageId);
- deliver(masterSecret, record);
+ deliver(record);
database.markAsPush(messageId);
database.markAsSecure(messageId);
database.markAsSent(messageId);
@@ -99,12 +99,12 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
}
}
- private void deliver(MasterSecret masterSecret, SmsMessageRecord message)
+ private void deliver(SmsMessageRecord message)
throws UntrustedIdentityException, InsecureFallbackApprovalException, RetryLaterException
{
try {
TextSecureAddress address = getPushAddress(message.getIndividualRecipient().getNumber());
- TextSecureMessageSender messageSender = messageSenderFactory.create(masterSecret);
+ TextSecureMessageSender messageSender = messageSenderFactory.create();
TextSecureDataMessage textSecureMessage = TextSecureDataMessage.newBuilder()
.withTimestamp(message.getDateSent())
.withBody(message.getBody().getBody())
diff --git a/src/org/thoughtcrime/securesms/jobs/RefreshPreKeysJob.java b/src/org/thoughtcrime/securesms/jobs/RefreshPreKeysJob.java
index 4ea11a0e8f..aac7109884 100644
--- a/src/org/thoughtcrime/securesms/jobs/RefreshPreKeysJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/RefreshPreKeysJob.java
@@ -57,10 +57,10 @@ public class RefreshPreKeysJob extends MasterSecretJob implements InjectableType
return;
}
- List preKeyRecords = PreKeyUtil.generatePreKeys(context, masterSecret);
- PreKeyRecord lastResortKeyRecord = PreKeyUtil.generateLastResortKey(context, masterSecret);
- IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret);
- SignedPreKeyRecord signedPreKeyRecord = PreKeyUtil.generateSignedPreKey(context, masterSecret, identityKey);
+ List preKeyRecords = PreKeyUtil.generatePreKeys(context);
+ PreKeyRecord lastResortKeyRecord = PreKeyUtil.generateLastResortKey(context);
+ IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context);
+ SignedPreKeyRecord signedPreKeyRecord = PreKeyUtil.generateSignedPreKey(context, identityKey);
Log.w(TAG, "Registering new prekeys...");
diff --git a/src/org/thoughtcrime/securesms/jobs/SmsDecryptJob.java b/src/org/thoughtcrime/securesms/jobs/SmsDecryptJob.java
deleted file mode 100644
index d2d7680f9c..0000000000
--- a/src/org/thoughtcrime/securesms/jobs/SmsDecryptJob.java
+++ /dev/null
@@ -1,104 +0,0 @@
-package org.thoughtcrime.securesms.jobs;
-
-import android.content.Context;
-import android.util.Log;
-
-import org.thoughtcrime.securesms.crypto.AsymmetricMasterCipher;
-import org.thoughtcrime.securesms.crypto.AsymmetricMasterSecret;
-import org.thoughtcrime.securesms.crypto.MasterSecret;
-import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
-import org.thoughtcrime.securesms.database.DatabaseFactory;
-import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
-import org.thoughtcrime.securesms.database.NoSuchMessageException;
-import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
-import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
-import org.thoughtcrime.securesms.notifications.MessageNotifier;
-import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage;
-import org.thoughtcrime.securesms.sms.IncomingTextMessage;
-import org.whispersystems.jobqueue.JobParameters;
-import org.whispersystems.libaxolotl.InvalidMessageException;
-import org.whispersystems.libaxolotl.util.guava.Optional;
-import org.whispersystems.textsecure.api.messages.TextSecureGroup;
-
-import java.io.IOException;
-
-public class SmsDecryptJob extends MasterSecretJob {
-
- private static final String TAG = SmsDecryptJob.class.getSimpleName();
-
- private final long messageId;
-
- public SmsDecryptJob(Context context, long messageId) {
- super(context, JobParameters.newBuilder()
- .withPersistence()
- .withRequirement(new MasterSecretRequirement(context))
- .create());
-
- this.messageId = messageId;
- }
-
- @Override
- public void onAdded() {}
-
- @Override
- public void onRun(MasterSecret masterSecret) throws NoSuchMessageException {
- EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
-
- try {
- SmsMessageRecord record = database.getMessage(masterSecret, messageId);
- IncomingTextMessage message = createIncomingTextMessage(masterSecret, record);
- long messageId = record.getId();
-
- if (message.isSecureMessage()) {
- database.markAsLegacyVersion(messageId);
- } else {
- database.updateMessageBody(masterSecret, messageId, message.getMessageBody());
- }
-
- MessageNotifier.updateNotification(context, masterSecret);
- } catch (InvalidMessageException e) {
- Log.w(TAG, e);
- database.markAsDecryptFailed(messageId);
- }
- }
-
- @Override
- public boolean onShouldRetryThrowable(Exception exception) {
- return false;
- }
-
- @Override
- public void onCanceled() {
- // TODO
- }
-
- private String getAsymmetricDecryptedBody(MasterSecret masterSecret, String body)
- throws InvalidMessageException
- {
- try {
- AsymmetricMasterSecret asymmetricMasterSecret = MasterSecretUtil.getAsymmetricMasterSecret(context, masterSecret);
- AsymmetricMasterCipher asymmetricMasterCipher = new AsymmetricMasterCipher(asymmetricMasterSecret);
-
- return asymmetricMasterCipher.decryptBody(body);
- } catch (IOException e) {
- throw new InvalidMessageException(e);
- }
- }
-
- private IncomingTextMessage createIncomingTextMessage(MasterSecret masterSecret, SmsMessageRecord record)
- throws InvalidMessageException
- {
- IncomingTextMessage message = new IncomingTextMessage(record.getRecipients().getPrimaryRecipient().getNumber(),
- record.getRecipientDeviceId(),
- record.getDateSent(),
- record.getBody().getBody(),
- Optional.absent());
-
- if (record.isAsymmetricEncryption()) {
- String plaintextBody = getAsymmetricDecryptedBody(masterSecret, record.getBody().getBody());
- return new IncomingTextMessage(message, plaintextBody);
- } else {
- return new IncomingEncryptedMessage(message, message.getMessageBody());
- }
- }
-}
diff --git a/src/org/thoughtcrime/securesms/jobs/SmsReceiveJob.java b/src/org/thoughtcrime/securesms/jobs/SmsReceiveJob.java
index e3c17c3c06..27a208cf16 100644
--- a/src/org/thoughtcrime/securesms/jobs/SmsReceiveJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/SmsReceiveJob.java
@@ -5,8 +5,8 @@ import android.telephony.SmsMessage;
import android.util.Log;
import android.util.Pair;
-import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.crypto.MasterSecret;
+import org.thoughtcrime.securesms.crypto.MasterSecretUnion;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
@@ -43,11 +43,20 @@ public class SmsReceiveJob extends ContextJob {
@Override
public void onRun() {
- Optional message = assembleMessageFragments(pdus);
+ Optional message = assembleMessageFragments(pdus);
+ MasterSecret masterSecret = KeyCachingService.getMasterSecret(context);
+
+ MasterSecretUnion masterSecretUnion;
+
+ if (masterSecret == null) {
+ masterSecretUnion = new MasterSecretUnion(MasterSecretUtil.getAsymmetricMasterSecret(context, null));
+ } else {
+ masterSecretUnion = new MasterSecretUnion(masterSecret);
+ }
if (message.isPresent() && !isBlocked(message.get())) {
- Pair messageAndThreadId = storeMessage(message.get());
- MessageNotifier.updateNotification(context, KeyCachingService.getMasterSecret(context), messageAndThreadId.second);
+ Pair messageAndThreadId = storeMessage(masterSecretUnion, message.get());
+ MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second);
} else if (message.isPresent()) {
Log.w(TAG, "*** Received blocked SMS, ignoring...");
}
@@ -72,21 +81,15 @@ public class SmsReceiveJob extends ContextJob {
return false;
}
- private Pair storeMessage(IncomingTextMessage message) {
- EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
- MasterSecret masterSecret = KeyCachingService.getMasterSecret(context);
+ private Pair storeMessage(MasterSecretUnion masterSecret, IncomingTextMessage message) {
+ EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
Pair messageAndThreadId;
if (message.isSecureMessage()) {
- messageAndThreadId = database.insertMessageInbox((MasterSecret)null, message);
+ IncomingTextMessage placeholder = new IncomingTextMessage(message, "");
+ messageAndThreadId = database.insertMessageInbox(placeholder);
database.markAsLegacyVersion(messageAndThreadId.first);
- } else if (masterSecret == null) {
- messageAndThreadId = database.insertMessageInbox(MasterSecretUtil.getAsymmetricMasterSecret(context, null), message);
-
- ApplicationContext.getInstance(context)
- .getJobManager()
- .add(new SmsDecryptJob(context, messageAndThreadId.first));
} else {
messageAndThreadId = database.insertMessageInbox(masterSecret, message);
}
diff --git a/src/org/thoughtcrime/securesms/mms/IncomingMediaMessage.java b/src/org/thoughtcrime/securesms/mms/IncomingMediaMessage.java
index a6980b5613..4659b647eb 100644
--- a/src/org/thoughtcrime/securesms/mms/IncomingMediaMessage.java
+++ b/src/org/thoughtcrime/securesms/mms/IncomingMediaMessage.java
@@ -2,8 +2,11 @@ package org.thoughtcrime.securesms.mms;
import android.text.TextUtils;
+import org.thoughtcrime.securesms.crypto.AsymmetricMasterCipher;
import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.crypto.MasterSecret;
+import org.thoughtcrime.securesms.crypto.MasterSecretUnion;
+import org.thoughtcrime.securesms.crypto.MediaKey;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.Util;
@@ -34,7 +37,7 @@ public class IncomingMediaMessage {
this.push = false;
}
- public IncomingMediaMessage(MasterSecret masterSecret,
+ public IncomingMediaMessage(MasterSecretUnion masterSecret,
String from,
String to,
long sentTimeMillis,
@@ -70,11 +73,11 @@ public class IncomingMediaMessage {
for (TextSecureAttachment attachment : attachments.get()) {
if (attachment.isPointer()) {
PduPart media = new PduPart();
- byte[] encryptedKey = new MasterCipher(masterSecret).encryptBytes(attachment.asPointer().getKey());
+ String encryptedKey = MediaKey.getEncrypted(masterSecret, attachment.asPointer().getKey());
media.setContentType(Util.toIsoBytes(attachment.getContentType()));
media.setContentLocation(Util.toIsoBytes(String.valueOf(attachment.asPointer().getId())));
- media.setContentDisposition(Util.toIsoBytes(Base64.encodeBytes(encryptedKey)));
+ media.setContentDisposition(Util.toIsoBytes(encryptedKey));
if (relay.isPresent()) {
media.setName(Util.toIsoBytes(relay.get()));
diff --git a/src/org/thoughtcrime/securesms/mms/OutgoingMediaMessage.java b/src/org/thoughtcrime/securesms/mms/OutgoingMediaMessage.java
index 5892bb9bf1..374f9ab678 100644
--- a/src/org/thoughtcrime/securesms/mms/OutgoingMediaMessage.java
+++ b/src/org/thoughtcrime/securesms/mms/OutgoingMediaMessage.java
@@ -3,11 +3,10 @@ package org.thoughtcrime.securesms.mms;
import android.content.Context;
import android.text.TextUtils;
-import org.thoughtcrime.securesms.crypto.MasterCipher;
-import org.thoughtcrime.securesms.crypto.MasterSecret;
+import org.thoughtcrime.securesms.crypto.MasterSecretUnion;
+import org.thoughtcrime.securesms.crypto.MediaKey;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.recipients.Recipients;
-import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.textsecure.api.messages.TextSecureAttachment;
@@ -40,7 +39,7 @@ public class OutgoingMediaMessage {
this(context, recipients, slideDeck.toPduBody(), message, distributionType);
}
- public OutgoingMediaMessage(Context context, MasterSecret masterSecret,
+ public OutgoingMediaMessage(Context context, MasterSecretUnion masterSecret,
Recipients recipients, List attachments,
String message)
{
@@ -74,17 +73,17 @@ public class OutgoingMediaMessage {
return false;
}
- private static PduBody pduBodyFor(MasterSecret masterSecret, List attachments) {
+ private static PduBody pduBodyFor(MasterSecretUnion masterSecret, List attachments) {
PduBody body = new PduBody();
for (TextSecureAttachment attachment : attachments) {
if (attachment.isPointer()) {
PduPart media = new PduPart();
- byte[] encryptedKey = new MasterCipher(masterSecret).encryptBytes(attachment.asPointer().getKey());
+ String encryptedKey = MediaKey.getEncrypted(masterSecret, attachment.asPointer().getKey());
media.setContentType(Util.toIsoBytes(attachment.getContentType()));
media.setContentLocation(Util.toIsoBytes(String.valueOf(attachment.asPointer().getId())));
- media.setContentDisposition(Util.toIsoBytes(Base64.encodeBytes(encryptedKey)));
+ media.setContentDisposition(Util.toIsoBytes(encryptedKey));
body.addPart(media);
}
diff --git a/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java b/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java
index 06ba1bb0b7..84c8a5f7f3 100644
--- a/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java
+++ b/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java
@@ -31,6 +31,7 @@ import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
+import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationCompat.Action;
@@ -118,7 +119,7 @@ public class MessageNotifier {
}
}
- public static void updateNotification(Context context, MasterSecret masterSecret) {
+ public static void updateNotification(@NonNull Context context, @Nullable MasterSecret masterSecret) {
if (!TextSecurePreferences.isNotificationsEnabled(context)) {
return;
}
@@ -126,7 +127,10 @@ public class MessageNotifier {
updateNotification(context, masterSecret, false, 0);
}
- public static void updateNotification(Context context, MasterSecret masterSecret, long threadId) {
+ public static void updateNotification(@NonNull Context context,
+ @Nullable MasterSecret masterSecret,
+ long threadId)
+ {
Recipients recipients = DatabaseFactory.getThreadDatabase(context)
.getRecipientsForThreadId(threadId);
@@ -146,7 +150,10 @@ public class MessageNotifier {
}
}
- private static void updateNotification(Context context, MasterSecret masterSecret, boolean signal, int reminderCount) {
+ private static void updateNotification(@NonNull Context context,
+ @Nullable MasterSecret masterSecret,
+ boolean signal, int reminderCount)
+ {
Cursor telcoCursor = null;
Cursor pushCursor = null;
@@ -182,9 +189,9 @@ public class MessageNotifier {
}
}
- private static void sendSingleThreadNotification(Context context,
- MasterSecret masterSecret,
- NotificationState notificationState,
+ private static void sendSingleThreadNotification(@NonNull Context context,
+ @Nullable MasterSecret masterSecret,
+ @NonNull NotificationState notificationState,
boolean signal)
{
if (notificationState.getNotifications().isEmpty()) {
@@ -266,9 +273,9 @@ public class MessageNotifier {
.notify(NOTIFICATION_ID, builder.build());
}
- private static void sendMultipleThreadNotification(Context context,
- MasterSecret masterSecret,
- NotificationState notificationState,
+ private static void sendMultipleThreadNotification(@NonNull Context context,
+ @Nullable MasterSecret masterSecret,
+ @NonNull NotificationState notificationState,
boolean signal)
{
List notifications = notificationState.getNotifications();
@@ -375,10 +382,10 @@ public class MessageNotifier {
}
}
- private static void appendPushNotificationState(Context context,
- MasterSecret masterSecret,
- NotificationState notificationState,
- Cursor cursor)
+ private static void appendPushNotificationState(@NonNull Context context,
+ @Nullable MasterSecret masterSecret,
+ @NonNull NotificationState notificationState,
+ @NonNull Cursor cursor)
{
if (masterSecret != null) return;
@@ -392,7 +399,7 @@ public class MessageNotifier {
Recipients recipients = RecipientFactory.getRecipientsFromString(context, envelope.getSource(), false);
Recipient recipient = recipients.getPrimaryRecipient();
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
- SpannableString body = new SpannableString(context.getString(R.string.MessageNotifier_encrypted_message));
+ SpannableString body = new SpannableString(context.getString(R.string.MessageNotifier_locked_message));
body.setSpan(new StyleSpan(android.graphics.Typeface.ITALIC), 0, body.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
if (!recipients.isMuted()) {
@@ -405,9 +412,9 @@ public class MessageNotifier {
}
}
- private static NotificationState constructNotificationState(Context context,
- MasterSecret masterSecret,
- Cursor cursor)
+ private static NotificationState constructNotificationState(@NonNull Context context,
+ @Nullable MasterSecret masterSecret,
+ @NonNull Cursor cursor)
{
NotificationState notificationState = new NotificationState();
MessageRecord record;
@@ -433,7 +440,7 @@ public class MessageNotifier {
}
if (SmsDatabase.Types.isDecryptInProgressType(record.getType()) || !record.getBody().isPlaintext()) {
- body = SpanUtil.italic(context.getString(R.string.MessageNotifier_encrypted_message));
+ body = SpanUtil.italic(context.getString(R.string.MessageNotifier_locked_message));
} else if (record.isMms() && TextUtils.isEmpty(body)) {
body = SpanUtil.italic(context.getString(R.string.MessageNotifier_media_message));
} else if (record.isMms() && !record.isMmsNotification()) {
diff --git a/src/org/thoughtcrime/securesms/service/KeyCachingService.java b/src/org/thoughtcrime/securesms/service/KeyCachingService.java
index 40e50aefb3..1f4998d4f7 100644
--- a/src/org/thoughtcrime/securesms/service/KeyCachingService.java
+++ b/src/org/thoughtcrime/securesms/service/KeyCachingService.java
@@ -28,6 +28,7 @@ import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.SystemClock;
+import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import android.widget.RemoteViews;
@@ -40,6 +41,7 @@ import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.crypto.InvalidPassphraseException;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
+import org.thoughtcrime.securesms.jobs.MasterSecretDecryptJob;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.ParcelUtil;
@@ -78,7 +80,7 @@ public class KeyCachingService extends Service {
public KeyCachingService() {}
- public static synchronized MasterSecret getMasterSecret(Context context) {
+ public static synchronized @Nullable MasterSecret getMasterSecret(Context context) {
if (masterSecret == null && TextSecurePreferences.isPasswordDisabled(context)) {
try {
MasterSecret masterSecret = MasterSecretUtil.getMasterSecret(context, MasterSecretUtil.UNENCRYPTED_PASSPHRASE);
@@ -103,13 +105,14 @@ public class KeyCachingService extends Service {
broadcastNewSecret();
startTimeoutIfAppropriate();
+ if (!TextSecurePreferences.isPasswordDisabled(this)) {
+ ApplicationContext.getInstance(this).getJobManager().add(new MasterSecretDecryptJob(this));
+ }
+
new AsyncTask() {
@Override
protected Void doInBackground(Void... params) {
if (!DatabaseUpgradeActivity.isUpdate(KeyCachingService.this)) {
- ApplicationContext.getInstance(KeyCachingService.this)
- .getJobManager()
- .setEncryptionKeys(new EncryptionKeys(ParcelUtil.serialize(masterSecret)));
MessageNotifier.updateNotification(KeyCachingService.this, masterSecret);
}
return null;
diff --git a/src/org/thoughtcrime/securesms/service/RegistrationService.java b/src/org/thoughtcrime/securesms/service/RegistrationService.java
index 3d0d3451a8..432ab95afa 100644
--- a/src/org/thoughtcrime/securesms/service/RegistrationService.java
+++ b/src/org/thoughtcrime/securesms/service/RegistrationService.java
@@ -14,7 +14,6 @@ import com.google.android.gms.gcm.GoogleCloudMessaging;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
-import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.PreKeyUtil;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.jobs.GcmRefreshJob;
@@ -153,15 +152,14 @@ public class RegistrationService extends Service {
private void handleVoiceRegistrationIntent(Intent intent) {
markAsVerifying(true);
- String number = intent.getStringExtra("e164number");
- String password = intent.getStringExtra("password" );
- String signalingKey = intent.getStringExtra("signaling_key");
- MasterSecret masterSecret = intent.getParcelableExtra("master_secret");
+ String number = intent.getStringExtra("e164number");
+ String password = intent.getStringExtra("password");
+ String signalingKey = intent.getStringExtra("signaling_key");
try {
TextSecureAccountManager accountManager = TextSecureCommunicationFactory.createManager(this, number, password);
- handleCommonRegistration(masterSecret, accountManager, number);
+ handleCommonRegistration(accountManager, number);
markAsVerified(number, password, signalingKey);
@@ -181,9 +179,8 @@ public class RegistrationService extends Service {
private void handleSmsRegistrationIntent(Intent intent) {
markAsVerifying(true);
- String number = intent.getStringExtra("e164number");
- MasterSecret masterSecret = intent.getParcelableExtra("master_secret");
- int registrationId = TextSecurePreferences.getLocalRegistrationId(this);
+ String number = intent.getStringExtra("e164number");
+ int registrationId = TextSecurePreferences.getLocalRegistrationId(this);
if (registrationId == 0) {
registrationId = KeyHelper.generateRegistrationId(false);
@@ -204,7 +201,7 @@ public class RegistrationService extends Service {
String challenge = waitForChallenge();
accountManager.verifyAccount(challenge, signalingKey, true, registrationId);
- handleCommonRegistration(masterSecret, accountManager, number);
+ handleCommonRegistration(accountManager, number);
markAsVerified(number, password, signalingKey);
setState(new RegistrationState(RegistrationState.STATE_COMPLETE, number));
@@ -230,15 +227,15 @@ public class RegistrationService extends Service {
}
}
- private void handleCommonRegistration(MasterSecret masterSecret, TextSecureAccountManager accountManager, String number)
+ private void handleCommonRegistration(TextSecureAccountManager accountManager, String number)
throws IOException
{
setState(new RegistrationState(RegistrationState.STATE_GENERATING_KEYS, number));
Recipient self = RecipientFactory.getRecipientsFromString(this, number, false).getPrimaryRecipient();
- IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(this, masterSecret);
- List records = PreKeyUtil.generatePreKeys(this, masterSecret);
- PreKeyRecord lastResort = PreKeyUtil.generateLastResortKey(this, masterSecret);
- SignedPreKeyRecord signedPreKey = PreKeyUtil.generateSignedPreKey(this, masterSecret, identityKey);
+ IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(this);
+ List records = PreKeyUtil.generatePreKeys(this);
+ PreKeyRecord lastResort = PreKeyUtil.generateLastResortKey(this);
+ SignedPreKeyRecord signedPreKey = PreKeyUtil.generateSignedPreKey(this, identityKey);
accountManager.setPreKeys(identityKey.getPublicKey(),lastResort, signedPreKey, records);
setState(new RegistrationState(RegistrationState.STATE_GCM_REGISTERING, number));
@@ -249,7 +246,7 @@ public class RegistrationService extends Service {
TextSecurePreferences.setGcmRegistrationId(this, gcmRegistrationId);
TextSecurePreferences.setWebsocketRegistered(this, true);
- DatabaseFactory.getIdentityDatabase(this).saveIdentity(masterSecret, self.getRecipientId(), identityKey.getPublicKey());
+ DatabaseFactory.getIdentityDatabase(this).saveIdentity(self.getRecipientId(), identityKey.getPublicKey());
DirectoryHelper.refreshDirectory(this, accountManager, number);
DirectoryRefreshListener.schedule(this);
diff --git a/src/org/thoughtcrime/securesms/sms/MessageSender.java b/src/org/thoughtcrime/securesms/sms/MessageSender.java
index eaf7bd3b47..a44ec759dd 100644
--- a/src/org/thoughtcrime/securesms/sms/MessageSender.java
+++ b/src/org/thoughtcrime/securesms/sms/MessageSender.java
@@ -22,6 +22,7 @@ import android.util.Pair;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.crypto.MasterSecret;
+import org.thoughtcrime.securesms.crypto.MasterSecretUnion;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
import org.thoughtcrime.securesms.database.MmsDatabase;
@@ -73,7 +74,7 @@ public class MessageSender {
allocatedThreadId = threadId;
}
- long messageId = database.insertMessageOutbox(masterSecret, allocatedThreadId, message, forceSms, System.currentTimeMillis());
+ long messageId = database.insertMessageOutbox(new MasterSecretUnion(masterSecret), allocatedThreadId, message, forceSms, System.currentTimeMillis());
sendTextMessage(context, recipients, forceSms, keyExchange, messageId);
@@ -99,7 +100,7 @@ public class MessageSender {
}
Recipients recipients = message.getRecipients();
- long messageId = database.insertMessageOutbox(masterSecret, message, allocatedThreadId, forceSms, System.currentTimeMillis());
+ long messageId = database.insertMessageOutbox(new MasterSecretUnion(masterSecret), message, allocatedThreadId, forceSms, System.currentTimeMillis());
sendMediaMessage(context, masterSecret, recipients, forceSms, messageId);