From ad6760b62c66b6da8d591906dd85ae94aaed642c Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Wed, 15 May 2013 14:09:13 -0700 Subject: [PATCH] Correctly handle deferred processing of key exchange message when locked. --- .../securesms/crypto/DecryptingQueue.java | 80 ++++++++++++++----- .../securesms/database/SmsDatabase.java | 10 ++- .../securesms/service/SmsReceiver.java | 12 ++- 3 files changed, 77 insertions(+), 25 deletions(-) diff --git a/src/org/thoughtcrime/securesms/crypto/DecryptingQueue.java b/src/org/thoughtcrime/securesms/crypto/DecryptingQueue.java index cce8f67dfe..6d871c3271 100644 --- a/src/org/thoughtcrime/securesms/crypto/DecryptingQueue.java +++ b/src/org/thoughtcrime/securesms/crypto/DecryptingQueue.java @@ -18,8 +18,10 @@ package org.thoughtcrime.securesms.crypto; import android.content.Context; import android.database.Cursor; +import android.preference.PreferenceManager; import android.util.Log; +import org.thoughtcrime.securesms.ApplicationPreferencesActivity; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.EncryptingSmsDatabase; import org.thoughtcrime.securesms.database.MmsDatabase; @@ -71,11 +73,11 @@ public class DecryptingQueue { } public static void scheduleDecryption(Context context, MasterSecret masterSecret, - long messageId, String originator, String body, - boolean isSecureMessage) + long messageId, long threadId, String originator, + String body, boolean isSecureMessage, boolean isKeyExchange) { - DecryptionWorkItem runnable = new DecryptionWorkItem(context, masterSecret, messageId, - originator, body, isSecureMessage); + DecryptionWorkItem runnable = new DecryptionWorkItem(context, masterSecret, messageId, threadId, + originator, body, isSecureMessage, isKeyExchange); synchronized (workQueue) { workQueue.add(runnable); workQueue.notifyAll(); @@ -122,11 +124,14 @@ public class DecryptingQueue { SmsMessageRecord record) { long messageId = record.getId(); + long threadId = record.getThreadId(); String body = record.getBody().getBody(); String originator = record.getIndividualRecipient().getNumber(); boolean isSecureMessage = record.isSecure(); + boolean isKeyExchange = record.isKeyExchange(); - scheduleDecryption(context, masterSecret, messageId, originator, body, isSecureMessage); + scheduleDecryption(context, masterSecret, messageId, threadId, + originator, body, isSecureMessage, isKeyExchange); } private static class MmsDecryptionItem implements Runnable { @@ -207,22 +212,26 @@ public class DecryptingQueue { private static class DecryptionWorkItem implements Runnable { - private final long messageId; - private final Context context; + private final long messageId; + private final long threadId; + private final Context context; private final MasterSecret masterSecret; - private final String body; - private final String originator; - private final boolean isSecureMessage; + private final String body; + private final String originator; + private final boolean isSecureMessage; + private final boolean isKeyExchange; - public DecryptionWorkItem(Context context, MasterSecret masterSecret, long messageId, - String originator, String body, boolean isSecureMessage) + public DecryptionWorkItem(Context context, MasterSecret masterSecret, long messageId, long threadId, + String originator, String body, boolean isSecureMessage, boolean isKeyExchange) { - this.context = context; - this.messageId = messageId; - this.masterSecret = masterSecret; - this.body = body; - this.originator = originator; + this.context = context; + this.messageId = messageId; + this.threadId = threadId; + this.masterSecret = masterSecret; + this.body = body; + this.originator = originator; this.isSecureMessage = isSecureMessage; + this.isKeyExchange = isKeyExchange; } private void handleRemoteAsymmetricEncrypt() { @@ -266,18 +275,47 @@ public class DecryptingQueue { try { AsymmetricMasterCipher asymmetricMasterCipher = new AsymmetricMasterCipher(MasterSecretUtil.getAsymmetricMasterSecret(context, masterSecret)); plaintextBody = asymmetricMasterCipher.decryptBody(body); + + if (isKeyExchange) { + handleKeyExchangeProcessing(plaintextBody); + } + + database.updateMessageBody(masterSecret, messageId, plaintextBody); + MessageNotifier.updateNotification(context, masterSecret); } catch (InvalidMessageException ime) { Log.w("DecryptionQueue", ime); database.markAsDecryptFailed(messageId); - return; } catch (IOException e) { Log.w("DecryptionQueue", e); database.markAsDecryptFailed(messageId); - return; } + } - database.updateMessageBody(masterSecret, messageId, plaintextBody); - MessageNotifier.updateNotification(context, masterSecret); + private void handleKeyExchangeProcessing(String plaintxtBody) { + if (PreferenceManager.getDefaultSharedPreferences(context) + .getBoolean(ApplicationPreferencesActivity.AUTO_KEY_EXCHANGE_PREF, true)) + { + try { + Recipient recipient = new Recipient(null, originator, null, null); + KeyExchangeMessage keyExchangeMessage = new KeyExchangeMessage(plaintxtBody); + KeyExchangeProcessor processor = new KeyExchangeProcessor(context, masterSecret, recipient); + + Log.w("DecryptingQuue", "KeyExchange with fingerprint: " + keyExchangeMessage.getPublicKey().getFingerprint()); + + if (processor.isStale(keyExchangeMessage)) { + DatabaseFactory.getEncryptingSmsDatabase(context).markAsStaleKeyExchange(messageId); + } else if (!processor.hasCompletedSession() || + processor.hasSameSessionIdentity(keyExchangeMessage)) + { + DatabaseFactory.getEncryptingSmsDatabase(context).markAsProcessedKeyExchange(messageId); + processor.processKeyExchangeMessage(keyExchangeMessage, threadId); + } + } catch (InvalidVersionException e) { + Log.w("DecryptingQueue", e); + } catch (InvalidKeyException e) { + Log.w("DecryptingQueue", e); + } + } } @Override diff --git a/src/org/thoughtcrime/securesms/database/SmsDatabase.java b/src/org/thoughtcrime/securesms/database/SmsDatabase.java index d3d5e75535..997b54d1f5 100644 --- a/src/org/thoughtcrime/securesms/database/SmsDatabase.java +++ b/src/org/thoughtcrime/securesms/database/SmsDatabase.java @@ -139,6 +139,14 @@ public class SmsDatabase extends Database implements MmsSmsColumns { return 0; } + public void markAsStaleKeyExchange(long id) { + updateTypeBitmask(id, 0, Types.KEY_EXCHANGE_STALE_BIT); + } + + public void markAsProcessedKeyExchange(long id) { + updateTypeBitmask(id, 0, Types.KEY_EXCHANGE_PROCESSED_BIT); + } + public void markAsDecryptFailed(long id) { updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_FAILED_BIT); } @@ -213,7 +221,7 @@ public class SmsDatabase extends Database implements MmsSmsColumns { if (message.isKeyExchange()) { type |= Types.KEY_EXCHANGE_BIT; if (((IncomingKeyExchangeMessage)message).isStale()) type |= Types.KEY_EXCHANGE_STALE_BIT; - else if (((IncomingKeyExchangeMessage)message).isProcessed()) {Log.w("SmsDatabase", "Setting processed bit..."); type |= Types.KEY_EXCHANGE_PROCESSED_BIT;} + else if (((IncomingKeyExchangeMessage)message).isProcessed()) type |= Types.KEY_EXCHANGE_PROCESSED_BIT; } else if (message.isSecureMessage()) { type |= Types.SECURE_MESSAGE_BIT; type |= Types.ENCRYPTION_REMOTE_BIT; diff --git a/src/org/thoughtcrime/securesms/service/SmsReceiver.java b/src/org/thoughtcrime/securesms/service/SmsReceiver.java index 1251bb6aab..6d3017c0b1 100644 --- a/src/org/thoughtcrime/securesms/service/SmsReceiver.java +++ b/src/org/thoughtcrime/securesms/service/SmsReceiver.java @@ -56,7 +56,9 @@ public class SmsReceiver { private IncomingTextMessage assembleMessageFragments(List messages) { IncomingTextMessage message = new IncomingTextMessage(messages); - if (WirePrefix.isEncryptedMessage(message.getMessageBody()) || WirePrefix.isKeyExchange(message.getMessageBody())) { + if (WirePrefix.isEncryptedMessage(message.getMessageBody()) || + WirePrefix.isKeyExchange(message.getMessageBody())) + { return multipartMessageHandler.processPotentialMultipartMessage(message); } else { return message; @@ -69,8 +71,9 @@ public class SmsReceiver { if (masterSecret != null) { DecryptingQueue.scheduleDecryption(context, masterSecret, messageAndThreadId.first, + messageAndThreadId.second, message.getSender(), message.getMessageBody(), - message.isSecureMessage()); + message.isSecureMessage(), message.isKeyExchange()); } return messageAndThreadId; @@ -92,7 +95,10 @@ public class SmsReceiver { private Pair storeKeyExchangeMessage(MasterSecret masterSecret, IncomingKeyExchangeMessage message) { - if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean(ApplicationPreferencesActivity.AUTO_KEY_EXCHANGE_PREF, true)) { + if (masterSecret != null && + PreferenceManager.getDefaultSharedPreferences(context) + .getBoolean(ApplicationPreferencesActivity.AUTO_KEY_EXCHANGE_PREF, true)) + { try { Recipient recipient = new Recipient(null, message.getSender(), null, null); KeyExchangeMessage keyExchangeMessage = new KeyExchangeMessage(message.getMessageBody());