Rearrange decrypt API.

1) Change SessionBuilder to only establish sessions via
   KeyExchangeMessage and PreKeyBundles.

2) Change SessionCipher to decrypt either WhisperMessage
   or PreKeyWhisperMessage items, automatically building
   a session for the latter.

3) Change SessionCipher to tear down new sessions built
   with PreKeyWhisperMessages if the embedded WhsiperMessage
   fails to decrypt.
This commit is contained in:
Moxie Marlinspike
2014-07-20 14:35:46 -07:00
parent 42cf53e487
commit 819982af7b
15 changed files with 276 additions and 174 deletions

View File

@@ -9,6 +9,7 @@ import com.google.protobuf.InvalidProtocolBufferException;
import org.thoughtcrime.securesms.crypto.DecryptingQueue;
import org.thoughtcrime.securesms.crypto.KeyExchangeProcessor;
import org.thoughtcrime.securesms.crypto.TextSecureCipher;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
import org.thoughtcrime.securesms.database.MmsDatabase;
@@ -24,16 +25,21 @@ import org.thoughtcrime.securesms.sms.IncomingKeyExchangeMessage;
import org.thoughtcrime.securesms.sms.IncomingPreKeyBundleMessage;
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libaxolotl.DuplicateMessageException;
import org.whispersystems.libaxolotl.InvalidKeyException;
import org.whispersystems.libaxolotl.InvalidMessageException;
import org.whispersystems.libaxolotl.InvalidVersionException;
import org.whispersystems.libaxolotl.LegacyMessageException;
import org.whispersystems.libaxolotl.NoSessionException;
import org.whispersystems.libaxolotl.UntrustedIdentityException;
import org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage;
import org.whispersystems.libaxolotl.state.SessionStore;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.crypto.TransportDetails;
import org.whispersystems.textsecure.push.IncomingPushMessage;
import org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent;
import org.whispersystems.libaxolotl.InvalidKeyIdException;
import org.whispersystems.textsecure.push.PushTransportDetails;
import org.whispersystems.textsecure.storage.RecipientDevice;
import org.whispersystems.textsecure.storage.TextSecureSessionStore;
import org.whispersystems.textsecure.util.Base64;
@@ -113,34 +119,39 @@ public class PushReceiver {
}
try {
Recipient recipient = RecipientFactory.getRecipientsFromString(context, message.getSource(), false).getPrimaryRecipient();
RecipientDevice recipientDevice = new RecipientDevice(recipient.getRecipientId(), message.getSourceDevice());
KeyExchangeProcessor processor = new KeyExchangeProcessor(context, masterSecret, recipientDevice);
PreKeyWhisperMessage preKeyExchange = new PreKeyWhisperMessage(message.getBody());
Recipient recipient = RecipientFactory.getRecipientsFromString(context, message.getSource(), false).getPrimaryRecipient();
RecipientDevice recipientDevice = new RecipientDevice(recipient.getRecipientId(), message.getSourceDevice());
PreKeyWhisperMessage preKeyWhisperMessage = new PreKeyWhisperMessage(message.getBody());
TransportDetails transportDetails = new PushTransportDetails(preKeyWhisperMessage.getMessageVersion());
TextSecureCipher cipher = new TextSecureCipher(context, masterSecret, recipientDevice, transportDetails);
byte[] plaintext = cipher.decrypt(preKeyWhisperMessage);
try {
processor.processKeyExchangeMessage(preKeyExchange);
IncomingPushMessage bundledMessage = message.withBody(plaintext);
handleReceivedMessage(masterSecret, bundledMessage, true);
IncomingPushMessage bundledMessage = message.withBody(preKeyExchange.getWhisperMessage().serialize());
handleReceivedSecureMessage(masterSecret, bundledMessage);
} catch (UntrustedIdentityException uie) {
Log.w("PushReceiver", uie);
String encoded = Base64.encodeBytes(message.getBody());
IncomingTextMessage textMessage = new IncomingTextMessage(message, encoded, null);
IncomingPreKeyBundleMessage bundleMessage = new IncomingPreKeyBundleMessage(textMessage, encoded);
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
Pair<Long, Long> messageAndThreadId = database.insertMessageInbox(masterSecret, bundleMessage);
MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second);
}
} catch (InvalidVersionException e) {
Log.w("PushReceiver", e);
handleReceivedCorruptedKey(masterSecret, message, true);
} catch (InvalidKeyException | InvalidKeyIdException | InvalidMessageException |
RecipientFormattingException e)
RecipientFormattingException | LegacyMessageException e)
{
Log.w("PushReceiver", e);
handleReceivedCorruptedKey(masterSecret, message, false);
} catch (DuplicateMessageException e) {
Log.w("PushReceiver", e);
handleReceivedDuplicateMessage(message);
} catch (NoSessionException e) {
Log.w("PushReceiver", e);
handleReceivedMessageForNoSession(masterSecret, message);
} catch (UntrustedIdentityException e) {
Log.w("PushReceiver", e);
String encoded = Base64.encodeBytes(message.getBody());
IncomingTextMessage textMessage = new IncomingTextMessage(message, encoded, null);
IncomingPreKeyBundleMessage bundleMessage = new IncomingPreKeyBundleMessage(textMessage, encoded);
Pair<Long, Long> messageAndThreadId = DatabaseFactory.getEncryptingSmsDatabase(context)
.insertMessageInbox(masterSecret, bundleMessage);
MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second);
}
}

View File

@@ -24,6 +24,7 @@ import android.util.Pair;
import org.thoughtcrime.securesms.crypto.DecryptingQueue;
import org.thoughtcrime.securesms.crypto.KeyExchangeProcessor;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.crypto.TextSecureCipher;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
import org.thoughtcrime.securesms.database.SmsDatabase;
@@ -42,10 +43,12 @@ import org.thoughtcrime.securesms.sms.MultipartSmsMessageHandler;
import org.thoughtcrime.securesms.sms.OutgoingKeyExchangeMessage;
import org.thoughtcrime.securesms.sms.SmsTransportDetails;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libaxolotl.DuplicateMessageException;
import org.whispersystems.libaxolotl.InvalidKeyException;
import org.whispersystems.libaxolotl.InvalidMessageException;
import org.whispersystems.libaxolotl.InvalidVersionException;
import org.whispersystems.libaxolotl.LegacyMessageException;
import org.whispersystems.libaxolotl.NoSessionException;
import org.whispersystems.libaxolotl.StaleKeyExchangeException;
import org.whispersystems.libaxolotl.UntrustedIdentityException;
import org.whispersystems.libaxolotl.protocol.KeyExchangeMessage;
@@ -115,39 +118,47 @@ public class SmsReceiver {
IncomingPreKeyBundleMessage message)
{
Log.w("SmsReceiver", "Processing prekey message...");
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
try {
Recipient recipient = RecipientFactory.getRecipientsFromString(context, message.getSender(), false).getPrimaryRecipient();
RecipientDevice recipientDevice = new RecipientDevice(recipient.getRecipientId(), message.getSenderDeviceId());
KeyExchangeProcessor processor = new KeyExchangeProcessor(context, masterSecret, recipientDevice);
SmsTransportDetails transportDetails = new SmsTransportDetails();
byte[] rawMessage = transportDetails.getDecodedMessage(message.getMessageBody().getBytes());
PreKeyWhisperMessage preKeyExchange = new PreKeyWhisperMessage(rawMessage);
if (masterSecret != null) {
try {
Recipient recipient = RecipientFactory.getRecipientsFromString(context, message.getSender(), false).getPrimaryRecipient();
RecipientDevice recipientDevice = new RecipientDevice(recipient.getRecipientId(), message.getSenderDeviceId());
SmsTransportDetails transportDetails = new SmsTransportDetails();
TextSecureCipher cipher = new TextSecureCipher(context, masterSecret, recipientDevice, transportDetails);
byte[] rawMessage = transportDetails.getDecodedMessage(message.getMessageBody().getBytes());
PreKeyWhisperMessage preKeyWhisperMessage = new PreKeyWhisperMessage(rawMessage);
byte[] plaintext = cipher.decrypt(preKeyWhisperMessage);
processor.processKeyExchangeMessage(preKeyExchange);
IncomingEncryptedMessage bundledMessage = new IncomingEncryptedMessage(message, new String(transportDetails.getEncodedMessage(preKeyWhisperMessage.getWhisperMessage().serialize())));
Pair<Long, Long> messageAndThreadId = database.insertMessageInbox(masterSecret, bundledMessage);
WhisperMessage ciphertextMessage = preKeyExchange.getWhisperMessage();
String bundledMessageBody = new String(transportDetails.getEncodedMessage(ciphertextMessage.serialize()));
IncomingEncryptedMessage bundledMessage = new IncomingEncryptedMessage(message, bundledMessageBody);
Pair<Long, Long> messageAndThreadId = storeSecureMessage(masterSecret, bundledMessage);
database.updateMessageBody(masterSecret, messageAndThreadId.first, new String(plaintext));
Intent intent = new Intent(KeyExchangeProcessor.SECURITY_UPDATE_EVENT);
intent.putExtra("thread_id", messageAndThreadId.second);
intent.setPackage(context.getPackageName());
context.sendBroadcast(intent, KeyCachingService.KEY_PERMISSION);
Intent intent = new Intent(KeyExchangeProcessor.SECURITY_UPDATE_EVENT);
intent.putExtra("thread_id", messageAndThreadId.second);
intent.setPackage(context.getPackageName());
context.sendBroadcast(intent, KeyCachingService.KEY_PERMISSION);
return messageAndThreadId;
} catch (InvalidKeyException | RecipientFormattingException | InvalidMessageException | IOException e) {
Log.w("SmsReceiver", e);
message.setCorrupted(true);
} catch (InvalidVersionException e) {
Log.w("SmsReceiver", e);
message.setInvalidVersion(true);
} catch (InvalidKeyIdException e) {
Log.w("SmsReceiver", e);
message.setStale(true);
} catch (UntrustedIdentityException e) {
Log.w("SmsReceiver", e);
return messageAndThreadId;
} catch (InvalidKeyException | RecipientFormattingException | InvalidMessageException | IOException | NoSessionException e) {
Log.w("SmsReceiver", e);
message.setCorrupted(true);
} catch (InvalidVersionException e) {
Log.w("SmsReceiver", e);
message.setInvalidVersion(true);
} catch (InvalidKeyIdException e) {
Log.w("SmsReceiver", e);
message.setStale(true);
} catch (UntrustedIdentityException e) {
Log.w("SmsReceiver", e);
} catch (DuplicateMessageException e) {
Log.w("SmsReceiver", e);
message.setDuplicate(true);
} catch (LegacyMessageException e) {
Log.w("SmsReceiver", e);
message.setLegacyVersion(true);
}
}
return storeStandardMessage(masterSecret, message);