diff --git a/library/src/org/whispersystems/textsecure/crypto/KeyUtil.java b/library/src/org/whispersystems/textsecure/crypto/KeyUtil.java index 6fc722fdb8..a08c18d83a 100644 --- a/library/src/org/whispersystems/textsecure/crypto/KeyUtil.java +++ b/library/src/org/whispersystems/textsecure/crypto/KeyUtil.java @@ -107,6 +107,11 @@ public class KeyUtil { (SessionRecord.hasSession(context, recipient)); } + public static boolean isNonPrekeySessionFor(Context context, MasterSecret masterSecret, CanonicalRecipientAddress recipient) { + return isSessionFor(context, recipient) && + !(new SessionRecord(context, masterSecret, recipient).isPrekeyBundleRequired()); + } + public static boolean isIdentityKeyFor(Context context, MasterSecret masterSecret, CanonicalRecipientAddress recipient) diff --git a/library/src/org/whispersystems/textsecure/crypto/MessageCipher.java b/library/src/org/whispersystems/textsecure/crypto/MessageCipher.java index 49fa4f57f0..6f45267773 100644 --- a/library/src/org/whispersystems/textsecure/crypto/MessageCipher.java +++ b/library/src/org/whispersystems/textsecure/crypto/MessageCipher.java @@ -36,17 +36,17 @@ public class MessageCipher { public static final int SUPPORTED_VERSION = 2; public static final int CRADLE_AGREEMENT_VERSION = 2; - static final int VERSION_LENGTH = 1; + public static final int VERSION_LENGTH = 1; private static final int SENDER_KEY_ID_LENGTH = 3; private static final int RECEIVER_KEY_ID_LENGTH = 3; - static final int NEXT_KEY_LENGTH = PublicKey.KEY_SIZE; + public static final int NEXT_KEY_LENGTH = PublicKey.KEY_SIZE; private static final int COUNTER_LENGTH = 3; public static final int HEADER_LENGTH = VERSION_LENGTH + SENDER_KEY_ID_LENGTH + RECEIVER_KEY_ID_LENGTH + COUNTER_LENGTH + NEXT_KEY_LENGTH; - static final int VERSION_OFFSET = 0; + public static final int VERSION_OFFSET = 0; private static final int SENDER_KEY_ID_OFFSET = VERSION_OFFSET + VERSION_LENGTH; - static final int RECEIVER_KEY_ID_OFFSET = SENDER_KEY_ID_OFFSET + SENDER_KEY_ID_LENGTH; - static final int NEXT_KEY_OFFSET = RECEIVER_KEY_ID_OFFSET + RECEIVER_KEY_ID_LENGTH; + public static final int RECEIVER_KEY_ID_OFFSET = SENDER_KEY_ID_OFFSET + SENDER_KEY_ID_LENGTH; + public static final int NEXT_KEY_OFFSET = RECEIVER_KEY_ID_OFFSET + RECEIVER_KEY_ID_LENGTH; private static final int COUNTER_OFFSET = NEXT_KEY_OFFSET + NEXT_KEY_LENGTH; private static final int TEXT_OFFSET = COUNTER_OFFSET + COUNTER_LENGTH; diff --git a/library/src/org/whispersystems/textsecure/crypto/SessionCipher.java b/library/src/org/whispersystems/textsecure/crypto/SessionCipher.java index b4ba361013..11d0d36d77 100644 --- a/library/src/org/whispersystems/textsecure/crypto/SessionCipher.java +++ b/library/src/org/whispersystems/textsecure/crypto/SessionCipher.java @@ -131,6 +131,7 @@ public class SessionCipher { context.getSessionRecord().setSessionKey(context.getSessionKey()); context.getSessionRecord().setSessionVersion(context.getNegotiatedVersion()); + context.getSessionRecord().setPrekeyBundleRequired(false); context.getSessionRecord().save(); return plaintextWithPadding; diff --git a/library/src/org/whispersystems/textsecure/push/PushTransportDetails.java b/library/src/org/whispersystems/textsecure/push/PushTransportDetails.java new file mode 100644 index 0000000000..9920066f52 --- /dev/null +++ b/library/src/org/whispersystems/textsecure/push/PushTransportDetails.java @@ -0,0 +1,44 @@ +/** + * Copyright (C) 2013 Open 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.whispersystems.textsecure.push; + +import org.whispersystems.textsecure.crypto.TransportDetails; +import org.whispersystems.textsecure.util.Base64; + +import java.io.IOException; + +public class PushTransportDetails implements TransportDetails { + @Override + public byte[] getStrippedPaddingMessageBody(byte[] messageWithPadding) { + return messageWithPadding; + } + + @Override + public byte[] getPaddedMessageBody(byte[] messageBody) { + return messageBody; + } + + @Override + public byte[] getEncodedMessage(byte[] messageWithMac) { + return Base64.encodeBytesWithoutPadding(messageWithMac).getBytes(); + } + + @Override + public byte[] getDecodedMessage(byte[] encodedMessageBytes) throws IOException { + return Base64.decodeWithoutPadding(new String(encodedMessageBytes)); + } +} diff --git a/library/src/org/whispersystems/textsecure/storage/SessionRecord.java b/library/src/org/whispersystems/textsecure/storage/SessionRecord.java index 566ea42cc6..6ec97e626f 100644 --- a/library/src/org/whispersystems/textsecure/storage/SessionRecord.java +++ b/library/src/org/whispersystems/textsecure/storage/SessionRecord.java @@ -36,8 +36,9 @@ import java.nio.channels.FileChannel; */ public class SessionRecord extends Record { - private static final int CURRENT_VERSION_MARKER = 0X55555556; - private static final int[] VALID_VERSION_MARKERS = {CURRENT_VERSION_MARKER, 0X55555555}; + + private static final int CURRENT_VERSION_MARKER = 0X55555557; + private static final int[] VALID_VERSION_MARKERS = {CURRENT_VERSION_MARKER, 0X55555556, 0X55555555}; private static final Object FILE_LOCK = new Object(); private int counter; @@ -48,6 +49,7 @@ public class SessionRecord extends Record { private IdentityKey identityKey; private SessionKey sessionKeyRecord; private boolean verifiedSessionKey; + private boolean prekeyBundleRequired; private final MasterSecret masterSecret; @@ -63,7 +65,7 @@ public class SessionRecord extends Record { } public static void delete(Context context, CanonicalRecipientAddress recipient) { - delete(context, SESSIONS_DIRECTORY, getRecipientId(context, recipient)+""); + delete(context, SESSIONS_DIRECTORY, getRecipientId(context, recipient) + ""); } public static boolean hasSession(Context context, CanonicalRecipientAddress recipient) { @@ -116,6 +118,14 @@ public class SessionRecord extends Record { return this.identityKey; } + public boolean isPrekeyBundleRequired() { + return prekeyBundleRequired; + } + + public void setPrekeyBundleRequired(boolean prekeyBundleRequired) { + this.prekeyBundleRequired = prekeyBundleRequired; + } + // public void setVerifiedSessionKey(boolean verifiedSessionKey) { // this.verifiedSessionKey = verifiedSessionKey; // } @@ -162,6 +172,7 @@ public class SessionRecord extends Record { writeInteger(sessionVersion, out); writeIdentityKey(out); writeInteger(verifiedSessionKey ? 1 : 0, out); + writeInteger(prekeyBundleRequired ? 1 : 0, out); if (sessionKeyRecord != null) writeBlob(sessionKeyRecord.serialize(), out); @@ -202,6 +213,10 @@ public class SessionRecord extends Record { this.verifiedSessionKey = (readInteger(in) == 1); } + if (versionMarker >= 0X55555557) { + this.prekeyBundleRequired = (readInteger(in) == 1); + } + if (in.available() != 0) this.sessionKeyRecord = new SessionKey(readBlob(in), masterSecret); @@ -226,4 +241,5 @@ public class SessionRecord extends Record { return null; } + } diff --git a/src/org/thoughtcrime/securesms/crypto/KeyExchangeProcessor.java b/src/org/thoughtcrime/securesms/crypto/KeyExchangeProcessor.java index 69b32856db..8a452db32d 100644 --- a/src/org/thoughtcrime/securesms/crypto/KeyExchangeProcessor.java +++ b/src/org/thoughtcrime/securesms/crypto/KeyExchangeProcessor.java @@ -110,6 +110,11 @@ public class KeyExchangeProcessor { Log.w("KeyExchangeProcessor", "Received pre-key with remote key ID: " + remoteKey.getId()); Log.w("KeyExchangeProcessor", "Received pre-key with local key ID: " + preKeyId); + if (!PreKeyRecord.hasRecord(context, preKeyId) && KeyUtil.isSessionFor(context, recipient)) { + Log.w("KeyExchangeProcessor", "We've already processed the prekey part, letting bundled message fall through..."); + return; + } + if (!PreKeyRecord.hasRecord(context, preKeyId)) throw new InvalidKeyIdException("No such prekey: " + preKeyId); @@ -152,6 +157,7 @@ public class KeyExchangeProcessor { remoteKeyRecord.getCurrentRemoteKey().getFingerprintBytes()); sessionRecord.setIdentityKey(message.getIdentityKey()); sessionRecord.setSessionVersion(MessageCipher.SUPPORTED_VERSION); + sessionRecord.setPrekeyBundleRequired(true); sessionRecord.save(); DatabaseFactory.getIdentityDatabase(context) diff --git a/src/org/thoughtcrime/securesms/protocol/PrekeyBundleWirePrefix.java b/src/org/thoughtcrime/securesms/protocol/PrekeyBundleWirePrefix.java new file mode 100644 index 0000000000..1596fa84fa --- /dev/null +++ b/src/org/thoughtcrime/securesms/protocol/PrekeyBundleWirePrefix.java @@ -0,0 +1,8 @@ +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/sms/MultipartSmsMessageHandler.java b/src/org/thoughtcrime/securesms/sms/MultipartSmsMessageHandler.java index 977f9240d7..4ecaa778cd 100644 --- a/src/org/thoughtcrime/securesms/sms/MultipartSmsMessageHandler.java +++ b/src/org/thoughtcrime/securesms/sms/MultipartSmsMessageHandler.java @@ -56,6 +56,8 @@ public class MultipartSmsMessageHandler { if (message.getWireType() == MultipartSmsTransportMessage.WIRETYPE_KEY) { return new IncomingKeyExchangeMessage(message.getBaseMessage(), strippedMessage); + } else if (message.getWireType() == MultipartSmsTransportMessage.WIRETYPE_PREKEY) { + return new IncomingPreKeyBundleMessage(message.getBaseMessage(), strippedMessage); } else { return new IncomingEncryptedMessage(message.getBaseMessage(), strippedMessage); } @@ -67,6 +69,8 @@ public class MultipartSmsMessageHandler { if (message.getWireType() == MultipartSmsTransportMessage.WIRETYPE_KEY) { return new IncomingKeyExchangeMessage(message.getBaseMessage(), strippedMessage); + } else if (message.getWireType() == MultipartSmsTransportMessage.WIRETYPE_PREKEY) { + return new IncomingPreKeyBundleMessage(message.getBaseMessage(), strippedMessage); } else { return new IncomingEncryptedMessage(message.getBaseMessage(), strippedMessage); } diff --git a/src/org/thoughtcrime/securesms/sms/MultipartSmsTransportMessage.java b/src/org/thoughtcrime/securesms/sms/MultipartSmsTransportMessage.java index 145c86eed2..4632fcfc37 100644 --- a/src/org/thoughtcrime/securesms/sms/MultipartSmsTransportMessage.java +++ b/src/org/thoughtcrime/securesms/sms/MultipartSmsTransportMessage.java @@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.sms; import android.util.Log; 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; @@ -22,6 +23,7 @@ public class MultipartSmsTransportMessage { public static final int WIRETYPE_SECURE = 1; public static final int WIRETYPE_KEY = 2; + public static final int WIRETYPE_PREKEY = 3; private static final int VERSION_OFFSET = 0; private static final int MULTIPART_OFFSET = 1; @@ -33,9 +35,12 @@ public class MultipartSmsTransportMessage { public MultipartSmsTransportMessage(IncomingTextMessage message) throws IOException { this.message = message; - this.wireType = WirePrefix.isEncryptedMessage(message.getMessageBody()) ? WIRETYPE_SECURE : WIRETYPE_KEY; 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 wireType = WIRETYPE_KEY; + Log.w(TAG, "Decoded message with version: " + getCurrentVersion()); } @@ -151,8 +156,9 @@ public class MultipartSmsTransportMessage { WirePrefix prefix; - if (message.isKeyExchange()) prefix = new KeyExchangeWirePrefix(); - else prefix = new SecureMessageWirePrefix(); + if (message.isKeyExchange()) prefix = new KeyExchangeWirePrefix(); + else if (message.isPreKeyBundle()) prefix = new PrekeyBundleWirePrefix(); + else prefix = new SecureMessageWirePrefix(); if (count == 1) return getSingleEncoded(decoded, prefix); else return getMultiEncoded(decoded, prefix, count, identifier); diff --git a/src/org/thoughtcrime/securesms/sms/OutgoingPrekeyBundleMessage.java b/src/org/thoughtcrime/securesms/sms/OutgoingPrekeyBundleMessage.java new file mode 100644 index 0000000000..ae7f9329a4 --- /dev/null +++ b/src/org/thoughtcrime/securesms/sms/OutgoingPrekeyBundleMessage.java @@ -0,0 +1,19 @@ +package org.thoughtcrime.securesms.sms; + + +public class OutgoingPrekeyBundleMessage extends OutgoingTextMessage { + + public OutgoingPrekeyBundleMessage(OutgoingTextMessage message, String body) { + super(message, body); + } + + @Override + public boolean isPreKeyBundle() { + return true; + } + + @Override + public OutgoingTextMessage withBody(String body) { + return new OutgoingPrekeyBundleMessage(this, body); + } +} diff --git a/src/org/thoughtcrime/securesms/sms/OutgoingTextMessage.java b/src/org/thoughtcrime/securesms/sms/OutgoingTextMessage.java index 0aafd52f1e..5eac49e7d7 100644 --- a/src/org/thoughtcrime/securesms/sms/OutgoingTextMessage.java +++ b/src/org/thoughtcrime/securesms/sms/OutgoingTextMessage.java @@ -19,8 +19,8 @@ public class OutgoingTextMessage { } protected OutgoingTextMessage(OutgoingTextMessage base, String body) { - this.recipients = base.getRecipients(); - this.message = body; + this.recipients = base.getRecipients(); + this.message = body; } public String getMessageBody() { @@ -39,6 +39,10 @@ public class OutgoingTextMessage { return false; } + public boolean isPreKeyBundle() { + return false; + } + public static OutgoingTextMessage from(SmsMessageRecord record) { if (record.isSecure()) { return new OutgoingEncryptedMessage(record.getIndividualRecipient(), record.getBody().getBody()); diff --git a/src/org/thoughtcrime/securesms/transport/PushTransport.java b/src/org/thoughtcrime/securesms/transport/PushTransport.java index b7f5aadb9c..0458fa2635 100644 --- a/src/org/thoughtcrime/securesms/transport/PushTransport.java +++ b/src/org/thoughtcrime/securesms/transport/PushTransport.java @@ -2,26 +2,27 @@ package org.thoughtcrime.securesms.transport; import android.content.Context; import android.util.Log; +import android.util.Pair; import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; import org.thoughtcrime.securesms.crypto.KeyExchangeProcessor; import org.thoughtcrime.securesms.database.model.SmsMessageRecord; import org.thoughtcrime.securesms.mms.PartParser; -import org.thoughtcrime.securesms.mms.TextTransport; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.sms.RawTransportDetails; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.Util; import org.whispersystems.textsecure.crypto.IdentityKey; 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.PreKeyBundleMessage; import org.whispersystems.textsecure.push.PreKeyEntity; import org.whispersystems.textsecure.push.PushAttachmentData; import org.whispersystems.textsecure.push.PushServiceSocket; +import org.whispersystems.textsecure.push.PushTransportDetails; import org.whispersystems.textsecure.push.RateLimitException; -import org.whispersystems.textsecure.storage.SessionRecord; import org.whispersystems.textsecure.util.PhoneNumberFormatter; import java.io.IOException; @@ -53,20 +54,14 @@ public class PushTransport extends BaseTransport { String password = TextSecurePreferences.getPushServerPassword(context); PushServiceSocket socket = new PushServiceSocket(context, localNumber, password); - Recipient recipient = message.getIndividualRecipient(); - String plaintext = message.getBody().getBody(); - String recipientCanonicalNumber = PhoneNumberFormatter.formatNumber(recipient.getNumber(), - localNumber); + Recipient recipient = message.getIndividualRecipient(); + String plaintext = message.getBody().getBody(); + String recipientCanonicalNumber = PhoneNumberFormatter.formatNumber(recipient.getNumber(), + localNumber); - if (SessionRecord.hasSession(context, recipient)) { - byte[] cipherText = getEncryptedMessageForExistingSession(recipient, plaintext); - socket.sendMessage(recipientCanonicalNumber, new String(cipherText), TYPE_MESSAGE_CIPHERTEXT); - } else { - byte[] cipherText = getEncryptedMessageForNewSession(socket, recipient, - recipientCanonicalNumber, - plaintext); - socket.sendMessage(recipientCanonicalNumber, new String(cipherText), TYPE_MESSAGE_PREKEY_BUNDLE); - } + Pair typeAndCiphertext = getEncryptedMessage(socket, recipient, recipientCanonicalNumber, plaintext); + + socket.sendMessage(recipientCanonicalNumber, typeAndCiphertext.second, typeAndCiphertext.first); context.sendBroadcast(constructSentIntent(context, message.getId(), message.getType())); } catch (RateLimitException e) { @@ -108,8 +103,42 @@ public class PushTransport extends BaseTransport { return attachments; } - private byte[] getEncryptedMessageForNewSession(PushServiceSocket socket, Recipient recipient, - String canonicalRecipientNumber, String plaintext) + private Pair getEncryptedMessage(PushServiceSocket socket, Recipient recipient, + String canonicalRecipientNumber, String plaintext) + throws IOException + { + if (KeyUtil.isNonPrekeySessionFor(context, masterSecret, recipient)) { + Log.w("PushTransport", "Sending standard ciphertext message..."); + String ciphertext = getEncryptedMessageForExistingSession(recipient, plaintext); + return new Pair(TYPE_MESSAGE_CIPHERTEXT, ciphertext); + } else if (KeyUtil.isSessionFor(context, recipient)) { + Log.w("PushTransport", "Sending prekeybundle ciphertext message for existing session..."); + String ciphertext = getEncryptedPrekeyBundleMessageForExistingSession(recipient, plaintext); + return new Pair(TYPE_MESSAGE_PREKEY_BUNDLE, ciphertext); + } else { + Log.w("PushTransport", "Sending prekeybundle ciphertext message for new session..."); + String ciphertext = getEncryptedPrekeyBundleMessageForNewSession(socket, recipient, canonicalRecipientNumber, plaintext); + return new Pair(TYPE_MESSAGE_PREKEY_BUNDLE, ciphertext); + } + } + + private String getEncryptedPrekeyBundleMessageForExistingSession(Recipient recipient, + String plaintext) + { + IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret); + IdentityKey identityKey = identityKeyPair.getPublicKey(); + + MessageCipher message = new MessageCipher(context, masterSecret, identityKeyPair, new RawTransportDetails()); + byte[] bundledMessage = message.encrypt(recipient, plaintext.getBytes()); + + PreKeyBundleMessage preKeyBundleMessage = new PreKeyBundleMessage(identityKey, bundledMessage); + return preKeyBundleMessage.serialize(); + } + + private String getEncryptedPrekeyBundleMessageForNewSession(PushServiceSocket socket, + Recipient recipient, + String canonicalRecipientNumber, + String plaintext) throws IOException { IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret); @@ -123,15 +152,17 @@ public class PushTransport extends BaseTransport { byte[] bundledMessage = message.encrypt(recipient, plaintext.getBytes()); PreKeyBundleMessage preKeyBundleMessage = new PreKeyBundleMessage(identityKey, bundledMessage); - return preKeyBundleMessage.serialize().getBytes(); + return preKeyBundleMessage.serialize(); } - private byte[] getEncryptedMessageForExistingSession(Recipient recipient, String plaintext) + private String getEncryptedMessageForExistingSession(Recipient recipient, String plaintext) throws IOException { - IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret); - MessageCipher message = new MessageCipher(context, masterSecret, identityKeyPair, new TextTransport()); - return message.encrypt(recipient, plaintext.getBytes()); + IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret); + MessageCipher messageCipher = new MessageCipher(context, masterSecret, identityKeyPair, + new PushTransportDetails()); + + return new String(messageCipher.encrypt(recipient, plaintext.getBytes())); } } diff --git a/src/org/thoughtcrime/securesms/transport/SmsTransport.java b/src/org/thoughtcrime/securesms/transport/SmsTransport.java index 51d0687eee..2ee86ead58 100644 --- a/src/org/thoughtcrime/securesms/transport/SmsTransport.java +++ b/src/org/thoughtcrime/securesms/transport/SmsTransport.java @@ -4,9 +4,12 @@ import android.app.PendingIntent; import android.content.Context; import android.telephony.SmsManager; import android.util.Log; +import android.util.Pair; import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; +import org.thoughtcrime.securesms.sms.OutgoingPrekeyBundleMessage;import org.thoughtcrime.securesms.sms.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; @@ -17,6 +20,7 @@ import org.thoughtcrime.securesms.sms.MultipartSmsMessageHandler; import org.thoughtcrime.securesms.sms.OutgoingTextMessage; import org.thoughtcrime.securesms.sms.SmsTransportDetails; import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.whispersystems.textsecure.crypto.protocol.PreKeyBundleMessage; import java.util.ArrayList; @@ -43,9 +47,7 @@ public class SmsTransport extends BaseTransport { OutgoingTextMessage transportMessage = OutgoingTextMessage.from(message); if (message.isSecure()) { - String encryptedMessage = getAsymmetricEncrypt(masterSecret, message.getBody().getBody(), - message.getIndividualRecipient()); - transportMessage = transportMessage.withBody(encryptedMessage); + transportMessage = getAsymmetricEncrypt(masterSecret, transportMessage); } ArrayList messages = multipartMessageHandler.divideMessage(transportMessage); @@ -139,9 +141,26 @@ public class SmsTransport extends BaseTransport { return deliveredIntents; } - private String getAsymmetricEncrypt(MasterSecret masterSecret, String body, Recipient recipient) { - IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret); - MessageCipher message = new MessageCipher(context, masterSecret, identityKey, new SmsTransportDetails()); - return new String(message.encrypt(recipient, body.getBytes())); + private OutgoingTextMessage getAsymmetricEncrypt(MasterSecret masterSecret, + OutgoingTextMessage message) + { + Recipient recipient = message.getRecipients().getPrimaryRecipient(); + String body = message.getMessageBody(); + IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret); + + 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)); + } 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); + + return new OutgoingPrekeyBundleMessage(message, preKeyBundleMessage.serialize()); + } } } diff --git a/src/org/thoughtcrime/securesms/transport/UniversalTransport.java b/src/org/thoughtcrime/securesms/transport/UniversalTransport.java index e468cc1c62..144c4592f6 100644 --- a/src/org/thoughtcrime/securesms/transport/UniversalTransport.java +++ b/src/org/thoughtcrime/securesms/transport/UniversalTransport.java @@ -1,3 +1,19 @@ +/** + * Copyright (C) 2013 Open 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.transport; import android.content.Context;