Support encrypted transport, properly handle multiple recipients.

1) Add encryption support for the transport layer.  This obscures
   metadata from the push messaging provider.

2) Better support the direction multiple destination messages is
   headed (one unique message per recipient).
This commit is contained in:
Moxie Marlinspike 2013-08-29 17:01:30 -07:00
parent 68ec0a3727
commit 0cc5837d7f
17 changed files with 1798 additions and 107 deletions

View File

@ -0,0 +1,19 @@
package textsecure;
option java_package = "org.whispersystems.textsecure.push";
option java_outer_classname = "PushMessageProtos";
message IncomingPushMessageSignal {
optional uint32 type = 1;
optional string source = 2;
repeated string destinations = 3;
optional bytes message = 4;
message AttachmentPointer {
optional string contentType = 1;
optional string key = 2;
}
repeated AttachmentPointer attachments = 5;
optional uint64 timestamp = 6;
}

View File

@ -1,3 +1,3 @@
all:
protoc --java_out=../src/ PreKeyEntity.proto
protoc --java_out=../src/ IncomingPushMessageSignal.proto

View File

@ -1,10 +0,0 @@
package textsecure;
option java_package = "org.whispersystems.textsecure.encoded";
option java_outer_classname = "PreKeyProtos";
message PreKeyEntity {
optional uint64 id = 1;
optional bytes public_key = 2;
optional bytes identity_key = 3;
}

View File

@ -96,8 +96,8 @@ public class PreKeyBundleMessage {
}
}
public String serialize() {
return Base64.encodeBytesWithoutPadding(this.messageBytes);
public byte[] serialize() {
return this.messageBytes;
}
public int getSupportedVersion() {

View File

@ -0,0 +1,136 @@
package org.whispersystems.textsecure.push;
import android.util.Log;
import org.whispersystems.textsecure.crypto.InvalidVersionException;
import org.whispersystems.textsecure.util.Base64;
import org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal;
import org.whispersystems.textsecure.util.Hex;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.Mac;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
public class IncomingEncryptedPushMessage {
private static final int SUPPORTED_VERSION = 1;
private static final int CIPHER_KEY_SIZE = 32;
private static final int MAC_KEY_SIZE = 20;
private static final int MAC_SIZE = 10;
private static final int VERSION_OFFSET = 0;
private static final int VERSION_LENGTH = 1;
private static final int IV_OFFSET = VERSION_OFFSET + VERSION_LENGTH;
private static final int IV_LENGTH = 16;
private static final int CIPHERTEXT_OFFSET = IV_OFFSET + IV_LENGTH;
private final IncomingPushMessage incomingPushMessage;
public IncomingEncryptedPushMessage(String message, String signalingKey)
throws IOException, InvalidVersionException
{
byte[] ciphertext = Base64.decode(message);
if (ciphertext.length < VERSION_LENGTH || ciphertext[VERSION_OFFSET] != SUPPORTED_VERSION)
throw new InvalidVersionException("Unsupported version!");
SecretKeySpec cipherKey = getCipherKey(signalingKey);
SecretKeySpec macKey = getMacKey(signalingKey);
verifyMac(ciphertext, macKey);
byte[] plaintext = getPlaintext(ciphertext, cipherKey);
IncomingPushMessageSignal signal = IncomingPushMessageSignal.parseFrom(plaintext);
this.incomingPushMessage = new IncomingPushMessage(signal);
}
public IncomingPushMessage getIncomingPushMessage() {
return incomingPushMessage;
}
private byte[] getPlaintext(byte[] ciphertext, SecretKeySpec cipherKey) throws IOException {
try {
byte[] ivBytes = new byte[IV_LENGTH];
System.arraycopy(ciphertext, IV_OFFSET, ivBytes, 0, ivBytes.length);
IvParameterSpec iv = new IvParameterSpec(ivBytes);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, cipherKey, iv);
return cipher.doFinal(ciphertext, CIPHERTEXT_OFFSET,
ciphertext.length - VERSION_LENGTH - IV_LENGTH - MAC_SIZE);
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
} catch (NoSuchPaddingException e) {
throw new AssertionError(e);
} catch (InvalidKeyException e) {
throw new AssertionError(e);
} catch (InvalidAlgorithmParameterException e) {
throw new AssertionError(e);
} catch (IllegalBlockSizeException e) {
throw new AssertionError(e);
} catch (BadPaddingException e) {
Log.w("IncomingEncryptedPushMessage", e);
throw new IOException("Bad padding?");
}
}
private void verifyMac(byte[] ciphertext, SecretKeySpec macKey) throws IOException {
try {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(macKey);
if (ciphertext.length < MAC_SIZE + 1)
throw new IOException("Invalid MAC!");
mac.update(ciphertext, 0, ciphertext.length - MAC_SIZE);
byte[] ourMacFull = mac.doFinal();
byte[] ourMacBytes = new byte[MAC_SIZE];
System.arraycopy(ourMacFull, 0, ourMacBytes, 0, ourMacBytes.length);
byte[] theirMacBytes = new byte[MAC_SIZE];
System.arraycopy(ciphertext, ciphertext.length-MAC_SIZE, theirMacBytes, 0, theirMacBytes.length);
Log.w("IncomingEncryptedPushMessage", "Our MAC: " + Hex.toString(ourMacBytes));
Log.w("IncomingEncryptedPushMessage", "Thr MAC: " + Hex.toString(theirMacBytes));
if (!Arrays.equals(ourMacBytes, theirMacBytes)) {
throw new IOException("Invalid MAC compare!");
}
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
} catch (InvalidKeyException e) {
throw new AssertionError(e);
}
}
private SecretKeySpec getCipherKey(String signalingKey) throws IOException {
byte[] signalingKeyBytes = Base64.decode(signalingKey);
byte[] cipherKey = new byte[CIPHER_KEY_SIZE];
System.arraycopy(signalingKeyBytes, 0, cipherKey, 0, cipherKey.length);
return new SecretKeySpec(cipherKey, "AES");
}
private SecretKeySpec getMacKey(String signalingKey) throws IOException {
byte[] signalingKeyBytes = Base64.decode(signalingKey);
byte[] macKey = new byte[MAC_KEY_SIZE];
System.arraycopy(signalingKeyBytes, CIPHER_KEY_SIZE, macKey, 0, macKey.length);
return new SecretKeySpec(macKey, "HmacSHA256");
}
}

View File

@ -1,12 +1,16 @@
package org.whispersystems.textsecure.push;
import org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal;
import org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer;
import org.whispersystems.textsecure.util.Base64;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.LinkedList;
import java.util.List;
public class IncomingPushMessage implements Parcelable {
public class IncomingPushMessage implements PushMessage, Parcelable {
public static final Parcelable.Creator<IncomingPushMessage> CREATOR = new Parcelable.Creator<IncomingPushMessage>() {
@Override
@ -23,19 +27,23 @@ public class IncomingPushMessage implements Parcelable {
private int type;
private String source;
private List<String> destinations;
private String messageText;
private byte[] message;
private List<PushAttachmentPointer> attachments;
private long timestamp;
public IncomingPushMessage(String source, List<String> destinations, String messageText,
int type, List<PushAttachmentPointer> attachments, long timestamp)
{
this.type = type;
this.source = source;
this.destinations = destinations;
this.messageText = messageText;
this.attachments = attachments;
this.timestamp = timestamp;
public IncomingPushMessage(IncomingPushMessageSignal signal) {
this.type = signal.getType();
this.source = signal.getSource();
this.destinations = signal.getDestinationsList();
this.message = signal.getMessage().toByteArray();
this.timestamp = signal.getTimestamp();
this.attachments = new LinkedList<PushAttachmentPointer>();
List<AttachmentPointer> attachmentPointers = signal.getAttachmentsList();
for (AttachmentPointer pointer : attachmentPointers) {
this.attachments.add(new PushAttachmentPointer(pointer.getContentType(), pointer.getKey()));
}
}
public IncomingPushMessage(Parcel in) {
@ -44,7 +52,8 @@ public class IncomingPushMessage implements Parcelable {
this.source = in.readString();
in.readStringList(destinations);
this.messageText = in.readString();
this.message = new byte[in.readInt()];
in.readByteArray(this.message);
in.readList(attachments, PushAttachmentPointer.class.getClassLoader());
this.timestamp = in.readLong();
}
@ -62,7 +71,14 @@ public class IncomingPushMessage implements Parcelable {
}
public String getMessageText() {
return messageText;
if (type == TYPE_MESSAGE_CIPHERTEXT ||
type == TYPE_MESSAGE_KEY_EXCHANGE ||
type == TYPE_MESSAGE_PREKEY_BUNDLE)
{
return Base64.encodeBytesWithoutPadding(message);
}
return new String(message);
}
public List<String> getDestinations() {
@ -82,7 +98,8 @@ public class IncomingPushMessage implements Parcelable {
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(source);
dest.writeStringList(destinations);
dest.writeString(messageText);
dest.writeInt(message.length);
dest.writeByteArray(message);
dest.writeList(attachments);
dest.writeLong(timestamp);
}

View File

@ -1,50 +1,40 @@
package org.whispersystems.textsecure.push;
import org.whispersystems.textsecure.util.Base64;
import java.util.LinkedList;
import java.util.List;
public class OutgoingPushMessage {
public static final int TYPE_MESSAGE_PLAINTEXT = 0;
public static final int TYPE_MESSAGE_CIPHERTEXT = 1;
public static final int TYPE_MESSAGE_KEY_EXCHANGE = 2;
public static final int TYPE_MESSAGE_PREKEY_BUNDLE = 3;
public class OutgoingPushMessage implements PushMessage {
private int type;
private List<String> destinations;
private String messageText;
private String destination;
private String body;
private List<PushAttachmentPointer> attachments;
public OutgoingPushMessage(String destination, String messageText, int type) {
this.destinations = new LinkedList<String>();
this.attachments = new LinkedList<PushAttachmentPointer>();
this.messageText = messageText;
this.destinations.add(destination);
this.type = type;
public OutgoingPushMessage(String destination, byte[] body, int type) {
this.attachments = new LinkedList<PushAttachmentPointer>();
this.destination = destination;
this.body = Base64.encodeBytes(body);
this.type = type;
}
public OutgoingPushMessage(List<String> destinations, String messageText, int type) {
this.destinations = destinations;
this.messageText = messageText;
this.attachments = new LinkedList<PushAttachmentPointer>();
this.type = type;
}
public OutgoingPushMessage(List<String> destinations, String messageText,
List<PushAttachmentPointer> attachments, int type)
public OutgoingPushMessage(String destination, byte[] body,
List<PushAttachmentPointer> attachments,
int type)
{
this.destinations = destinations;
this.messageText = messageText;
this.attachments = attachments;
this.type = type;
this.destination = destination;
this.body = Base64.encodeBytes(body);
this.attachments = attachments;
this.type = type;
}
public List<String> getDestinations() {
return destinations;
public String getDestination() {
return destination;
}
public String getMessageText() {
return messageText;
public String getBody() {
return body;
}
public List<PushAttachmentPointer> getAttachments() {

View File

@ -0,0 +1,22 @@
package org.whispersystems.textsecure.push;
import java.util.LinkedList;
import java.util.List;
public class OutgoingPushMessageList {
private List<OutgoingPushMessage> messages;
public OutgoingPushMessageList(OutgoingPushMessage message) {
this.messages = new LinkedList<OutgoingPushMessage>();
this.messages.add(message);
}
public OutgoingPushMessageList(List<OutgoingPushMessage> messages) {
this.messages = messages;
}
public List<OutgoingPushMessage> getMessages() {
return messages;
}
}

View File

@ -0,0 +1,9 @@
package org.whispersystems.textsecure.push;
public interface PushMessage {
public static final int TYPE_MESSAGE_PLAINTEXT = 0;
public static final int TYPE_MESSAGE_CIPHERTEXT = 1;
public static final int TYPE_MESSAGE_KEY_EXCHANGE = 2;
public static final int TYPE_MESSAGE_PREKEY_BUNDLE = 3;
}

File diff suppressed because it is too large Load Diff

View File

@ -4,13 +4,10 @@ import android.content.Context;
import android.util.Log;
import android.util.Pair;
import com.google.protobuf.ByteString;
import com.google.thoughtcrimegson.Gson;
import org.whispersystems.textsecure.R;
import org.whispersystems.textsecure.Release;
import org.whispersystems.textsecure.crypto.IdentityKey;
import org.whispersystems.textsecure.crypto.PreKeyPair;
import org.whispersystems.textsecure.crypto.PreKeyPublic;
import org.whispersystems.textsecure.directory.DirectoryDescriptor;
import org.whispersystems.textsecure.storage.PreKeyRecord;
import org.whispersystems.textsecure.util.Base64;
@ -33,6 +30,7 @@ import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
@ -79,31 +77,45 @@ public class PushServiceSocket {
makeRequest(REGISTER_GCM_PATH, "DELETE", null);
}
public void sendMessage(String recipient, String messageText, int type)
public void sendMessage(String recipient, byte[] body, int type)
throws IOException
{
OutgoingPushMessage message = new OutgoingPushMessage(recipient, messageText, type);
sendMessage(message);
OutgoingPushMessage message = new OutgoingPushMessage(recipient, body, type);
sendMessage(new OutgoingPushMessageList(message));
}
public void sendMessage(List<String> recipients, String messageText, int type)
public void sendMessage(List<String> recipients, List<byte[]> bodies,
List<List<PushAttachmentData>> attachmentsList, int type)
throws IOException
{
OutgoingPushMessage message = new OutgoingPushMessage(recipients, messageText, type);
sendMessage(message);
List<OutgoingPushMessage> messages = new LinkedList<OutgoingPushMessage>();
Iterator<String> recipientsIterator = recipients.iterator();
Iterator<byte[]> bodiesIterator = bodies.iterator();
Iterator<List<PushAttachmentData>> attachmentsIterator = attachmentsList.iterator();
while (recipientsIterator.hasNext()) {
String recipient = recipientsIterator.next();
byte[] body = bodiesIterator.next();
List<PushAttachmentData> attachments = attachmentsIterator.next();
OutgoingPushMessage message;
if (!attachments.isEmpty()) {
List<PushAttachmentPointer> attachmentIds = sendAttachments(attachments);
message = new OutgoingPushMessage(recipient, body, attachmentIds, type);
} else {
message = new OutgoingPushMessage(recipient, body, type);
}
messages.add(message);
}
sendMessage(new OutgoingPushMessageList(messages));
}
public void sendMessage(List<String> recipients, String messageText,
List<PushAttachmentData> attachments, int type)
throws IOException
{
List<PushAttachmentPointer> attachmentIds = sendAttachments(attachments);
OutgoingPushMessage message = new OutgoingPushMessage(recipients, messageText, attachmentIds, type);
sendMessage(message);
}
private void sendMessage(OutgoingPushMessage message) throws IOException {
String responseText = makeRequest(MESSAGE_PATH, "POST", new Gson().toJson(message));
private void sendMessage(OutgoingPushMessageList messages) throws IOException {
String responseText = makeRequest(MESSAGE_PATH, "POST", new Gson().toJson(messages));
PushMessageResponse response = new Gson().fromJson(responseText, PushMessageResponse.class);
if (response.getFailure().size() != 0)

View File

@ -34,11 +34,11 @@ public class PushTransportDetails implements TransportDetails {
@Override
public byte[] getEncodedMessage(byte[] messageWithMac) {
return Base64.encodeBytesWithoutPadding(messageWithMac).getBytes();
return messageWithMac;
}
@Override
public byte[] getDecodedMessage(byte[] encodedMessageBytes) throws IOException {
return Base64.decodeWithoutPadding(new String(encodedMessageBytes));
return encodedMessageBytes;
}
}

View File

@ -5,11 +5,12 @@ import android.content.Intent;
import android.util.Log;
import com.google.android.gcm.GCMBaseIntentService;
import com.google.thoughtcrimegson.Gson;
import org.thoughtcrime.securesms.service.RegistrationService;
import org.thoughtcrime.securesms.service.SendReceiveService;
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.textsecure.crypto.InvalidVersionException;
import org.whispersystems.textsecure.push.IncomingEncryptedPushMessage;
import org.whispersystems.textsecure.push.IncomingPushMessage;
import org.whispersystems.textsecure.push.PushServiceSocket;
import org.whispersystems.textsecure.util.Util;
@ -48,16 +49,24 @@ public class GcmIntentService extends GCMBaseIntentService {
@Override
protected void onMessage(Context context, Intent intent) {
String data = intent.getStringExtra("message");
Log.w("GcmIntentService", "GCM message: " + data);
try {
String data = intent.getStringExtra("message");
Log.w("GcmIntentService", "GCM message: " + data);
if (Util.isEmpty(data))
return;
if (Util.isEmpty(data))
return;
IncomingPushMessage message = new Gson().fromJson(data, IncomingPushMessage.class);
String sessionKey = TextSecurePreferences.getSignalingKey(context);
IncomingEncryptedPushMessage encryptedMessage = new IncomingEncryptedPushMessage(data, sessionKey);
IncomingPushMessage message = encryptedMessage.getIncomingPushMessage();
if (!message.hasAttachments()) handleIncomingTextMessage(context, message);
else handleIncomingMediaMessage(context, message);
if (!message.hasAttachments()) handleIncomingTextMessage(context, message);
else handleIncomingMediaMessage(context, message);
} catch (IOException e) {
Log.w("GcmIntentService", e);
} catch (InvalidVersionException e) {
Log.w("GcmIntentService", e);
}
}
@Override

View File

@ -42,6 +42,7 @@ import org.whispersystems.textsecure.crypto.InvalidVersionException;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.crypto.protocol.PreKeyBundleMessage;
import org.whispersystems.textsecure.push.OutgoingPushMessage;
import org.whispersystems.textsecure.push.PushMessage;
import org.whispersystems.textsecure.storage.InvalidKeyIdException;
import java.util.List;
@ -62,11 +63,11 @@ public class SmsReceiver {
IncomingTextMessage message = messages.get(0);
switch (pushType) {
case OutgoingPushMessage.TYPE_MESSAGE_CIPHERTEXT:
case PushMessage.TYPE_MESSAGE_CIPHERTEXT:
return new IncomingEncryptedMessage(message, message.getMessageBody());
case OutgoingPushMessage.TYPE_MESSAGE_PREKEY_BUNDLE:
case PushMessage.TYPE_MESSAGE_PREKEY_BUNDLE:
return new IncomingPreKeyBundleMessage(message, message.getMessageBody());
case OutgoingPushMessage.TYPE_MESSAGE_KEY_EXCHANGE:
case PushMessage.TYPE_MESSAGE_KEY_EXCHANGE:
return new IncomingKeyExchangeMessage(message, message.getMessageBody());
}

View File

@ -55,7 +55,7 @@ public class PushTransport extends BaseTransport {
String recipientCanonicalNumber = PhoneNumberFormatter.formatNumber(recipient.getNumber(),
localNumber);
Pair<Integer, String> typeAndCiphertext = getEncryptedMessage(socket, recipient, recipientCanonicalNumber, plaintext);
Pair<Integer, byte[]> typeAndCiphertext = getEncryptedMessage(socket, recipient, recipientCanonicalNumber, plaintext);
socket.sendMessage(recipientCanonicalNumber, typeAndCiphertext.second, typeAndCiphertext.first);
@ -71,11 +71,19 @@ public class PushTransport extends BaseTransport {
String localNumber = TextSecurePreferences.getLocalNumber(context);
String password = TextSecurePreferences.getPushServerPassword(context);
PushServiceSocket socket = new PushServiceSocket(context, localNumber, password);
String messageText = PartParser.getMessageText(message.getBody());
byte[] messageText = PartParser.getMessageText(message.getBody()).getBytes();
List<PushAttachmentData> attachments = getAttachmentsFromBody(message.getBody());
if (attachments.isEmpty()) socket.sendMessage(destinations, messageText, OutgoingPushMessage.TYPE_MESSAGE_PLAINTEXT);
else socket.sendMessage(destinations, messageText, attachments, OutgoingPushMessage.TYPE_MESSAGE_PLAINTEXT);
List<byte[]> messagesList = new LinkedList<byte[]>();
List<List<PushAttachmentData>> attachmentsList = new LinkedList<List<PushAttachmentData>>();
for (String recipient : destinations) {
messagesList.add(messageText);
attachmentsList.add(attachments);
}
socket.sendMessage(destinations, messagesList, attachmentsList,
OutgoingPushMessage.TYPE_MESSAGE_PLAINTEXT);
} catch (RateLimitException e) {
Log.w("PushTransport", e);
throw new IOException("Rate limit exceeded.");
@ -99,26 +107,26 @@ public class PushTransport extends BaseTransport {
return attachments;
}
private Pair<Integer, String> getEncryptedMessage(PushServiceSocket socket, Recipient recipient,
private Pair<Integer, byte[]> 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<Integer, String>(OutgoingPushMessage.TYPE_MESSAGE_CIPHERTEXT, ciphertext);
byte[] ciphertext = getEncryptedMessageForExistingSession(recipient, plaintext);
return new Pair<Integer, byte[]>(OutgoingPushMessage.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<Integer, String>(OutgoingPushMessage.TYPE_MESSAGE_PREKEY_BUNDLE, ciphertext);
byte[] ciphertext = getEncryptedPrekeyBundleMessageForExistingSession(recipient, plaintext);
return new Pair<Integer, byte[]>(OutgoingPushMessage.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<Integer, String>(OutgoingPushMessage.TYPE_MESSAGE_PREKEY_BUNDLE, ciphertext);
byte[] ciphertext = getEncryptedPrekeyBundleMessageForNewSession(socket, recipient, canonicalRecipientNumber, plaintext);
return new Pair<Integer, byte[]>(OutgoingPushMessage.TYPE_MESSAGE_PREKEY_BUNDLE, ciphertext);
}
}
private String getEncryptedPrekeyBundleMessageForExistingSession(Recipient recipient,
private byte[] getEncryptedPrekeyBundleMessageForExistingSession(Recipient recipient,
String plaintext)
{
IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret);
@ -131,7 +139,7 @@ public class PushTransport extends BaseTransport {
return preKeyBundleMessage.serialize();
}
private String getEncryptedPrekeyBundleMessageForNewSession(PushServiceSocket socket,
private byte[] getEncryptedPrekeyBundleMessageForNewSession(PushServiceSocket socket,
Recipient recipient,
String canonicalRecipientNumber,
String plaintext)
@ -151,14 +159,14 @@ public class PushTransport extends BaseTransport {
return preKeyBundleMessage.serialize();
}
private String getEncryptedMessageForExistingSession(Recipient recipient, String plaintext)
private byte[] getEncryptedMessageForExistingSession(Recipient recipient, String plaintext)
throws IOException
{
IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret);
MessageCipher messageCipher = new MessageCipher(context, masterSecret, identityKeyPair,
new PushTransportDetails());
return new String(messageCipher.encrypt(recipient, plaintext.getBytes()));
return messageCipher.encrypt(recipient, plaintext.getBytes());
}
}

View File

@ -21,6 +21,7 @@ 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 org.whispersystems.textsecure.util.Base64;
import java.util.ArrayList;
@ -160,7 +161,7 @@ public class SmsTransport extends BaseTransport {
byte[] bundledMessage = messageCipher.encrypt(recipient, body.getBytes());
PreKeyBundleMessage preKeyBundleMessage = new PreKeyBundleMessage(identityKey.getPublicKey(), bundledMessage);
return new OutgoingPrekeyBundleMessage(message, preKeyBundleMessage.serialize());
return new OutgoingPrekeyBundleMessage(message, Base64.encodeBytesWithoutPadding(preKeyBundleMessage.serialize()));
}
}
}

View File

@ -59,6 +59,10 @@ public class TextSecurePreferences {
setStringPreference(context, SIGNALING_KEY_PREF, signalingKey);
}
public static String getSignalingKey(Context context) {
return getStringPreference(context, SIGNALING_KEY_PREF, null);
}
public static boolean isEnterImeKeyEnabled(Context context) {
return getBooleanPreference(context, ENTER_PRESENT_PREF, false);
}