Refactor the ciphertext message parsing and building.

This commit is contained in:
Moxie Marlinspike
2013-10-31 17:23:45 -07:00
parent 6e640db39c
commit dbc070cd65
17 changed files with 363 additions and 316 deletions

View File

@@ -16,6 +16,7 @@ import org.thoughtcrime.securesms.mms.MmsSendHelper;
import org.thoughtcrime.securesms.mms.TextTransport;
import org.thoughtcrime.securesms.protocol.WirePrefix;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.whispersystems.textsecure.crypto.protocol.CiphertextMessage;
import org.whispersystems.textsecure.util.Hex;
import java.io.IOException;
@@ -134,10 +135,14 @@ public class MmsTransport {
return encryptedPdu;
}
private byte[] getEncryptedPdu(MasterSecret masterSecret, String recipient, byte[] pduBytes) {
IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret);
MessageCipher message = new MessageCipher(context, masterSecret, identityKey, new TextTransport());
return message.encrypt(new Recipient(null, recipient, null, null), pduBytes);
private byte[] getEncryptedPdu(MasterSecret masterSecret, String recipientString, byte[] pduBytes) {
TextTransport transportDetails = new TextTransport();
Recipient recipient = new Recipient(null, recipientString, null, null);
IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret);
MessageCipher messageCipher = new MessageCipher(context, masterSecret, identityKey);
CiphertextMessage ciphertextMessage = messageCipher.encrypt(recipient, pduBytes);
return transportDetails.getEncodedMessage(ciphertextMessage.serialize());
}
private boolean isInconsistentResponse(SendReq message, SendConf response) {

View File

@@ -2,9 +2,9 @@ package org.thoughtcrime.securesms.transport;
import android.content.Context;
import android.util.Log;
import android.util.Pair;
import com.google.protobuf.ByteString;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.crypto.KeyExchangeProcessor;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
@@ -13,7 +13,6 @@ import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.TextSecurePushCredentials;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.textsecure.crypto.AttachmentCipher;
@@ -22,19 +21,17 @@ import org.whispersystems.textsecure.crypto.IdentityKeyPair;
import org.whispersystems.textsecure.crypto.KeyUtil;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.crypto.MessageCipher;
import org.whispersystems.textsecure.crypto.protocol.CiphertextMessage;
import org.whispersystems.textsecure.crypto.protocol.PreKeyBundleMessage;
import org.whispersystems.textsecure.directory.Directory;
import org.whispersystems.textsecure.push.OutgoingPushMessage;
import org.whispersystems.textsecure.push.PreKeyEntity;
import org.whispersystems.textsecure.push.PushAttachmentData;
import org.whispersystems.textsecure.push.PushAttachmentPointer;
import org.whispersystems.textsecure.push.PushBody;
import org.whispersystems.textsecure.push.PushDestination;
import org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent;
import org.whispersystems.textsecure.push.PushServiceSocket;
import org.whispersystems.textsecure.push.PushTransportDetails;
import org.whispersystems.textsecure.push.RateLimitException;
import org.whispersystems.textsecure.push.RawTransportDetails;
import org.whispersystems.textsecure.util.PhoneNumberFormatter;
import java.io.IOException;
import java.util.LinkedList;
@@ -62,14 +59,11 @@ public class PushTransport extends BaseTransport {
PushDestination destination = PushDestination.create(context, credentials,
recipient.getNumber());
String plaintextBody = message.getBody().getBody();
byte[] plaintext = PushMessageContent.newBuilder()
.setBody(plaintextBody)
.build().toByteArray();
String plaintextBody = message.getBody().getBody();
byte[] plaintext = PushMessageContent.newBuilder().setBody(plaintextBody).build().toByteArray();
PushBody pushBody = getEncryptedMessage(socket, recipient, destination, plaintext);
Pair<Integer, byte[]> typeAndCiphertext = getEncryptedMessage(socket, recipient, destination, plaintext);
socket.sendMessage(destination, typeAndCiphertext.second, typeAndCiphertext.first);
socket.sendMessage(destination, pushBody);
context.sendBroadcast(constructSentIntent(context, message.getId(), message.getType()));
} catch (RateLimitException e) {
@@ -83,9 +77,7 @@ public class PushTransport extends BaseTransport {
TextSecurePushCredentials credentials = TextSecurePushCredentials.getInstance();
PushServiceSocket socket = new PushServiceSocket(context, credentials);
String messageBody = PartParser.getMessageText(message.getBody());
List<byte[]> ciphertext = new LinkedList<byte[]>();
List<Integer> types = new LinkedList<Integer>();
List<PushBody> pushBodies = new LinkedList<PushBody>();
for (PushDestination destination : destinations) {
Recipients recipients = RecipientFactory.getRecipientsFromString(context, destination.getNumber(), false);
@@ -107,15 +99,13 @@ public class PushTransport extends BaseTransport {
builder.addAttachments(attachmentBuilder.build());
}
byte[] plaintext = builder.build().toByteArray();
Pair<Integer, byte[]> typeAndCiphertext = getEncryptedMessage(socket, recipients.getPrimaryRecipient(),
destination, plaintext);
byte[] plaintext = builder.build().toByteArray();
PushBody pushBody = getEncryptedMessage(socket, recipients.getPrimaryRecipient(), destination, plaintext);
types.add(typeAndCiphertext.first);
ciphertext.add(typeAndCiphertext.second);
pushBodies.add(pushBody);
}
socket.sendMessage(destinations, ciphertext, types);
socket.sendMessage(destinations, pushBodies);
} catch (RateLimitException e) {
Log.w("PushTransport", e);
@@ -150,37 +140,34 @@ public class PushTransport extends BaseTransport {
return attachments;
}
private Pair<Integer, byte[]> getEncryptedMessage(PushServiceSocket socket,
Recipient recipient,
PushDestination pushDestination,
byte[] plaintext)
private PushBody getEncryptedMessage(PushServiceSocket socket, Recipient recipient,
PushDestination pushDestination, byte[] plaintext)
throws IOException
{
if (KeyUtil.isNonPrekeySessionFor(context, masterSecret, recipient)) {
Log.w("PushTransport", "Sending standard ciphertext message...");
byte[] ciphertext = getEncryptedMessageForExistingSession(recipient, plaintext);
return new Pair<Integer, byte[]>(OutgoingPushMessage.TYPE_MESSAGE_CIPHERTEXT, ciphertext);
return new PushBody(OutgoingPushMessage.TYPE_MESSAGE_CIPHERTEXT, ciphertext);
} else if (KeyUtil.isSessionFor(context, recipient)) {
Log.w("PushTransport", "Sending prekeybundle ciphertext message for existing session...");
byte[] ciphertext = getEncryptedPrekeyBundleMessageForExistingSession(recipient, plaintext);
return new Pair<Integer, byte[]>(OutgoingPushMessage.TYPE_MESSAGE_PREKEY_BUNDLE, ciphertext);
return new PushBody(OutgoingPushMessage.TYPE_MESSAGE_PREKEY_BUNDLE, ciphertext);
} else {
Log.w("PushTransport", "Sending prekeybundle ciphertext message for new session...");
byte[] ciphertext = getEncryptedPrekeyBundleMessageForNewSession(socket, recipient, pushDestination, plaintext);
return new Pair<Integer, byte[]>(OutgoingPushMessage.TYPE_MESSAGE_PREKEY_BUNDLE, ciphertext);
return new PushBody(OutgoingPushMessage.TYPE_MESSAGE_PREKEY_BUNDLE, ciphertext);
}
}
private byte[] getEncryptedPrekeyBundleMessageForExistingSession(Recipient recipient,
byte[] plaintext)
{
IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret);
IdentityKey identityKey = identityKeyPair.getPublicKey();
IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret);
IdentityKey identityKey = identityKeyPair.getPublicKey();
MessageCipher messageCipher = new MessageCipher(context, masterSecret, identityKeyPair);
CiphertextMessage ciphertextMessage = messageCipher.encrypt(recipient, plaintext);
PreKeyBundleMessage preKeyBundleMessage = new PreKeyBundleMessage(ciphertextMessage, identityKey);
MessageCipher message = new MessageCipher(context, masterSecret, identityKeyPair, new RawTransportDetails());
byte[] bundledMessage = message.encrypt(recipient, plaintext);
PreKeyBundleMessage preKeyBundleMessage = new PreKeyBundleMessage(identityKey, bundledMessage);
return preKeyBundleMessage.serialize();
}
@@ -197,21 +184,21 @@ public class PushTransport extends BaseTransport {
processor.processKeyExchangeMessage(preKey);
MessageCipher message = new MessageCipher(context, masterSecret, identityKeyPair, new RawTransportDetails());
byte[] bundledMessage = message.encrypt(recipient, plaintext);
MessageCipher messageCipher = new MessageCipher(context, masterSecret, identityKeyPair);
CiphertextMessage ciphertextMessage = messageCipher.encrypt(recipient, plaintext);
PreKeyBundleMessage preKeyBundleMessage = new PreKeyBundleMessage(ciphertextMessage, identityKey);
PreKeyBundleMessage preKeyBundleMessage = new PreKeyBundleMessage(identityKey, bundledMessage);
return preKeyBundleMessage.serialize();
}
private byte[] getEncryptedMessageForExistingSession(Recipient recipient, byte[] plaintext)
throws IOException
{
IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret);
MessageCipher messageCipher = new MessageCipher(context, masterSecret, identityKeyPair,
new PushTransportDetails());
IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret);
MessageCipher messageCipher = new MessageCipher(context, masterSecret, identityKeyPair);
CiphertextMessage ciphertextMessage = messageCipher.encrypt(recipient, plaintext);
return messageCipher.encrypt(recipient, plaintext);
return ciphertextMessage.serialize();
}
}

View File

@@ -6,21 +6,21 @@ import android.telephony.SmsManager;
import android.util.Log;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.sms.OutgoingPrekeyBundleMessage;import org.whispersystems.textsecure.push.RawTransportDetails;
import org.whispersystems.textsecure.crypto.IdentityKeyPair;
import org.whispersystems.textsecure.crypto.KeyUtil;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.crypto.MessageCipher;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.SendReceiveService;
import org.thoughtcrime.securesms.service.SmsDeliveryListener;
import org.thoughtcrime.securesms.sms.MultipartSmsMessageHandler;
import org.thoughtcrime.securesms.sms.OutgoingPrekeyBundleMessage;
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
import org.thoughtcrime.securesms.sms.SmsTransportDetails;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.textsecure.crypto.IdentityKeyPair;
import org.whispersystems.textsecure.crypto.KeyUtil;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.crypto.MessageCipher;
import org.whispersystems.textsecure.crypto.protocol.CiphertextMessage;
import org.whispersystems.textsecure.crypto.protocol.PreKeyBundleMessage;
import org.whispersystems.textsecure.util.Base64;
import java.util.ArrayList;
@@ -144,23 +144,28 @@ public class SmsTransport extends BaseTransport {
private OutgoingTextMessage getAsymmetricEncrypt(MasterSecret masterSecret,
OutgoingTextMessage message)
{
Recipient recipient = message.getRecipients().getPrimaryRecipient();
String body = message.getMessageBody();
IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret);
Recipient recipient = message.getRecipients().getPrimaryRecipient();
String body = message.getMessageBody();
IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret);
SmsTransportDetails transportDetails = new SmsTransportDetails();
if (KeyUtil.isNonPrekeySessionFor(context, masterSecret, recipient)) {
Log.w("SmsTransport", "Delivering standard ciphertext...");
MessageCipher messageCipher = new MessageCipher(context, masterSecret, identityKey, new SmsTransportDetails());
byte[] ciphertext = messageCipher.encrypt(recipient, body.getBytes());
return message.withBody(new String(ciphertext));
MessageCipher messageCipher = new MessageCipher(context, masterSecret, identityKey);
byte[] paddedPlaintext = transportDetails.getPaddedMessageBody(body.getBytes());
CiphertextMessage ciphertextMessage = messageCipher.encrypt(recipient, paddedPlaintext);
String ciphertxt = new String(transportDetails.getEncodedMessage(ciphertextMessage.serialize()));
return message.withBody(ciphertxt);
} else {
Log.w("SmsTransport", "Delivering prekeybundle ciphertext...");
MessageCipher messageCipher = new MessageCipher(context, masterSecret, identityKey, new RawTransportDetails());
byte[] bundledMessage = messageCipher.encrypt(recipient, body.getBytes());
PreKeyBundleMessage preKeyBundleMessage = new PreKeyBundleMessage(identityKey.getPublicKey(), bundledMessage);
MessageCipher messageCipher = new MessageCipher(context, masterSecret, identityKey);
CiphertextMessage ciphertextMessage = messageCipher.encrypt(recipient, body.getBytes());
PreKeyBundleMessage preKeyBundleMessage = new PreKeyBundleMessage(ciphertextMessage, identityKey.getPublicKey());
byte[] cipherText = preKeyBundleMessage.serialize();
return new OutgoingPrekeyBundleMessage(message, Base64.encodeBytesWithoutPadding(preKeyBundleMessage.serialize()));
return new OutgoingPrekeyBundleMessage(message, new String(transportDetails.getEncodedMessage(cipherText)));
}
}
}