From 073b1f69e3914233bac2c0c20950fa3d833d341d Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Sat, 14 Sep 2013 13:33:23 -0700 Subject: [PATCH] Rollbacks, v2 sms-transport key exchanges, push identity conflicts. 1) Stop protocol rollbacks. 2) Handle v2 version key exchange messages. 3) Handle identity key conflicts on prekeybundle messages. --- .../textsecure/crypto/SessionCipher.java | 9 ++- .../textsecure/storage/SessionRecord.java | 43 +++++++---- res/values/strings.xml | 14 ++++ .../securesms/ConversationItem.java | 1 + .../securesms/ReceiveKeyActivity.java | 71 ++++++++++++++++--- .../securesms/VerifyIdentityActivity.java | 8 ++- .../crypto/KeyExchangeProcessor.java | 9 ++- .../crypto/protocol/KeyExchangeMessage.java | 45 ++++++++---- .../securesms/database/DatabaseFactory.java | 2 +- .../database/EncryptingSmsDatabase.java | 5 ++ .../securesms/database/MmsSmsColumns.java | 5 ++ .../securesms/database/SmsDatabase.java | 5 ++ .../database/model/MessageRecord.java | 4 ++ .../database/model/SmsMessageRecord.java | 6 ++ .../securesms/service/PushReceiver.java | 11 ++- .../securesms/service/SmsReceiver.java | 2 - 16 files changed, 192 insertions(+), 48 deletions(-) diff --git a/library/src/org/whispersystems/textsecure/crypto/SessionCipher.java b/library/src/org/whispersystems/textsecure/crypto/SessionCipher.java index 11d0d36d77..54ae1ff9d1 100644 --- a/library/src/org/whispersystems/textsecure/crypto/SessionCipher.java +++ b/library/src/org/whispersystems/textsecure/crypto/SessionCipher.java @@ -89,7 +89,14 @@ public class SessionCipher { throws InvalidMessageException { try { - KeyRecords records = getKeyRecords(context, masterSecret, recipient); + KeyRecords records = getKeyRecords(context, masterSecret, recipient); + + if (messageVersion < records.getSessionRecord().getNegotiatedSessionVersion()) { + throw new InvalidMessageException("Message version: " + messageVersion + + " but negotiated session version: " + + records.getSessionRecord().getNegotiatedSessionVersion()); + } + SessionKey sessionKey = getSessionKey(masterSecret, Cipher.DECRYPT_MODE, messageVersion, localIdentityKey, records, recipientKeyId, senderKeyId); return new SessionCipherContext(records, sessionKey, senderKeyId, recipientKeyId, nextKey, counter, diff --git a/library/src/org/whispersystems/textsecure/storage/SessionRecord.java b/library/src/org/whispersystems/textsecure/storage/SessionRecord.java index 6ec97e626f..de42a112c3 100644 --- a/library/src/org/whispersystems/textsecure/storage/SessionRecord.java +++ b/library/src/org/whispersystems/textsecure/storage/SessionRecord.java @@ -44,7 +44,8 @@ public class SessionRecord extends Record { private int counter; private byte[] localFingerprint; private byte[] remoteFingerprint; - private int sessionVersion; + private int negotiatedSessionVersion; + private int currentSessionVersion; private IdentityKey identityKey; private SessionKey sessionKeyRecord; @@ -59,8 +60,8 @@ public class SessionRecord extends Record { public SessionRecord(Context context, MasterSecret masterSecret, long recipientId) { super(context, SESSIONS_DIRECTORY, recipientId+""); - this.masterSecret = masterSecret; - this.sessionVersion = 31337; + this.masterSecret = masterSecret; + this.currentSessionVersion = 31337; loadData(); } @@ -91,11 +92,19 @@ public class SessionRecord extends Record { } public int getSessionVersion() { - return (sessionVersion == 31337 ? 0 : sessionVersion); + return (currentSessionVersion == 31337 ? 0 : currentSessionVersion); + } + + public int getNegotiatedSessionVersion() { + return negotiatedSessionVersion; + } + + public void setNegotiatedSessionVersion(int sessionVersion) { + this.negotiatedSessionVersion = sessionVersion; } public void setSessionVersion(int sessionVersion) { - this.sessionVersion = sessionVersion; + this.currentSessionVersion = sessionVersion; } public int getCounter() { @@ -169,10 +178,11 @@ public class SessionRecord extends Record { writeInteger(counter, out); writeBlob(localFingerprint, out); writeBlob(remoteFingerprint, out); - writeInteger(sessionVersion, out); + writeInteger(currentSessionVersion, out); writeIdentityKey(out); writeInteger(verifiedSessionKey ? 1 : 0, out); writeInteger(prekeyBundleRequired ? 1 : 0, out); + writeInteger(negotiatedSessionVersion, out); if (sessionKeyRecord != null) writeBlob(sessionKeyRecord.serialize(), out); @@ -193,20 +203,20 @@ public class SessionRecord extends Record { // Sigh, always put a version number on everything. if (!isValidVersionMarker(versionMarker)) { - this.counter = versionMarker; - this.localFingerprint = readBlob(in); - this.remoteFingerprint = readBlob(in); - this.sessionVersion = 31337; + this.counter = versionMarker; + this.localFingerprint = readBlob(in); + this.remoteFingerprint = readBlob(in); + this.currentSessionVersion = 31337; if (in.available() != 0) this.sessionKeyRecord = new SessionKey(readBlob(in), masterSecret); in.close(); } else { - this.counter = readInteger(in); - this.localFingerprint = readBlob(in); - this.remoteFingerprint = readBlob(in); - this.sessionVersion = readInteger(in); + this.counter = readInteger(in); + this.localFingerprint = readBlob (in); + this.remoteFingerprint = readBlob (in); + this.currentSessionVersion = readInteger(in); if (versionMarker >= 0X55555556) { readIdentityKey(in); @@ -214,7 +224,10 @@ public class SessionRecord extends Record { } if (versionMarker >= 0X55555557) { - this.prekeyBundleRequired = (readInteger(in) == 1); + this.prekeyBundleRequired = (readInteger(in) == 1); + this.negotiatedSessionVersion = readInteger(in); + } else { + this.negotiatedSessionVersion = currentSessionVersion; } if (in.available() != 0) diff --git a/res/values/strings.xml b/res/values/strings.xml index a5cee015e8..aceeb60fda 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -188,6 +188,8 @@ signature on this key exchange is trusted, but you have the \'automatically complete key exchanges\' setting disabled. + Processing + Processing key exchange… Connect With TextSecure @@ -246,6 +248,18 @@ Registration Error TextSecure registration has encountered a problem. + + Received corrupted key + exchange message! + + + Received key exchange message for invalid protocol version. + + + Received message with unknown identity key. Click to process and display. + + + You do not have an identity key. Recipient has no identity key. diff --git a/src/org/thoughtcrime/securesms/ConversationItem.java b/src/org/thoughtcrime/securesms/ConversationItem.java index 9fbd95d39a..178c0bb654 100644 --- a/src/org/thoughtcrime/securesms/ConversationItem.java +++ b/src/org/thoughtcrime/securesms/ConversationItem.java @@ -336,6 +336,7 @@ public class ConversationItem extends LinearLayout { intent.putExtra("body", messageRecord.getBody().getBody()); intent.putExtra("thread_id", messageRecord.getThreadId()); intent.putExtra("message_id", messageRecord.getId()); + intent.putExtra("is_bundle", messageRecord.isBundleKeyExchange()); intent.putExtra("master_secret", masterSecret); intent.putExtra("sent", messageRecord.isOutgoing()); context.startActivity(intent); diff --git a/src/org/thoughtcrime/securesms/ReceiveKeyActivity.java b/src/org/thoughtcrime/securesms/ReceiveKeyActivity.java index 0e38236035..b4373011d4 100644 --- a/src/org/thoughtcrime/securesms/ReceiveKeyActivity.java +++ b/src/org/thoughtcrime/securesms/ReceiveKeyActivity.java @@ -30,6 +30,8 @@ import android.view.View; import android.widget.Button; import android.widget.TextView; +import org.thoughtcrime.securesms.crypto.DecryptingQueue; +import org.thoughtcrime.securesms.sms.SmsTransportDetails; import org.whispersystems.textsecure.crypto.InvalidKeyException; import org.whispersystems.textsecure.crypto.InvalidVersionException; import org.thoughtcrime.securesms.crypto.protocol.KeyExchangeMessage; @@ -38,6 +40,10 @@ import org.whispersystems.textsecure.crypto.MasterSecret; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.util.MemoryCleaner; +import org.whispersystems.textsecure.crypto.protocol.PreKeyBundleMessage; +import org.whispersystems.textsecure.storage.InvalidKeyIdException; + +import java.io.IOException; /** * Activity for displaying sent/received session keys. @@ -57,6 +63,7 @@ public class ReceiveKeyActivity extends Activity { private long messageId; private MasterSecret masterSecret; + private PreKeyBundleMessage keyExchangeMessageBundle; private KeyExchangeMessage keyExchangeMessage; private KeyExchangeProcessor keyExchangeProcessor; @@ -85,8 +92,8 @@ public class ReceiveKeyActivity extends Activity { } private void initializeText() { - if (keyExchangeProcessor.isTrusted(keyExchangeMessage)) initializeTrustedText(); - else initializeUntrustedText(); + if (isTrusted(keyExchangeMessage, keyExchangeMessageBundle)) initializeTrustedText(); + else initializeUntrustedText(); } private void initializeTrustedText() { @@ -102,6 +109,9 @@ public class ReceiveKeyActivity extends Activity { Intent intent = new Intent(ReceiveKeyActivity.this, VerifyIdentityActivity.class); intent.putExtra("recipient", recipient); intent.putExtra("master_secret", masterSecret); + intent.putExtra("remote_identity", + keyExchangeMessage == null ? + keyExchangeMessageBundle.getIdentityKey() : keyExchangeMessage.getIdentityKey()); startActivity(intent); } }, getString(R.string.ReceiveKeyActivity_the_signature_on_this_key_exchange_is_different).length() +1, @@ -111,9 +121,26 @@ public class ReceiveKeyActivity extends Activity { descriptionText.setMovementMethod(LinkMovementMethod.getInstance()); } + private boolean isTrusted(KeyExchangeMessage message, PreKeyBundleMessage messageBundle) { + return (message != null && keyExchangeProcessor.isTrusted(message)) || + (messageBundle != null && keyExchangeProcessor.isTrusted(messageBundle)); + } + private void initializeKey() throws InvalidKeyException, InvalidVersionException { - String messageBody = getIntent().getStringExtra("body"); - this.keyExchangeMessage = new KeyExchangeMessage(messageBody); + try { + String messageBody = getIntent().getStringExtra("body"); + + if (getIntent().getBooleanExtra("is_bundle", false)) { + SmsTransportDetails transportDetails = new SmsTransportDetails(); + byte[] body = transportDetails.getDecodedMessage(messageBody.getBytes()); + + this.keyExchangeMessageBundle = new PreKeyBundleMessage(body); + } else { + this.keyExchangeMessage = new KeyExchangeMessage(messageBody); + } + } catch (IOException e) { + throw new AssertionError(e); + } } private void initializeResources() { @@ -123,7 +150,7 @@ public class ReceiveKeyActivity extends Activity { this.recipient = getIntent().getParcelableExtra("recipient"); this.threadId = getIntent().getLongExtra("thread_id", -1); this.messageId = getIntent().getLongExtra("message_id", -1); - this.masterSecret = (MasterSecret)getIntent().getParcelableExtra("master_secret"); + this.masterSecret = getIntent().getParcelableExtra("master_secret"); this.keyExchangeProcessor = new KeyExchangeProcessor(this, masterSecret, recipient); } @@ -140,15 +167,39 @@ public class ReceiveKeyActivity extends Activity { @Override protected void onPreExecute() { - dialog = ProgressDialog.show(ReceiveKeyActivity.this, "Processing", - "Processing key exchange...", true); + dialog = ProgressDialog.show(ReceiveKeyActivity.this, + getString(R.string.ReceiveKeyActivity_processing), + getString(R.string.ReceiveKeyActivity_processing_key_exchange), + true); } @Override protected Void doInBackground(Void... params) { - keyExchangeProcessor.processKeyExchangeMessage(keyExchangeMessage, threadId); - DatabaseFactory.getEncryptingSmsDatabase(ReceiveKeyActivity.this) - .markAsProcessedKeyExchange(messageId); + if (keyExchangeMessage != null) { + keyExchangeProcessor.processKeyExchangeMessage(keyExchangeMessage, threadId); + DatabaseFactory.getEncryptingSmsDatabase(ReceiveKeyActivity.this) + .markAsProcessedKeyExchange(messageId); + } else if (keyExchangeMessageBundle != null) { + try { + keyExchangeProcessor.processKeyExchangeMessage(keyExchangeMessageBundle); + byte[] bundledMessage = keyExchangeMessageBundle.getBundledMessage(); + SmsTransportDetails transportDetails = new SmsTransportDetails(); + String messageBody = new String(transportDetails.getEncodedMessage(bundledMessage)); + + DatabaseFactory.getEncryptingSmsDatabase(ReceiveKeyActivity.this) + .updateBundleMessageBody(masterSecret, messageId, messageBody); + + DecryptingQueue.scheduleDecryption(ReceiveKeyActivity.this, masterSecret, messageId, + threadId, recipient.getNumber(), messageBody, + true, false); + } catch (InvalidKeyIdException e) { + Log.w("ReceiveKeyActivity", e); + DatabaseFactory.getEncryptingSmsDatabase(ReceiveKeyActivity.this) + .markAsCorruptKeyExchange(messageId); + } + } + + return null; } diff --git a/src/org/thoughtcrime/securesms/VerifyIdentityActivity.java b/src/org/thoughtcrime/securesms/VerifyIdentityActivity.java index 062004ab9b..5c8763d2cb 100644 --- a/src/org/thoughtcrime/securesms/VerifyIdentityActivity.java +++ b/src/org/thoughtcrime/securesms/VerifyIdentityActivity.java @@ -66,8 +66,12 @@ public class VerifyIdentityActivity extends KeyScanningActivity { } private void initializeRemoteIdentityKey() { - SessionRecord sessionRecord = new SessionRecord(this, masterSecret, recipient); - IdentityKey identityKey = sessionRecord.getIdentityKey(); + IdentityKey identityKey = getIntent().getParcelableExtra("remote_identity"); + + if (identityKey == null) { + SessionRecord sessionRecord = new SessionRecord(this, masterSecret, recipient); + identityKey = sessionRecord.getIdentityKey(); + } if (identityKey == null) { remoteIdentityFingerprint.setText(R.string.VerifyIdentityActivity_recipient_has_no_identity_key); diff --git a/src/org/thoughtcrime/securesms/crypto/KeyExchangeProcessor.java b/src/org/thoughtcrime/securesms/crypto/KeyExchangeProcessor.java index 8a452db32d..781041f116 100644 --- a/src/org/thoughtcrime/securesms/crypto/KeyExchangeProcessor.java +++ b/src/org/thoughtcrime/securesms/crypto/KeyExchangeProcessor.java @@ -40,6 +40,7 @@ import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.sms.MessageSender; import org.thoughtcrime.securesms.sms.OutgoingKeyExchangeMessage; import org.whispersystems.textsecure.util.Conversions; +import org.whispersystems.textsecure.util.Medium; /** * This class processes key exchange interactions. @@ -131,13 +132,15 @@ public class KeyExchangeProcessor { remoteKeyRecord.getCurrentRemoteKey().getFingerprintBytes()); sessionRecord.setIdentityKey(remoteIdentity); sessionRecord.setSessionVersion(Math.min(message.getSupportedVersion(), MessageCipher.SUPPORTED_VERSION)); - + sessionRecord.setNegotiatedSessionVersion(sessionRecord.getSessionVersion()); localKeyRecord.save(); remoteKeyRecord.save(); sessionRecord.save(); - PreKeyRecord.delete(context, preKeyId); + if (preKeyId != Medium.MAX_VALUE) { + PreKeyRecord.delete(context, preKeyId); + } DatabaseFactory.getIdentityDatabase(context) .saveIdentity(masterSecret, recipient, remoteIdentity); @@ -156,6 +159,7 @@ public class KeyExchangeProcessor { sessionRecord.setSessionId(localKeyRecord.getCurrentKeyPair().getPublicKey().getFingerprintBytes(), remoteKeyRecord.getCurrentRemoteKey().getFingerprintBytes()); sessionRecord.setIdentityKey(message.getIdentityKey()); + sessionRecord.setNegotiatedSessionVersion(MessageCipher.SUPPORTED_VERSION); sessionRecord.setSessionVersion(MessageCipher.SUPPORTED_VERSION); sessionRecord.setPrekeyBundleRequired(true); sessionRecord.save(); @@ -185,6 +189,7 @@ public class KeyExchangeProcessor { remoteKeyRecord.getCurrentRemoteKey().getFingerprintBytes()); sessionRecord.setIdentityKey(message.getIdentityKey()); sessionRecord.setSessionVersion(Math.min(MessageCipher.SUPPORTED_VERSION, message.getMaxVersion())); + sessionRecord.setNegotiatedSessionVersion(sessionRecord.getSessionVersion()); Log.w("KeyExchangeUtil", "Setting session version: " + Math.min(MessageCipher.SUPPORTED_VERSION, message.getMaxVersion())); diff --git a/src/org/thoughtcrime/securesms/crypto/protocol/KeyExchangeMessage.java b/src/org/thoughtcrime/securesms/crypto/protocol/KeyExchangeMessage.java index 9d0e6143f5..11aa016393 100644 --- a/src/org/thoughtcrime/securesms/crypto/protocol/KeyExchangeMessage.java +++ b/src/org/thoughtcrime/securesms/crypto/protocol/KeyExchangeMessage.java @@ -29,6 +29,7 @@ import org.whispersystems.textsecure.crypto.PublicKey; import org.whispersystems.textsecure.storage.LocalKeyRecord; import org.whispersystems.textsecure.util.Base64; import org.whispersystems.textsecure.util.Conversions; +import org.whispersystems.textsecure.util.Util; import java.io.IOException; @@ -70,20 +71,26 @@ public class KeyExchangeMessage { this.supportedVersion = MessageCipher.SUPPORTED_VERSION; publicKey.setId(publicKey.getId() | (highIdBits << 12)); - + + byte[] versionBytes = {Conversions.intsToByteHighAndLow(messageVersion, supportedVersion)}; byte[] publicKeyBytes = publicKey.serialize(); - byte[] keyExchangeBytes = new byte[1 + publicKeyBytes.length]; - - keyExchangeBytes[0] = Conversions.intsToByteHighAndLow(messageVersion, supportedVersion); - System.arraycopy(publicKeyBytes, 0, keyExchangeBytes, 1, publicKeyBytes.length); - if (includeIdentitySignature(messageVersion, context)) - keyExchangeBytes = IdentityKeyUtil.getSignedKeyExchange(context, masterSecret, keyExchangeBytes); + byte[] serializedBytes; - if (messageVersion < 1) - this.serialized = Base64.encodeBytes(keyExchangeBytes); - else - this.serialized = Base64.encodeBytesWithoutPadding(keyExchangeBytes); + if (includeIdentityNoSignature(messageVersion, context)) { + byte[] identityKey = IdentityKeyUtil.getIdentityKey(context).serialize(); + + serializedBytes = Util.combine(versionBytes, publicKeyBytes, identityKey); + } else if (includeIdentitySignature(messageVersion, context)) { + byte[] prolog = Util.combine(versionBytes, publicKeyBytes); + + serializedBytes = IdentityKeyUtil.getSignedKeyExchange(context, masterSecret, prolog); + } else { + serializedBytes = Util.combine(versionBytes, publicKeyBytes); + } + + if (messageVersion < 1) this.serialized = Base64.encodeBytes(serializedBytes); + else this.serialized = Base64.encodeBytesWithoutPadding(serializedBytes); } public KeyExchangeMessage(String messageBody) throws InvalidVersionException, InvalidKeyException { @@ -104,23 +111,33 @@ public class KeyExchangeMessage { if (keyBytes.length <= PublicKey.KEY_SIZE + 1) { this.identityKey = null; - } else { + } else if (messageVersion == 1) { try { this.identityKey = IdentityKeyUtil.verifySignedKeyExchange(keyBytes); } catch (InvalidKeyException ike) { Log.w("KeyUtil", ike); this.identityKey = null; } - } + } else if (messageVersion == 2) { + try { + this.identityKey = new IdentityKey(keyBytes, 1 + PublicKey.KEY_SIZE); + } catch (InvalidKeyException ike) { + Log.w("KeyUtil", ike); + this.identityKey = null; + } + } } catch (IOException ioe) { throw new InvalidKeyException(ioe); } } private static boolean includeIdentitySignature(int messageVersion, Context context) { - return IdentityKeyUtil.hasIdentityKey(context) && (messageVersion >= 1); + return IdentityKeyUtil.hasIdentityKey(context) && (messageVersion == 1); } + private static boolean includeIdentityNoSignature(int messageVersion, Context context) { + return IdentityKeyUtil.hasIdentityKey(context) && (messageVersion >= 2); + } public PublicKey getPublicKey() { return publicKey; diff --git a/src/org/thoughtcrime/securesms/database/DatabaseFactory.java b/src/org/thoughtcrime/securesms/database/DatabaseFactory.java index 3315f7ada0..f88ec7ca65 100644 --- a/src/org/thoughtcrime/securesms/database/DatabaseFactory.java +++ b/src/org/thoughtcrime/securesms/database/DatabaseFactory.java @@ -628,7 +628,7 @@ public class DatabaseFactory { if (oldVersion < INTRODUCED_PUSH_DATABASE_VERSION) { db.execSQL("CREATE TABLE push (_id INTEGER PRIMARY KEY, type INTEGER, source TEXT, destinations TEXT, body TEXT, TIMESTAMP INTEGER);"); db.execSQL("ALTER TABLE part ADD COLUMN pending_push INTEGER;"); - db.execSQL("CREATE INDEX IF NOT EXISTS pending_push_index ON parts (pending_push);"); + db.execSQL("CREATE INDEX IF NOT EXISTS pending_push_index ON part (pending_push);"); } db.setTransactionSuccessful(); diff --git a/src/org/thoughtcrime/securesms/database/EncryptingSmsDatabase.java b/src/org/thoughtcrime/securesms/database/EncryptingSmsDatabase.java index d4ed2f0f4b..a3a3eeaf58 100644 --- a/src/org/thoughtcrime/securesms/database/EncryptingSmsDatabase.java +++ b/src/org/thoughtcrime/securesms/database/EncryptingSmsDatabase.java @@ -96,6 +96,11 @@ public class EncryptingSmsDatabase extends SmsDatabase { return insertMessageInbox(message, type); } + public void updateBundleMessageBody(MasterSecret masterSecret, long messageId, String body) { + updateMessageBodyAndType(messageId, body, Types.TOTAL_MASK, + Types.BASE_INBOX_TYPE | Types.ENCRYPTION_REMOTE_BIT | Types.SECURE_MESSAGE_BIT); + } + public void updateMessageBody(MasterSecret masterSecret, long messageId, String body) { String encryptedBody = getEncryptedBody(masterSecret, body); updateMessageBodyAndType(messageId, encryptedBody, Types.ENCRYPTION_MASK, diff --git a/src/org/thoughtcrime/securesms/database/MmsSmsColumns.java b/src/org/thoughtcrime/securesms/database/MmsSmsColumns.java index 0579d1dd4e..9bdba4565c 100644 --- a/src/org/thoughtcrime/securesms/database/MmsSmsColumns.java +++ b/src/org/thoughtcrime/securesms/database/MmsSmsColumns.java @@ -32,6 +32,7 @@ public interface MmsSmsColumns { protected static final long KEY_EXCHANGE_PROCESSED_BIT = 0x2000; protected static final long KEY_EXCHANGE_CORRUPTED_BIT = 0x1000; protected static final long KEY_EXCHANGE_INVALID_VERSION_BIT = 0x800; + protected static final long KEY_EXCHANGE_BUNDLE_BIT = 0x400; // Secure Message Information protected static final long SECURE_MESSAGE_BIT = 0x800000; @@ -91,6 +92,10 @@ public interface MmsSmsColumns { return (type & KEY_EXCHANGE_INVALID_VERSION_BIT) != 0; } + public static boolean isBundleKeyExchange(long type) { + return (type & KEY_EXCHANGE_BUNDLE_BIT) != 0; + } + public static boolean isSymmetricEncryption(long type) { return (type & ENCRYPTION_SYMMETRIC_BIT) != 0; } diff --git a/src/org/thoughtcrime/securesms/database/SmsDatabase.java b/src/org/thoughtcrime/securesms/database/SmsDatabase.java index 703d43ac5b..06800a3d20 100644 --- a/src/org/thoughtcrime/securesms/database/SmsDatabase.java +++ b/src/org/thoughtcrime/securesms/database/SmsDatabase.java @@ -162,6 +162,10 @@ public class SmsDatabase extends Database implements MmsSmsColumns { updateTypeBitmask(id, 0, Types.KEY_EXCHANGE_PROCESSED_BIT); } + public void markAsCorruptKeyExchange(long id) { + updateTypeBitmask(id, 0, Types.KEY_EXCHANGE_CORRUPTED_BIT); + } + public void markAsDecryptFailed(long id) { updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_FAILED_BIT); } @@ -239,6 +243,7 @@ public class SmsDatabase extends Database implements MmsSmsColumns { else if (((IncomingKeyExchangeMessage)message).isProcessed()) type |= Types.KEY_EXCHANGE_PROCESSED_BIT; else if (((IncomingKeyExchangeMessage)message).isCorrupted()) type |= Types.KEY_EXCHANGE_CORRUPTED_BIT; else if (((IncomingKeyExchangeMessage)message).isInvalidVersion()) type |= Types.KEY_EXCHANGE_INVALID_VERSION_BIT; + else if (((IncomingKeyExchangeMessage)message).isPreKeyBundle()) type |= Types.KEY_EXCHANGE_BUNDLE_BIT; } else if (message.isSecureMessage()) { type |= Types.SECURE_MESSAGE_BIT; type |= Types.ENCRYPTION_REMOTE_BIT; diff --git a/src/org/thoughtcrime/securesms/database/model/MessageRecord.java b/src/org/thoughtcrime/securesms/database/model/MessageRecord.java index ec43b0843e..db8f6611f0 100644 --- a/src/org/thoughtcrime/securesms/database/model/MessageRecord.java +++ b/src/org/thoughtcrime/securesms/database/model/MessageRecord.java @@ -103,6 +103,10 @@ public abstract class MessageRecord extends DisplayRecord { return SmsDatabase.Types.isProcessedKeyExchange(type); } + public boolean isBundleKeyExchange() { + return SmsDatabase.Types.isBundleKeyExchange(type); + } + public boolean isCorruptedKeyExchange() { return SmsDatabase.Types.isCorruptedKeyExchange(type); } diff --git a/src/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java b/src/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java index 41dd394c0f..3347b02ebc 100644 --- a/src/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java +++ b/src/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java @@ -56,6 +56,12 @@ public class SmsMessageRecord extends MessageRecord { return emphasisAdded(context.getString(R.string.ConversationItem_received_and_processed_key_exchange_message)); } else if (isStaleKeyExchange()) { return emphasisAdded(context.getString(R.string.ConversationItem_error_received_stale_key_exchange_message)); + } else if (isCorruptedKeyExchange()) { + return emphasisAdded(context.getString(R.string.SmsMessageRecord_received_corrupted_key_exchange_message)); + } else if (isInvalidVersionKeyExchange()) { + return emphasisAdded(context.getString(R.string.SmsMessageRecord_received_key_exchange_message_for_invalid_protocol_version)); + } else if (isBundleKeyExchange()) { + return emphasisAdded(context.getString(R.string.SmsMessageRecord_received_message_with_unknown_identity_key_click_to_process)); } else if (isKeyExchange() && isOutgoing()) { return emphasisAdded(context.getString(R.string.ConversationListAdapter_key_exchange_message)); } else if (isKeyExchange() && !isOutgoing()) { diff --git a/src/org/thoughtcrime/securesms/service/PushReceiver.java b/src/org/thoughtcrime/securesms/service/PushReceiver.java index ce7fc3d9b9..ead7b8dab4 100644 --- a/src/org/thoughtcrime/securesms/service/PushReceiver.java +++ b/src/org/thoughtcrime/securesms/service/PushReceiver.java @@ -18,7 +18,10 @@ import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage; import org.thoughtcrime.securesms.sms.IncomingKeyExchangeMessage; +import org.thoughtcrime.securesms.sms.IncomingPreKeyBundleMessage; import org.thoughtcrime.securesms.sms.IncomingTextMessage; +import org.thoughtcrime.securesms.sms.SmsTransportDetails; +import org.thoughtcrime.securesms.transport.SmsTransport; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.whispersystems.textsecure.crypto.InvalidKeyException; import org.whispersystems.textsecure.crypto.InvalidVersionException; @@ -99,7 +102,12 @@ public class PushReceiver { IncomingPushMessage bundledMessage = message.withBody(preKeyExchange.getBundledMessage()); handleReceivedSecureMessage(masterSecret, bundledMessage); } else { - /// XXX + SmsTransportDetails transportDetails = new SmsTransportDetails(); + String encoded = new String(transportDetails.getEncodedMessage(message.getBody())); + IncomingTextMessage textMessage = new IncomingTextMessage(message, ""); + + textMessage = new IncomingPreKeyBundleMessage(textMessage, encoded); + DatabaseFactory.getEncryptingSmsDatabase(context).insertMessageInbox(masterSecret, textMessage); } } catch (InvalidKeyException e) { Log.w("SmsReceiver", e); @@ -118,6 +126,7 @@ public class PushReceiver { boolean secure) { try { + Log.w("PushReceiver", "Processing: " + new String(message.getBody())); PushMessageContent messageContent = PushMessageContent.parseFrom(message.getBody()); if (messageContent.getAttachmentsCount() > 0 || message.getDestinations().size() > 0) { diff --git a/src/org/thoughtcrime/securesms/service/SmsReceiver.java b/src/org/thoughtcrime/securesms/service/SmsReceiver.java index 528da2a4ca..bf0b205c82 100644 --- a/src/org/thoughtcrime/securesms/service/SmsReceiver.java +++ b/src/org/thoughtcrime/securesms/service/SmsReceiver.java @@ -120,8 +120,6 @@ public class SmsReceiver { context.sendBroadcast(intent, KeyCachingService.KEY_PERMISSION); return messageAndThreadId; - } else { - /// XXX } } catch (InvalidKeyException e) { Log.w("SmsReceiver", e);