Move API around.

This commit is contained in:
Moxie Marlinspike 2014-11-04 15:01:32 -08:00
parent a3f1d9cdfd
commit 99f42e2ee1
17 changed files with 303 additions and 458 deletions

View File

@ -2,40 +2,15 @@ package org.whispersystems.textsecure.api;
import android.content.Context; import android.content.Context;
import com.google.protobuf.InvalidProtocolBufferException;
import org.whispersystems.libaxolotl.DuplicateMessageException;
import org.whispersystems.libaxolotl.InvalidKeyException;
import org.whispersystems.libaxolotl.InvalidKeyIdException;
import org.whispersystems.libaxolotl.InvalidMessageException; import org.whispersystems.libaxolotl.InvalidMessageException;
import org.whispersystems.libaxolotl.InvalidVersionException;
import org.whispersystems.libaxolotl.LegacyMessageException;
import org.whispersystems.libaxolotl.NoSessionException;
import org.whispersystems.libaxolotl.UntrustedIdentityException;
import org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage;
import org.whispersystems.libaxolotl.protocol.WhisperMessage;
import org.whispersystems.libaxolotl.state.AxolotlStore; import org.whispersystems.libaxolotl.state.AxolotlStore;
import org.whispersystems.libaxolotl.util.guava.Optional;
import org.whispersystems.textsecure.api.messages.TextSecureAttachment;
import org.whispersystems.textsecure.api.messages.TextSecureAttachmentPointer; import org.whispersystems.textsecure.api.messages.TextSecureAttachmentPointer;
import org.whispersystems.textsecure.api.messages.TextSecureGroup;
import org.whispersystems.textsecure.api.messages.TextSecureMessage;
import org.whispersystems.textsecure.crypto.AttachmentCipherInputStream; import org.whispersystems.textsecure.crypto.AttachmentCipherInputStream;
import org.whispersystems.textsecure.crypto.TextSecureCipher;
import org.whispersystems.textsecure.push.IncomingEncryptedPushMessage;
import org.whispersystems.textsecure.push.IncomingPushMessage;
import org.whispersystems.textsecure.push.PushAddress;
import org.whispersystems.textsecure.push.PushServiceSocket; import org.whispersystems.textsecure.push.PushServiceSocket;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.LinkedList;
import java.util.List;
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent;
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer;
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.GroupContext.Type.DELIVER;
public class TextSecureMessageReceiver { public class TextSecureMessageReceiver {
@ -61,95 +36,4 @@ public class TextSecureMessageReceiver {
return new AttachmentCipherInputStream(destination, pointer.getKey()); return new AttachmentCipherInputStream(destination, pointer.getKey());
} }
public IncomingPushMessage receiveSignal(String signal)
throws IOException, InvalidVersionException
{
IncomingEncryptedPushMessage encrypted = new IncomingEncryptedPushMessage(signal, signalingKey);
return encrypted.getIncomingPushMessage();
}
public TextSecureMessage receiveMessage(long recipientId, IncomingPushMessage signal)
throws InvalidVersionException, InvalidMessageException, NoSessionException,
LegacyMessageException, InvalidKeyIdException, DuplicateMessageException,
InvalidKeyException, UntrustedIdentityException
{
try {
PushAddress sender = new PushAddress(recipientId, signal.getSource(), signal.getSourceDevice(), signal.getRelay());
TextSecureCipher cipher = new TextSecureCipher(axolotlStore, sender);
PushMessageContent message;
if (signal.isPreKeyBundle()) {
PreKeyWhisperMessage bundle = new PreKeyWhisperMessage(signal.getBody());
message = PushMessageContent.parseFrom(cipher.decrypt(bundle));
} else if (signal.isSecureMessage()) {
WhisperMessage ciphertext = new WhisperMessage(signal.getBody());
message = PushMessageContent.parseFrom(cipher.decrypt(ciphertext));
} else if (signal.isPlaintext()) {
message = PushMessageContent.parseFrom(signal.getBody());
} else {
throw new InvalidMessageException("Unknown type: " + signal.getType());
}
return createTextSecureMessage(signal, message);
} catch (InvalidProtocolBufferException e) {
throw new InvalidMessageException(e);
}
}
private TextSecureMessage createTextSecureMessage(IncomingPushMessage signal, PushMessageContent content) {
TextSecureGroup groupInfo = createGroupInfo(signal, content);
List<TextSecureAttachment> attachments = new LinkedList<>();
boolean endSession = ((content.getFlags() & PushMessageContent.Flags.END_SESSION_VALUE) != 0);
boolean secure = signal.isSecureMessage() || signal.isPreKeyBundle();
for (AttachmentPointer pointer : content.getAttachmentsList()) {
attachments.add(new TextSecureAttachmentPointer(pointer.getId(),
pointer.getContentType(),
pointer.getKey().toByteArray(),
signal.getRelay()));
}
return new TextSecureMessage(signal.getTimestampMillis(), groupInfo, attachments,
content.getBody(), secure, endSession);
}
private TextSecureGroup createGroupInfo(IncomingPushMessage signal, PushMessageContent content) {
if (!content.hasGroup()) return null;
TextSecureGroup.Type type;
switch (content.getGroup().getType()) {
case DELIVER: type = TextSecureGroup.Type.DELIVER; break;
case UPDATE: type = TextSecureGroup.Type.UPDATE; break;
case QUIT: type = TextSecureGroup.Type.QUIT; break;
default: type = TextSecureGroup.Type.UNKNOWN; break;
}
if (content.getGroup().getType() != DELIVER) {
String name = null;
List<String> members = null;
TextSecureAttachmentPointer avatar = null;
if (content.getGroup().hasName()) {
name = content.getGroup().getName();
}
if (content.getGroup().getMembersCount() > 0) {
members = content.getGroup().getMembersList();
}
if (content.getGroup().hasAvatar()) {
avatar = new TextSecureAttachmentPointer(content.getGroup().getAvatar().getId(),
content.getGroup().getAvatar().getContentType(),
content.getGroup().getAvatar().getKey().toByteArray(),
signal.getRelay());
}
return new TextSecureGroup(type, content.getGroup().getId().toByteArray(), name, members, avatar);
}
return new TextSecureGroup(content.getGroup().getId().toByteArray());
}
} }

View File

@ -173,11 +173,13 @@ public class TextSecureMessageSender {
List<AttachmentPointer> pointers = new LinkedList<>(); List<AttachmentPointer> pointers = new LinkedList<>();
if (!attachments.isPresent() || attachments.get().isEmpty()) { if (!attachments.isPresent() || attachments.get().isEmpty()) {
Log.w(TAG, "No attachments present...");
return pointers; return pointers;
} }
for (TextSecureAttachment attachment : attachments.get()) { for (TextSecureAttachment attachment : attachments.get()) {
if (attachment.isStream()) { if (attachment.isStream()) {
Log.w(TAG, "Found attachment, creating pointer...");
pointers.add(createAttachmentPointer(attachment.asStream())); pointers.add(createAttachmentPointer(attachment.asStream()));
} }
} }
@ -249,7 +251,7 @@ public class TextSecureMessageSender {
} }
} }
TextSecureCipher cipher = new TextSecureCipher(store, recipient); TextSecureCipher cipher = new TextSecureCipher(store, recipient.getRecipientId(), recipient.getDeviceId());
CiphertextMessage message = cipher.encrypt(plaintext); CiphertextMessage message = cipher.encrypt(plaintext);
int remoteRegistrationId = cipher.getRemoteRegistrationId(); int remoteRegistrationId = cipher.getRemoteRegistrationId();

View File

@ -15,12 +15,12 @@ public class TextSecureAttachmentStream extends TextSecureAttachment {
@Override @Override
public boolean isStream() { public boolean isStream() {
return false; return true;
} }
@Override @Override
public boolean isPointer() { public boolean isPointer() {
return true; return false;
} }
public InputStream getInputStream() { public InputStream getInputStream() {

View File

@ -1,12 +1,20 @@
package org.whispersystems.textsecure.push; package org.whispersystems.textsecure.api.messages;
import android.util.Log; import android.util.Log;
import com.google.protobuf.ByteString;
import org.whispersystems.libaxolotl.InvalidVersionException; import org.whispersystems.libaxolotl.InvalidVersionException;
import org.whispersystems.textsecure.util.Base64;
import org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal; import org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal;
import org.whispersystems.textsecure.util.Base64;
import org.whispersystems.textsecure.util.Hex; import org.whispersystems.textsecure.util.Hex;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import javax.crypto.BadPaddingException; import javax.crypto.BadPaddingException;
import javax.crypto.Cipher; import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException; import javax.crypto.IllegalBlockSizeException;
@ -14,13 +22,10 @@ import javax.crypto.Mac;
import javax.crypto.NoSuchPaddingException; import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec; 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 { public class TextSecureEnvelope {
private static final String TAG = TextSecureEnvelope.class.getSimpleName();
private static final int SUPPORTED_VERSION = 1; private static final int SUPPORTED_VERSION = 1;
private static final int CIPHER_KEY_SIZE = 32; private static final int CIPHER_KEY_SIZE = 32;
@ -33,9 +38,9 @@ public class IncomingEncryptedPushMessage {
private static final int IV_LENGTH = 16; private static final int IV_LENGTH = 16;
private static final int CIPHERTEXT_OFFSET = IV_OFFSET + IV_LENGTH; private static final int CIPHERTEXT_OFFSET = IV_OFFSET + IV_LENGTH;
private final IncomingPushMessage incomingPushMessage; private final IncomingPushMessageSignal signal;
public IncomingEncryptedPushMessage(String message, String signalingKey) public TextSecureEnvelope(String message, String signalingKey)
throws IOException, InvalidVersionException throws IOException, InvalidVersionException
{ {
byte[] ciphertext = Base64.decode(message); byte[] ciphertext = Base64.decode(message);
@ -48,14 +53,60 @@ public class IncomingEncryptedPushMessage {
verifyMac(ciphertext, macKey); verifyMac(ciphertext, macKey);
byte[] plaintext = getPlaintext(ciphertext, cipherKey); this.signal = IncomingPushMessageSignal.parseFrom(getPlaintext(ciphertext, cipherKey));
IncomingPushMessageSignal signal = IncomingPushMessageSignal.parseFrom(plaintext);
this.incomingPushMessage = new IncomingPushMessage(signal);
} }
public IncomingPushMessage getIncomingPushMessage() { public TextSecureEnvelope(int type, String source, int sourceDevice,
return incomingPushMessage; String relay, long timestamp, byte[] message)
{
this.signal = IncomingPushMessageSignal.newBuilder()
.setType(IncomingPushMessageSignal.Type.valueOf(type))
.setSource(source)
.setSourceDevice(sourceDevice)
.setRelay(relay)
.setTimestamp(timestamp)
.setMessage(ByteString.copyFrom(message))
.build();
}
public String getSource() {
return signal.getSource();
}
public int getSourceDevice() {
return signal.getSourceDevice();
}
public int getType() {
return signal.getType().getNumber();
}
public String getRelay() {
return signal.getRelay();
}
public long getTimestamp() {
return signal.getTimestamp();
}
public byte[] getMessage() {
return signal.getMessage().toByteArray();
}
public boolean isWhisperMessage() {
return signal.getType().getNumber() == IncomingPushMessageSignal.Type.CIPHERTEXT_VALUE;
}
public boolean isPreKeyWhisperMessage() {
return signal.getType().getNumber() == IncomingPushMessageSignal.Type.PREKEY_BUNDLE_VALUE;
}
public boolean isPlaintext() {
return signal.getType().getNumber() == IncomingPushMessageSignal.Type.PLAINTEXT_VALUE;
}
public boolean isReceipt() {
return signal.getType().getNumber() == IncomingPushMessageSignal.Type.RECEIPT_VALUE;
} }
private byte[] getPlaintext(byte[] ciphertext, SecretKeySpec cipherKey) throws IOException { private byte[] getPlaintext(byte[] ciphertext, SecretKeySpec cipherKey) throws IOException {
@ -72,7 +123,7 @@ public class IncomingEncryptedPushMessage {
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException e) { } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException e) {
throw new AssertionError(e); throw new AssertionError(e);
} catch (BadPaddingException e) { } catch (BadPaddingException e) {
Log.w("IncomingEncryptedPushMessage", e); Log.w(TAG, e);
throw new IOException("Bad padding?"); throw new IOException("Bad padding?");
} }
} }
@ -94,15 +145,13 @@ public class IncomingEncryptedPushMessage {
byte[] theirMacBytes = new byte[MAC_SIZE]; byte[] theirMacBytes = new byte[MAC_SIZE];
System.arraycopy(ciphertext, ciphertext.length-MAC_SIZE, theirMacBytes, 0, theirMacBytes.length); System.arraycopy(ciphertext, ciphertext.length-MAC_SIZE, theirMacBytes, 0, theirMacBytes.length);
Log.w("IncomingEncryptedPushMessage", "Our MAC: " + Hex.toString(ourMacBytes)); Log.w(TAG, "Our MAC: " + Hex.toString(ourMacBytes));
Log.w("IncomingEncryptedPushMessage", "Thr MAC: " + Hex.toString(theirMacBytes)); Log.w(TAG, "Thr MAC: " + Hex.toString(theirMacBytes));
if (!Arrays.equals(ourMacBytes, theirMacBytes)) { if (!Arrays.equals(ourMacBytes, theirMacBytes)) {
throw new IOException("Invalid MAC compare!"); throw new IOException("Invalid MAC compare!");
} }
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException | InvalidKeyException e) {
throw new AssertionError(e);
} catch (InvalidKeyException e) {
throw new AssertionError(e); throw new AssertionError(e);
} }
} }

View File

@ -1,9 +1,12 @@
package org.whispersystems.textsecure.crypto; package org.whispersystems.textsecure.crypto;
import com.google.protobuf.InvalidProtocolBufferException;
import org.whispersystems.libaxolotl.DuplicateMessageException; import org.whispersystems.libaxolotl.DuplicateMessageException;
import org.whispersystems.libaxolotl.InvalidKeyException; import org.whispersystems.libaxolotl.InvalidKeyException;
import org.whispersystems.libaxolotl.InvalidKeyIdException; import org.whispersystems.libaxolotl.InvalidKeyIdException;
import org.whispersystems.libaxolotl.InvalidMessageException; import org.whispersystems.libaxolotl.InvalidMessageException;
import org.whispersystems.libaxolotl.InvalidVersionException;
import org.whispersystems.libaxolotl.LegacyMessageException; import org.whispersystems.libaxolotl.LegacyMessageException;
import org.whispersystems.libaxolotl.NoSessionException; import org.whispersystems.libaxolotl.NoSessionException;
import org.whispersystems.libaxolotl.SessionCipher; import org.whispersystems.libaxolotl.SessionCipher;
@ -12,46 +15,120 @@ import org.whispersystems.libaxolotl.protocol.CiphertextMessage;
import org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage; import org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage;
import org.whispersystems.libaxolotl.protocol.WhisperMessage; import org.whispersystems.libaxolotl.protocol.WhisperMessage;
import org.whispersystems.libaxolotl.state.AxolotlStore; import org.whispersystems.libaxolotl.state.AxolotlStore;
import org.whispersystems.textsecure.push.PushAddress; import org.whispersystems.textsecure.api.messages.TextSecureAttachment;
import org.whispersystems.textsecure.api.messages.TextSecureAttachmentPointer;
import org.whispersystems.textsecure.api.messages.TextSecureEnvelope;
import org.whispersystems.textsecure.api.messages.TextSecureGroup;
import org.whispersystems.textsecure.api.messages.TextSecureMessage;
import org.whispersystems.textsecure.push.PushTransportDetails; import org.whispersystems.textsecure.push.PushTransportDetails;
import java.util.LinkedList;
import java.util.List;
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent;
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.GroupContext.Type.DELIVER;
public class TextSecureCipher { public class TextSecureCipher {
private final SessionCipher sessionCipher; private final SessionCipher sessionCipher;
private final TransportDetails transportDetails; private final TransportDetails transportDetails;
public TextSecureCipher(AxolotlStore axolotlStore, PushAddress pushAddress) { public TextSecureCipher(AxolotlStore axolotlStore, long recipientId, int deviceId) {
int sessionVersion = axolotlStore.loadSession(pushAddress.getRecipientId(), int sessionVersion = axolotlStore.loadSession(recipientId, deviceId)
pushAddress.getDeviceId())
.getSessionState().getSessionVersion(); .getSessionState().getSessionVersion();
this.transportDetails = new PushTransportDetails(sessionVersion); this.transportDetails = new PushTransportDetails(sessionVersion);
this.sessionCipher = new SessionCipher(axolotlStore, pushAddress.getRecipientId(), this.sessionCipher = new SessionCipher(axolotlStore, recipientId, deviceId);
pushAddress.getDeviceId());
} }
public CiphertextMessage encrypt(byte[] unpaddedMessage) { public CiphertextMessage encrypt(byte[] unpaddedMessage) {
return sessionCipher.encrypt(transportDetails.getPaddedMessageBody(unpaddedMessage)); return sessionCipher.encrypt(transportDetails.getPaddedMessageBody(unpaddedMessage));
} }
public byte[] decrypt(WhisperMessage message) public TextSecureMessage decrypt(TextSecureEnvelope envelope)
throws DuplicateMessageException, LegacyMessageException, InvalidMessageException, NoSessionException throws InvalidVersionException, InvalidMessageException, InvalidKeyException,
DuplicateMessageException, InvalidKeyIdException, UntrustedIdentityException,
LegacyMessageException, NoSessionException
{ {
byte[] paddedMessage = sessionCipher.decrypt(message); try {
return transportDetails.getStrippedPaddingMessageBody(paddedMessage); byte[] paddedMessage;
}
public byte[] decrypt(PreKeyWhisperMessage message) if (envelope.isPreKeyWhisperMessage()) {
throws InvalidKeyException, LegacyMessageException, InvalidMessageException, paddedMessage = sessionCipher.decrypt(new PreKeyWhisperMessage(envelope.getMessage()));
DuplicateMessageException, InvalidKeyIdException, UntrustedIdentityException, NoSessionException } else if (envelope.isWhisperMessage()) {
{ paddedMessage = sessionCipher.decrypt(new WhisperMessage(envelope.getMessage()));
byte[] paddedMessage = sessionCipher.decrypt(message); } else if (envelope.isPlaintext()) {
return transportDetails.getStrippedPaddingMessageBody(paddedMessage); paddedMessage = envelope.getMessage();
} else {
throw new InvalidMessageException("Unknown type: " + envelope.getType());
}
PushMessageContent content = PushMessageContent.parseFrom(transportDetails.getStrippedPaddingMessageBody(paddedMessage));
return createTextSecureMessage(envelope, content);
} catch (InvalidProtocolBufferException e) {
throw new InvalidMessageException(e);
}
} }
public int getRemoteRegistrationId() { public int getRemoteRegistrationId() {
return sessionCipher.getRemoteRegistrationId(); return sessionCipher.getRemoteRegistrationId();
} }
private TextSecureMessage createTextSecureMessage(TextSecureEnvelope envelope, PushMessageContent content) {
TextSecureGroup groupInfo = createGroupInfo(envelope, content);
List<TextSecureAttachment> attachments = new LinkedList<>();
boolean endSession = ((content.getFlags() & PushMessageContent.Flags.END_SESSION_VALUE) != 0);
boolean secure = envelope.isWhisperMessage() || envelope.isPreKeyWhisperMessage();
for (PushMessageContent.AttachmentPointer pointer : content.getAttachmentsList()) {
attachments.add(new TextSecureAttachmentPointer(pointer.getId(),
pointer.getContentType(),
pointer.getKey().toByteArray(),
envelope.getRelay()));
}
return new TextSecureMessage(envelope.getTimestamp(), groupInfo, attachments,
content.getBody(), secure, endSession);
}
private TextSecureGroup createGroupInfo(TextSecureEnvelope envelope, PushMessageContent content) {
if (!content.hasGroup()) return null;
TextSecureGroup.Type type;
switch (content.getGroup().getType()) {
case DELIVER: type = TextSecureGroup.Type.DELIVER; break;
case UPDATE: type = TextSecureGroup.Type.UPDATE; break;
case QUIT: type = TextSecureGroup.Type.QUIT; break;
default: type = TextSecureGroup.Type.UNKNOWN; break;
}
if (content.getGroup().getType() != DELIVER) {
String name = null;
List<String> members = null;
TextSecureAttachmentPointer avatar = null;
if (content.getGroup().hasName()) {
name = content.getGroup().getName();
}
if (content.getGroup().getMembersCount() > 0) {
members = content.getGroup().getMembersList();
}
if (content.getGroup().hasAvatar()) {
avatar = new TextSecureAttachmentPointer(content.getGroup().getAvatar().getId(),
content.getGroup().getAvatar().getContentType(),
content.getGroup().getAvatar().getKey().toByteArray(),
envelope.getRelay());
}
return new TextSecureGroup(type, content.getGroup().getId().toByteArray(), name, members, avatar);
}
return new TextSecureGroup(content.getGroup().getId().toByteArray());
}
} }

View File

@ -1,149 +0,0 @@
/**
* 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 android.os.Parcel;
import android.os.Parcelable;
import org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal;
public class IncomingPushMessage implements Parcelable {
public static final Parcelable.Creator<IncomingPushMessage> CREATOR = new Parcelable.Creator<IncomingPushMessage>() {
@Override
public IncomingPushMessage createFromParcel(Parcel in) {
return new IncomingPushMessage(in);
}
@Override
public IncomingPushMessage[] newArray(int size) {
return new IncomingPushMessage[size];
}
};
private int type;
private String source;
private int sourceDevice;
private byte[] message;
private long timestamp;
private String relay;
private IncomingPushMessage(IncomingPushMessage message, byte[] body) {
this.type = message.type;
this.source = message.source;
this.sourceDevice = message.sourceDevice;
this.timestamp = message.timestamp;
this.relay = message.relay;
this.message = body;
}
public IncomingPushMessage(IncomingPushMessageSignal signal) {
this.type = signal.getType().getNumber();
this.source = signal.getSource();
this.sourceDevice = signal.getSourceDevice();
this.message = signal.getMessage().toByteArray();
this.timestamp = signal.getTimestamp();
this.relay = signal.getRelay();
}
public IncomingPushMessage(Parcel in) {
this.type = in.readInt();
this.source = in.readString();
this.sourceDevice = in.readInt();
if (in.readInt() == 1) {
this.relay = in.readString();
}
this.message = new byte[in.readInt()];
in.readByteArray(this.message);
this.timestamp = in.readLong();
}
public IncomingPushMessage(int type, String source, int sourceDevice,
byte[] body, long timestamp)
{
this.type = type;
this.source = source;
this.sourceDevice = sourceDevice;
this.message = body;
this.timestamp = timestamp;
}
public String getRelay() {
return relay;
}
public long getTimestampMillis() {
return timestamp;
}
public String getSource() {
return source;
}
public int getSourceDevice() {
return sourceDevice;
}
public byte[] getBody() {
return message;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(type);
dest.writeString(source);
dest.writeInt(sourceDevice);
dest.writeInt(relay == null ? 0 : 1);
if (relay != null) {
dest.writeString(relay);
}
dest.writeInt(message.length);
dest.writeByteArray(message);
dest.writeLong(timestamp);
}
public IncomingPushMessage withBody(byte[] body) {
return new IncomingPushMessage(this, body);
}
public int getType() {
return type;
}
public boolean isSecureMessage() {
return getType() == IncomingPushMessageSignal.Type.CIPHERTEXT_VALUE;
}
public boolean isPreKeyBundle() {
return getType() == IncomingPushMessageSignal.Type.PREKEY_BUNDLE_VALUE;
}
public boolean isReceipt() {
return getType() == IncomingPushMessageSignal.Type.RECEIPT_VALUE;
}
public boolean isPlaintext() {
return getType() == IncomingPushMessageSignal.Type.PLAINTEXT_VALUE;
}
}

View File

@ -6,15 +6,15 @@ import android.database.Cursor;
import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log; import android.util.Log;
import org.whispersystems.textsecure.push.IncomingPushMessage; import org.whispersystems.textsecure.api.messages.TextSecureEnvelope;
import org.whispersystems.textsecure.util.Base64; import org.whispersystems.textsecure.util.Base64;
import org.whispersystems.textsecure.util.Util;
import java.io.IOException; import java.io.IOException;
import java.util.List;
public class PushDatabase extends Database { public class PushDatabase extends Database {
private static final String TAG = PushDatabase.class.getSimpleName();
private static final String TABLE_NAME = "push"; private static final String TABLE_NAME = "push";
public static final String ID = "_id"; public static final String ID = "_id";
public static final String TYPE = "type"; public static final String TYPE = "type";
@ -30,18 +30,18 @@ public class PushDatabase extends Database {
super(context, databaseHelper); super(context, databaseHelper);
} }
public long insert(IncomingPushMessage message) { public long insert(TextSecureEnvelope envelope) {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(TYPE, message.getType()); values.put(TYPE, envelope.getType());
values.put(SOURCE, message.getSource()); values.put(SOURCE, envelope.getSource());
values.put(DEVICE_ID, message.getSourceDevice()); values.put(DEVICE_ID, envelope.getSourceDevice());
values.put(BODY, Base64.encodeBytes(message.getBody())); values.put(BODY, Base64.encodeBytes(envelope.getMessage()));
values.put(TIMESTAMP, message.getTimestampMillis()); values.put(TIMESTAMP, envelope.getTimestamp());
return databaseHelper.getWritableDatabase().insert(TABLE_NAME, null, values); return databaseHelper.getWritableDatabase().insert(TABLE_NAME, null, values);
} }
public IncomingPushMessage get(long id) throws NoSuchMessageException { public TextSecureEnvelope get(long id) throws NoSuchMessageException {
Cursor cursor = null; Cursor cursor = null;
try { try {
@ -50,14 +50,15 @@ public class PushDatabase extends Database {
null, null, null); null, null, null);
if (cursor != null && cursor.moveToNext()) { if (cursor != null && cursor.moveToNext()) {
return new IncomingPushMessage(cursor.getInt(cursor.getColumnIndexOrThrow(TYPE)), return new TextSecureEnvelope(cursor.getInt(cursor.getColumnIndexOrThrow(TYPE)),
cursor.getString(cursor.getColumnIndexOrThrow(SOURCE)), cursor.getString(cursor.getColumnIndexOrThrow(SOURCE)),
cursor.getInt(cursor.getColumnIndexOrThrow(DEVICE_ID)), cursor.getInt(cursor.getColumnIndexOrThrow(DEVICE_ID)),
Base64.decode(cursor.getString(cursor.getColumnIndexOrThrow(BODY))), "",
cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP))); cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP)),
Base64.decode(cursor.getString(cursor.getColumnIndexOrThrow(BODY))));
} }
} catch (IOException e) { } catch (IOException e) {
Log.w("PushDatabase", e); Log.w(TAG, e);
throw new NoSuchMessageException(e); throw new NoSuchMessageException(e);
} finally { } finally {
if (cursor != null) if (cursor != null)
@ -86,7 +87,7 @@ public class PushDatabase extends Database {
this.cursor = cursor; this.cursor = cursor;
} }
public IncomingPushMessage getNext() { public TextSecureEnvelope getNext() {
try { try {
if (cursor == null || !cursor.moveToNext()) if (cursor == null || !cursor.moveToNext())
return null; return null;
@ -97,16 +98,12 @@ public class PushDatabase extends Database {
byte[] body = Base64.decode(cursor.getString(cursor.getColumnIndexOrThrow(BODY))); byte[] body = Base64.decode(cursor.getString(cursor.getColumnIndexOrThrow(BODY)));
long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP)); long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP));
return new IncomingPushMessage(type, source, deviceId, body, timestamp); return new TextSecureEnvelope(type, source, deviceId, "", timestamp, body);
} catch (IOException e) { } catch (IOException e) {
throw new AssertionError(e); throw new AssertionError(e);
} }
} }
public long getCurrentId() {
return cursor.getLong(cursor.getColumnIndexOrThrow(ID));
}
public void close() { public void close() {
this.cursor.close(); this.cursor.close();
} }

View File

@ -18,9 +18,9 @@ import org.thoughtcrime.securesms.sms.IncomingGroupMessage;
import org.thoughtcrime.securesms.sms.IncomingTextMessage; import org.thoughtcrime.securesms.sms.IncomingTextMessage;
import org.whispersystems.libaxolotl.util.guava.Optional; import org.whispersystems.libaxolotl.util.guava.Optional;
import org.whispersystems.textsecure.api.messages.TextSecureAttachment; import org.whispersystems.textsecure.api.messages.TextSecureAttachment;
import org.whispersystems.textsecure.api.messages.TextSecureEnvelope;
import org.whispersystems.textsecure.api.messages.TextSecureGroup; import org.whispersystems.textsecure.api.messages.TextSecureGroup;
import org.whispersystems.textsecure.api.messages.TextSecureMessage; import org.whispersystems.textsecure.api.messages.TextSecureMessage;
import org.whispersystems.textsecure.push.IncomingPushMessage;
import org.whispersystems.textsecure.util.Base64; import org.whispersystems.textsecure.util.Base64;
import java.util.HashSet; import java.util.HashSet;
@ -38,7 +38,7 @@ public class GroupMessageProcessor {
public static void process(Context context, public static void process(Context context,
MasterSecret masterSecret, MasterSecret masterSecret,
IncomingPushMessage push, TextSecureEnvelope envelope,
TextSecureMessage message) TextSecureMessage message)
{ {
if (!message.getGroupInfo().isPresent() || message.getGroupInfo().get().getGroupId() == null) { if (!message.getGroupInfo().isPresent() || message.getGroupInfo().get().getGroupId() == null) {
@ -57,11 +57,11 @@ public class GroupMessageProcessor {
GroupRecord record = database.getGroup(id); GroupRecord record = database.getGroup(id);
if (record != null && group.getType() == TextSecureGroup.Type.UPDATE) { if (record != null && group.getType() == TextSecureGroup.Type.UPDATE) {
handleGroupUpdate(context, masterSecret, push, group, record); handleGroupUpdate(context, masterSecret, envelope, group, record);
} else if (record == null && group.getType() == TextSecureGroup.Type.UPDATE) { } else if (record == null && group.getType() == TextSecureGroup.Type.UPDATE) {
handleGroupCreate(context, masterSecret, push, group); handleGroupCreate(context, masterSecret, envelope, group);
} else if (record != null && group.getType() == TextSecureGroup.Type.QUIT) { } else if (record != null && group.getType() == TextSecureGroup.Type.QUIT) {
handleGroupLeave(context, masterSecret, push, group, record); handleGroupLeave(context, masterSecret, envelope, group, record);
} else { } else {
Log.w(TAG, "Received unknown type, ignoring..."); Log.w(TAG, "Received unknown type, ignoring...");
} }
@ -69,7 +69,7 @@ public class GroupMessageProcessor {
private static void handleGroupCreate(Context context, private static void handleGroupCreate(Context context,
MasterSecret masterSecret, MasterSecret masterSecret,
IncomingPushMessage message, TextSecureEnvelope envelope,
TextSecureGroup group) TextSecureGroup group)
{ {
GroupDatabase database = DatabaseFactory.getGroupDatabase(context); GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
@ -81,14 +81,14 @@ public class GroupMessageProcessor {
database.create(id, group.getName().orNull(), group.getMembers().orNull(), database.create(id, group.getName().orNull(), group.getMembers().orNull(),
avatar != null && avatar.isPointer() ? avatar.asPointer() : null, avatar != null && avatar.isPointer() ? avatar.asPointer() : null,
message.getRelay()); envelope.getRelay());
storeMessage(context, masterSecret, message, group, builder.build()); storeMessage(context, masterSecret, envelope, group, builder.build());
} }
private static void handleGroupUpdate(Context context, private static void handleGroupUpdate(Context context,
MasterSecret masterSecret, MasterSecret masterSecret,
IncomingPushMessage push, TextSecureEnvelope envelope,
TextSecureGroup group, TextSecureGroup group,
GroupRecord groupRecord) GroupRecord groupRecord)
{ {
@ -133,12 +133,12 @@ public class GroupMessageProcessor {
if (!groupRecord.isActive()) database.setActive(id, true); if (!groupRecord.isActive()) database.setActive(id, true);
storeMessage(context, masterSecret, push, group, builder.build()); storeMessage(context, masterSecret, envelope, group, builder.build());
} }
private static void handleGroupLeave(Context context, private static void handleGroupLeave(Context context,
MasterSecret masterSecret, MasterSecret masterSecret,
IncomingPushMessage message, TextSecureEnvelope envelope,
TextSecureGroup group, TextSecureGroup group,
GroupRecord record) GroupRecord record)
{ {
@ -149,16 +149,16 @@ public class GroupMessageProcessor {
GroupContext.Builder builder = createGroupContext(group); GroupContext.Builder builder = createGroupContext(group);
builder.setType(GroupContext.Type.QUIT); builder.setType(GroupContext.Type.QUIT);
if (members.contains(message.getSource())) { if (members.contains(envelope.getSource())) {
database.remove(id, message.getSource()); database.remove(id, envelope.getSource());
storeMessage(context, masterSecret, message, group, builder.build()); storeMessage(context, masterSecret, envelope, group, builder.build());
} }
} }
private static void storeMessage(Context context, MasterSecret masterSecret, private static void storeMessage(Context context, MasterSecret masterSecret,
IncomingPushMessage push, TextSecureGroup group, TextSecureEnvelope envelope, TextSecureGroup group,
GroupContext storage) GroupContext storage)
{ {
if (group.getAvatar().isPresent()) { if (group.getAvatar().isPresent()) {
@ -168,7 +168,7 @@ public class GroupMessageProcessor {
EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context); EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context);
String body = Base64.encodeBytes(storage.toByteArray()); String body = Base64.encodeBytes(storage.toByteArray());
IncomingTextMessage incoming = new IncomingTextMessage(push.getSource(), push.getSourceDevice(), push.getTimestampMillis(), body, Optional.of(group)); IncomingTextMessage incoming = new IncomingTextMessage(envelope.getSource(), envelope.getSourceDevice(), envelope.getTimestamp(), body, Optional.of(group));
IncomingGroupMessage groupMessage = new IncomingGroupMessage(incoming, storage, body); IncomingGroupMessage groupMessage = new IncomingGroupMessage(incoming, storage, body);
Pair<Long, Long> messageAndThreadId = smsDatabase.insertMessageInbox(masterSecret, groupMessage); Pair<Long, Long> messageAndThreadId = smsDatabase.insertMessageInbox(masterSecret, groupMessage);

View File

@ -5,8 +5,9 @@ import android.util.Log;
import android.util.Pair; import android.util.Pair;
import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.crypto.SecurityEvent;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.SecurityEvent;
import org.thoughtcrime.securesms.crypto.storage.TextSecureAxolotlStore;
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore; import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase; import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
@ -16,8 +17,8 @@ import org.thoughtcrime.securesms.groups.GroupMessageProcessor;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement; import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.mms.IncomingMediaMessage; import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.push.TextSecureMessageReceiverFactory;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage; import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage;
@ -34,12 +35,13 @@ import org.whispersystems.libaxolotl.InvalidVersionException;
import org.whispersystems.libaxolotl.LegacyMessageException; import org.whispersystems.libaxolotl.LegacyMessageException;
import org.whispersystems.libaxolotl.NoSessionException; import org.whispersystems.libaxolotl.NoSessionException;
import org.whispersystems.libaxolotl.UntrustedIdentityException; import org.whispersystems.libaxolotl.UntrustedIdentityException;
import org.whispersystems.libaxolotl.state.AxolotlStore;
import org.whispersystems.libaxolotl.state.SessionStore; import org.whispersystems.libaxolotl.state.SessionStore;
import org.whispersystems.libaxolotl.util.guava.Optional; import org.whispersystems.libaxolotl.util.guava.Optional;
import org.whispersystems.textsecure.api.TextSecureMessageReceiver; import org.whispersystems.textsecure.api.messages.TextSecureEnvelope;
import org.whispersystems.textsecure.api.messages.TextSecureGroup; import org.whispersystems.textsecure.api.messages.TextSecureGroup;
import org.whispersystems.textsecure.api.messages.TextSecureMessage; import org.whispersystems.textsecure.api.messages.TextSecureMessage;
import org.whispersystems.textsecure.push.IncomingPushMessage; import org.whispersystems.textsecure.crypto.TextSecureCipher;
import org.whispersystems.textsecure.util.Base64; import org.whispersystems.textsecure.util.Base64;
import ws.com.google.android.mms.MmsException; import ws.com.google.android.mms.MmsException;
@ -68,11 +70,11 @@ public class PushDecryptJob extends MasterSecretJob {
@Override @Override
public void onRun() throws RequirementNotMetException { public void onRun() throws RequirementNotMetException {
try { try {
MasterSecret masterSecret = getMasterSecret(); MasterSecret masterSecret = getMasterSecret();
PushDatabase database = DatabaseFactory.getPushDatabase(context); PushDatabase database = DatabaseFactory.getPushDatabase(context);
IncomingPushMessage push = database.get(messageId); TextSecureEnvelope envelope = database.get(messageId);
handleMessage(masterSecret, push); handleMessage(masterSecret, envelope);
database.delete(messageId); database.delete(messageId);
} catch (PushDatabase.NoSuchMessageException e) { } catch (PushDatabase.NoSuchMessageException e) {
@ -91,42 +93,46 @@ public class PushDecryptJob extends MasterSecretJob {
return false; return false;
} }
private void handleMessage(MasterSecret masterSecret, IncomingPushMessage push) { private void handleMessage(MasterSecret masterSecret, TextSecureEnvelope envelope) {
try { try {
Recipients recipients = RecipientFactory.getRecipientsFromMessage(context, push, false); Recipients recipients = RecipientFactory.getRecipientsFromString(context, envelope.getSource(), false);
long recipientId = recipients.getPrimaryRecipient().getRecipientId(); long recipientId = recipients.getPrimaryRecipient().getRecipientId();
TextSecureMessageReceiver messageReceiver = TextSecureMessageReceiverFactory.create(context, masterSecret); int deviceId = envelope.getSourceDevice();
AxolotlStore axolotlStore = new TextSecureAxolotlStore(context, masterSecret);
TextSecureCipher cipher = new TextSecureCipher(axolotlStore, recipientId, deviceId);
TextSecureMessage message = messageReceiver.receiveMessage(recipientId, push); TextSecureMessage message = cipher.decrypt(envelope);
if (message.isEndSession()) handleEndSessionMessage(masterSecret, recipientId, push, message); if (message.isEndSession()) handleEndSessionMessage(masterSecret, recipientId, envelope, message);
else if (message.isGroupUpdate()) handleGroupMessage(masterSecret, push, message); else if (message.isGroupUpdate()) handleGroupMessage(masterSecret, envelope, message);
else if (message.getAttachments().isPresent()) handleMediaMessage(masterSecret, push, message); else if (message.getAttachments().isPresent()) handleMediaMessage(masterSecret, envelope, message);
else handleTextMessage(masterSecret, push, message); else handleTextMessage(masterSecret, envelope, message);
} catch (InvalidVersionException e) { } catch (InvalidVersionException e) {
Log.w(TAG, e); Log.w(TAG, e);
handleInvalidVersionMessage(masterSecret, push); handleInvalidVersionMessage(masterSecret, envelope);
} catch (InvalidMessageException | InvalidKeyIdException | InvalidKeyException | MmsException e) { } catch (InvalidMessageException | InvalidKeyIdException | InvalidKeyException | MmsException | RecipientFormattingException e) {
Log.w(TAG, e); Log.w(TAG, e);
handleCorruptMessage(masterSecret, push); handleCorruptMessage(masterSecret, envelope);
} catch (NoSessionException e) { } catch (NoSessionException e) {
Log.w(TAG, e); Log.w(TAG, e);
handleNoSessionMessage(masterSecret, push); handleNoSessionMessage(masterSecret, envelope);
} catch (LegacyMessageException e) { } catch (LegacyMessageException e) {
Log.w(TAG, e); Log.w(TAG, e);
handleLegacyMessage(masterSecret, push); handleLegacyMessage(masterSecret, envelope);
} catch (DuplicateMessageException e) { } catch (DuplicateMessageException e) {
Log.w(TAG, e); Log.w(TAG, e);
handleDuplicateMessage(masterSecret, push); handleDuplicateMessage(masterSecret, envelope);
} catch (UntrustedIdentityException e) { } catch (UntrustedIdentityException e) {
Log.w(TAG, e); Log.w(TAG, e);
handleUntrustedIdentityMessage(masterSecret, push); handleUntrustedIdentityMessage(masterSecret, envelope);
} }
} }
private void handleEndSessionMessage(MasterSecret masterSecret, long recipientId, IncomingPushMessage push, TextSecureMessage message) { private void handleEndSessionMessage(MasterSecret masterSecret, long recipientId,
IncomingTextMessage incomingTextMessage = new IncomingTextMessage(push.getSource(), TextSecureEnvelope envelope, TextSecureMessage message)
push.getSourceDevice(), {
IncomingTextMessage incomingTextMessage = new IncomingTextMessage(envelope.getSource(),
envelope.getSourceDevice(),
message.getTimestamp(), message.getTimestamp(),
"", Optional.<TextSecureGroup>absent()); "", Optional.<TextSecureGroup>absent());
@ -141,18 +147,18 @@ public class PushDecryptJob extends MasterSecretJob {
MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second); MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second);
} }
private void handleGroupMessage(MasterSecret masterSecret, IncomingPushMessage push, TextSecureMessage message) { private void handleGroupMessage(MasterSecret masterSecret, TextSecureEnvelope envelope, TextSecureMessage message) {
GroupMessageProcessor.process(context, masterSecret, push, message); GroupMessageProcessor.process(context, masterSecret, envelope, message);
} }
private void handleMediaMessage(MasterSecret masterSecret, IncomingPushMessage signal, TextSecureMessage message) private void handleMediaMessage(MasterSecret masterSecret, TextSecureEnvelope envelope, TextSecureMessage message)
throws MmsException throws MmsException
{ {
String localNumber = TextSecurePreferences.getLocalNumber(context); String localNumber = TextSecurePreferences.getLocalNumber(context);
MmsDatabase database = DatabaseFactory.getMmsDatabase(context); MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(masterSecret, signal.getSource(), IncomingMediaMessage mediaMessage = new IncomingMediaMessage(masterSecret, envelope.getSource(),
localNumber, message.getTimestamp(), localNumber, message.getTimestamp(),
Optional.fromNullable(signal.getRelay()), Optional.fromNullable(envelope.getRelay()),
message.getBody(), message.getBody(),
message.getGroupInfo(), message.getGroupInfo(),
message.getAttachments()); message.getAttachments());
@ -172,11 +178,11 @@ public class PushDecryptJob extends MasterSecretJob {
MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second); MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second);
} }
private void handleTextMessage(MasterSecret masterSecret, IncomingPushMessage signal, TextSecureMessage message) { private void handleTextMessage(MasterSecret masterSecret, TextSecureEnvelope envelope, TextSecureMessage message) {
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context); EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
String body = message.getBody().isPresent() ? message.getBody().get() : ""; String body = message.getBody().isPresent() ? message.getBody().get() : "";
IncomingTextMessage textMessage = new IncomingTextMessage(signal.getSource(), IncomingTextMessage textMessage = new IncomingTextMessage(envelope.getSource(),
signal.getSourceDevice(), envelope.getSourceDevice(),
message.getTimestamp(), body, message.getTimestamp(), body,
message.getGroupInfo()); message.getGroupInfo());
@ -185,50 +191,48 @@ public class PushDecryptJob extends MasterSecretJob {
} }
Pair<Long, Long> messageAndThreadId = database.insertMessageInbox(masterSecret, textMessage); Pair<Long, Long> messageAndThreadId = database.insertMessageInbox(masterSecret, textMessage);
// database.updateMessageBody(masterSecret, messageAndThreadId.first, body);
MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second); MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second);
} }
private void handleInvalidVersionMessage(MasterSecret masterSecret, IncomingPushMessage push) { private void handleInvalidVersionMessage(MasterSecret masterSecret, TextSecureEnvelope envelope) {
Pair<Long, Long> messageAndThreadId = insertPlaceholder(masterSecret, push); Pair<Long, Long> messageAndThreadId = insertPlaceholder(masterSecret, envelope);
DatabaseFactory.getEncryptingSmsDatabase(context).markAsInvalidVersionKeyExchange(messageAndThreadId.first); DatabaseFactory.getEncryptingSmsDatabase(context).markAsInvalidVersionKeyExchange(messageAndThreadId.first);
MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second); MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second);
} }
private void handleCorruptMessage(MasterSecret masterSecret, IncomingPushMessage push) { private void handleCorruptMessage(MasterSecret masterSecret, TextSecureEnvelope envelope) {
Pair<Long, Long> messageAndThreadId = insertPlaceholder(masterSecret, push); Pair<Long, Long> messageAndThreadId = insertPlaceholder(masterSecret, envelope);
DatabaseFactory.getEncryptingSmsDatabase(context).markAsDecryptFailed(messageAndThreadId.first); DatabaseFactory.getEncryptingSmsDatabase(context).markAsDecryptFailed(messageAndThreadId.first);
MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second); MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second);
} }
private void handleNoSessionMessage(MasterSecret masterSecret, IncomingPushMessage push) { private void handleNoSessionMessage(MasterSecret masterSecret, TextSecureEnvelope envelope) {
Pair<Long, Long> messageAndThreadId = insertPlaceholder(masterSecret, push); Pair<Long, Long> messageAndThreadId = insertPlaceholder(masterSecret, envelope);
DatabaseFactory.getEncryptingSmsDatabase(context).markAsNoSession(messageAndThreadId.first); DatabaseFactory.getEncryptingSmsDatabase(context).markAsNoSession(messageAndThreadId.first);
MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second); MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second);
} }
private void handleLegacyMessage(MasterSecret masterSecret, IncomingPushMessage push) { private void handleLegacyMessage(MasterSecret masterSecret, TextSecureEnvelope envelope) {
Pair<Long, Long> messageAndThreadId = insertPlaceholder(masterSecret, push); Pair<Long, Long> messageAndThreadId = insertPlaceholder(masterSecret, envelope);
DatabaseFactory.getEncryptingSmsDatabase(context).markAsLegacyVersion(messageAndThreadId.first); DatabaseFactory.getEncryptingSmsDatabase(context).markAsLegacyVersion(messageAndThreadId.first);
MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second); MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second);
} }
private void handleDuplicateMessage(MasterSecret masterSecret, IncomingPushMessage push) { private void handleDuplicateMessage(MasterSecret masterSecret, TextSecureEnvelope envelope) {
Pair<Long, Long> messageAndThreadId = insertPlaceholder(masterSecret, push); Pair<Long, Long> messageAndThreadId = insertPlaceholder(masterSecret, envelope);
DatabaseFactory.getEncryptingSmsDatabase(context).markAsDecryptDuplicate(messageAndThreadId.first); DatabaseFactory.getEncryptingSmsDatabase(context).markAsDecryptDuplicate(messageAndThreadId.first);
MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second); MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second);
} }
private void handleUntrustedIdentityMessage(MasterSecret masterSecret, IncomingPushMessage push) { private void handleUntrustedIdentityMessage(MasterSecret masterSecret, TextSecureEnvelope envelope) {
String encoded = Base64.encodeBytes(push.getBody()); String encoded = Base64.encodeBytes(envelope.getMessage());
IncomingTextMessage textMessage = new IncomingTextMessage(push.getSource(), push.getSourceDevice(), IncomingTextMessage textMessage = new IncomingTextMessage(envelope.getSource(), envelope.getSourceDevice(),
push.getTimestampMillis(), encoded, envelope.getTimestamp(), encoded,
Optional.<TextSecureGroup>absent()); Optional.<TextSecureGroup>absent());
IncomingPreKeyBundleMessage bundleMessage = new IncomingPreKeyBundleMessage(textMessage, encoded); IncomingPreKeyBundleMessage bundleMessage = new IncomingPreKeyBundleMessage(textMessage, encoded);
@ -238,11 +242,11 @@ public class PushDecryptJob extends MasterSecretJob {
MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second); MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second);
} }
private Pair<Long, Long> insertPlaceholder(MasterSecret masterSecret, IncomingPushMessage push) { private Pair<Long, Long> insertPlaceholder(MasterSecret masterSecret, TextSecureEnvelope envelope) {
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context); EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
IncomingTextMessage textMessage = new IncomingTextMessage(push.getSource(), push.getSourceDevice(), IncomingTextMessage textMessage = new IncomingTextMessage(envelope.getSource(), envelope.getSourceDevice(),
push.getTimestampMillis(), "", envelope.getTimestamp(), "",
Optional.<TextSecureGroup>absent()); Optional.<TextSecureGroup>absent());
textMessage = new IncomingEncryptedMessage(textMessage, ""); textMessage = new IncomingEncryptedMessage(textMessage, "");

View File

@ -9,11 +9,10 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.jobqueue.JobManager; import org.whispersystems.jobqueue.JobManager;
import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.libaxolotl.InvalidVersionException; import org.whispersystems.libaxolotl.InvalidVersionException;
import org.whispersystems.textsecure.api.messages.TextSecureEnvelope;
import org.whispersystems.textsecure.directory.Directory; import org.whispersystems.textsecure.directory.Directory;
import org.whispersystems.textsecure.directory.NotInDirectoryException; import org.whispersystems.textsecure.directory.NotInDirectoryException;
import org.whispersystems.textsecure.push.ContactTokenDetails; import org.whispersystems.textsecure.push.ContactTokenDetails;
import org.whispersystems.textsecure.push.IncomingEncryptedPushMessage;
import org.whispersystems.textsecure.push.IncomingPushMessage;
import java.io.IOException; import java.io.IOException;
@ -37,20 +36,19 @@ public class PushReceiveJob extends ContextJob {
@Override @Override
public void onRun() { public void onRun() {
try { try {
String sessionKey = TextSecurePreferences.getSignalingKey(context); String sessionKey = TextSecurePreferences.getSignalingKey(context);
IncomingEncryptedPushMessage encrypted = new IncomingEncryptedPushMessage(data, sessionKey); TextSecureEnvelope envelope = new TextSecureEnvelope(data, sessionKey);
IncomingPushMessage message = encrypted.getIncomingPushMessage();
if (!isActiveNumber(context, message.getSource())) { if (!isActiveNumber(context, envelope.getSource())) {
Directory directory = Directory.getInstance(context); Directory directory = Directory.getInstance(context);
ContactTokenDetails contactTokenDetails = new ContactTokenDetails(); ContactTokenDetails contactTokenDetails = new ContactTokenDetails();
contactTokenDetails.setNumber(message.getSource()); contactTokenDetails.setNumber(envelope.getSource());
directory.setNumber(contactTokenDetails, true); directory.setNumber(contactTokenDetails, true);
} }
if (message.isReceipt()) handleReceipt(message); if (envelope.isReceipt()) handleReceipt(envelope);
else handleMessage(message); else handleMessage(envelope);
} catch (IOException | InvalidVersionException e) { } catch (IOException | InvalidVersionException e) {
Log.w(TAG, e); Log.w(TAG, e);
} }
@ -66,21 +64,21 @@ public class PushReceiveJob extends ContextJob {
return false; return false;
} }
private void handleMessage(IncomingPushMessage message) { private void handleMessage(TextSecureEnvelope envelope) {
JobManager jobManager = ApplicationContext.getInstance(context).getJobManager(); JobManager jobManager = ApplicationContext.getInstance(context).getJobManager();
long messageId = DatabaseFactory.getPushDatabase(context).insert(message); long messageId = DatabaseFactory.getPushDatabase(context).insert(envelope);
jobManager.add(new DeliveryReceiptJob(context, message.getSource(), jobManager.add(new DeliveryReceiptJob(context, envelope.getSource(),
message.getTimestampMillis(), envelope.getTimestamp(),
message.getRelay())); envelope.getRelay()));
jobManager.add(new PushDecryptJob(context, messageId)); jobManager.add(new PushDecryptJob(context, messageId));
} }
private void handleReceipt(IncomingPushMessage message) { private void handleReceipt(TextSecureEnvelope envelope) {
Log.w(TAG, String.format("Received receipt: (XXXXX, %d)", message.getTimestampMillis())); Log.w(TAG, String.format("Received receipt: (XXXXX, %d)", envelope.getTimestamp()));
DatabaseFactory.getMmsSmsDatabase(context).incrementDeliveryReceiptCount(message.getSource(), DatabaseFactory.getMmsSmsDatabase(context).incrementDeliveryReceiptCount(envelope.getSource(),
message.getTimestampMillis()); envelope.getTimestamp());
} }
private boolean isActiveNumber(Context context, String e164number) { private boolean isActiveNumber(Context context, String e164number) {

View File

@ -1,14 +1,12 @@
package org.thoughtcrime.securesms.mms; package org.thoughtcrime.securesms.mms;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.crypto.MasterCipher; import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libaxolotl.util.guava.Optional; import org.whispersystems.libaxolotl.util.guava.Optional;
import org.whispersystems.textsecure.api.messages.TextSecureAttachment; import org.whispersystems.textsecure.api.messages.TextSecureAttachment;
import org.whispersystems.textsecure.api.messages.TextSecureGroup; import org.whispersystems.textsecure.api.messages.TextSecureGroup;
import org.whispersystems.textsecure.push.IncomingPushMessage;
import org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent;
import org.whispersystems.textsecure.util.Base64; import org.whispersystems.textsecure.util.Base64;
import java.util.List; import java.util.List;

View File

@ -39,17 +39,17 @@ import android.util.Log;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.RoutingActivity; import org.thoughtcrime.securesms.RoutingActivity;
import org.thoughtcrime.securesms.database.PushDatabase;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsSmsDatabase; import org.thoughtcrime.securesms.database.MmsSmsDatabase;
import org.thoughtcrime.securesms.database.PushDatabase;
import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.textsecure.push.IncomingPushMessage; import org.whispersystems.textsecure.api.messages.TextSecureEnvelope;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
@ -285,24 +285,24 @@ public class MessageNotifier {
if (masterSecret != null) return; if (masterSecret != null) return;
PushDatabase.Reader reader = null; PushDatabase.Reader reader = null;
IncomingPushMessage message; TextSecureEnvelope envelope;
try { try {
reader = DatabaseFactory.getPushDatabase(context).readerFor(cursor); reader = DatabaseFactory.getPushDatabase(context).readerFor(cursor);
while ((message = reader.getNext()) != null) { while ((envelope = reader.getNext()) != null) {
Recipient recipient; Recipients recipients;
try { try {
recipient = RecipientFactory.getRecipientsFromString(context, message.getSource(), false).getPrimaryRecipient(); recipients = RecipientFactory.getRecipientsFromString(context, envelope.getSource(), false);
} catch (RecipientFormattingException e) { } catch (RecipientFormattingException e) {
Log.w("MessageNotifier", e); Log.w("MessageNotifier", e);
recipient = Recipient.getUnknownRecipient(context); recipients = new Recipients(Recipient.getUnknownRecipient(context));
} }
Recipients recipients = RecipientFactory.getRecipientsFromMessage(context, message, false); Recipient recipient = recipients.getPrimaryRecipient();
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients); long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
SpannableString body = new SpannableString(context.getString(R.string.MessageNotifier_encrypted_message)); SpannableString body = new SpannableString(context.getString(R.string.MessageNotifier_encrypted_message));
body.setSpan(new StyleSpan(android.graphics.Typeface.ITALIC), 0, body.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); body.setSpan(new StyleSpan(android.graphics.Typeface.ITALIC), 0, body.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
notificationState.addNotification(new NotificationItem(recipient, recipients, null, threadId, body, null)); notificationState.addNotification(new NotificationItem(recipient, recipients, null, threadId, body, null));

View File

@ -21,7 +21,6 @@ import android.util.Log;
import org.thoughtcrime.securesms.contacts.ContactPhotoFactory; import org.thoughtcrime.securesms.contacts.ContactPhotoFactory;
import org.thoughtcrime.securesms.database.CanonicalAddressDatabase; import org.thoughtcrime.securesms.database.CanonicalAddressDatabase;
import org.whispersystems.textsecure.push.IncomingPushMessage;
import org.whispersystems.textsecure.util.Util; import org.whispersystems.textsecure.util.Util;
import java.util.LinkedList; import java.util.LinkedList;
@ -73,18 +72,6 @@ public class RecipientFactory {
return new Recipients(results); return new Recipients(results);
} }
public static Recipients getRecipientsFromMessage(Context context,
IncomingPushMessage message,
boolean asynchronous)
{
try {
return getRecipientsFromString(context, message.getSource(), asynchronous);
} catch (RecipientFormattingException e) {
Log.w("RecipientFactory", e);
return new Recipients(Recipient.getUnknownRecipient(context));
}
}
private static Recipient getRecipientFromProviderId(Context context, String recipientId, boolean asynchronous) { private static Recipient getRecipientFromProviderId(Context context, String recipientId, boolean asynchronous) {
try { try {
return provider.getRecipient(context, Long.parseLong(recipientId), asynchronous); return provider.getRecipient(context, Long.parseLong(recipientId), asynchronous);

View File

@ -76,7 +76,7 @@ public class MmsSender {
try { try {
Log.w("MmsSender", "Passing to MMS transport: " + message.getDatabaseMessageId()); Log.w("MmsSender", "Passing to MMS transport: " + message.getDatabaseMessageId());
database.markAsSending(message.getDatabaseMessageId()); database.markAsSending(message.getDatabaseMessageId());
MmsSendResult result = transport.deliver(message, threadId); MmsSendResult result = transport.deliver(message);
if (result.isUpgradedSecure()) database.markAsSecure(message.getDatabaseMessageId()); if (result.isUpgradedSecure()) database.markAsSecure(message.getDatabaseMessageId());
if (result.isPush()) database.markAsPush(message.getDatabaseMessageId()); if (result.isPush()) database.markAsPush(message.getDatabaseMessageId());

View File

@ -7,13 +7,10 @@ import android.telephony.SmsMessage;
import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.libaxolotl.util.guava.Optional; import org.whispersystems.libaxolotl.util.guava.Optional;
import org.whispersystems.textsecure.api.messages.TextSecureGroup; import org.whispersystems.textsecure.api.messages.TextSecureGroup;
import org.whispersystems.textsecure.push.IncomingPushMessage;
import org.whispersystems.textsecure.storage.RecipientDevice; import org.whispersystems.textsecure.storage.RecipientDevice;
import java.util.List; import java.util.List;
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.GroupContext;
public class IncomingTextMessage implements Parcelable { public class IncomingTextMessage implements Parcelable {
public static final Parcelable.Creator<IncomingTextMessage> CREATOR = new Parcelable.Creator<IncomingTextMessage>() { public static final Parcelable.Creator<IncomingTextMessage> CREATOR = new Parcelable.Creator<IncomingTextMessage>() {

View File

@ -89,7 +89,7 @@ public class PushTransport extends BaseTransport {
} }
} }
public void deliverGroupMessage(SendReq message, long threadId) public void deliverGroupMessage(SendReq message)
throws IOException, RecipientFormattingException, InvalidNumberException, EncapsulatedExceptions throws IOException, RecipientFormattingException, InvalidNumberException, EncapsulatedExceptions
{ {
TextSecureMessageSender messageSender = TextSecureMessageSenderFactory.create(context, masterSecret); TextSecureMessageSender messageSender = TextSecureMessageSenderFactory.create(context, masterSecret);
@ -121,7 +121,7 @@ public class PushTransport extends BaseTransport {
} }
} }
public void deliver(SendReq message, long threadId) public void deliver(SendReq message)
throws IOException, RecipientFormattingException, InvalidNumberException, EncapsulatedExceptions throws IOException, RecipientFormattingException, InvalidNumberException, EncapsulatedExceptions
{ {
TextSecureMessageSender messageSender = TextSecureMessageSenderFactory.create(context, masterSecret); TextSecureMessageSender messageSender = TextSecureMessageSenderFactory.create(context, masterSecret);
@ -131,7 +131,7 @@ public class PushTransport extends BaseTransport {
List<UnregisteredUserException> unregisteredUsers = new LinkedList<>(); List<UnregisteredUserException> unregisteredUsers = new LinkedList<>();
if (GroupUtil.isEncodedGroup(destination)) { if (GroupUtil.isEncodedGroup(destination)) {
deliverGroupMessage(message, threadId); deliverGroupMessage(message);
return; return;
} }
@ -182,6 +182,7 @@ public class PushTransport extends BaseTransport {
ContentType.isVideoType(contentType)) ContentType.isVideoType(contentType))
{ {
byte[] data = message.getBody().getPart(i).getData(); byte[] data = message.getBody().getPart(i).getData();
Log.w(TAG, "Adding attachment...");
attachments.add(new TextSecureAttachmentStream(new ByteArrayInputStream(data), contentType, data.length)); attachments.add(new TextSecureAttachmentStream(new ByteArrayInputStream(data), contentType, data.length));
} }
} }

View File

@ -111,7 +111,7 @@ public class UniversalTransport {
} }
} }
public MmsSendResult deliver(SendReq mediaMessage, long threadId) public MmsSendResult deliver(SendReq mediaMessage)
throws UndeliverableMessageException, RetryLaterException, UntrustedIdentityException, throws UndeliverableMessageException, RetryLaterException, UntrustedIdentityException,
SecureFallbackApprovalException, InsecureFallbackApprovalException SecureFallbackApprovalException, InsecureFallbackApprovalException
{ {
@ -124,7 +124,7 @@ public class UniversalTransport {
} }
if (GroupUtil.isEncodedGroup(mediaMessage.getTo()[0].getString())) { if (GroupUtil.isEncodedGroup(mediaMessage.getTo()[0].getString())) {
return deliverGroupMessage(mediaMessage, threadId); return deliverGroupMessage(mediaMessage);
} }
if (!TextSecurePreferences.isPushRegistered(context)) { if (!TextSecurePreferences.isPushRegistered(context)) {
@ -143,7 +143,7 @@ public class UniversalTransport {
try { try {
Log.w(TAG, "Using GCM as transport..."); Log.w(TAG, "Using GCM as transport...");
pushTransport.deliver(mediaMessage, threadId); pushTransport.deliver(mediaMessage);
return new MmsSendResult("push".getBytes("UTF-8"), 0, true, true); return new MmsSendResult("push".getBytes("UTF-8"), 0, true, true);
} catch (IOException ioe) { } catch (IOException ioe) {
Log.w(TAG, ioe); Log.w(TAG, ioe);
@ -216,7 +216,7 @@ public class UniversalTransport {
} }
} }
private MmsSendResult deliverGroupMessage(SendReq mediaMessage, long threadId) private MmsSendResult deliverGroupMessage(SendReq mediaMessage)
throws RetryLaterException, UndeliverableMessageException throws RetryLaterException, UndeliverableMessageException
{ {
if (!TextSecurePreferences.isPushRegistered(context)) { if (!TextSecurePreferences.isPushRegistered(context)) {
@ -224,7 +224,7 @@ public class UniversalTransport {
} }
try { try {
pushTransport.deliver(mediaMessage, threadId); pushTransport.deliver(mediaMessage);
return new MmsSendResult("push".getBytes("UTF-8"), 0, true, true); return new MmsSendResult("push".getBytes("UTF-8"), 0, true, true);
} catch (IOException e) { } catch (IOException e) {
Log.w(TAG, e); Log.w(TAG, e);