mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-26 19:56:02 +01:00
Basic support for encrypted push-based attachments.
1) Move the attachment structures into the encrypted message body.
2) Encrypt attachments with symmetric keys transmitted in the
encryptd attachment pointer structure.
3) Correctly handle asynchronous decryption and categorization of
encrypted push messages.
TODO: Correct notification process and network/interruption
retries.
This commit is contained in:
@@ -1,12 +1,26 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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 org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -24,12 +38,20 @@ public class IncomingPushMessage implements PushMessage, Parcelable {
|
||||
}
|
||||
};
|
||||
|
||||
private int type;
|
||||
private String source;
|
||||
private List<String> destinations;
|
||||
private byte[] message;
|
||||
private List<PushAttachmentPointer> attachments;
|
||||
private long timestamp;
|
||||
private int type;
|
||||
private String source;
|
||||
private List<String> destinations;
|
||||
private byte[] message;
|
||||
private long timestamp;
|
||||
|
||||
private IncomingPushMessage(IncomingPushMessage message, byte[] body) {
|
||||
this.type = message.type;
|
||||
this.source = message.source;
|
||||
this.destinations = new LinkedList<String>();
|
||||
this.destinations.addAll(message.destinations);
|
||||
this.message = body;
|
||||
this.timestamp = message.timestamp;
|
||||
}
|
||||
|
||||
public IncomingPushMessage(IncomingPushMessageSignal signal) {
|
||||
this.type = signal.getType();
|
||||
@@ -37,25 +59,15 @@ public class IncomingPushMessage implements PushMessage, Parcelable {
|
||||
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) {
|
||||
this.destinations = new LinkedList<String>();
|
||||
this.attachments = new LinkedList<PushAttachmentPointer>();
|
||||
|
||||
this.type = in.readInt();
|
||||
this.source = in.readString();
|
||||
in.readStringList(destinations);
|
||||
this.message = new byte[in.readInt()];
|
||||
in.readByteArray(this.message);
|
||||
in.readList(attachments, PushAttachmentPointer.class.getClassLoader());
|
||||
this.timestamp = in.readLong();
|
||||
}
|
||||
|
||||
@@ -67,10 +79,6 @@ public class IncomingPushMessage implements PushMessage, Parcelable {
|
||||
return source;
|
||||
}
|
||||
|
||||
public List<PushAttachmentPointer> getAttachments() {
|
||||
return attachments;
|
||||
}
|
||||
|
||||
public byte[] getBody() {
|
||||
return message;
|
||||
}
|
||||
@@ -79,10 +87,6 @@ public class IncomingPushMessage implements PushMessage, Parcelable {
|
||||
return destinations;
|
||||
}
|
||||
|
||||
public boolean hasAttachments() {
|
||||
return getAttachments() != null && !getAttachments().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
@@ -95,11 +99,22 @@ public class IncomingPushMessage implements PushMessage, Parcelable {
|
||||
dest.writeStringList(destinations);
|
||||
dest.writeInt(message.length);
|
||||
dest.writeByteArray(message);
|
||||
dest.writeList(attachments);
|
||||
dest.writeLong(timestamp);
|
||||
}
|
||||
|
||||
public IncomingPushMessage withBody(byte[] body) {
|
||||
return new IncomingPushMessage(this, body);
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public boolean isSecureMessage() {
|
||||
return getType() == PushMessage.TYPE_MESSAGE_CIPHERTEXT;
|
||||
}
|
||||
|
||||
public boolean isPreKeyBundle() {
|
||||
return getType() == PushMessage.TYPE_MESSAGE_PREKEY_BUNDLE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +1,35 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.whispersystems.textsecure.push;
|
||||
|
||||
import org.whispersystems.textsecure.util.Base64;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class OutgoingPushMessage implements PushMessage {
|
||||
|
||||
private int type;
|
||||
private String destination;
|
||||
private String body;
|
||||
private List<PushAttachmentPointer> attachments;
|
||||
private int type;
|
||||
private String destination;
|
||||
private String body;
|
||||
|
||||
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(String destination, byte[] body,
|
||||
List<PushAttachmentPointer> attachments,
|
||||
int type)
|
||||
{
|
||||
this.destination = destination;
|
||||
this.body = Base64.encodeBytes(body);
|
||||
this.attachments = attachments;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getDestination() {
|
||||
return destination;
|
||||
}
|
||||
@@ -37,10 +38,6 @@ public class OutgoingPushMessage implements PushMessage {
|
||||
return body;
|
||||
}
|
||||
|
||||
public List<PushAttachmentPointer> getAttachments() {
|
||||
return attachments;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@@ -18,23 +18,33 @@ public class PushAttachmentPointer implements Parcelable {
|
||||
};
|
||||
|
||||
private final String contentType;
|
||||
private final String key;
|
||||
private final long id;
|
||||
private final byte[] key;
|
||||
|
||||
public PushAttachmentPointer(String contentType, String key) {
|
||||
public PushAttachmentPointer(String contentType, long id, byte[] key) {
|
||||
this.contentType = contentType;
|
||||
this.id = id;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public PushAttachmentPointer(Parcel in) {
|
||||
this.contentType = in.readString();
|
||||
this.key = in.readString();
|
||||
this.id = in.readLong();
|
||||
|
||||
int keyLength = in.readInt();
|
||||
this.key = new byte[keyLength];
|
||||
in.readByteArray(this.key);
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return contentType;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public byte[] getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@@ -46,6 +56,8 @@ public class PushAttachmentPointer implements Parcelable {
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeString(contentType);
|
||||
dest.writeString(key);
|
||||
dest.writeLong(id);
|
||||
dest.writeInt(this.key.length);
|
||||
dest.writeByteArray(this.key);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -84,31 +84,21 @@ public class PushServiceSocket {
|
||||
sendMessage(new OutgoingPushMessageList(message));
|
||||
}
|
||||
|
||||
public void sendMessage(List<String> recipients, List<byte[]> bodies,
|
||||
List<List<PushAttachmentData>> attachmentsList, int type)
|
||||
public void sendMessage(List<String> recipients, List<byte[]> bodies, List<Integer> types)
|
||||
throws IOException
|
||||
{
|
||||
List<OutgoingPushMessage> messages = new LinkedList<OutgoingPushMessage>();
|
||||
|
||||
Iterator<String> recipientsIterator = recipients.iterator();
|
||||
Iterator<byte[]> bodiesIterator = bodies.iterator();
|
||||
Iterator<List<PushAttachmentData>> attachmentsIterator = attachmentsList.iterator();
|
||||
Iterator<String> recipientsIterator = recipients.iterator();
|
||||
Iterator<byte[]> bodiesIterator = bodies.iterator();
|
||||
Iterator<Integer> typesIterator = types.iterator();
|
||||
|
||||
while (recipientsIterator.hasNext()) {
|
||||
String recipient = recipientsIterator.next();
|
||||
byte[] body = bodiesIterator.next();
|
||||
List<PushAttachmentData> attachments = attachmentsIterator.next();
|
||||
String recipient = recipientsIterator.next();
|
||||
byte[] body = bodiesIterator.next();
|
||||
int type = typesIterator.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);
|
||||
messages.add(new OutgoingPushMessage(recipient, body, type));
|
||||
}
|
||||
|
||||
sendMessage(new OutgoingPushMessageList(messages));
|
||||
@@ -149,20 +139,7 @@ public class PushServiceSocket {
|
||||
return PreKeyEntity.fromJson(responseText);
|
||||
}
|
||||
|
||||
private List<PushAttachmentPointer> sendAttachments(List<PushAttachmentData> attachments)
|
||||
throws IOException
|
||||
{
|
||||
List<PushAttachmentPointer> attachmentIds = new LinkedList<PushAttachmentPointer>();
|
||||
|
||||
for (PushAttachmentData attachment : attachments) {
|
||||
attachmentIds.add(new PushAttachmentPointer(attachment.getContentType(),
|
||||
sendAttachment(attachment)));
|
||||
}
|
||||
|
||||
return attachmentIds;
|
||||
}
|
||||
|
||||
private String sendAttachment(PushAttachmentData attachment) throws IOException {
|
||||
public long sendAttachment(PushAttachmentData attachment) throws IOException {
|
||||
Pair<String, String> response = makeRequestForResponseHeader(String.format(ATTACHMENT_PATH, ""),
|
||||
"GET", null, "Content-Location");
|
||||
|
||||
@@ -178,25 +155,18 @@ public class PushServiceSocket {
|
||||
return new Gson().fromJson(response.second, AttachmentKey.class).getId();
|
||||
}
|
||||
|
||||
public List<Pair<File,String>> retrieveAttachments(List<PushAttachmentPointer> attachmentIds)
|
||||
throws IOException
|
||||
{
|
||||
List<Pair<File,String>> attachments = new LinkedList<Pair<File,String>>();
|
||||
public File retrieveAttachment(long attachmentId) throws IOException {
|
||||
Pair<String, String> response = makeRequestForResponseHeader(String.format(ATTACHMENT_PATH, String.valueOf(attachmentId)),
|
||||
"GET", null, "Content-Location");
|
||||
|
||||
for (PushAttachmentPointer attachmentId : attachmentIds) {
|
||||
Pair<String, String> response = makeRequestForResponseHeader(String.format(ATTACHMENT_PATH, attachmentId.getKey()),
|
||||
"GET", null, "Content-Location");
|
||||
Log.w("PushServiceSocket", "Attachment: " + attachmentId + " is at: " + response.first);
|
||||
|
||||
Log.w("PushServiceSocket", "Attachment: " + attachmentId.getKey() + " is at: " + response.first);
|
||||
File attachment = File.createTempFile("attachment", ".tmp", context.getFilesDir());
|
||||
attachment.deleteOnExit();
|
||||
|
||||
File attachment = File.createTempFile("attachment", ".tmp", context.getFilesDir());
|
||||
attachment.deleteOnExit();
|
||||
downloadExternalFile(response.first, attachment);
|
||||
|
||||
downloadExternalFile(response.first, attachment);
|
||||
attachments.add(new Pair<File, String>(attachment, attachmentId.getContentType()));
|
||||
}
|
||||
|
||||
return attachments;
|
||||
return attachment;
|
||||
}
|
||||
|
||||
public Pair<DirectoryDescriptor, File> retrieveDirectory() {
|
||||
@@ -394,13 +364,13 @@ public class PushServiceSocket {
|
||||
}
|
||||
|
||||
private static class AttachmentKey {
|
||||
private String id;
|
||||
private long id;
|
||||
|
||||
public AttachmentKey(String id) {
|
||||
public AttachmentKey(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user