Support for an 'end session' protocol message.

1) On the push side, this message is a flag in PushMessageContent.
   Any secure message with that flag will terminate the current
   sessin.

2) On the SMS side, there is an "end session" wire type and
   the convention that a message with this wire type must be
   secure and contain the string "TERMINATE."
This commit is contained in:
Moxie Marlinspike
2014-02-19 13:46:49 -08:00
parent 0688dd0c2c
commit 19dddd7adf
32 changed files with 400 additions and 84 deletions

View File

@@ -8,6 +8,7 @@ import android.util.Pair;
import com.google.protobuf.InvalidProtocolBufferException;
import org.thoughtcrime.securesms.crypto.DecryptingQueue;
import org.thoughtcrime.securesms.crypto.KeyExchangeProcessor;
import org.thoughtcrime.securesms.crypto.KeyExchangeProcessorV2;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
@@ -20,6 +21,7 @@ import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage;
import org.thoughtcrime.securesms.sms.IncomingEndSessionMessage;
import org.thoughtcrime.securesms.sms.IncomingKeyExchangeMessage;
import org.thoughtcrime.securesms.sms.IncomingPreKeyBundleMessage;
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
@@ -34,6 +36,7 @@ import org.whispersystems.textsecure.push.IncomingPushMessage;
import org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent;
import org.whispersystems.textsecure.storage.InvalidKeyIdException;
import org.whispersystems.textsecure.storage.RecipientDevice;
import org.whispersystems.textsecure.storage.Session;
import ws.com.google.android.mms.MmsException;
@@ -150,7 +153,10 @@ public class PushReceiver {
try {
PushMessageContent messageContent = PushMessageContent.parseFrom(message.getBody());
if (messageContent.hasGroup()) {
if (secure && (messageContent.getFlags() & PushMessageContent.Flags.END_SESSION_VALUE) != 0) {
Log.w("PushReceiver", "Received end session message...");
handleEndSessionMessage(masterSecret, message, messageContent);
} else if (messageContent.hasGroup()) {
Log.w("PushReceiver", "Received push group message...");
handleReceivedGroupMessage(masterSecret, message, messageContent, secure);
} else if (messageContent.getAttachmentsCount() > 0) {
@@ -222,6 +228,27 @@ public class PushReceiver {
}
}
private void handleEndSessionMessage(MasterSecret masterSecret,
IncomingPushMessage message,
PushMessageContent messageContent)
{
try {
Recipient recipient = RecipientFactory.getRecipientsFromString(context, message.getSource(), true).getPrimaryRecipient();
IncomingTextMessage incomingTextMessage = new IncomingTextMessage(message, "", null);
IncomingEndSessionMessage incomingEndSessionMessage = new IncomingEndSessionMessage(incomingTextMessage);
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
Pair<Long, Long> messageAndThreadId = database.insertMessageInbox(masterSecret, incomingEndSessionMessage);
database.updateMessageBody(masterSecret, messageAndThreadId.first, messageContent.getBody());
Session.abortSessionFor(context, recipient);
KeyExchangeProcessor.broadcastSecurityUpdateEvent(context, messageAndThreadId.second);
} catch (RecipientFormattingException e) {
Log.w("PushReceiver", e);
}
}
private void handleReceivedMediaMessage(MasterSecret masterSecret,
IncomingPushMessage message,
PushMessageContent messageContent,

View File

@@ -68,7 +68,8 @@ public class SmsReceiver {
if (WirePrefix.isEncryptedMessage(message.getMessageBody()) ||
WirePrefix.isKeyExchange(message.getMessageBody()) ||
WirePrefix.isPreKeyBundle(message.getMessageBody()))
WirePrefix.isPreKeyBundle(message.getMessageBody()) ||
WirePrefix.isEndSession(message.getMessageBody()))
{
return multipartMessageHandler.processPotentialMultipartMessage(message);
} else {
@@ -85,7 +86,7 @@ public class SmsReceiver {
messageAndThreadId.second,
message.getSender(), message.getSenderDeviceId(),
message.getMessageBody(), message.isSecureMessage(),
message.isKeyExchange());
message.isKeyExchange(), message.isEndSession());
}
return messageAndThreadId;
@@ -197,10 +198,13 @@ public class SmsReceiver {
if (message.isSecureMessage()) return storeSecureMessage(masterSecret, message);
else if (message.isPreKeyBundle()) return storePreKeyWhisperMessage(masterSecret, (IncomingPreKeyBundleMessage) message);
else if (message.isKeyExchange()) return storeKeyExchangeMessage(masterSecret, (IncomingKeyExchangeMessage) message);
else if (message.isEndSession()) return storeSecureMessage(masterSecret, message);
else return storeStandardMessage(masterSecret, message);
}
private void handleReceiveMessage(MasterSecret masterSecret, Intent intent) {
if (intent.getExtras() == null) return;
List<IncomingTextMessage> messagesList = intent.getExtras().getParcelableArrayList("text_messages");
IncomingTextMessage message = assembleMessageFragments(messagesList);

View File

@@ -21,13 +21,16 @@ import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.telephony.SmsManager;
import android.telephony.SmsMessage;
import android.util.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.crypto.KeyExchangeProcessor;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipients;
@@ -38,6 +41,7 @@ import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import org.thoughtcrime.securesms.transport.UniversalTransport;
import org.thoughtcrime.securesms.transport.UntrustedIdentityException;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.storage.Session;
public class SmsSender {
@@ -109,10 +113,22 @@ public class SmsSender {
Log.w("SMSReceiverService", "Running sent callback: " + messageId);
if (result == Activity.RESULT_OK) {
DatabaseFactory.getSmsDatabase(context).markAsSent(messageId);
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
Cursor cursor = database.getMessage(messageId);
SmsDatabase.Reader reader = database.readerFor(cursor);
database.markAsSent(messageId);
if (upgraded) {
DatabaseFactory.getSmsDatabase(context).markAsSecure(messageId);
database.markAsSecure(messageId);
}
SmsMessageRecord record = reader.getNext();
if (record != null && record.isEndSession()) {
Log.w("SmsSender", "Ending session...");
Session.abortSessionFor(context, record.getIndividualRecipient());
KeyExchangeProcessor.broadcastSecurityUpdateEvent(context, record.getThreadId());
}
unregisterForRadioChanges();
@@ -161,4 +177,5 @@ public class SmsSender {
null, context, SendReceiveService.class),
PendingIntent.FLAG_UPDATE_CURRENT));
}
}