From dadabdfaa8a15a0f1d64cda6f32307a30ba237d4 Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Mon, 18 Nov 2013 13:16:18 -0800 Subject: [PATCH] Make UI responsive to UniversalTransport upgrades. --- .../securesms/crypto/KeyExchangeProcessor.java | 8 +++++++- .../securesms/database/SmsDatabase.java | 4 ++++ .../securesms/service/MmsSender.java | 15 +++++++-------- .../securesms/service/SmsSender.java | 10 ++++++++-- .../securesms/sms/MessageSender.java | 1 + .../securesms/transport/BaseTransport.java | 3 ++- .../securesms/transport/PushTransport.java | 18 +++++++++++------- .../securesms/transport/SmsTransport.java | 11 ++++++----- .../transport/UniversalTransport.java | 6 ++++-- 9 files changed, 50 insertions(+), 26 deletions(-) diff --git a/src/org/thoughtcrime/securesms/crypto/KeyExchangeProcessor.java b/src/org/thoughtcrime/securesms/crypto/KeyExchangeProcessor.java index 78de44d75a..30595bf3c4 100644 --- a/src/org/thoughtcrime/securesms/crypto/KeyExchangeProcessor.java +++ b/src/org/thoughtcrime/securesms/crypto/KeyExchangeProcessor.java @@ -146,7 +146,7 @@ public class KeyExchangeProcessor { .saveIdentity(masterSecret, recipient, remoteIdentity); } - public void processKeyExchangeMessage(PreKeyEntity message) { + public void processKeyExchangeMessage(PreKeyEntity message, long threadId) { PublicKey remoteKey = new PublicKey(message.getKeyId(), message.getPublicKey()); remoteKeyRecord.setCurrentRemoteKey(remoteKey); remoteKeyRecord.setLastRemoteKey(remoteKey); @@ -166,6 +166,8 @@ public class KeyExchangeProcessor { DatabaseFactory.getIdentityDatabase(context) .saveIdentity(masterSecret, recipient, message.getIdentityKey()); + + broadcastSecurityUpdateEvent(context, threadId); } public void processKeyExchangeMessage(KeyExchangeMessage message, long threadId) { @@ -202,6 +204,10 @@ public class KeyExchangeProcessor { DecryptingQueue.scheduleRogueMessages(context, masterSecret, recipient); + broadcastSecurityUpdateEvent(context, threadId); + } + + private static void broadcastSecurityUpdateEvent(Context context, long threadId) { Intent intent = new Intent(SECURITY_UPDATE_EVENT); intent.putExtra("thread_id", threadId); intent.setPackage(context.getPackageName()); diff --git a/src/org/thoughtcrime/securesms/database/SmsDatabase.java b/src/org/thoughtcrime/securesms/database/SmsDatabase.java index 06800a3d20..100eb27a0e 100644 --- a/src/org/thoughtcrime/securesms/database/SmsDatabase.java +++ b/src/org/thoughtcrime/securesms/database/SmsDatabase.java @@ -166,6 +166,10 @@ public class SmsDatabase extends Database implements MmsSmsColumns { updateTypeBitmask(id, 0, Types.KEY_EXCHANGE_CORRUPTED_BIT); } + public void markAsSecure(long id) { + updateTypeBitmask(id, 0, Types.SECURE_MESSAGE_BIT); + } + public void markAsDecryptFailed(long id) { updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_FAILED_BIT); } diff --git a/src/org/thoughtcrime/securesms/service/MmsSender.java b/src/org/thoughtcrime/securesms/service/MmsSender.java index e63c576dfc..a38d8130d4 100644 --- a/src/org/thoughtcrime/securesms/service/MmsSender.java +++ b/src/org/thoughtcrime/securesms/service/MmsSender.java @@ -21,7 +21,6 @@ import android.content.Intent; import android.util.Log; import android.util.Pair; -import org.whispersystems.textsecure.crypto.MasterSecret; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.ThreadDatabase; @@ -30,23 +29,22 @@ import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.service.SendReceiveService.ToastHandler; import org.thoughtcrime.securesms.transport.UndeliverableMessageException; import org.thoughtcrime.securesms.transport.UniversalTransport; +import org.whispersystems.textsecure.crypto.MasterSecret; import ws.com.google.android.mms.MmsException; import ws.com.google.android.mms.pdu.SendReq; public class MmsSender { - private final Context context; - private final ToastHandler toastHandler; + private final Context context; public MmsSender(Context context, ToastHandler toastHandler) { - this.context = context; - this.toastHandler = toastHandler; + this.context = context; } public void process(MasterSecret masterSecret, Intent intent) { Log.w("MmsSender", "Got intent action: " + intent.getAction()); - if (intent.getAction().equals(SendReceiveService.SEND_MMS_ACTION)) { + if (SendReceiveService.SEND_MMS_ACTION.equals(intent.getAction())) { handleSendMms(masterSecret, intent); } } @@ -61,15 +59,16 @@ public class MmsSender { SendReq[] messages = database.getOutgoingMessages(masterSecret, messageId); for (SendReq message : messages) { + long threadId = database.getThreadIdForMessage(message.getDatabaseMessageId()); + try { Log.w("MmsSender", "Passing to MMS transport: " + message.getDatabaseMessageId()); database.markAsSending(message.getDatabaseMessageId()); - Pair result = transport.deliver(message); + Pair result = transport.deliver(message, threadId); database.markAsSent(message.getDatabaseMessageId(), result.first, result.second); } catch (UndeliverableMessageException e) { Log.w("MmsSender", e); database.markAsSentFailed(message.getDatabaseMessageId()); - long threadId = database.getThreadIdForMessage(messageId); Recipients recipients = threads.getRecipientsForThreadId(threadId); MessageNotifier.notifyMessageDeliveryFailed(context, recipients, threadId); } diff --git a/src/org/thoughtcrime/securesms/service/SmsSender.java b/src/org/thoughtcrime/securesms/service/SmsSender.java index a982bda673..175f87ef54 100644 --- a/src/org/thoughtcrime/securesms/service/SmsSender.java +++ b/src/org/thoughtcrime/securesms/service/SmsSender.java @@ -83,14 +83,20 @@ public class SmsSender { } private void handleSentMessage(Intent intent) { - long messageId = intent.getLongExtra("message_id", -1); - int result = intent.getIntExtra("ResultCode", -31337); + long messageId = intent.getLongExtra("message_id", -1); + int result = intent.getIntExtra("ResultCode", -31337); + boolean upgraded = intent.getBooleanExtra("upgraded", false); Log.w("SMSReceiverService", "Intent resultcode: " + result); Log.w("SMSReceiverService", "Running sent callback: " + messageId); if (result == Activity.RESULT_OK) { DatabaseFactory.getSmsDatabase(context).markAsSent(messageId); + + if (upgraded) { + DatabaseFactory.getSmsDatabase(context).markAsSecure(messageId); + } + unregisterForRadioChanges(); } else if (result == SmsManager.RESULT_ERROR_NO_SERVICE || result == SmsManager.RESULT_ERROR_RADIO_OFF) { DatabaseFactory.getSmsDatabase(context).markAsOutbox(messageId); diff --git a/src/org/thoughtcrime/securesms/sms/MessageSender.java b/src/org/thoughtcrime/securesms/sms/MessageSender.java index 8ef6347a18..b3aed40846 100644 --- a/src/org/thoughtcrime/securesms/sms/MessageSender.java +++ b/src/org/thoughtcrime/securesms/sms/MessageSender.java @@ -133,6 +133,7 @@ public class MessageSender { Intent intent = new Intent(SendReceiveService.SEND_MMS_ACTION, null, context, SendReceiveService.class); intent.putExtra("message_id", messageId); + intent.putExtra("thread_id", threadId); context.startService(intent); } diff --git a/src/org/thoughtcrime/securesms/transport/BaseTransport.java b/src/org/thoughtcrime/securesms/transport/BaseTransport.java index 9dc469ae08..bd357dc9f6 100644 --- a/src/org/thoughtcrime/securesms/transport/BaseTransport.java +++ b/src/org/thoughtcrime/securesms/transport/BaseTransport.java @@ -9,13 +9,14 @@ import org.thoughtcrime.securesms.service.SmsListener; public abstract class BaseTransport { - protected Intent constructSentIntent(Context context, long messageId, long type) { + protected Intent constructSentIntent(Context context, long messageId, long type, boolean upgraded) { Intent pending = new Intent(SendReceiveService.SENT_SMS_ACTION, Uri.parse("custom://" + messageId + System.currentTimeMillis()), context, SmsListener.class); pending.putExtra("type", type); pending.putExtra("message_id", messageId); + pending.putExtra("upgraded", upgraded); return pending; } diff --git a/src/org/thoughtcrime/securesms/transport/PushTransport.java b/src/org/thoughtcrime/securesms/transport/PushTransport.java index 8b4e897b03..121dc3c459 100644 --- a/src/org/thoughtcrime/securesms/transport/PushTransport.java +++ b/src/org/thoughtcrime/securesms/transport/PushTransport.java @@ -73,24 +73,27 @@ public class PushTransport extends BaseTransport { try { TextSecurePushCredentials credentials = TextSecurePushCredentials.getInstance(); Recipient recipient = message.getIndividualRecipient(); + long threadId = message.getThreadId(); PushServiceSocket socket = new PushServiceSocket(context, credentials); PushDestination destination = PushDestination.create(context, credentials, recipient.getNumber()); String plaintextBody = message.getBody().getBody(); byte[] plaintext = PushMessageContent.newBuilder().setBody(plaintextBody).build().toByteArray(); - PushBody pushBody = getEncryptedMessage(socket, recipient, destination, plaintext); + PushBody pushBody = getEncryptedMessage(socket, threadId, recipient, destination, plaintext); socket.sendMessage(destination, pushBody); - context.sendBroadcast(constructSentIntent(context, message.getId(), message.getType())); + context.sendBroadcast(constructSentIntent(context, message.getId(), message.getType(), true)); } catch (RateLimitException e) { Log.w("PushTransport", e); throw new IOException("Rate limit exceeded."); } } - public void deliver(SendReq message, List destinations) throws IOException { + public void deliver(SendReq message, List destinations, long threadId) + throws IOException + { try { TextSecurePushCredentials credentials = TextSecurePushCredentials.getInstance(); PushServiceSocket socket = new PushServiceSocket(context, credentials); @@ -118,7 +121,7 @@ public class PushTransport extends BaseTransport { } byte[] plaintext = builder.build().toByteArray(); - PushBody pushBody = getEncryptedMessage(socket, recipients.getPrimaryRecipient(), destination, plaintext); + PushBody pushBody = getEncryptedMessage(socket, threadId, recipients.getPrimaryRecipient(), destination, plaintext); pushBodies.add(pushBody); } @@ -158,7 +161,7 @@ public class PushTransport extends BaseTransport { return attachments; } - private PushBody getEncryptedMessage(PushServiceSocket socket, Recipient recipient, + private PushBody getEncryptedMessage(PushServiceSocket socket, long threadId, Recipient recipient, PushDestination pushDestination, byte[] plaintext) throws IOException { @@ -172,7 +175,7 @@ public class PushTransport extends BaseTransport { return new PushBody(OutgoingPushMessage.TYPE_MESSAGE_PREKEY_BUNDLE, ciphertext); } else { Log.w("PushTransport", "Sending prekeybundle ciphertext message for new session..."); - byte[] ciphertext = getEncryptedPrekeyBundleMessageForNewSession(socket, recipient, pushDestination, plaintext); + byte[] ciphertext = getEncryptedPrekeyBundleMessageForNewSession(socket, threadId, recipient, pushDestination, plaintext); return new PushBody(OutgoingPushMessage.TYPE_MESSAGE_PREKEY_BUNDLE, ciphertext); } } @@ -190,6 +193,7 @@ public class PushTransport extends BaseTransport { } private byte[] getEncryptedPrekeyBundleMessageForNewSession(PushServiceSocket socket, + long threadId, Recipient recipient, PushDestination pushDestination, byte[] plaintext) @@ -200,7 +204,7 @@ public class PushTransport extends BaseTransport { PreKeyEntity preKey = socket.getPreKey(pushDestination); KeyExchangeProcessor processor = new KeyExchangeProcessor(context, masterSecret, recipient); - processor.processKeyExchangeMessage(preKey); + processor.processKeyExchangeMessage(preKey, threadId); MessageCipher messageCipher = new MessageCipher(context, masterSecret, identityKeyPair); CiphertextMessage ciphertextMessage = messageCipher.encrypt(recipient, plaintext); diff --git a/src/org/thoughtcrime/securesms/transport/SmsTransport.java b/src/org/thoughtcrime/securesms/transport/SmsTransport.java index 768e36d176..38928059f1 100644 --- a/src/org/thoughtcrime/securesms/transport/SmsTransport.java +++ b/src/org/thoughtcrime/securesms/transport/SmsTransport.java @@ -37,7 +37,6 @@ import org.whispersystems.textsecure.crypto.KeyUtil; import org.whispersystems.textsecure.crypto.MasterSecret; import org.whispersystems.textsecure.crypto.MessageCipher; import org.whispersystems.textsecure.crypto.ecc.Curve; -import org.whispersystems.textsecure.crypto.ecc.ECPublicKey; import org.whispersystems.textsecure.crypto.protocol.CiphertextMessage; import org.whispersystems.textsecure.crypto.protocol.PreKeyBundleMessage; @@ -70,7 +69,7 @@ public class SmsTransport extends BaseTransport { } ArrayList messages = multipartMessageHandler.divideMessage(transportMessage); - ArrayList sentIntents = constructSentIntents(message.getId(), message.getType(), messages); + ArrayList sentIntents = constructSentIntents(message.getId(), message.getType(), messages, true); ArrayList deliveredIntents = constructDeliveredIntents(message.getId(), message.getType(), messages); Log.w("SmsTransport", "Secure divide into message parts: " + messages.size()); @@ -103,7 +102,7 @@ public class SmsTransport extends BaseTransport { throws UndeliverableMessageException { ArrayList messages = SmsManager.getDefault().divideMessage(message.getBody().getBody()); - ArrayList sentIntents = constructSentIntents(message.getId(), message.getType(), messages); + ArrayList sentIntents = constructSentIntents(message.getId(), message.getType(), messages, false); ArrayList deliveredIntents = constructDeliveredIntents(message.getId(), message.getType(), messages); String recipient = message.getIndividualRecipient().getNumber(); @@ -132,12 +131,14 @@ public class SmsTransport extends BaseTransport { } } - private ArrayList constructSentIntents(long messageId, long type, ArrayList messages) { + private ArrayList constructSentIntents(long messageId, long type, + ArrayList messages, boolean secure) + { ArrayList sentIntents = new ArrayList(messages.size()); for (String ignored : messages) { sentIntents.add(PendingIntent.getBroadcast(context, 0, - constructSentIntent(context, messageId, type), + constructSentIntent(context, messageId, type, secure), 0)); } diff --git a/src/org/thoughtcrime/securesms/transport/UniversalTransport.java b/src/org/thoughtcrime/securesms/transport/UniversalTransport.java index 37fd4993cb..4b5230c256 100644 --- a/src/org/thoughtcrime/securesms/transport/UniversalTransport.java +++ b/src/org/thoughtcrime/securesms/transport/UniversalTransport.java @@ -76,7 +76,9 @@ public class UniversalTransport { } } - public Pair deliver(SendReq mediaMessage) throws UndeliverableMessageException { + public Pair deliver(SendReq mediaMessage, long threadId) + throws UndeliverableMessageException + { if (!TextSecurePreferences.isPushRegistered(context)) { return mmsTransport.deliver(mediaMessage); } @@ -86,7 +88,7 @@ public class UniversalTransport { if (isPushTransport(destinations)) { try { Log.w("UniversalTransport", "Delivering media message with GCM..."); - pushTransport.deliver(mediaMessage, destinations); + pushTransport.deliver(mediaMessage, destinations, threadId); return new Pair("push".getBytes("UTF-8"), 0); } catch (IOException ioe) { Log.w("UniversalTransport", ioe);