diff --git a/library/protobuf/IncomingPushMessageSignal.proto b/library/protobuf/IncomingPushMessageSignal.proto new file mode 100644 index 0000000000..1e82872705 --- /dev/null +++ b/library/protobuf/IncomingPushMessageSignal.proto @@ -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; +} \ No newline at end of file diff --git a/library/protobuf/Makefile b/library/protobuf/Makefile index 836c360af6..55640f5437 100644 --- a/library/protobuf/Makefile +++ b/library/protobuf/Makefile @@ -1,3 +1,3 @@ all: - protoc --java_out=../src/ PreKeyEntity.proto + protoc --java_out=../src/ IncomingPushMessageSignal.proto diff --git a/library/protobuf/PreKeyEntity.proto b/library/protobuf/PreKeyEntity.proto deleted file mode 100644 index 490c1cc5ba..0000000000 --- a/library/protobuf/PreKeyEntity.proto +++ /dev/null @@ -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; -} diff --git a/library/src/org/whispersystems/textsecure/crypto/protocol/PreKeyBundleMessage.java b/library/src/org/whispersystems/textsecure/crypto/protocol/PreKeyBundleMessage.java index 02641c1cff..48e17fdedf 100644 --- a/library/src/org/whispersystems/textsecure/crypto/protocol/PreKeyBundleMessage.java +++ b/library/src/org/whispersystems/textsecure/crypto/protocol/PreKeyBundleMessage.java @@ -96,8 +96,8 @@ public class PreKeyBundleMessage { } } - public String serialize() { - return Base64.encodeBytesWithoutPadding(this.messageBytes); + public byte[] serialize() { + return this.messageBytes; } public int getSupportedVersion() { diff --git a/library/src/org/whispersystems/textsecure/push/IncomingEncryptedPushMessage.java b/library/src/org/whispersystems/textsecure/push/IncomingEncryptedPushMessage.java new file mode 100644 index 0000000000..45d173a643 --- /dev/null +++ b/library/src/org/whispersystems/textsecure/push/IncomingEncryptedPushMessage.java @@ -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"); + } + +} diff --git a/library/src/org/whispersystems/textsecure/push/IncomingPushMessage.java b/library/src/org/whispersystems/textsecure/push/IncomingPushMessage.java index 8a146a68bc..cb2427cd65 100644 --- a/library/src/org/whispersystems/textsecure/push/IncomingPushMessage.java +++ b/library/src/org/whispersystems/textsecure/push/IncomingPushMessage.java @@ -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 CREATOR = new Parcelable.Creator() { @Override @@ -23,19 +27,23 @@ public class IncomingPushMessage implements Parcelable { private int type; private String source; private List destinations; - private String messageText; + private byte[] message; private List attachments; private long timestamp; - public IncomingPushMessage(String source, List destinations, String messageText, - int type, List 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(); + + List 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 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); } diff --git a/library/src/org/whispersystems/textsecure/push/OutgoingPushMessage.java b/library/src/org/whispersystems/textsecure/push/OutgoingPushMessage.java index 097d2dd01d..487a0da07d 100644 --- a/library/src/org/whispersystems/textsecure/push/OutgoingPushMessage.java +++ b/library/src/org/whispersystems/textsecure/push/OutgoingPushMessage.java @@ -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 destinations; - private String messageText; + private String destination; + private String body; private List attachments; - public OutgoingPushMessage(String destination, String messageText, int type) { - this.destinations = new LinkedList(); - this.attachments = new LinkedList(); - this.messageText = messageText; - this.destinations.add(destination); - this.type = type; + public OutgoingPushMessage(String destination, byte[] body, int type) { + this.attachments = new LinkedList(); + this.destination = destination; + this.body = Base64.encodeBytes(body); + this.type = type; } - public OutgoingPushMessage(List destinations, String messageText, int type) { - this.destinations = destinations; - this.messageText = messageText; - this.attachments = new LinkedList(); - this.type = type; - } - - public OutgoingPushMessage(List destinations, String messageText, - List attachments, int type) + public OutgoingPushMessage(String destination, byte[] body, + List 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 getDestinations() { - return destinations; + public String getDestination() { + return destination; } - public String getMessageText() { - return messageText; + public String getBody() { + return body; } public List getAttachments() { diff --git a/library/src/org/whispersystems/textsecure/push/OutgoingPushMessageList.java b/library/src/org/whispersystems/textsecure/push/OutgoingPushMessageList.java new file mode 100644 index 0000000000..6504ba5b7a --- /dev/null +++ b/library/src/org/whispersystems/textsecure/push/OutgoingPushMessageList.java @@ -0,0 +1,22 @@ +package org.whispersystems.textsecure.push; + +import java.util.LinkedList; +import java.util.List; + +public class OutgoingPushMessageList { + + private List messages; + + public OutgoingPushMessageList(OutgoingPushMessage message) { + this.messages = new LinkedList(); + this.messages.add(message); + } + + public OutgoingPushMessageList(List messages) { + this.messages = messages; + } + + public List getMessages() { + return messages; + } +} diff --git a/library/src/org/whispersystems/textsecure/push/PushMessage.java b/library/src/org/whispersystems/textsecure/push/PushMessage.java new file mode 100644 index 0000000000..2cec4f4153 --- /dev/null +++ b/library/src/org/whispersystems/textsecure/push/PushMessage.java @@ -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; +} diff --git a/library/src/org/whispersystems/textsecure/push/PushMessageProtos.java b/library/src/org/whispersystems/textsecure/push/PushMessageProtos.java new file mode 100644 index 0000000000..ff79ffa74e --- /dev/null +++ b/library/src/org/whispersystems/textsecure/push/PushMessageProtos.java @@ -0,0 +1,1473 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: IncomingPushMessageSignal.proto + +package org.whispersystems.textsecure.push; + +public final class PushMessageProtos { + private PushMessageProtos() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + } + public interface IncomingPushMessageSignalOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint32 type = 1; + boolean hasType(); + int getType(); + + // optional string source = 2; + boolean hasSource(); + String getSource(); + + // repeated string destinations = 3; + java.util.List getDestinationsList(); + int getDestinationsCount(); + String getDestinations(int index); + + // optional bytes message = 4; + boolean hasMessage(); + com.google.protobuf.ByteString getMessage(); + + // repeated .textsecure.IncomingPushMessageSignal.AttachmentPointer attachments = 5; + java.util.List + getAttachmentsList(); + org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer getAttachments(int index); + int getAttachmentsCount(); + java.util.List + getAttachmentsOrBuilderList(); + org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointerOrBuilder getAttachmentsOrBuilder( + int index); + + // optional uint64 timestamp = 6; + boolean hasTimestamp(); + long getTimestamp(); + } + public static final class IncomingPushMessageSignal extends + com.google.protobuf.GeneratedMessage + implements IncomingPushMessageSignalOrBuilder { + // Use IncomingPushMessageSignal.newBuilder() to construct. + private IncomingPushMessageSignal(Builder builder) { + super(builder); + } + private IncomingPushMessageSignal(boolean noInit) {} + + private static final IncomingPushMessageSignal defaultInstance; + public static IncomingPushMessageSignal getDefaultInstance() { + return defaultInstance; + } + + public IncomingPushMessageSignal getDefaultInstanceForType() { + return defaultInstance; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.textsecure.push.PushMessageProtos.internal_static_textsecure_IncomingPushMessageSignal_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.textsecure.push.PushMessageProtos.internal_static_textsecure_IncomingPushMessageSignal_fieldAccessorTable; + } + + public interface AttachmentPointerOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional string contentType = 1; + boolean hasContentType(); + String getContentType(); + + // optional string key = 2; + boolean hasKey(); + String getKey(); + } + public static final class AttachmentPointer extends + com.google.protobuf.GeneratedMessage + implements AttachmentPointerOrBuilder { + // Use AttachmentPointer.newBuilder() to construct. + private AttachmentPointer(Builder builder) { + super(builder); + } + private AttachmentPointer(boolean noInit) {} + + private static final AttachmentPointer defaultInstance; + public static AttachmentPointer getDefaultInstance() { + return defaultInstance; + } + + public AttachmentPointer getDefaultInstanceForType() { + return defaultInstance; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.textsecure.push.PushMessageProtos.internal_static_textsecure_IncomingPushMessageSignal_AttachmentPointer_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.textsecure.push.PushMessageProtos.internal_static_textsecure_IncomingPushMessageSignal_AttachmentPointer_fieldAccessorTable; + } + + private int bitField0_; + // optional string contentType = 1; + public static final int CONTENTTYPE_FIELD_NUMBER = 1; + private java.lang.Object contentType_; + public boolean hasContentType() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + public String getContentType() { + java.lang.Object ref = contentType_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (com.google.protobuf.Internal.isValidUtf8(bs)) { + contentType_ = s; + } + return s; + } + } + private com.google.protobuf.ByteString getContentTypeBytes() { + java.lang.Object ref = contentType_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8((String) ref); + contentType_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional string key = 2; + public static final int KEY_FIELD_NUMBER = 2; + private java.lang.Object key_; + public boolean hasKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + public String getKey() { + java.lang.Object ref = key_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (com.google.protobuf.Internal.isValidUtf8(bs)) { + key_ = s; + } + return s; + } + } + private com.google.protobuf.ByteString getKeyBytes() { + java.lang.Object ref = key_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8((String) ref); + key_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private void initFields() { + contentType_ = ""; + key_ = ""; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, getContentTypeBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, getKeyBytes()); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, getContentTypeBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, getKeyBytes()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data).buildParsed(); + } + public static org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data, extensionRegistry) + .buildParsed(); + } + public static org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data).buildParsed(); + } + public static org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data, extensionRegistry) + .buildParsed(); + } + public static org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer parseFrom(java.io.InputStream input) + throws java.io.IOException { + return newBuilder().mergeFrom(input).buildParsed(); + } + public static org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return newBuilder().mergeFrom(input, extensionRegistry) + .buildParsed(); + } + public static org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + Builder builder = newBuilder(); + if (builder.mergeDelimitedFrom(input)) { + return builder.buildParsed(); + } else { + return null; + } + } + public static org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + Builder builder = newBuilder(); + if (builder.mergeDelimitedFrom(input, extensionRegistry)) { + return builder.buildParsed(); + } else { + return null; + } + } + public static org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return newBuilder().mergeFrom(input).buildParsed(); + } + public static org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return newBuilder().mergeFrom(input, extensionRegistry) + .buildParsed(); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointerOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.textsecure.push.PushMessageProtos.internal_static_textsecure_IncomingPushMessageSignal_AttachmentPointer_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.textsecure.push.PushMessageProtos.internal_static_textsecure_IncomingPushMessageSignal_AttachmentPointer_fieldAccessorTable; + } + + // Construct using org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder(BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + contentType_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + key_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.getDescriptor(); + } + + public org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer getDefaultInstanceForType() { + return org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.getDefaultInstance(); + } + + public org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer build() { + org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + private org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer buildParsed() + throws com.google.protobuf.InvalidProtocolBufferException { + org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException( + result).asInvalidProtocolBufferException(); + } + return result; + } + + public org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer buildPartial() { + org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer result = new org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.contentType_ = contentType_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.key_ = key_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer) { + return mergeFrom((org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer other) { + if (other == org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.getDefaultInstance()) return this; + if (other.hasContentType()) { + setContentType(other.getContentType()); + } + if (other.hasKey()) { + setKey(other.getKey()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder( + this.getUnknownFields()); + while (true) { + int tag = input.readTag(); + switch (tag) { + case 0: + this.setUnknownFields(unknownFields.build()); + onChanged(); + return this; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + this.setUnknownFields(unknownFields.build()); + onChanged(); + return this; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + contentType_ = input.readBytes(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + key_ = input.readBytes(); + break; + } + } + } + } + + private int bitField0_; + + // optional string contentType = 1; + private java.lang.Object contentType_ = ""; + public boolean hasContentType() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + public String getContentType() { + java.lang.Object ref = contentType_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref).toStringUtf8(); + contentType_ = s; + return s; + } else { + return (String) ref; + } + } + public Builder setContentType(String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + contentType_ = value; + onChanged(); + return this; + } + public Builder clearContentType() { + bitField0_ = (bitField0_ & ~0x00000001); + contentType_ = getDefaultInstance().getContentType(); + onChanged(); + return this; + } + void setContentType(com.google.protobuf.ByteString value) { + bitField0_ |= 0x00000001; + contentType_ = value; + onChanged(); + } + + // optional string key = 2; + private java.lang.Object key_ = ""; + public boolean hasKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + public String getKey() { + java.lang.Object ref = key_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref).toStringUtf8(); + key_ = s; + return s; + } else { + return (String) ref; + } + } + public Builder setKey(String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + key_ = value; + onChanged(); + return this; + } + public Builder clearKey() { + bitField0_ = (bitField0_ & ~0x00000002); + key_ = getDefaultInstance().getKey(); + onChanged(); + return this; + } + void setKey(com.google.protobuf.ByteString value) { + bitField0_ |= 0x00000002; + key_ = value; + onChanged(); + } + + // @@protoc_insertion_point(builder_scope:textsecure.IncomingPushMessageSignal.AttachmentPointer) + } + + static { + defaultInstance = new AttachmentPointer(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.IncomingPushMessageSignal.AttachmentPointer) + } + + private int bitField0_; + // optional uint32 type = 1; + public static final int TYPE_FIELD_NUMBER = 1; + private int type_; + public boolean hasType() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + public int getType() { + return type_; + } + + // optional string source = 2; + public static final int SOURCE_FIELD_NUMBER = 2; + private java.lang.Object source_; + public boolean hasSource() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + public String getSource() { + java.lang.Object ref = source_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (com.google.protobuf.Internal.isValidUtf8(bs)) { + source_ = s; + } + return s; + } + } + private com.google.protobuf.ByteString getSourceBytes() { + java.lang.Object ref = source_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8((String) ref); + source_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // repeated string destinations = 3; + public static final int DESTINATIONS_FIELD_NUMBER = 3; + private com.google.protobuf.LazyStringList destinations_; + public java.util.List + getDestinationsList() { + return destinations_; + } + public int getDestinationsCount() { + return destinations_.size(); + } + public String getDestinations(int index) { + return destinations_.get(index); + } + + // optional bytes message = 4; + public static final int MESSAGE_FIELD_NUMBER = 4; + private com.google.protobuf.ByteString message_; + public boolean hasMessage() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + public com.google.protobuf.ByteString getMessage() { + return message_; + } + + // repeated .textsecure.IncomingPushMessageSignal.AttachmentPointer attachments = 5; + public static final int ATTACHMENTS_FIELD_NUMBER = 5; + private java.util.List attachments_; + public java.util.List getAttachmentsList() { + return attachments_; + } + public java.util.List + getAttachmentsOrBuilderList() { + return attachments_; + } + public int getAttachmentsCount() { + return attachments_.size(); + } + public org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer getAttachments(int index) { + return attachments_.get(index); + } + public org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointerOrBuilder getAttachmentsOrBuilder( + int index) { + return attachments_.get(index); + } + + // optional uint64 timestamp = 6; + public static final int TIMESTAMP_FIELD_NUMBER = 6; + private long timestamp_; + public boolean hasTimestamp() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + public long getTimestamp() { + return timestamp_; + } + + private void initFields() { + type_ = 0; + source_ = ""; + destinations_ = com.google.protobuf.LazyStringArrayList.EMPTY; + message_ = com.google.protobuf.ByteString.EMPTY; + attachments_ = java.util.Collections.emptyList(); + timestamp_ = 0L; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(1, type_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, getSourceBytes()); + } + for (int i = 0; i < destinations_.size(); i++) { + output.writeBytes(3, destinations_.getByteString(i)); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(4, message_); + } + for (int i = 0; i < attachments_.size(); i++) { + output.writeMessage(5, attachments_.get(i)); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeUInt64(6, timestamp_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(1, type_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, getSourceBytes()); + } + { + int dataSize = 0; + for (int i = 0; i < destinations_.size(); i++) { + dataSize += com.google.protobuf.CodedOutputStream + .computeBytesSizeNoTag(destinations_.getByteString(i)); + } + size += dataSize; + size += 1 * getDestinationsList().size(); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(4, message_); + } + for (int i = 0; i < attachments_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(5, attachments_.get(i)); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt64Size(6, timestamp_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data).buildParsed(); + } + public static org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data, extensionRegistry) + .buildParsed(); + } + public static org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data).buildParsed(); + } + public static org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data, extensionRegistry) + .buildParsed(); + } + public static org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal parseFrom(java.io.InputStream input) + throws java.io.IOException { + return newBuilder().mergeFrom(input).buildParsed(); + } + public static org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return newBuilder().mergeFrom(input, extensionRegistry) + .buildParsed(); + } + public static org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + Builder builder = newBuilder(); + if (builder.mergeDelimitedFrom(input)) { + return builder.buildParsed(); + } else { + return null; + } + } + public static org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + Builder builder = newBuilder(); + if (builder.mergeDelimitedFrom(input, extensionRegistry)) { + return builder.buildParsed(); + } else { + return null; + } + } + public static org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return newBuilder().mergeFrom(input).buildParsed(); + } + public static org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return newBuilder().mergeFrom(input, extensionRegistry) + .buildParsed(); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignalOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.textsecure.push.PushMessageProtos.internal_static_textsecure_IncomingPushMessageSignal_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.textsecure.push.PushMessageProtos.internal_static_textsecure_IncomingPushMessageSignal_fieldAccessorTable; + } + + // Construct using org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder(BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getAttachmentsFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + type_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + source_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + destinations_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + message_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000008); + if (attachmentsBuilder_ == null) { + attachments_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000010); + } else { + attachmentsBuilder_.clear(); + } + timestamp_ = 0L; + bitField0_ = (bitField0_ & ~0x00000020); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.getDescriptor(); + } + + public org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal getDefaultInstanceForType() { + return org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.getDefaultInstance(); + } + + public org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal build() { + org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + private org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal buildParsed() + throws com.google.protobuf.InvalidProtocolBufferException { + org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException( + result).asInvalidProtocolBufferException(); + } + return result; + } + + public org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal buildPartial() { + org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal result = new org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.type_ = type_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.source_ = source_; + if (((bitField0_ & 0x00000004) == 0x00000004)) { + destinations_ = new com.google.protobuf.UnmodifiableLazyStringList( + destinations_); + bitField0_ = (bitField0_ & ~0x00000004); + } + result.destinations_ = destinations_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000004; + } + result.message_ = message_; + if (attachmentsBuilder_ == null) { + if (((bitField0_ & 0x00000010) == 0x00000010)) { + attachments_ = java.util.Collections.unmodifiableList(attachments_); + bitField0_ = (bitField0_ & ~0x00000010); + } + result.attachments_ = attachments_; + } else { + result.attachments_ = attachmentsBuilder_.build(); + } + if (((from_bitField0_ & 0x00000020) == 0x00000020)) { + to_bitField0_ |= 0x00000008; + } + result.timestamp_ = timestamp_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal) { + return mergeFrom((org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal other) { + if (other == org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.getDefaultInstance()) return this; + if (other.hasType()) { + setType(other.getType()); + } + if (other.hasSource()) { + setSource(other.getSource()); + } + if (!other.destinations_.isEmpty()) { + if (destinations_.isEmpty()) { + destinations_ = other.destinations_; + bitField0_ = (bitField0_ & ~0x00000004); + } else { + ensureDestinationsIsMutable(); + destinations_.addAll(other.destinations_); + } + onChanged(); + } + if (other.hasMessage()) { + setMessage(other.getMessage()); + } + if (attachmentsBuilder_ == null) { + if (!other.attachments_.isEmpty()) { + if (attachments_.isEmpty()) { + attachments_ = other.attachments_; + bitField0_ = (bitField0_ & ~0x00000010); + } else { + ensureAttachmentsIsMutable(); + attachments_.addAll(other.attachments_); + } + onChanged(); + } + } else { + if (!other.attachments_.isEmpty()) { + if (attachmentsBuilder_.isEmpty()) { + attachmentsBuilder_.dispose(); + attachmentsBuilder_ = null; + attachments_ = other.attachments_; + bitField0_ = (bitField0_ & ~0x00000010); + attachmentsBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + getAttachmentsFieldBuilder() : null; + } else { + attachmentsBuilder_.addAllMessages(other.attachments_); + } + } + } + if (other.hasTimestamp()) { + setTimestamp(other.getTimestamp()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder( + this.getUnknownFields()); + while (true) { + int tag = input.readTag(); + switch (tag) { + case 0: + this.setUnknownFields(unknownFields.build()); + onChanged(); + return this; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + this.setUnknownFields(unknownFields.build()); + onChanged(); + return this; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + type_ = input.readUInt32(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + source_ = input.readBytes(); + break; + } + case 26: { + ensureDestinationsIsMutable(); + destinations_.add(input.readBytes()); + break; + } + case 34: { + bitField0_ |= 0x00000008; + message_ = input.readBytes(); + break; + } + case 42: { + org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.Builder subBuilder = org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.newBuilder(); + input.readMessage(subBuilder, extensionRegistry); + addAttachments(subBuilder.buildPartial()); + break; + } + case 48: { + bitField0_ |= 0x00000020; + timestamp_ = input.readUInt64(); + break; + } + } + } + } + + private int bitField0_; + + // optional uint32 type = 1; + private int type_ ; + public boolean hasType() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + public int getType() { + return type_; + } + public Builder setType(int value) { + bitField0_ |= 0x00000001; + type_ = value; + onChanged(); + return this; + } + public Builder clearType() { + bitField0_ = (bitField0_ & ~0x00000001); + type_ = 0; + onChanged(); + return this; + } + + // optional string source = 2; + private java.lang.Object source_ = ""; + public boolean hasSource() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + public String getSource() { + java.lang.Object ref = source_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref).toStringUtf8(); + source_ = s; + return s; + } else { + return (String) ref; + } + } + public Builder setSource(String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + source_ = value; + onChanged(); + return this; + } + public Builder clearSource() { + bitField0_ = (bitField0_ & ~0x00000002); + source_ = getDefaultInstance().getSource(); + onChanged(); + return this; + } + void setSource(com.google.protobuf.ByteString value) { + bitField0_ |= 0x00000002; + source_ = value; + onChanged(); + } + + // repeated string destinations = 3; + private com.google.protobuf.LazyStringList destinations_ = com.google.protobuf.LazyStringArrayList.EMPTY; + private void ensureDestinationsIsMutable() { + if (!((bitField0_ & 0x00000004) == 0x00000004)) { + destinations_ = new com.google.protobuf.LazyStringArrayList(destinations_); + bitField0_ |= 0x00000004; + } + } + public java.util.List + getDestinationsList() { + return java.util.Collections.unmodifiableList(destinations_); + } + public int getDestinationsCount() { + return destinations_.size(); + } + public String getDestinations(int index) { + return destinations_.get(index); + } + public Builder setDestinations( + int index, String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureDestinationsIsMutable(); + destinations_.set(index, value); + onChanged(); + return this; + } + public Builder addDestinations(String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureDestinationsIsMutable(); + destinations_.add(value); + onChanged(); + return this; + } + public Builder addAllDestinations( + java.lang.Iterable values) { + ensureDestinationsIsMutable(); + super.addAll(values, destinations_); + onChanged(); + return this; + } + public Builder clearDestinations() { + destinations_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + onChanged(); + return this; + } + void addDestinations(com.google.protobuf.ByteString value) { + ensureDestinationsIsMutable(); + destinations_.add(value); + onChanged(); + } + + // optional bytes message = 4; + private com.google.protobuf.ByteString message_ = com.google.protobuf.ByteString.EMPTY; + public boolean hasMessage() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + public com.google.protobuf.ByteString getMessage() { + return message_; + } + public Builder setMessage(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + message_ = value; + onChanged(); + return this; + } + public Builder clearMessage() { + bitField0_ = (bitField0_ & ~0x00000008); + message_ = getDefaultInstance().getMessage(); + onChanged(); + return this; + } + + // repeated .textsecure.IncomingPushMessageSignal.AttachmentPointer attachments = 5; + private java.util.List attachments_ = + java.util.Collections.emptyList(); + private void ensureAttachmentsIsMutable() { + if (!((bitField0_ & 0x00000010) == 0x00000010)) { + attachments_ = new java.util.ArrayList(attachments_); + bitField0_ |= 0x00000010; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer, org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.Builder, org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointerOrBuilder> attachmentsBuilder_; + + public java.util.List getAttachmentsList() { + if (attachmentsBuilder_ == null) { + return java.util.Collections.unmodifiableList(attachments_); + } else { + return attachmentsBuilder_.getMessageList(); + } + } + public int getAttachmentsCount() { + if (attachmentsBuilder_ == null) { + return attachments_.size(); + } else { + return attachmentsBuilder_.getCount(); + } + } + public org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer getAttachments(int index) { + if (attachmentsBuilder_ == null) { + return attachments_.get(index); + } else { + return attachmentsBuilder_.getMessage(index); + } + } + public Builder setAttachments( + int index, org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer value) { + if (attachmentsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureAttachmentsIsMutable(); + attachments_.set(index, value); + onChanged(); + } else { + attachmentsBuilder_.setMessage(index, value); + } + return this; + } + public Builder setAttachments( + int index, org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.Builder builderForValue) { + if (attachmentsBuilder_ == null) { + ensureAttachmentsIsMutable(); + attachments_.set(index, builderForValue.build()); + onChanged(); + } else { + attachmentsBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + public Builder addAttachments(org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer value) { + if (attachmentsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureAttachmentsIsMutable(); + attachments_.add(value); + onChanged(); + } else { + attachmentsBuilder_.addMessage(value); + } + return this; + } + public Builder addAttachments( + int index, org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer value) { + if (attachmentsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureAttachmentsIsMutable(); + attachments_.add(index, value); + onChanged(); + } else { + attachmentsBuilder_.addMessage(index, value); + } + return this; + } + public Builder addAttachments( + org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.Builder builderForValue) { + if (attachmentsBuilder_ == null) { + ensureAttachmentsIsMutable(); + attachments_.add(builderForValue.build()); + onChanged(); + } else { + attachmentsBuilder_.addMessage(builderForValue.build()); + } + return this; + } + public Builder addAttachments( + int index, org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.Builder builderForValue) { + if (attachmentsBuilder_ == null) { + ensureAttachmentsIsMutable(); + attachments_.add(index, builderForValue.build()); + onChanged(); + } else { + attachmentsBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + public Builder addAllAttachments( + java.lang.Iterable values) { + if (attachmentsBuilder_ == null) { + ensureAttachmentsIsMutable(); + super.addAll(values, attachments_); + onChanged(); + } else { + attachmentsBuilder_.addAllMessages(values); + } + return this; + } + public Builder clearAttachments() { + if (attachmentsBuilder_ == null) { + attachments_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000010); + onChanged(); + } else { + attachmentsBuilder_.clear(); + } + return this; + } + public Builder removeAttachments(int index) { + if (attachmentsBuilder_ == null) { + ensureAttachmentsIsMutable(); + attachments_.remove(index); + onChanged(); + } else { + attachmentsBuilder_.remove(index); + } + return this; + } + public org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.Builder getAttachmentsBuilder( + int index) { + return getAttachmentsFieldBuilder().getBuilder(index); + } + public org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointerOrBuilder getAttachmentsOrBuilder( + int index) { + if (attachmentsBuilder_ == null) { + return attachments_.get(index); } else { + return attachmentsBuilder_.getMessageOrBuilder(index); + } + } + public java.util.List + getAttachmentsOrBuilderList() { + if (attachmentsBuilder_ != null) { + return attachmentsBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(attachments_); + } + } + public org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.Builder addAttachmentsBuilder() { + return getAttachmentsFieldBuilder().addBuilder( + org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.getDefaultInstance()); + } + public org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.Builder addAttachmentsBuilder( + int index) { + return getAttachmentsFieldBuilder().addBuilder( + index, org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.getDefaultInstance()); + } + public java.util.List + getAttachmentsBuilderList() { + return getAttachmentsFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer, org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.Builder, org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointerOrBuilder> + getAttachmentsFieldBuilder() { + if (attachmentsBuilder_ == null) { + attachmentsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer, org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.Builder, org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointerOrBuilder>( + attachments_, + ((bitField0_ & 0x00000010) == 0x00000010), + getParentForChildren(), + isClean()); + attachments_ = null; + } + return attachmentsBuilder_; + } + + // optional uint64 timestamp = 6; + private long timestamp_ ; + public boolean hasTimestamp() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + public long getTimestamp() { + return timestamp_; + } + public Builder setTimestamp(long value) { + bitField0_ |= 0x00000020; + timestamp_ = value; + onChanged(); + return this; + } + public Builder clearTimestamp() { + bitField0_ = (bitField0_ & ~0x00000020); + timestamp_ = 0L; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.IncomingPushMessageSignal) + } + + static { + defaultInstance = new IncomingPushMessageSignal(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.IncomingPushMessageSignal) + } + + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_IncomingPushMessageSignal_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_IncomingPushMessageSignal_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_IncomingPushMessageSignal_AttachmentPointer_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_IncomingPushMessageSignal_AttachmentPointer_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + java.lang.String[] descriptorData = { + "\n\037IncomingPushMessageSignal.proto\022\ntexts" + + "ecure\"\370\001\n\031IncomingPushMessageSignal\022\014\n\004t" + + "ype\030\001 \001(\r\022\016\n\006source\030\002 \001(\t\022\024\n\014destination" + + "s\030\003 \003(\t\022\017\n\007message\030\004 \001(\014\022L\n\013attachments\030" + + "\005 \003(\01327.textsecure.IncomingPushMessageSi" + + "gnal.AttachmentPointer\022\021\n\ttimestamp\030\006 \001(" + + "\004\0325\n\021AttachmentPointer\022\023\n\013contentType\030\001 " + + "\001(\t\022\013\n\003key\030\002 \001(\tB7\n\"org.whispersystems.t" + + "extsecure.pushB\021PushMessageProtos" + }; + com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = + new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { + public com.google.protobuf.ExtensionRegistry assignDescriptors( + com.google.protobuf.Descriptors.FileDescriptor root) { + descriptor = root; + internal_static_textsecure_IncomingPushMessageSignal_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_textsecure_IncomingPushMessageSignal_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_IncomingPushMessageSignal_descriptor, + new java.lang.String[] { "Type", "Source", "Destinations", "Message", "Attachments", "Timestamp", }, + org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.class, + org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.Builder.class); + internal_static_textsecure_IncomingPushMessageSignal_AttachmentPointer_descriptor = + internal_static_textsecure_IncomingPushMessageSignal_descriptor.getNestedTypes().get(0); + internal_static_textsecure_IncomingPushMessageSignal_AttachmentPointer_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_IncomingPushMessageSignal_AttachmentPointer_descriptor, + new java.lang.String[] { "ContentType", "Key", }, + org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.class, + org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.Builder.class); + return null; + } + }; + com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }, assigner); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/library/src/org/whispersystems/textsecure/push/PushServiceSocket.java b/library/src/org/whispersystems/textsecure/push/PushServiceSocket.java index c363dae0f2..acde1f67c1 100644 --- a/library/src/org/whispersystems/textsecure/push/PushServiceSocket.java +++ b/library/src/org/whispersystems/textsecure/push/PushServiceSocket.java @@ -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 recipients, String messageText, int type) + public void sendMessage(List recipients, List bodies, + List> attachmentsList, int type) throws IOException { - OutgoingPushMessage message = new OutgoingPushMessage(recipients, messageText, type); - sendMessage(message); + List messages = new LinkedList(); + + Iterator recipientsIterator = recipients.iterator(); + Iterator bodiesIterator = bodies.iterator(); + Iterator> attachmentsIterator = attachmentsList.iterator(); + + while (recipientsIterator.hasNext()) { + String recipient = recipientsIterator.next(); + byte[] body = bodiesIterator.next(); + List attachments = attachmentsIterator.next(); + + OutgoingPushMessage message; + + if (!attachments.isEmpty()) { + List 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 recipients, String messageText, - List attachments, int type) - throws IOException - { - List 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) diff --git a/library/src/org/whispersystems/textsecure/push/PushTransportDetails.java b/library/src/org/whispersystems/textsecure/push/PushTransportDetails.java index 9920066f52..f8a87d5539 100644 --- a/library/src/org/whispersystems/textsecure/push/PushTransportDetails.java +++ b/library/src/org/whispersystems/textsecure/push/PushTransportDetails.java @@ -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; } } diff --git a/src/org/thoughtcrime/securesms/gcm/GcmIntentService.java b/src/org/thoughtcrime/securesms/gcm/GcmIntentService.java index f14cd95848..4e29af7b90 100644 --- a/src/org/thoughtcrime/securesms/gcm/GcmIntentService.java +++ b/src/org/thoughtcrime/securesms/gcm/GcmIntentService.java @@ -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 diff --git a/src/org/thoughtcrime/securesms/service/SmsReceiver.java b/src/org/thoughtcrime/securesms/service/SmsReceiver.java index 395467fb1f..643b901c4f 100644 --- a/src/org/thoughtcrime/securesms/service/SmsReceiver.java +++ b/src/org/thoughtcrime/securesms/service/SmsReceiver.java @@ -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()); } diff --git a/src/org/thoughtcrime/securesms/transport/PushTransport.java b/src/org/thoughtcrime/securesms/transport/PushTransport.java index 67fe597bb3..368202e2e7 100644 --- a/src/org/thoughtcrime/securesms/transport/PushTransport.java +++ b/src/org/thoughtcrime/securesms/transport/PushTransport.java @@ -55,7 +55,7 @@ public class PushTransport extends BaseTransport { String recipientCanonicalNumber = PhoneNumberFormatter.formatNumber(recipient.getNumber(), localNumber); - Pair typeAndCiphertext = getEncryptedMessage(socket, recipient, recipientCanonicalNumber, plaintext); + Pair 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 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 messagesList = new LinkedList(); + List> attachmentsList = new LinkedList>(); + + 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 getEncryptedMessage(PushServiceSocket socket, Recipient recipient, + 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(OutgoingPushMessage.TYPE_MESSAGE_CIPHERTEXT, ciphertext); + byte[] ciphertext = getEncryptedMessageForExistingSession(recipient, plaintext); + return new Pair(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(OutgoingPushMessage.TYPE_MESSAGE_PREKEY_BUNDLE, ciphertext); + byte[] ciphertext = getEncryptedPrekeyBundleMessageForExistingSession(recipient, plaintext); + return new Pair(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(OutgoingPushMessage.TYPE_MESSAGE_PREKEY_BUNDLE, ciphertext); + byte[] ciphertext = getEncryptedPrekeyBundleMessageForNewSession(socket, recipient, canonicalRecipientNumber, plaintext); + return new Pair(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()); } } diff --git a/src/org/thoughtcrime/securesms/transport/SmsTransport.java b/src/org/thoughtcrime/securesms/transport/SmsTransport.java index 2ee86ead58..39e6c46f53 100644 --- a/src/org/thoughtcrime/securesms/transport/SmsTransport.java +++ b/src/org/thoughtcrime/securesms/transport/SmsTransport.java @@ -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())); } } } diff --git a/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java b/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java index 856f658691..009d36f1e1 100644 --- a/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java +++ b/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java @@ -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); }