Correctly handle deferred processing of key exchange message when locked.

This commit is contained in:
Moxie Marlinspike 2013-05-15 14:09:13 -07:00
parent 3d49e90779
commit ad6760b62c
3 changed files with 77 additions and 25 deletions

View File

@ -18,8 +18,10 @@ package org.thoughtcrime.securesms.crypto;
import android.content.Context; import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.preference.PreferenceManager;
import android.util.Log; import android.util.Log;
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase; import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.MmsDatabase;
@ -71,11 +73,11 @@ public class DecryptingQueue {
} }
public static void scheduleDecryption(Context context, MasterSecret masterSecret, public static void scheduleDecryption(Context context, MasterSecret masterSecret,
long messageId, String originator, String body, long messageId, long threadId, String originator,
boolean isSecureMessage) String body, boolean isSecureMessage, boolean isKeyExchange)
{ {
DecryptionWorkItem runnable = new DecryptionWorkItem(context, masterSecret, messageId, DecryptionWorkItem runnable = new DecryptionWorkItem(context, masterSecret, messageId, threadId,
originator, body, isSecureMessage); originator, body, isSecureMessage, isKeyExchange);
synchronized (workQueue) { synchronized (workQueue) {
workQueue.add(runnable); workQueue.add(runnable);
workQueue.notifyAll(); workQueue.notifyAll();
@ -122,11 +124,14 @@ public class DecryptingQueue {
SmsMessageRecord record) SmsMessageRecord record)
{ {
long messageId = record.getId(); long messageId = record.getId();
long threadId = record.getThreadId();
String body = record.getBody().getBody(); String body = record.getBody().getBody();
String originator = record.getIndividualRecipient().getNumber(); String originator = record.getIndividualRecipient().getNumber();
boolean isSecureMessage = record.isSecure(); 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 { private static class MmsDecryptionItem implements Runnable {
@ -208,21 +213,25 @@ public class DecryptingQueue {
private static class DecryptionWorkItem implements Runnable { private static class DecryptionWorkItem implements Runnable {
private final long messageId; private final long messageId;
private final long threadId;
private final Context context; private final Context context;
private final MasterSecret masterSecret; private final MasterSecret masterSecret;
private final String body; private final String body;
private final String originator; private final String originator;
private final boolean isSecureMessage; private final boolean isSecureMessage;
private final boolean isKeyExchange;
public DecryptionWorkItem(Context context, MasterSecret masterSecret, long messageId, public DecryptionWorkItem(Context context, MasterSecret masterSecret, long messageId, long threadId,
String originator, String body, boolean isSecureMessage) String originator, String body, boolean isSecureMessage, boolean isKeyExchange)
{ {
this.context = context; this.context = context;
this.messageId = messageId; this.messageId = messageId;
this.threadId = threadId;
this.masterSecret = masterSecret; this.masterSecret = masterSecret;
this.body = body; this.body = body;
this.originator = originator; this.originator = originator;
this.isSecureMessage = isSecureMessage; this.isSecureMessage = isSecureMessage;
this.isKeyExchange = isKeyExchange;
} }
private void handleRemoteAsymmetricEncrypt() { private void handleRemoteAsymmetricEncrypt() {
@ -266,18 +275,47 @@ public class DecryptingQueue {
try { try {
AsymmetricMasterCipher asymmetricMasterCipher = new AsymmetricMasterCipher(MasterSecretUtil.getAsymmetricMasterSecret(context, masterSecret)); AsymmetricMasterCipher asymmetricMasterCipher = new AsymmetricMasterCipher(MasterSecretUtil.getAsymmetricMasterSecret(context, masterSecret));
plaintextBody = asymmetricMasterCipher.decryptBody(body); plaintextBody = asymmetricMasterCipher.decryptBody(body);
} catch (InvalidMessageException ime) {
Log.w("DecryptionQueue", ime); if (isKeyExchange) {
database.markAsDecryptFailed(messageId); handleKeyExchangeProcessing(plaintextBody);
return;
} catch (IOException e) {
Log.w("DecryptionQueue", e);
database.markAsDecryptFailed(messageId);
return;
} }
database.updateMessageBody(masterSecret, messageId, plaintextBody); database.updateMessageBody(masterSecret, messageId, plaintextBody);
MessageNotifier.updateNotification(context, masterSecret); MessageNotifier.updateNotification(context, masterSecret);
} catch (InvalidMessageException ime) {
Log.w("DecryptionQueue", ime);
database.markAsDecryptFailed(messageId);
} catch (IOException e) {
Log.w("DecryptionQueue", e);
database.markAsDecryptFailed(messageId);
}
}
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 @Override

View File

@ -139,6 +139,14 @@ public class SmsDatabase extends Database implements MmsSmsColumns {
return 0; 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) { public void markAsDecryptFailed(long id) {
updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_FAILED_BIT); updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_FAILED_BIT);
} }
@ -213,7 +221,7 @@ public class SmsDatabase extends Database implements MmsSmsColumns {
if (message.isKeyExchange()) { if (message.isKeyExchange()) {
type |= Types.KEY_EXCHANGE_BIT; type |= Types.KEY_EXCHANGE_BIT;
if (((IncomingKeyExchangeMessage)message).isStale()) type |= Types.KEY_EXCHANGE_STALE_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()) { } else if (message.isSecureMessage()) {
type |= Types.SECURE_MESSAGE_BIT; type |= Types.SECURE_MESSAGE_BIT;
type |= Types.ENCRYPTION_REMOTE_BIT; type |= Types.ENCRYPTION_REMOTE_BIT;

View File

@ -56,7 +56,9 @@ public class SmsReceiver {
private IncomingTextMessage assembleMessageFragments(List<IncomingTextMessage> messages) { private IncomingTextMessage assembleMessageFragments(List<IncomingTextMessage> messages) {
IncomingTextMessage message = new IncomingTextMessage(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); return multipartMessageHandler.processPotentialMultipartMessage(message);
} else { } else {
return message; return message;
@ -69,8 +71,9 @@ public class SmsReceiver {
if (masterSecret != null) { if (masterSecret != null) {
DecryptingQueue.scheduleDecryption(context, masterSecret, messageAndThreadId.first, DecryptingQueue.scheduleDecryption(context, masterSecret, messageAndThreadId.first,
messageAndThreadId.second,
message.getSender(), message.getMessageBody(), message.getSender(), message.getMessageBody(),
message.isSecureMessage()); message.isSecureMessage(), message.isKeyExchange());
} }
return messageAndThreadId; return messageAndThreadId;
@ -92,7 +95,10 @@ public class SmsReceiver {
private Pair<Long, Long> storeKeyExchangeMessage(MasterSecret masterSecret, private Pair<Long, Long> storeKeyExchangeMessage(MasterSecret masterSecret,
IncomingKeyExchangeMessage message) 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 { try {
Recipient recipient = new Recipient(null, message.getSender(), null, null); Recipient recipient = new Recipient(null, message.getSender(), null, null);
KeyExchangeMessage keyExchangeMessage = new KeyExchangeMessage(message.getMessageBody()); KeyExchangeMessage keyExchangeMessage = new KeyExchangeMessage(message.getMessageBody());