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

@@ -80,6 +80,7 @@ import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.sms.OutgoingEncryptedMessage;
import org.thoughtcrime.securesms.sms.OutgoingEndSessionMessage;
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
import org.thoughtcrime.securesms.util.ActionBarUtil;
import org.thoughtcrime.securesms.util.BitmapDecodingException;
@@ -93,7 +94,9 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.textsecure.crypto.InvalidMessageException;
import org.whispersystems.textsecure.crypto.MasterCipher;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.storage.RecipientDevice;
import org.whispersystems.textsecure.storage.Session;
import org.whispersystems.textsecure.storage.SessionRecordV2;
import org.whispersystems.textsecure.util.Util;
import java.io.IOException;
@@ -368,9 +371,23 @@ public class ConversationActivity extends PassphraseRequiredSherlockFragmentActi
@Override
public void onClick(DialogInterface dialog, int which) {
if (isSingleConversation()) {
Session.abortSessionFor(ConversationActivity.this, getRecipients().getPrimaryRecipient());
initializeSecurity();
initializeTitleBar();
ConversationActivity self = ConversationActivity.this;
Recipient recipient = getRecipients().getPrimaryRecipient();
if (SessionRecordV2.hasSession(self, masterSecret,
recipient.getRecipientId(),
RecipientDevice.DEFAULT_DEVICE_ID))
{
OutgoingEndSessionMessage endSessionMessage =
new OutgoingEndSessionMessage(new OutgoingTextMessage(getRecipients(), "TERMINATE"));
long allocatedThreadId = MessageSender.send(self, masterSecret,
endSessionMessage, threadId);
sendComplete(recipients, allocatedThreadId, allocatedThreadId != self.threadId);
} else {
Session.abortSessionFor(self, recipient);
}
}
}
});

View File

@@ -236,7 +236,7 @@ public class ReceiveKeyActivity extends Activity {
DecryptingQueue.scheduleDecryption(ReceiveKeyActivity.this, masterSecret, messageId,
threadId, recipient.getNumber(), recipientDeviceId,
messageBody, true, false);
messageBody, true, false, false);
} catch (InvalidKeyIdException e) {
Log.w("ReceiveKeyActivity", e);
DatabaseFactory.getEncryptingSmsDatabase(ReceiveKeyActivity.this)

View File

@@ -81,11 +81,12 @@ public class DecryptingQueue {
public static void scheduleDecryption(Context context, MasterSecret masterSecret,
long messageId, long threadId, String originator, int deviceId,
String body, boolean isSecureMessage, boolean isKeyExchange)
String body, boolean isSecureMessage, boolean isKeyExchange,
boolean isEndSession)
{
DecryptionWorkItem runnable = new DecryptionWorkItem(context, masterSecret, messageId, threadId,
originator, deviceId, body,
isSecureMessage, isKeyExchange);
isSecureMessage, isKeyExchange, isEndSession);
executor.execute(runnable);
}
@@ -167,10 +168,11 @@ public class DecryptingQueue {
int originatorDeviceId = record.getRecipientDeviceId();
boolean isSecureMessage = record.isSecure();
boolean isKeyExchange = record.isKeyExchange();
boolean isEndSession = record.isEndSession();
scheduleDecryption(context, masterSecret, messageId, threadId,
originator, originatorDeviceId, body,
isSecureMessage, isKeyExchange);
isSecureMessage, isKeyExchange, isEndSession);
}
private static class PushDecryptionWorkItem implements Runnable {
@@ -332,10 +334,11 @@ public class DecryptingQueue {
private final int deviceId;
private final boolean isSecureMessage;
private final boolean isKeyExchange;
private final boolean isEndSession;
public DecryptionWorkItem(Context context, MasterSecret masterSecret, long messageId, long threadId,
String originator, int deviceId, String body, boolean isSecureMessage,
boolean isKeyExchange)
boolean isKeyExchange, boolean isEndSession)
{
this.context = context;
this.messageId = messageId;
@@ -346,6 +349,7 @@ public class DecryptingQueue {
this.deviceId = deviceId;
this.isSecureMessage = isSecureMessage;
this.isKeyExchange = isKeyExchange;
this.isEndSession = isEndSession;
}
private void handleRemoteAsymmetricEncrypt() {
@@ -368,6 +372,13 @@ public class DecryptingQueue {
byte[] paddedPlaintext = sessionCipher.decrypt(decodedCiphertext);
plaintextBody = new String(transportDetails.getStrippedPaddingMessageBody(paddedPlaintext));
if (isEndSession &&
"TERMINATE".equals(plaintextBody) &&
SessionRecordV2.hasSession(context, masterSecret, recipientDevice))
{
Session.abortSessionFor(context, recipient);
}
} catch (InvalidMessageException e) {
Log.w("DecryptionQueue", e);
database.markAsDecryptFailed(messageId);
@@ -441,7 +452,7 @@ public class DecryptingQueue {
@Override
public void run() {
if (isSecureMessage) {
if (isSecureMessage || isEndSession) {
handleRemoteAsymmetricEncrypt();
} else {
handleLocalAsymmetricEncrypt();

View File

@@ -18,10 +18,12 @@
package org.thoughtcrime.securesms.crypto;
import android.content.Context;
import android.content.Intent;
import org.thoughtcrime.securesms.crypto.protocol.KeyExchangeMessage;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.whispersystems.textsecure.crypto.InvalidMessageException;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.storage.RecipientDevice;
@@ -45,4 +47,12 @@ public abstract class KeyExchangeProcessor {
return new KeyExchangeProcessorV2(context, masterSecret, recipientDevice);
}
}
public static void broadcastSecurityUpdateEvent(Context context, long threadId) {
Intent intent = new Intent(SECURITY_UPDATE_EVENT);
intent.putExtra("thread_id", threadId);
intent.setPackage(context.getPackageName());
context.sendBroadcast(intent, KeyCachingService.KEY_PERMISSION);
}
}

View File

@@ -1,7 +1,6 @@
package org.thoughtcrime.securesms.crypto;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import org.thoughtcrime.securesms.crypto.protocol.KeyExchangeMessage;
@@ -9,7 +8,6 @@ import org.thoughtcrime.securesms.crypto.protocol.KeyExchangeMessageV1;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.sms.OutgoingKeyExchangeMessage;
import org.whispersystems.textsecure.crypto.IdentityKey;
@@ -129,13 +127,6 @@ public class KeyExchangeProcessorV1 extends KeyExchangeProcessor {
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());
context.sendBroadcast(intent, KeyCachingService.KEY_PERMISSION);
}
public LocalKeyRecord initializeRecordFor(Context context,
MasterSecret masterSecret,
CanonicalRecipient recipient)

View File

@@ -1,7 +1,6 @@
package org.thoughtcrime.securesms.crypto;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import org.thoughtcrime.securesms.crypto.protocol.KeyExchangeMessage;
@@ -9,7 +8,6 @@ import org.thoughtcrime.securesms.crypto.protocol.KeyExchangeMessageV2;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.sms.OutgoingKeyExchangeMessage;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
@@ -230,11 +228,4 @@ public class KeyExchangeProcessorV2 extends KeyExchangeProcessor {
}
}
private static void broadcastSecurityUpdateEvent(Context context, long threadId) {
Intent intent = new Intent(KeyExchangeProcessorV1.SECURITY_UPDATE_EVENT);
intent.putExtra("thread_id", threadId);
intent.setPackage(context.getPackageName());
context.sendBroadcast(intent, KeyCachingService.KEY_PERMISSION);
}
}

View File

@@ -73,7 +73,7 @@ public class EncryptingSmsDatabase extends SmsDatabase {
{
long type = Types.BASE_INBOX_TYPE;
if (!message.isSecureMessage()) {
if (!message.isSecureMessage() && !message.isEndSession()) {
type |= Types.ENCRYPTION_SYMMETRIC_BIT;
message = message.withMessageBody(getEncryptedBody(masterSecret, message.getMessageBody()));
}

View File

@@ -39,6 +39,7 @@ public interface MmsSmsColumns {
// Secure Message Information
protected static final long SECURE_MESSAGE_BIT = 0x800000;
protected static final long END_SESSION_BIT = 0x400000;
// Encrypted Storage Information
protected static final long ENCRYPTION_MASK = 0xFF000000;
@@ -75,6 +76,10 @@ public interface MmsSmsColumns {
return (type & SECURE_MESSAGE_BIT) != 0;
}
public static boolean isEndSessionType(long type) {
return (type & END_SESSION_BIT) != 0;
}
public static boolean isKeyExchangeType(long type) {
return (type & KEY_EXCHANGE_BIT) != 0;
}

View File

@@ -256,6 +256,10 @@ public class SmsDatabase extends Database implements MmsSmsColumns {
} else if (message.isSecureMessage()) {
type |= Types.SECURE_MESSAGE_BIT;
type |= Types.ENCRYPTION_REMOTE_BIT;
} else if (message.isEndSession()) {
type |= Types.END_SESSION_BIT;
type |= Types.SECURE_MESSAGE_BIT;
type |= Types.ENCRYPTION_REMOTE_BIT;
}
Recipients recipients;
@@ -328,6 +332,7 @@ public class SmsDatabase extends Database implements MmsSmsColumns {
protected List<Long> insertMessageOutbox(long threadId, OutgoingTextMessage message, long type) {
if (message.isKeyExchange()) type |= Types.KEY_EXCHANGE_BIT;
else if (message.isSecureMessage()) type |= Types.SECURE_MESSAGE_BIT;
else if (message.isEndSession()) type |= Types.END_SESSION_BIT;
long date = System.currentTimeMillis();
List<Long> messageIds = new LinkedList<Long>();

View File

@@ -84,6 +84,10 @@ public abstract class DisplayRecord {
return SmsDatabase.Types.isKeyExchangeType(type);
}
public boolean isEndSession() {
return SmsDatabase.Types.isEndSessionType(type);
}
public int getGroupAction() {
return groupAction;
}

View File

@@ -79,6 +79,9 @@ public class SmsMessageRecord extends MessageRecord {
return emphasisAdded(context.getString(R.string.MessageDisplayHelper_message_encrypted_for_non_existing_session));
} else if (!getBody().isPlaintext()) {
return emphasisAdded(context.getString(R.string.MessageNotifier_encrypted_message));
} else if (SmsDatabase.Types.isEndSessionType(type)) {
// TODO jake is going to fix this up
return new SpannableString("Session closed!");
} else if (isOutgoing() && Tag.isTagged(getBody().getBody())) {
return new SpannableString(Tag.stripTag(getBody().getBody()));
} else {

View File

@@ -78,6 +78,9 @@ public class ThreadRecord extends DisplayRecord {
return emphasisAdded(context.getString(R.string.MessageDisplayHelper_message_encrypted_for_non_existing_session));
} else if (!getBody().isPlaintext()) {
return emphasisAdded(context.getString(R.string.MessageNotifier_encrypted_message));
} else if (SmsDatabase.Types.isEndSessionType(type)) {
// TODO jake is going to fix this up
return emphasisAdded("Session closed!");
} else {
if (Util.isEmpty(getBody().getBody())) {
return new SpannableString(context.getString(R.string.MessageNotifier_no_subject));

View File

@@ -0,0 +1,8 @@
package org.thoughtcrime.securesms.protocol;
public class EndSessionWirePrefix extends WirePrefix {
@Override
public String calculatePrefix(String message) {
return super.calculateEndSessionPrefix(message);
}
}

View File

@@ -51,6 +51,10 @@ public abstract class WirePrefix {
return verifyPrefix("?TSP", message);
}
public static boolean isEndSession(String message) {
return verifyPrefix("?TSE", message);
}
public static String calculateKeyExchangePrefix(String message) {
return calculatePrefix(("?TSK" + message).getBytes(), PREFIX_BYTES);
}
@@ -63,6 +67,10 @@ public abstract class WirePrefix {
return calculatePrefix(("?TSP" + message).getBytes(), PREFIX_BYTES);
}
public static String calculateEndSessionPrefix(String message) {
return calculatePrefix(("?TSE" + message).getBytes(), PREFIX_BYTES);
}
private static boolean verifyPrefix(String prefixType, String message) {
if (message.length() <= PREFIX_SIZE)
return false;

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));
}
}

View File

@@ -0,0 +1,22 @@
package org.thoughtcrime.securesms.sms;
public class IncomingEndSessionMessage extends IncomingTextMessage {
public IncomingEndSessionMessage(IncomingTextMessage base) {
this(base, base.getMessageBody());
}
public IncomingEndSessionMessage(IncomingTextMessage base, String newBody) {
super(base, newBody);
}
@Override
public IncomingEndSessionMessage withMessageBody(String messageBody) {
return new IncomingEndSessionMessage(this, messageBody);
}
@Override
public boolean isEndSession() {
return true;
}
}

View File

@@ -216,6 +216,10 @@ public class IncomingTextMessage implements Parcelable {
return false;
}
public boolean isEndSession() {
return false;
}
public boolean isIdentityUpdate() {
return false;
}

View File

@@ -71,6 +71,8 @@ public class MultipartSmsMessageHandler {
return new IncomingKeyExchangeMessage(message.getBaseMessage(), strippedMessage);
} else if (message.getWireType() == MultipartSmsTransportMessage.WIRETYPE_PREKEY) {
return new IncomingPreKeyBundleMessage(message.getBaseMessage(), strippedMessage);
} else if (message.getWireType() == MultipartSmsTransportMessage.WIRETYPE_END_SESSION) {
return new IncomingEndSessionMessage(message.getBaseMessage(), strippedMessage);
} else {
return new IncomingEncryptedMessage(message.getBaseMessage(), strippedMessage);
}

View File

@@ -2,12 +2,14 @@ package org.thoughtcrime.securesms.sms;
import android.util.Log;
import org.thoughtcrime.securesms.protocol.EndSessionWirePrefix;
import org.thoughtcrime.securesms.protocol.KeyExchangeWirePrefix;
import org.thoughtcrime.securesms.protocol.PrekeyBundleWirePrefix;
import org.thoughtcrime.securesms.protocol.SecureMessageWirePrefix;
import org.thoughtcrime.securesms.protocol.WirePrefix;
import org.whispersystems.textsecure.util.Base64;
import org.whispersystems.textsecure.util.Conversions;
import org.whispersystems.textsecure.util.Hex;
import java.io.IOException;
import java.util.ArrayList;
@@ -21,9 +23,10 @@ public class MultipartSmsTransportMessage {
public static final int MULTI_MESSAGE_MULTIPART_OVERHEAD = 3;
public static final int FIRST_MULTI_MESSAGE_MULTIPART_OVERHEAD = 2;
public static final int WIRETYPE_SECURE = 1;
public static final int WIRETYPE_KEY = 2;
public static final int WIRETYPE_PREKEY = 3;
public static final int WIRETYPE_SECURE = 1;
public static final int WIRETYPE_KEY = 2;
public static final int WIRETYPE_PREKEY = 3;
public static final int WIRETYPE_END_SESSION = 4;
private static final int VERSION_OFFSET = 0;
private static final int MULTIPART_OFFSET = 1;
@@ -39,6 +42,7 @@ public class MultipartSmsTransportMessage {
if (WirePrefix.isEncryptedMessage(message.getMessageBody())) wireType = WIRETYPE_SECURE;
else if (WirePrefix.isPreKeyBundle(message.getMessageBody())) wireType = WIRETYPE_PREKEY;
else if (WirePrefix.isEndSession(message.getMessageBody())) wireType = WIRETYPE_END_SESSION;
else wireType = WIRETYPE_KEY;
Log.w(TAG, "Decoded message with version: " + getCurrentVersion());
@@ -158,6 +162,7 @@ public class MultipartSmsTransportMessage {
if (message.isKeyExchange()) prefix = new KeyExchangeWirePrefix();
else if (message.isPreKeyBundle()) prefix = new PrekeyBundleWirePrefix();
else if (message.isEndSession()) prefix = new EndSessionWirePrefix();
else prefix = new SecureMessageWirePrefix();
if (count == 1) return getSingleEncoded(decoded, prefix);

View File

@@ -0,0 +1,22 @@
package org.thoughtcrime.securesms.sms;
public class OutgoingEndSessionMessage extends OutgoingTextMessage {
public OutgoingEndSessionMessage(OutgoingTextMessage base) {
this(base, base.getMessageBody());
}
public OutgoingEndSessionMessage(OutgoingTextMessage message, String body) {
super(message, body);
}
@Override
public boolean isEndSession() {
return true;
}
@Override
public OutgoingTextMessage withBody(String body) {
return new OutgoingEndSessionMessage(this, body);
}
}

View File

@@ -52,6 +52,10 @@ public class OutgoingTextMessage {
return false;
}
public boolean isEndSession() {
return false;
}
public boolean isPreKeyBundle() {
return false;
}
@@ -61,6 +65,8 @@ public class OutgoingTextMessage {
return new OutgoingEncryptedMessage(record.getIndividualRecipient(), record.getBody().getBody());
} else if (record.isKeyExchange()) {
return new OutgoingKeyExchangeMessage(record.getIndividualRecipient(), record.getBody().getBody());
} else if (record.isEndSession()) {
return new OutgoingEndSessionMessage(new OutgoingTextMessage(record.getIndividualRecipient(), record.getBody().getBody()));
} else {
return new OutgoingTextMessage(record.getIndividualRecipient(), record.getBody().getBody());
}

View File

@@ -22,6 +22,7 @@ import android.util.Log;
import com.google.protobuf.ByteString;
import org.thoughtcrime.securesms.crypto.KeyExchangeProcessor;
import org.thoughtcrime.securesms.crypto.KeyExchangeProcessorV2;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
@@ -85,12 +86,15 @@ public class PushTransport extends BaseTransport {
Recipient recipient = message.getIndividualRecipient();
long threadId = message.getThreadId();
PushServiceSocket socket = PushServiceSocketFactory.create(context);
byte[] plaintext = PushMessageContent.newBuilder()
.setBody(message.getBody().getBody())
.build().toByteArray();
byte[] plaintext = getPlaintextMessage(message);
deliver(socket, recipient, threadId, plaintext);
if (message.isEndSession()) {
SessionRecordV2.deleteAll(context, recipient);
KeyExchangeProcessor.broadcastSecurityUpdateEvent(context, threadId);
}
context.sendBroadcast(constructSentIntent(context, message.getId(), message.getType(), true));
} catch (InvalidNumberException e) {
@@ -295,6 +299,17 @@ public class PushTransport extends BaseTransport {
return builder.build().toByteArray();
}
private byte[] getPlaintextMessage(SmsMessageRecord record) {
PushMessageContent.Builder builder = PushMessageContent.newBuilder()
.setBody(record.getBody().getBody());
if (record.isEndSession()) {
builder.setFlags(PushMessageContent.Flags.END_SESSION_VALUE);
}
return builder.build().toByteArray();
}
private OutgoingPushMessageList getEncryptedMessages(PushServiceSocket socket, long threadId,
Recipient recipient, byte[] plaintext)
throws IOException, InvalidNumberException, UntrustedIdentityException
@@ -352,8 +367,4 @@ public class PushTransport extends BaseTransport {
throw new AssertionError("Unknown ciphertext type: " + message.getType());
}
}
private void destroySessions(Recipient recipient) {
SessionRecordV2.deleteAll(context, recipient);
}
}

View File

@@ -33,6 +33,7 @@ import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.crypto.SessionCipher;
import org.whispersystems.textsecure.crypto.protocol.CiphertextMessage;
import org.whispersystems.textsecure.storage.RecipientDevice;
import org.whispersystems.textsecure.util.Hex;
import java.util.ArrayList;
@@ -47,7 +48,7 @@ public class SmsTransport extends BaseTransport {
}
public void deliver(SmsMessageRecord message) throws UndeliverableMessageException {
if (message.isSecure() || message.isKeyExchange()) {
if (message.isSecure() || message.isKeyExchange() || message.isEndSession()) {
deliverSecureMessage(message);
} else {
deliverPlaintextMessage(message);
@@ -58,7 +59,7 @@ public class SmsTransport extends BaseTransport {
MultipartSmsMessageHandler multipartMessageHandler = new MultipartSmsMessageHandler();
OutgoingTextMessage transportMessage = OutgoingTextMessage.from(message);
if (message.isSecure()) {
if (message.isSecure() || message.isEndSession()) {
transportMessage = getAsymmetricEncrypt(masterSecret, transportMessage);
}

View File

@@ -19,12 +19,12 @@ package org.thoughtcrime.securesms.util;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Typeface;
import android.os.Build;
import android.provider.Telephony;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.StyleSpan;
import android.util.Log;
import android.os.Build;
import android.provider.Telephony;
import org.whispersystems.textsecure.util.InvalidNumberException;
import org.whispersystems.textsecure.util.PhoneNumberFormatter;