mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-23 18:15:22 +00:00
Move API around.
This commit is contained in:
parent
a3f1d9cdfd
commit
99f42e2ee1
@ -2,40 +2,15 @@ package org.whispersystems.textsecure.api;
|
||||
|
||||
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.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.util.guava.Optional;
|
||||
import org.whispersystems.textsecure.api.messages.TextSecureAttachment;
|
||||
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.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 java.io.File;
|
||||
import java.io.IOException;
|
||||
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 {
|
||||
|
||||
@ -61,95 +36,4 @@ public class TextSecureMessageReceiver {
|
||||
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());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -173,11 +173,13 @@ public class TextSecureMessageSender {
|
||||
List<AttachmentPointer> pointers = new LinkedList<>();
|
||||
|
||||
if (!attachments.isPresent() || attachments.get().isEmpty()) {
|
||||
Log.w(TAG, "No attachments present...");
|
||||
return pointers;
|
||||
}
|
||||
|
||||
for (TextSecureAttachment attachment : attachments.get()) {
|
||||
if (attachment.isStream()) {
|
||||
Log.w(TAG, "Found attachment, creating pointer...");
|
||||
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);
|
||||
int remoteRegistrationId = cipher.getRemoteRegistrationId();
|
||||
|
||||
|
@ -15,12 +15,12 @@ public class TextSecureAttachmentStream extends TextSecureAttachment {
|
||||
|
||||
@Override
|
||||
public boolean isStream() {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPointer() {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public InputStream getInputStream() {
|
||||
|
@ -1,12 +1,20 @@
|
||||
package org.whispersystems.textsecure.push;
|
||||
package org.whispersystems.textsecure.api.messages;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
|
||||
import org.whispersystems.libaxolotl.InvalidVersionException;
|
||||
import org.whispersystems.textsecure.util.Base64;
|
||||
import org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal;
|
||||
import org.whispersystems.textsecure.util.Base64;
|
||||
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.Cipher;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
@ -14,13 +22,10 @@ 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 {
|
||||
public class TextSecureEnvelope {
|
||||
|
||||
private static final String TAG = TextSecureEnvelope.class.getSimpleName();
|
||||
|
||||
private static final int SUPPORTED_VERSION = 1;
|
||||
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 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
|
||||
{
|
||||
byte[] ciphertext = Base64.decode(message);
|
||||
@ -48,14 +53,60 @@ public class IncomingEncryptedPushMessage {
|
||||
|
||||
verifyMac(ciphertext, macKey);
|
||||
|
||||
byte[] plaintext = getPlaintext(ciphertext, cipherKey);
|
||||
IncomingPushMessageSignal signal = IncomingPushMessageSignal.parseFrom(plaintext);
|
||||
|
||||
this.incomingPushMessage = new IncomingPushMessage(signal);
|
||||
this.signal = IncomingPushMessageSignal.parseFrom(getPlaintext(ciphertext, cipherKey));
|
||||
}
|
||||
|
||||
public IncomingPushMessage getIncomingPushMessage() {
|
||||
return incomingPushMessage;
|
||||
public TextSecureEnvelope(int type, String source, int sourceDevice,
|
||||
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 {
|
||||
@ -72,7 +123,7 @@ public class IncomingEncryptedPushMessage {
|
||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException e) {
|
||||
throw new AssertionError(e);
|
||||
} catch (BadPaddingException e) {
|
||||
Log.w("IncomingEncryptedPushMessage", e);
|
||||
Log.w(TAG, e);
|
||||
throw new IOException("Bad padding?");
|
||||
}
|
||||
}
|
||||
@ -94,15 +145,13 @@ public class IncomingEncryptedPushMessage {
|
||||
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));
|
||||
Log.w(TAG, "Our MAC: " + Hex.toString(ourMacBytes));
|
||||
Log.w(TAG, "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) {
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
@ -1,9 +1,12 @@
|
||||
package org.whispersystems.textsecure.crypto;
|
||||
|
||||
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.InvalidVersionException;
|
||||
import org.whispersystems.libaxolotl.LegacyMessageException;
|
||||
import org.whispersystems.libaxolotl.NoSessionException;
|
||||
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.WhisperMessage;
|
||||
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 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 {
|
||||
|
||||
private final SessionCipher sessionCipher;
|
||||
private final TransportDetails transportDetails;
|
||||
|
||||
public TextSecureCipher(AxolotlStore axolotlStore, PushAddress pushAddress) {
|
||||
int sessionVersion = axolotlStore.loadSession(pushAddress.getRecipientId(),
|
||||
pushAddress.getDeviceId())
|
||||
public TextSecureCipher(AxolotlStore axolotlStore, long recipientId, int deviceId) {
|
||||
int sessionVersion = axolotlStore.loadSession(recipientId, deviceId)
|
||||
.getSessionState().getSessionVersion();
|
||||
|
||||
this.transportDetails = new PushTransportDetails(sessionVersion);
|
||||
this.sessionCipher = new SessionCipher(axolotlStore, pushAddress.getRecipientId(),
|
||||
pushAddress.getDeviceId());
|
||||
this.sessionCipher = new SessionCipher(axolotlStore, recipientId, deviceId);
|
||||
}
|
||||
|
||||
public CiphertextMessage encrypt(byte[] unpaddedMessage) {
|
||||
return sessionCipher.encrypt(transportDetails.getPaddedMessageBody(unpaddedMessage));
|
||||
}
|
||||
|
||||
public byte[] decrypt(WhisperMessage message)
|
||||
throws DuplicateMessageException, LegacyMessageException, InvalidMessageException, NoSessionException
|
||||
public TextSecureMessage decrypt(TextSecureEnvelope envelope)
|
||||
throws InvalidVersionException, InvalidMessageException, InvalidKeyException,
|
||||
DuplicateMessageException, InvalidKeyIdException, UntrustedIdentityException,
|
||||
LegacyMessageException, NoSessionException
|
||||
{
|
||||
byte[] paddedMessage = sessionCipher.decrypt(message);
|
||||
return transportDetails.getStrippedPaddingMessageBody(paddedMessage);
|
||||
}
|
||||
try {
|
||||
byte[] paddedMessage;
|
||||
|
||||
public byte[] decrypt(PreKeyWhisperMessage message)
|
||||
throws InvalidKeyException, LegacyMessageException, InvalidMessageException,
|
||||
DuplicateMessageException, InvalidKeyIdException, UntrustedIdentityException, NoSessionException
|
||||
{
|
||||
byte[] paddedMessage = sessionCipher.decrypt(message);
|
||||
return transportDetails.getStrippedPaddingMessageBody(paddedMessage);
|
||||
if (envelope.isPreKeyWhisperMessage()) {
|
||||
paddedMessage = sessionCipher.decrypt(new PreKeyWhisperMessage(envelope.getMessage()));
|
||||
} else if (envelope.isWhisperMessage()) {
|
||||
paddedMessage = sessionCipher.decrypt(new WhisperMessage(envelope.getMessage()));
|
||||
} else if (envelope.isPlaintext()) {
|
||||
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() {
|
||||
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());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -6,15 +6,15 @@ import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
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.Util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public class PushDatabase extends Database {
|
||||
|
||||
private static final String TAG = PushDatabase.class.getSimpleName();
|
||||
|
||||
private static final String TABLE_NAME = "push";
|
||||
public static final String ID = "_id";
|
||||
public static final String TYPE = "type";
|
||||
@ -30,18 +30,18 @@ public class PushDatabase extends Database {
|
||||
super(context, databaseHelper);
|
||||
}
|
||||
|
||||
public long insert(IncomingPushMessage message) {
|
||||
public long insert(TextSecureEnvelope envelope) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(TYPE, message.getType());
|
||||
values.put(SOURCE, message.getSource());
|
||||
values.put(DEVICE_ID, message.getSourceDevice());
|
||||
values.put(BODY, Base64.encodeBytes(message.getBody()));
|
||||
values.put(TIMESTAMP, message.getTimestampMillis());
|
||||
values.put(TYPE, envelope.getType());
|
||||
values.put(SOURCE, envelope.getSource());
|
||||
values.put(DEVICE_ID, envelope.getSourceDevice());
|
||||
values.put(BODY, Base64.encodeBytes(envelope.getMessage()));
|
||||
values.put(TIMESTAMP, envelope.getTimestamp());
|
||||
|
||||
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;
|
||||
|
||||
try {
|
||||
@ -50,14 +50,15 @@ public class PushDatabase extends Database {
|
||||
null, null, null);
|
||||
|
||||
if (cursor != null && cursor.moveToNext()) {
|
||||
return new IncomingPushMessage(cursor.getInt(cursor.getColumnIndexOrThrow(TYPE)),
|
||||
cursor.getString(cursor.getColumnIndexOrThrow(SOURCE)),
|
||||
cursor.getInt(cursor.getColumnIndexOrThrow(DEVICE_ID)),
|
||||
Base64.decode(cursor.getString(cursor.getColumnIndexOrThrow(BODY))),
|
||||
cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP)));
|
||||
return new TextSecureEnvelope(cursor.getInt(cursor.getColumnIndexOrThrow(TYPE)),
|
||||
cursor.getString(cursor.getColumnIndexOrThrow(SOURCE)),
|
||||
cursor.getInt(cursor.getColumnIndexOrThrow(DEVICE_ID)),
|
||||
"",
|
||||
cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP)),
|
||||
Base64.decode(cursor.getString(cursor.getColumnIndexOrThrow(BODY))));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.w("PushDatabase", e);
|
||||
Log.w(TAG, e);
|
||||
throw new NoSuchMessageException(e);
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
@ -86,7 +87,7 @@ public class PushDatabase extends Database {
|
||||
this.cursor = cursor;
|
||||
}
|
||||
|
||||
public IncomingPushMessage getNext() {
|
||||
public TextSecureEnvelope getNext() {
|
||||
try {
|
||||
if (cursor == null || !cursor.moveToNext())
|
||||
return null;
|
||||
@ -97,16 +98,12 @@ public class PushDatabase extends Database {
|
||||
byte[] body = Base64.decode(cursor.getString(cursor.getColumnIndexOrThrow(BODY)));
|
||||
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) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public long getCurrentId() {
|
||||
return cursor.getLong(cursor.getColumnIndexOrThrow(ID));
|
||||
}
|
||||
|
||||
public void close() {
|
||||
this.cursor.close();
|
||||
}
|
||||
|
@ -18,9 +18,9 @@ import org.thoughtcrime.securesms.sms.IncomingGroupMessage;
|
||||
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
|
||||
import org.whispersystems.libaxolotl.util.guava.Optional;
|
||||
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.TextSecureMessage;
|
||||
import org.whispersystems.textsecure.push.IncomingPushMessage;
|
||||
import org.whispersystems.textsecure.util.Base64;
|
||||
|
||||
import java.util.HashSet;
|
||||
@ -38,7 +38,7 @@ public class GroupMessageProcessor {
|
||||
|
||||
public static void process(Context context,
|
||||
MasterSecret masterSecret,
|
||||
IncomingPushMessage push,
|
||||
TextSecureEnvelope envelope,
|
||||
TextSecureMessage message)
|
||||
{
|
||||
if (!message.getGroupInfo().isPresent() || message.getGroupInfo().get().getGroupId() == null) {
|
||||
@ -57,11 +57,11 @@ public class GroupMessageProcessor {
|
||||
GroupRecord record = database.getGroup(id);
|
||||
|
||||
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) {
|
||||
handleGroupCreate(context, masterSecret, push, group);
|
||||
handleGroupCreate(context, masterSecret, envelope, group);
|
||||
} else if (record != null && group.getType() == TextSecureGroup.Type.QUIT) {
|
||||
handleGroupLeave(context, masterSecret, push, group, record);
|
||||
handleGroupLeave(context, masterSecret, envelope, group, record);
|
||||
} else {
|
||||
Log.w(TAG, "Received unknown type, ignoring...");
|
||||
}
|
||||
@ -69,7 +69,7 @@ public class GroupMessageProcessor {
|
||||
|
||||
private static void handleGroupCreate(Context context,
|
||||
MasterSecret masterSecret,
|
||||
IncomingPushMessage message,
|
||||
TextSecureEnvelope envelope,
|
||||
TextSecureGroup group)
|
||||
{
|
||||
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
|
||||
@ -81,14 +81,14 @@ public class GroupMessageProcessor {
|
||||
|
||||
database.create(id, group.getName().orNull(), group.getMembers().orNull(),
|
||||
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,
|
||||
MasterSecret masterSecret,
|
||||
IncomingPushMessage push,
|
||||
TextSecureEnvelope envelope,
|
||||
TextSecureGroup group,
|
||||
GroupRecord groupRecord)
|
||||
{
|
||||
@ -133,12 +133,12 @@ public class GroupMessageProcessor {
|
||||
|
||||
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,
|
||||
MasterSecret masterSecret,
|
||||
IncomingPushMessage message,
|
||||
TextSecureEnvelope envelope,
|
||||
TextSecureGroup group,
|
||||
GroupRecord record)
|
||||
{
|
||||
@ -149,16 +149,16 @@ public class GroupMessageProcessor {
|
||||
GroupContext.Builder builder = createGroupContext(group);
|
||||
builder.setType(GroupContext.Type.QUIT);
|
||||
|
||||
if (members.contains(message.getSource())) {
|
||||
database.remove(id, message.getSource());
|
||||
if (members.contains(envelope.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,
|
||||
IncomingPushMessage push, TextSecureGroup group,
|
||||
TextSecureEnvelope envelope, TextSecureGroup group,
|
||||
GroupContext storage)
|
||||
{
|
||||
if (group.getAvatar().isPresent()) {
|
||||
@ -168,7 +168,7 @@ public class GroupMessageProcessor {
|
||||
|
||||
EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context);
|
||||
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);
|
||||
|
||||
Pair<Long, Long> messageAndThreadId = smsDatabase.insertMessageInbox(masterSecret, groupMessage);
|
||||
|
@ -5,8 +5,9 @@ import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.crypto.SecurityEvent;
|
||||
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.database.DatabaseFactory;
|
||||
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.mms.IncomingMediaMessage;
|
||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||
import org.thoughtcrime.securesms.push.TextSecureMessageReceiverFactory;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||
import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage;
|
||||
@ -34,12 +35,13 @@ import org.whispersystems.libaxolotl.InvalidVersionException;
|
||||
import org.whispersystems.libaxolotl.LegacyMessageException;
|
||||
import org.whispersystems.libaxolotl.NoSessionException;
|
||||
import org.whispersystems.libaxolotl.UntrustedIdentityException;
|
||||
import org.whispersystems.libaxolotl.state.AxolotlStore;
|
||||
import org.whispersystems.libaxolotl.state.SessionStore;
|
||||
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.TextSecureMessage;
|
||||
import org.whispersystems.textsecure.push.IncomingPushMessage;
|
||||
import org.whispersystems.textsecure.crypto.TextSecureCipher;
|
||||
import org.whispersystems.textsecure.util.Base64;
|
||||
|
||||
import ws.com.google.android.mms.MmsException;
|
||||
@ -68,11 +70,11 @@ public class PushDecryptJob extends MasterSecretJob {
|
||||
@Override
|
||||
public void onRun() throws RequirementNotMetException {
|
||||
try {
|
||||
MasterSecret masterSecret = getMasterSecret();
|
||||
PushDatabase database = DatabaseFactory.getPushDatabase(context);
|
||||
IncomingPushMessage push = database.get(messageId);
|
||||
MasterSecret masterSecret = getMasterSecret();
|
||||
PushDatabase database = DatabaseFactory.getPushDatabase(context);
|
||||
TextSecureEnvelope envelope = database.get(messageId);
|
||||
|
||||
handleMessage(masterSecret, push);
|
||||
handleMessage(masterSecret, envelope);
|
||||
database.delete(messageId);
|
||||
|
||||
} catch (PushDatabase.NoSuchMessageException e) {
|
||||
@ -91,42 +93,46 @@ public class PushDecryptJob extends MasterSecretJob {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void handleMessage(MasterSecret masterSecret, IncomingPushMessage push) {
|
||||
private void handleMessage(MasterSecret masterSecret, TextSecureEnvelope envelope) {
|
||||
try {
|
||||
Recipients recipients = RecipientFactory.getRecipientsFromMessage(context, push, false);
|
||||
long recipientId = recipients.getPrimaryRecipient().getRecipientId();
|
||||
TextSecureMessageReceiver messageReceiver = TextSecureMessageReceiverFactory.create(context, masterSecret);
|
||||
Recipients recipients = RecipientFactory.getRecipientsFromString(context, envelope.getSource(), false);
|
||||
long recipientId = recipients.getPrimaryRecipient().getRecipientId();
|
||||
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);
|
||||
else if (message.isGroupUpdate()) handleGroupMessage(masterSecret, push, message);
|
||||
else if (message.getAttachments().isPresent()) handleMediaMessage(masterSecret, push, message);
|
||||
else handleTextMessage(masterSecret, push, message);
|
||||
if (message.isEndSession()) handleEndSessionMessage(masterSecret, recipientId, envelope, message);
|
||||
else if (message.isGroupUpdate()) handleGroupMessage(masterSecret, envelope, message);
|
||||
else if (message.getAttachments().isPresent()) handleMediaMessage(masterSecret, envelope, message);
|
||||
else handleTextMessage(masterSecret, envelope, message);
|
||||
} catch (InvalidVersionException e) {
|
||||
Log.w(TAG, e);
|
||||
handleInvalidVersionMessage(masterSecret, push);
|
||||
} catch (InvalidMessageException | InvalidKeyIdException | InvalidKeyException | MmsException e) {
|
||||
handleInvalidVersionMessage(masterSecret, envelope);
|
||||
} catch (InvalidMessageException | InvalidKeyIdException | InvalidKeyException | MmsException | RecipientFormattingException e) {
|
||||
Log.w(TAG, e);
|
||||
handleCorruptMessage(masterSecret, push);
|
||||
handleCorruptMessage(masterSecret, envelope);
|
||||
} catch (NoSessionException e) {
|
||||
Log.w(TAG, e);
|
||||
handleNoSessionMessage(masterSecret, push);
|
||||
handleNoSessionMessage(masterSecret, envelope);
|
||||
} catch (LegacyMessageException e) {
|
||||
Log.w(TAG, e);
|
||||
handleLegacyMessage(masterSecret, push);
|
||||
handleLegacyMessage(masterSecret, envelope);
|
||||
} catch (DuplicateMessageException e) {
|
||||
Log.w(TAG, e);
|
||||
handleDuplicateMessage(masterSecret, push);
|
||||
handleDuplicateMessage(masterSecret, envelope);
|
||||
} catch (UntrustedIdentityException e) {
|
||||
Log.w(TAG, e);
|
||||
handleUntrustedIdentityMessage(masterSecret, push);
|
||||
handleUntrustedIdentityMessage(masterSecret, envelope);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleEndSessionMessage(MasterSecret masterSecret, long recipientId, IncomingPushMessage push, TextSecureMessage message) {
|
||||
IncomingTextMessage incomingTextMessage = new IncomingTextMessage(push.getSource(),
|
||||
push.getSourceDevice(),
|
||||
private void handleEndSessionMessage(MasterSecret masterSecret, long recipientId,
|
||||
TextSecureEnvelope envelope, TextSecureMessage message)
|
||||
{
|
||||
IncomingTextMessage incomingTextMessage = new IncomingTextMessage(envelope.getSource(),
|
||||
envelope.getSourceDevice(),
|
||||
message.getTimestamp(),
|
||||
"", Optional.<TextSecureGroup>absent());
|
||||
|
||||
@ -141,18 +147,18 @@ public class PushDecryptJob extends MasterSecretJob {
|
||||
MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second);
|
||||
}
|
||||
|
||||
private void handleGroupMessage(MasterSecret masterSecret, IncomingPushMessage push, TextSecureMessage message) {
|
||||
GroupMessageProcessor.process(context, masterSecret, push, message);
|
||||
private void handleGroupMessage(MasterSecret masterSecret, TextSecureEnvelope envelope, TextSecureMessage 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
|
||||
{
|
||||
String localNumber = TextSecurePreferences.getLocalNumber(context);
|
||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(masterSecret, signal.getSource(),
|
||||
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(masterSecret, envelope.getSource(),
|
||||
localNumber, message.getTimestamp(),
|
||||
Optional.fromNullable(signal.getRelay()),
|
||||
Optional.fromNullable(envelope.getRelay()),
|
||||
message.getBody(),
|
||||
message.getGroupInfo(),
|
||||
message.getAttachments());
|
||||
@ -172,11 +178,11 @@ public class PushDecryptJob extends MasterSecretJob {
|
||||
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);
|
||||
String body = message.getBody().isPresent() ? message.getBody().get() : "";
|
||||
IncomingTextMessage textMessage = new IncomingTextMessage(signal.getSource(),
|
||||
signal.getSourceDevice(),
|
||||
IncomingTextMessage textMessage = new IncomingTextMessage(envelope.getSource(),
|
||||
envelope.getSourceDevice(),
|
||||
message.getTimestamp(), body,
|
||||
message.getGroupInfo());
|
||||
|
||||
@ -185,50 +191,48 @@ public class PushDecryptJob extends MasterSecretJob {
|
||||
}
|
||||
|
||||
Pair<Long, Long> messageAndThreadId = database.insertMessageInbox(masterSecret, textMessage);
|
||||
// database.updateMessageBody(masterSecret, messageAndThreadId.first, body);
|
||||
|
||||
MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second);
|
||||
}
|
||||
|
||||
private void handleInvalidVersionMessage(MasterSecret masterSecret, IncomingPushMessage push) {
|
||||
Pair<Long, Long> messageAndThreadId = insertPlaceholder(masterSecret, push);
|
||||
private void handleInvalidVersionMessage(MasterSecret masterSecret, TextSecureEnvelope envelope) {
|
||||
Pair<Long, Long> messageAndThreadId = insertPlaceholder(masterSecret, envelope);
|
||||
DatabaseFactory.getEncryptingSmsDatabase(context).markAsInvalidVersionKeyExchange(messageAndThreadId.first);
|
||||
|
||||
MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second);
|
||||
}
|
||||
|
||||
private void handleCorruptMessage(MasterSecret masterSecret, IncomingPushMessage push) {
|
||||
Pair<Long, Long> messageAndThreadId = insertPlaceholder(masterSecret, push);
|
||||
private void handleCorruptMessage(MasterSecret masterSecret, TextSecureEnvelope envelope) {
|
||||
Pair<Long, Long> messageAndThreadId = insertPlaceholder(masterSecret, envelope);
|
||||
DatabaseFactory.getEncryptingSmsDatabase(context).markAsDecryptFailed(messageAndThreadId.first);
|
||||
|
||||
MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second);
|
||||
}
|
||||
|
||||
private void handleNoSessionMessage(MasterSecret masterSecret, IncomingPushMessage push) {
|
||||
Pair<Long, Long> messageAndThreadId = insertPlaceholder(masterSecret, push);
|
||||
private void handleNoSessionMessage(MasterSecret masterSecret, TextSecureEnvelope envelope) {
|
||||
Pair<Long, Long> messageAndThreadId = insertPlaceholder(masterSecret, envelope);
|
||||
DatabaseFactory.getEncryptingSmsDatabase(context).markAsNoSession(messageAndThreadId.first);
|
||||
|
||||
MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second);
|
||||
}
|
||||
|
||||
private void handleLegacyMessage(MasterSecret masterSecret, IncomingPushMessage push) {
|
||||
Pair<Long, Long> messageAndThreadId = insertPlaceholder(masterSecret, push);
|
||||
private void handleLegacyMessage(MasterSecret masterSecret, TextSecureEnvelope envelope) {
|
||||
Pair<Long, Long> messageAndThreadId = insertPlaceholder(masterSecret, envelope);
|
||||
DatabaseFactory.getEncryptingSmsDatabase(context).markAsLegacyVersion(messageAndThreadId.first);
|
||||
|
||||
MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second);
|
||||
}
|
||||
|
||||
private void handleDuplicateMessage(MasterSecret masterSecret, IncomingPushMessage push) {
|
||||
Pair<Long, Long> messageAndThreadId = insertPlaceholder(masterSecret, push);
|
||||
private void handleDuplicateMessage(MasterSecret masterSecret, TextSecureEnvelope envelope) {
|
||||
Pair<Long, Long> messageAndThreadId = insertPlaceholder(masterSecret, envelope);
|
||||
DatabaseFactory.getEncryptingSmsDatabase(context).markAsDecryptDuplicate(messageAndThreadId.first);
|
||||
|
||||
MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second);
|
||||
}
|
||||
|
||||
private void handleUntrustedIdentityMessage(MasterSecret masterSecret, IncomingPushMessage push) {
|
||||
String encoded = Base64.encodeBytes(push.getBody());
|
||||
IncomingTextMessage textMessage = new IncomingTextMessage(push.getSource(), push.getSourceDevice(),
|
||||
push.getTimestampMillis(), encoded,
|
||||
private void handleUntrustedIdentityMessage(MasterSecret masterSecret, TextSecureEnvelope envelope) {
|
||||
String encoded = Base64.encodeBytes(envelope.getMessage());
|
||||
IncomingTextMessage textMessage = new IncomingTextMessage(envelope.getSource(), envelope.getSourceDevice(),
|
||||
envelope.getTimestamp(), encoded,
|
||||
Optional.<TextSecureGroup>absent());
|
||||
|
||||
IncomingPreKeyBundleMessage bundleMessage = new IncomingPreKeyBundleMessage(textMessage, encoded);
|
||||
@ -238,11 +242,11 @@ public class PushDecryptJob extends MasterSecretJob {
|
||||
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);
|
||||
|
||||
IncomingTextMessage textMessage = new IncomingTextMessage(push.getSource(), push.getSourceDevice(),
|
||||
push.getTimestampMillis(), "",
|
||||
IncomingTextMessage textMessage = new IncomingTextMessage(envelope.getSource(), envelope.getSourceDevice(),
|
||||
envelope.getTimestamp(), "",
|
||||
Optional.<TextSecureGroup>absent());
|
||||
|
||||
textMessage = new IncomingEncryptedMessage(textMessage, "");
|
||||
|
@ -9,11 +9,10 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.jobqueue.JobManager;
|
||||
import org.whispersystems.jobqueue.JobParameters;
|
||||
import org.whispersystems.libaxolotl.InvalidVersionException;
|
||||
import org.whispersystems.textsecure.api.messages.TextSecureEnvelope;
|
||||
import org.whispersystems.textsecure.directory.Directory;
|
||||
import org.whispersystems.textsecure.directory.NotInDirectoryException;
|
||||
import org.whispersystems.textsecure.push.ContactTokenDetails;
|
||||
import org.whispersystems.textsecure.push.IncomingEncryptedPushMessage;
|
||||
import org.whispersystems.textsecure.push.IncomingPushMessage;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@ -37,20 +36,19 @@ public class PushReceiveJob extends ContextJob {
|
||||
@Override
|
||||
public void onRun() {
|
||||
try {
|
||||
String sessionKey = TextSecurePreferences.getSignalingKey(context);
|
||||
IncomingEncryptedPushMessage encrypted = new IncomingEncryptedPushMessage(data, sessionKey);
|
||||
IncomingPushMessage message = encrypted.getIncomingPushMessage();
|
||||
String sessionKey = TextSecurePreferences.getSignalingKey(context);
|
||||
TextSecureEnvelope envelope = new TextSecureEnvelope(data, sessionKey);
|
||||
|
||||
if (!isActiveNumber(context, message.getSource())) {
|
||||
if (!isActiveNumber(context, envelope.getSource())) {
|
||||
Directory directory = Directory.getInstance(context);
|
||||
ContactTokenDetails contactTokenDetails = new ContactTokenDetails();
|
||||
contactTokenDetails.setNumber(message.getSource());
|
||||
contactTokenDetails.setNumber(envelope.getSource());
|
||||
|
||||
directory.setNumber(contactTokenDetails, true);
|
||||
}
|
||||
|
||||
if (message.isReceipt()) handleReceipt(message);
|
||||
else handleMessage(message);
|
||||
if (envelope.isReceipt()) handleReceipt(envelope);
|
||||
else handleMessage(envelope);
|
||||
} catch (IOException | InvalidVersionException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
@ -66,21 +64,21 @@ public class PushReceiveJob extends ContextJob {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void handleMessage(IncomingPushMessage message) {
|
||||
private void handleMessage(TextSecureEnvelope envelope) {
|
||||
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(),
|
||||
message.getTimestampMillis(),
|
||||
message.getRelay()));
|
||||
jobManager.add(new DeliveryReceiptJob(context, envelope.getSource(),
|
||||
envelope.getTimestamp(),
|
||||
envelope.getRelay()));
|
||||
|
||||
jobManager.add(new PushDecryptJob(context, messageId));
|
||||
}
|
||||
|
||||
private void handleReceipt(IncomingPushMessage message) {
|
||||
Log.w(TAG, String.format("Received receipt: (XXXXX, %d)", message.getTimestampMillis()));
|
||||
DatabaseFactory.getMmsSmsDatabase(context).incrementDeliveryReceiptCount(message.getSource(),
|
||||
message.getTimestampMillis());
|
||||
private void handleReceipt(TextSecureEnvelope envelope) {
|
||||
Log.w(TAG, String.format("Received receipt: (XXXXX, %d)", envelope.getTimestamp()));
|
||||
DatabaseFactory.getMmsSmsDatabase(context).incrementDeliveryReceiptCount(envelope.getSource(),
|
||||
envelope.getTimestamp());
|
||||
}
|
||||
|
||||
private boolean isActiveNumber(Context context, String e164number) {
|
||||
|
@ -1,14 +1,12 @@
|
||||
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.MasterSecret;
|
||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.libaxolotl.util.guava.Optional;
|
||||
import org.whispersystems.textsecure.api.messages.TextSecureAttachment;
|
||||
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 java.util.List;
|
||||
|
@ -39,17 +39,17 @@ import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
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.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.PushDatabase;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
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.util.TextSecurePreferences;
|
||||
import org.whispersystems.textsecure.push.IncomingPushMessage;
|
||||
import org.whispersystems.textsecure.api.messages.TextSecureEnvelope;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
@ -285,24 +285,24 @@ public class MessageNotifier {
|
||||
if (masterSecret != null) return;
|
||||
|
||||
PushDatabase.Reader reader = null;
|
||||
IncomingPushMessage message;
|
||||
TextSecureEnvelope envelope;
|
||||
|
||||
try {
|
||||
reader = DatabaseFactory.getPushDatabase(context).readerFor(cursor);
|
||||
|
||||
while ((message = reader.getNext()) != null) {
|
||||
Recipient recipient;
|
||||
while ((envelope = reader.getNext()) != null) {
|
||||
Recipients recipients;
|
||||
|
||||
try {
|
||||
recipient = RecipientFactory.getRecipientsFromString(context, message.getSource(), false).getPrimaryRecipient();
|
||||
recipients = RecipientFactory.getRecipientsFromString(context, envelope.getSource(), false);
|
||||
} catch (RecipientFormattingException e) {
|
||||
Log.w("MessageNotifier", e);
|
||||
recipient = Recipient.getUnknownRecipient(context);
|
||||
recipients = new Recipients(Recipient.getUnknownRecipient(context));
|
||||
}
|
||||
|
||||
Recipients recipients = RecipientFactory.getRecipientsFromMessage(context, message, false);
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
|
||||
SpannableString body = new SpannableString(context.getString(R.string.MessageNotifier_encrypted_message));
|
||||
Recipient recipient = recipients.getPrimaryRecipient();
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
|
||||
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);
|
||||
|
||||
notificationState.addNotification(new NotificationItem(recipient, recipients, null, threadId, body, null));
|
||||
|
@ -21,7 +21,6 @@ import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.contacts.ContactPhotoFactory;
|
||||
import org.thoughtcrime.securesms.database.CanonicalAddressDatabase;
|
||||
import org.whispersystems.textsecure.push.IncomingPushMessage;
|
||||
import org.whispersystems.textsecure.util.Util;
|
||||
|
||||
import java.util.LinkedList;
|
||||
@ -73,18 +72,6 @@ public class RecipientFactory {
|
||||
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) {
|
||||
try {
|
||||
return provider.getRecipient(context, Long.parseLong(recipientId), asynchronous);
|
||||
|
@ -76,7 +76,7 @@ public class MmsSender {
|
||||
try {
|
||||
Log.w("MmsSender", "Passing to MMS transport: " + 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.isPush()) database.markAsPush(message.getDatabaseMessageId());
|
||||
|
@ -7,13 +7,10 @@ import android.telephony.SmsMessage;
|
||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
import org.whispersystems.libaxolotl.util.guava.Optional;
|
||||
import org.whispersystems.textsecure.api.messages.TextSecureGroup;
|
||||
import org.whispersystems.textsecure.push.IncomingPushMessage;
|
||||
import org.whispersystems.textsecure.storage.RecipientDevice;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.GroupContext;
|
||||
|
||||
public class IncomingTextMessage implements Parcelable {
|
||||
|
||||
public static final Parcelable.Creator<IncomingTextMessage> CREATOR = new Parcelable.Creator<IncomingTextMessage>() {
|
||||
|
@ -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
|
||||
{
|
||||
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
|
||||
{
|
||||
TextSecureMessageSender messageSender = TextSecureMessageSenderFactory.create(context, masterSecret);
|
||||
@ -131,7 +131,7 @@ public class PushTransport extends BaseTransport {
|
||||
List<UnregisteredUserException> unregisteredUsers = new LinkedList<>();
|
||||
|
||||
if (GroupUtil.isEncodedGroup(destination)) {
|
||||
deliverGroupMessage(message, threadId);
|
||||
deliverGroupMessage(message);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -182,6 +182,7 @@ public class PushTransport extends BaseTransport {
|
||||
ContentType.isVideoType(contentType))
|
||||
{
|
||||
byte[] data = message.getBody().getPart(i).getData();
|
||||
Log.w(TAG, "Adding attachment...");
|
||||
attachments.add(new TextSecureAttachmentStream(new ByteArrayInputStream(data), contentType, data.length));
|
||||
}
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ public class UniversalTransport {
|
||||
}
|
||||
}
|
||||
|
||||
public MmsSendResult deliver(SendReq mediaMessage, long threadId)
|
||||
public MmsSendResult deliver(SendReq mediaMessage)
|
||||
throws UndeliverableMessageException, RetryLaterException, UntrustedIdentityException,
|
||||
SecureFallbackApprovalException, InsecureFallbackApprovalException
|
||||
{
|
||||
@ -124,7 +124,7 @@ public class UniversalTransport {
|
||||
}
|
||||
|
||||
if (GroupUtil.isEncodedGroup(mediaMessage.getTo()[0].getString())) {
|
||||
return deliverGroupMessage(mediaMessage, threadId);
|
||||
return deliverGroupMessage(mediaMessage);
|
||||
}
|
||||
|
||||
if (!TextSecurePreferences.isPushRegistered(context)) {
|
||||
@ -143,7 +143,7 @@ public class UniversalTransport {
|
||||
|
||||
try {
|
||||
Log.w(TAG, "Using GCM as transport...");
|
||||
pushTransport.deliver(mediaMessage, threadId);
|
||||
pushTransport.deliver(mediaMessage);
|
||||
return new MmsSendResult("push".getBytes("UTF-8"), 0, true, true);
|
||||
} catch (IOException 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
|
||||
{
|
||||
if (!TextSecurePreferences.isPushRegistered(context)) {
|
||||
@ -224,7 +224,7 @@ public class UniversalTransport {
|
||||
}
|
||||
|
||||
try {
|
||||
pushTransport.deliver(mediaMessage, threadId);
|
||||
pushTransport.deliver(mediaMessage);
|
||||
return new MmsSendResult("push".getBytes("UTF-8"), 0, true, true);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
|
Loading…
Reference in New Issue
Block a user