From 92593d459b80ae0a9a275e7869a448dbf8003142 Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Wed, 23 Sep 2015 14:43:50 -0700 Subject: [PATCH] Remove the final vestiges of encrypted SMS Fixes #4026 // FREEBIE --- .../securesms/jobs/MmsDownloadJob.java | 14 +- .../securesms/jobs/SmsReceiveJob.java | 14 +- .../protocol/EndSessionWirePrefix.java | 8 - .../protocol/KeyExchangeWirePrefix.java | 26 -- .../protocol/PrekeyBundleWirePrefix.java | 8 - .../protocol/SecureMessageWirePrefix.java | 26 -- .../securesms/protocol/WirePrefix.java | 128 ---------- .../securesms/service/MmsListener.java | 26 +- .../securesms/service/SmsListener.java | 3 +- .../securesms/sms/MultipartSmsIdentifier.java | 32 --- .../sms/MultipartSmsTransportMessage.java | 222 ------------------ ...MultipartSmsTransportMessageFragments.java | 57 ----- .../securesms/sms/SmsTransportDetails.java | 83 ------- 13 files changed, 6 insertions(+), 641 deletions(-) delete mode 100644 src/org/thoughtcrime/securesms/protocol/EndSessionWirePrefix.java delete mode 100644 src/org/thoughtcrime/securesms/protocol/KeyExchangeWirePrefix.java delete mode 100644 src/org/thoughtcrime/securesms/protocol/PrekeyBundleWirePrefix.java delete mode 100644 src/org/thoughtcrime/securesms/protocol/SecureMessageWirePrefix.java delete mode 100644 src/org/thoughtcrime/securesms/protocol/WirePrefix.java delete mode 100644 src/org/thoughtcrime/securesms/sms/MultipartSmsIdentifier.java delete mode 100644 src/org/thoughtcrime/securesms/sms/MultipartSmsTransportMessage.java delete mode 100644 src/org/thoughtcrime/securesms/sms/MultipartSmsTransportMessageFragments.java diff --git a/src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java b/src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java index 53922d86b4..2abada1bd0 100644 --- a/src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java +++ b/src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java @@ -15,7 +15,6 @@ import org.thoughtcrime.securesms.mms.CompatMmsConnection; import org.thoughtcrime.securesms.mms.IncomingMediaMessage; import org.thoughtcrime.securesms.mms.MmsRadioException; import org.thoughtcrime.securesms.notifications.MessageNotifier; -import org.thoughtcrime.securesms.protocol.WirePrefix; import org.thoughtcrime.securesms.service.KeyCachingService; import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.requirements.NetworkRequirement; @@ -144,16 +143,9 @@ public class MmsDownloadJob extends MasterSecretJob { MmsDatabase database = DatabaseFactory.getMmsDatabase(context); IncomingMediaMessage message = new IncomingMediaMessage(retrieved); - Pair messageAndThreadId; - - if (retrieved.getSubject() != null && WirePrefix.isEncryptedMmsSubject(retrieved.getSubject().getString())) { - database.markAsLegacyVersion(messageId, threadId); - messageAndThreadId = new Pair<>(messageId, threadId); - } else { - messageAndThreadId = database.insertMessageInbox(new MasterSecretUnion(masterSecret), - message, contentLocation, threadId); - database.delete(messageId); - } + Pair messageAndThreadId = database.insertMessageInbox(new MasterSecretUnion(masterSecret), + message, contentLocation, threadId); + database.delete(messageId); MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second); } diff --git a/src/org/thoughtcrime/securesms/jobs/SmsReceiveJob.java b/src/org/thoughtcrime/securesms/jobs/SmsReceiveJob.java index 27a208cf16..23c3f874db 100644 --- a/src/org/thoughtcrime/securesms/jobs/SmsReceiveJob.java +++ b/src/org/thoughtcrime/securesms/jobs/SmsReceiveJob.java @@ -11,11 +11,9 @@ import org.thoughtcrime.securesms.crypto.MasterSecretUtil; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.EncryptingSmsDatabase; import org.thoughtcrime.securesms.notifications.MessageNotifier; -import org.thoughtcrime.securesms.protocol.WirePrefix; import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.service.KeyCachingService; -import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage; import org.thoughtcrime.securesms.sms.IncomingTextMessage; import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.libaxolotl.util.guava.Optional; @@ -108,16 +106,6 @@ public class SmsReceiveJob extends ContextJob { return Optional.absent(); } - IncomingTextMessage message = new IncomingTextMessage(messages); - - if (WirePrefix.isEncryptedMessage(message.getMessageBody()) || - WirePrefix.isKeyExchange(message.getMessageBody()) || - WirePrefix.isPreKeyBundle(message.getMessageBody()) || - WirePrefix.isEndSession(message.getMessageBody())) - { - return Optional.of(new IncomingEncryptedMessage(message, message.getMessageBody())); - } else { - return Optional.of(message); - } + return Optional.of(new IncomingTextMessage(messages)); } } diff --git a/src/org/thoughtcrime/securesms/protocol/EndSessionWirePrefix.java b/src/org/thoughtcrime/securesms/protocol/EndSessionWirePrefix.java deleted file mode 100644 index 9b458fef10..0000000000 --- a/src/org/thoughtcrime/securesms/protocol/EndSessionWirePrefix.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.thoughtcrime.securesms.protocol; - -public class EndSessionWirePrefix extends WirePrefix { - @Override - public String calculatePrefix(String message) { - return super.calculateEndSessionPrefix(message); - } -} diff --git a/src/org/thoughtcrime/securesms/protocol/KeyExchangeWirePrefix.java b/src/org/thoughtcrime/securesms/protocol/KeyExchangeWirePrefix.java deleted file mode 100644 index eff482938c..0000000000 --- a/src/org/thoughtcrime/securesms/protocol/KeyExchangeWirePrefix.java +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (C) 2011 Whisper Systems - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.thoughtcrime.securesms.protocol; - -public class KeyExchangeWirePrefix extends WirePrefix { - - @Override - public String calculatePrefix(String message) { - return super.calculateKeyExchangePrefix(message); - } - -} diff --git a/src/org/thoughtcrime/securesms/protocol/PrekeyBundleWirePrefix.java b/src/org/thoughtcrime/securesms/protocol/PrekeyBundleWirePrefix.java deleted file mode 100644 index 1596fa84fa..0000000000 --- a/src/org/thoughtcrime/securesms/protocol/PrekeyBundleWirePrefix.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.thoughtcrime.securesms.protocol; - -public class PrekeyBundleWirePrefix extends WirePrefix { - @Override - public String calculatePrefix(String message) { - return super.calculatePreKeyBundlePrefix(message); - } -} diff --git a/src/org/thoughtcrime/securesms/protocol/SecureMessageWirePrefix.java b/src/org/thoughtcrime/securesms/protocol/SecureMessageWirePrefix.java deleted file mode 100644 index 660bfb36e2..0000000000 --- a/src/org/thoughtcrime/securesms/protocol/SecureMessageWirePrefix.java +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (C) 2011 Whisper Systems - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.thoughtcrime.securesms.protocol; - -public class SecureMessageWirePrefix extends WirePrefix { - - @Override - public String calculatePrefix(String message) { - return super.calculateEncryptedMesagePrefix(message); - } - -} diff --git a/src/org/thoughtcrime/securesms/protocol/WirePrefix.java b/src/org/thoughtcrime/securesms/protocol/WirePrefix.java deleted file mode 100644 index 0177c9032a..0000000000 --- a/src/org/thoughtcrime/securesms/protocol/WirePrefix.java +++ /dev/null @@ -1,128 +0,0 @@ -/** - * Copyright (C) 2011 Whisper Systems - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.thoughtcrime.securesms.protocol; - - -import org.thoughtcrime.securesms.util.Base64; - -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; - -/** - * Calculates prefixes that identify a message as - * being part of an encrypted session. The idea was to - * make calculating and identifying these prefixes somewhat - * expensive, so that filtering them en-mass would come at a cost. - * - * @author Moxie Marlinspike - */ - -public abstract class WirePrefix { - - private static final int HASH_ITERATIONS = 1000; - private static final int PREFIX_BYTES = 3; - public static final int PREFIX_SIZE = 4; - - public abstract String calculatePrefix(String message); - - public static boolean isKeyExchange(String message) { - return verifyPrefix("?TSK", message); - } - - public static boolean isEncryptedMessage(String message) { - return verifyPrefix("?TSM", message); - } - - public static boolean isPreKeyBundle(String message) { - 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); - } - - public static String calculateEncryptedMesagePrefix(String message) { - return calculatePrefix(("?TSM" + message).getBytes(), PREFIX_BYTES); - } - - public static String calculatePreKeyBundlePrefix(String message) { - 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; - - String prefix = message.substring(0, PREFIX_SIZE); - message = message.substring(PREFIX_SIZE); - - String calculatedPrefix = calculatePrefix((prefixType + message).getBytes(), PREFIX_BYTES); - - assert(calculatedPrefix.length() == PREFIX_SIZE); - - return prefix.equals(calculatedPrefix); - } - - private static String calculatePrefix(byte[] message, int byteCount) { - try { - MessageDigest md = MessageDigest.getInstance("SHA1"); - byte[] runningDigest = message; - - for (int i=0;i idMap = new HashMap(); - - public synchronized byte getIdForRecipient(String recipient) { - Integer currentId; - - if (idMap.containsKey(recipient)) { - currentId = idMap.get(recipient); - idMap.remove(recipient); - } else { - currentId = 0; - } - - byte id = currentId.byteValue(); - idMap.put(recipient, (currentId + 1) % 255); - - return id; - } - -} diff --git a/src/org/thoughtcrime/securesms/sms/MultipartSmsTransportMessage.java b/src/org/thoughtcrime/securesms/sms/MultipartSmsTransportMessage.java deleted file mode 100644 index c79506fca8..0000000000 --- a/src/org/thoughtcrime/securesms/sms/MultipartSmsTransportMessage.java +++ /dev/null @@ -1,222 +0,0 @@ -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.thoughtcrime.securesms.util.Base64; -import org.thoughtcrime.securesms.util.Conversions; - -import java.io.IOException; -import java.util.ArrayList; - -public class MultipartSmsTransportMessage { - private static final String TAG = MultipartSmsTransportMessage.class.getName(); - - private static final int MULTIPART_SUPPORTED_AFTER_VERSION = 1; - - public static final int SINGLE_MESSAGE_MULTIPART_OVERHEAD = 1; - 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_END_SESSION = 4; - - private static final int VERSION_OFFSET = 0; - private static final int MULTIPART_OFFSET = 1; - private static final int IDENTIFIER_OFFSET = 2; - - private final int wireType; - private final byte[] decodedMessage; - private final IncomingTextMessage message; - - public MultipartSmsTransportMessage(IncomingTextMessage message) throws IOException { - this.message = message; - this.decodedMessage = Base64.decodeWithoutPadding(message.getMessageBody().substring(WirePrefix.PREFIX_SIZE)); - - 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()); - } - - public int getWireType() { - return wireType; - } - - public int getCurrentVersion() { - return Conversions.highBitsToInt(decodedMessage[VERSION_OFFSET]); - } - - public int getMultipartIndex() { - return Conversions.highBitsToInt(decodedMessage[MULTIPART_OFFSET]); - } - - public int getMultipartCount() { - if (isDeprecatedTransport()) - return 1; - - return Conversions.lowBitsToInt(decodedMessage[MULTIPART_OFFSET]); - } - - public int getIdentifier() { - return decodedMessage[IDENTIFIER_OFFSET] & 0xFF; - } - - public boolean isDeprecatedTransport() { - return getCurrentVersion() < MULTIPART_SUPPORTED_AFTER_VERSION; - } - - public boolean isInvalid() { - return getMultipartIndex() >= getMultipartCount(); - } - - public boolean isSinglePart() { - return getMultipartCount() == 1; - } - - public byte[] getStrippedMessage() { - if (isDeprecatedTransport()) return getStrippedMessageForDeprecatedTransport(); - else if (getMultipartCount() == 1) return getStrippedMessageForSinglePart(); - else return getStrippedMessageForMultiPart(); - } - - /* - * We're dealing with a message that isn't using the multipart transport. - * - */ - private byte[] getStrippedMessageForDeprecatedTransport() { - return decodedMessage; - } - - /* - * We're dealing with a transport message that is of the format: - * Version (1 byte) - * Index_And_Count (1 byte) - * Message (remainder) - * - * The version byte was stolen off the message, so we strip Index_And_Count byte out, - * put the version byte back on the front of the message, and return. - */ - private byte[] getStrippedMessageForSinglePart() { - byte[] stripped = new byte[decodedMessage.length - 1]; - System.arraycopy(decodedMessage, 1, stripped, 0, decodedMessage.length - 1); - stripped[0] = decodedMessage[VERSION_OFFSET]; - - return stripped; - } - - /* - * We're dealing with a transport message that is of the format: - * - * Version (1 byte) - * Index_And_Count (1 byte) - * Identifier (1 byte) - * Message (remainder) - * - * The version byte was stolen off the first byte of the message, but only for the first fragment - * of the message. So for the first fragment we strip off everything and put the version byte - * back on. For the remaining fragments, we just strip everything. - */ - - private byte[] getStrippedMessageForMultiPart() { - byte[] strippedMessage = new byte[decodedMessage.length - (getMultipartIndex() == 0 ? 2 : 3)]; - - int copyDestinationIndex = 0; - int copyDestinationLength = strippedMessage.length; - - if (getMultipartIndex() == 0) { - strippedMessage[0] = decodedMessage[0]; - copyDestinationIndex++; - copyDestinationLength--; - } - - System.arraycopy(decodedMessage, 3, strippedMessage, copyDestinationIndex, copyDestinationLength); - return strippedMessage; - - } - - public String getKey() { - return message.getSender() + getIdentifier(); - } - - public IncomingTextMessage getBaseMessage() { - return message; - } - - public static ArrayList getEncoded(OutgoingTextMessage message, byte identifier) - { - try { - byte[] decoded = Base64.decodeWithoutPadding(message.getMessageBody()); - int count = new SmsTransportDetails().getMessageCountForBytes(decoded.length); - - WirePrefix prefix; - - 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); - else return getMultiEncoded(decoded, prefix, count, identifier); - - } catch (IOException e) { - throw new AssertionError(e); - } - } - - private static ArrayList getSingleEncoded(byte[] decoded, WirePrefix prefix) { - ArrayList list = new ArrayList(1); - byte[] messageWithMultipartHeader = new byte[decoded.length + 1]; - System.arraycopy(decoded, 0, messageWithMultipartHeader, 1, decoded.length); - - messageWithMultipartHeader[VERSION_OFFSET] = decoded[VERSION_OFFSET]; - messageWithMultipartHeader[MULTIPART_OFFSET] = Conversions.intsToByteHighAndLow(0, 1); - - String encodedMessage = Base64.encodeBytesWithoutPadding(messageWithMultipartHeader); - - list.add(prefix.calculatePrefix(encodedMessage) + encodedMessage); - - Log.w(TAG, "Complete fragment size: " + list.get(list.size()-1).length()); - - return list; - } - - private static ArrayList getMultiEncoded(byte[] decoded, WirePrefix prefix, - int segmentCount, byte id) - { - ArrayList list = new ArrayList(segmentCount); - byte versionByte = decoded[VERSION_OFFSET]; - int messageOffset = 1; - int segmentIndex = 0; - - while (messageOffset < decoded.length-1) { - int segmentSize = Math.min(SmsTransportDetails.BASE_MAX_BYTES, decoded.length-messageOffset+3); - - byte[] segment = new byte[segmentSize]; - segment[VERSION_OFFSET] = versionByte; - segment[MULTIPART_OFFSET] = Conversions.intsToByteHighAndLow(segmentIndex++, segmentCount); - segment[IDENTIFIER_OFFSET] = id; - - Log.w(TAG, "Fragment: (" + segmentIndex + "/" + segmentCount +") -- ID: " + id); - - System.arraycopy(decoded, messageOffset, segment, 3, segmentSize-3); - messageOffset += segmentSize-3; - - String encodedSegment = Base64.encodeBytesWithoutPadding(segment); - list.add(prefix.calculatePrefix(encodedSegment) + encodedSegment); - - Log.w(TAG, "Complete fragment size: " + list.get(list.size()-1).length()); - } - - return list; - } - -} diff --git a/src/org/thoughtcrime/securesms/sms/MultipartSmsTransportMessageFragments.java b/src/org/thoughtcrime/securesms/sms/MultipartSmsTransportMessageFragments.java deleted file mode 100644 index 810988a193..0000000000 --- a/src/org/thoughtcrime/securesms/sms/MultipartSmsTransportMessageFragments.java +++ /dev/null @@ -1,57 +0,0 @@ -package org.thoughtcrime.securesms.sms; - -public class MultipartSmsTransportMessageFragments { - - private static final long VALID_TIME = 60 * 60 * 1000; // 1 Hour - - private final byte[][] fragments; - private final long initializedTime; - - public MultipartSmsTransportMessageFragments(int count) { - this.fragments = new byte[count][]; - this.initializedTime = System.currentTimeMillis(); - } - - public void add(MultipartSmsTransportMessage fragment) { - this.fragments[fragment.getMultipartIndex()] = fragment.getStrippedMessage(); - } - - public int getSize() { - return this.fragments.length; - } - - public boolean isExpired() { - return (System.currentTimeMillis() - initializedTime) >= VALID_TIME; - } - - public boolean isComplete() { - for (int i=0;i 0) - messageCount++; - - return messageCount; - } }