Support for sealed sender - Part 1

This commit is contained in:
Moxie Marlinspike
2018-05-22 02:13:10 -07:00
committed by Greyson Parrelli
parent b7b9554364
commit 5f31762220
67 changed files with 1679 additions and 801 deletions

View File

@@ -193,7 +193,7 @@ public class AttachmentDownloadJob extends MasterSecretJob implements Injectable
Log.i(TAG, "Downloading attachment with no digest...");
}
return new SignalServiceAttachmentPointer(id, null, key, relay,
return new SignalServiceAttachmentPointer(id, null, key,
Optional.of(Util.toIntExact(attachment.getSize())),
Optional.absent(),
0, 0,

View File

@@ -98,7 +98,7 @@ public class AvatarDownloadJob extends MasterSecretJob implements InjectableType
attachment = File.createTempFile("avatar", "tmp", context.getCacheDir());
attachment.deleteOnExit();
SignalServiceAttachmentPointer pointer = new SignalServiceAttachmentPointer(avatarId, contentType, key, relay, Optional.of(0), Optional.absent(), 0, 0, digest, fileName, false);
SignalServiceAttachmentPointer pointer = new SignalServiceAttachmentPointer(avatarId, contentType, key, Optional.of(0), Optional.absent(), 0, 0, digest, fileName, false);
InputStream inputStream = receiver.retrieveAttachment(pointer, attachment, MAX_AVATAR_SIZE);
Bitmap avatar = BitmapUtil.createScaledBitmap(context, new AttachmentModel(attachment, key, 0, digest), 500, 500);

View File

@@ -252,7 +252,7 @@ public class MmsDownloadJob extends MasterSecretJob {
group = Optional.of(Address.fromSerialized(DatabaseFactory.getGroupDatabase(context).getOrCreateGroupForMembers(new LinkedList<>(members), true)));
}
IncomingMediaMessage message = new IncomingMediaMessage(from, group, body, retrieved.getDate() * 1000L, attachments, subscriptionId, 0, false);
IncomingMediaMessage message = new IncomingMediaMessage(from, group, body, retrieved.getDate() * 1000L, attachments, subscriptionId, 0, false, false);
Optional<InsertResult> insertResult = database.insertMessageInbox(message, contentLocation, threadId);
if (insertResult.isPresent()) {

View File

@@ -4,6 +4,7 @@ import android.content.Context;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientReader;
@@ -30,6 +31,7 @@ public class MultiDeviceBlockedUpdateJob extends MasterSecretJob implements Inje
private static final long serialVersionUID = 1L;
@SuppressWarnings("unused")
private static final String TAG = MultiDeviceBlockedUpdateJob.class.getSimpleName();
@Inject transient SignalServiceMessageSender messageSender;
@@ -75,7 +77,8 @@ public class MultiDeviceBlockedUpdateJob extends MasterSecretJob implements Inje
}
}
messageSender.sendMessage(SignalServiceSyncMessage.forBlocked(new BlockedListMessage(blockedIndividuals, blockedGroups)));
messageSender.sendMessage(SignalServiceSyncMessage.forBlocked(new BlockedListMessage(blockedIndividuals, blockedGroups)),
UnidentifiedAccessUtil.getAccessForSync(context));
}
}

View File

@@ -15,6 +15,7 @@ import org.thoughtcrime.securesms.contacts.ContactAccessor;
import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.IdentityDatabase;
@@ -238,7 +239,8 @@ public class MultiDeviceContactUpdateJob extends MasterSecretJob implements Inje
.build();
try {
messageSender.sendMessage(SignalServiceSyncMessage.forContacts(new ContactsMessage(attachmentStream, complete)));
messageSender.sendMessage(SignalServiceSyncMessage.forContacts(new ContactsMessage(attachmentStream, complete)),
UnidentifiedAccessUtil.getAccessForSync(context));
} catch (IOException ioe) {
throw new NetworkException(ioe);
}

View File

@@ -5,6 +5,7 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
@@ -131,7 +132,8 @@ public class MultiDeviceGroupUpdateJob extends MasterSecretJob implements Inject
.withLength(contactsFile.length())
.build();
messageSender.sendMessage(SignalServiceSyncMessage.forGroups(attachmentStream));
messageSender.sendMessage(SignalServiceSyncMessage.forGroups(attachmentStream),
UnidentifiedAccessUtil.getAccessForSync(context));
}

View File

@@ -9,6 +9,7 @@ import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
@@ -86,7 +87,7 @@ public class MultiDeviceProfileKeyUpdateJob extends MasterSecretJob implements I
SignalServiceSyncMessage syncMessage = SignalServiceSyncMessage.forContacts(new ContactsMessage(attachmentStream, false));
messageSender.sendMessage(syncMessage);
messageSender.sendMessage(syncMessage, UnidentifiedAccessUtil.getAccessForSync(context));
}
@Override

View File

@@ -4,6 +4,7 @@ package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.SafeData;
@@ -58,7 +59,8 @@ public class MultiDeviceReadReceiptUpdateJob extends ContextJob implements Injec
@Override
public void onRun() throws IOException, UntrustedIdentityException {
messageSender.sendMessage(SignalServiceSyncMessage.forConfiguration(new ConfigurationMessage(Optional.of(enabled))));
messageSender.sendMessage(SignalServiceSyncMessage.forConfiguration(new ConfigurationMessage(Optional.of(enabled))),
UnidentifiedAccessUtil.getAccessForSync(context));
}
@Override

View File

@@ -9,6 +9,7 @@ import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
@@ -100,7 +101,8 @@ public class MultiDeviceReadUpdateJob extends MasterSecretJob implements Injecta
readMessages.add(new ReadMessage(messageId.sender, messageId.timestamp));
}
messageSender.sendMessage(SignalServiceSyncMessage.forRead(readMessages));
messageSender.sendMessage(SignalServiceSyncMessage.forRead(readMessages),
UnidentifiedAccessUtil.getAccessForSync(context));
}
@Override

View File

@@ -7,11 +7,14 @@ import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.InvalidKeyException;
@@ -101,7 +104,8 @@ public class MultiDeviceVerifiedUpdateJob extends ContextJob implements Injectab
VerifiedMessage.VerifiedState verifiedState = getVerifiedState(verifiedStatus);
VerifiedMessage verifiedMessage = new VerifiedMessage(canonicalDestination.toPhoneString(), new IdentityKey(identityKey, 0), verifiedState, timestamp);
messageSender.sendMessage(SignalServiceSyncMessage.forVerified(verifiedMessage));
messageSender.sendMessage(SignalServiceSyncMessage.forVerified(verifiedMessage),
UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(destination), false)));
} catch (InvalidKeyException e) {
throw new IOException(e);
}

View File

@@ -1,5 +1,6 @@
package org.thoughtcrime.securesms.jobs;
import android.annotation.SuppressLint;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
@@ -7,11 +8,18 @@ import android.os.Build;
import android.support.annotation.NonNull;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import android.util.Pair;
import org.signal.libsignal.metadata.InvalidMetadataMessageException;
import org.signal.libsignal.metadata.InvalidMetadataVersionException;
import org.signal.libsignal.metadata.ProtocolDuplicateMessageException;
import org.signal.libsignal.metadata.ProtocolInvalidKeyException;
import org.signal.libsignal.metadata.ProtocolInvalidKeyIdException;
import org.signal.libsignal.metadata.ProtocolInvalidMessageException;
import org.signal.libsignal.metadata.ProtocolInvalidVersionException;
import org.signal.libsignal.metadata.ProtocolLegacyMessageException;
import org.signal.libsignal.metadata.ProtocolNoSessionException;
import org.signal.libsignal.metadata.ProtocolUntrustedIdentityException;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.ConversationListActivity;
import org.thoughtcrime.securesms.R;
@@ -22,6 +30,7 @@ import org.thoughtcrime.securesms.contactshare.Contact;
import org.thoughtcrime.securesms.contactshare.ContactModelMapper;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.crypto.SecurityEvent;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl;
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore;
import org.thoughtcrime.securesms.database.Address;
@@ -40,6 +49,8 @@ import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import org.thoughtcrime.securesms.groups.GroupMessageProcessor;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.OutgoingExpirationUpdateMessage;
@@ -53,25 +64,13 @@ import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.WebRtcCallService;
import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage;
import org.thoughtcrime.securesms.sms.IncomingEndSessionMessage;
import org.thoughtcrime.securesms.sms.IncomingPreKeyBundleMessage;
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
import org.thoughtcrime.securesms.sms.OutgoingEncryptedMessage;
import org.thoughtcrime.securesms.sms.OutgoingEndSessionMessage;
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.IdentityUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.DuplicateMessageException;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.InvalidKeyException;
import org.whispersystems.libsignal.InvalidKeyIdException;
import org.whispersystems.libsignal.InvalidMessageException;
import org.whispersystems.libsignal.InvalidVersionException;
import org.whispersystems.libsignal.LegacyMessageException;
import org.whispersystems.libsignal.NoSessionException;
import org.whispersystems.libsignal.UntrustedIdentityException;
import org.whispersystems.libsignal.protocol.PreKeySignalMessage;
import org.whispersystems.libsignal.state.SessionStore;
import org.whispersystems.libsignal.state.SignalProtocolStore;
import org.whispersystems.libsignal.util.guava.Optional;
@@ -212,11 +211,11 @@ public class PushDecryptJob extends ContextJob {
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
SignalProtocolStore axolotlStore = new SignalProtocolStoreImpl(context);
SignalServiceAddress localAddress = new SignalServiceAddress(TextSecurePreferences.getLocalNumber(context));
SignalServiceCipher cipher = new SignalServiceCipher(localAddress, axolotlStore);
SignalServiceCipher cipher = new SignalServiceCipher(localAddress, axolotlStore, UnidentifiedAccessUtil.getCertificateValidator());
SignalServiceContent content = cipher.decrypt(envelope);
if (shouldIgnore(envelope, content)) {
if (shouldIgnore(content)) {
Log.i(TAG, "Ignoring message.");
return;
}
@@ -225,41 +224,45 @@ public class PushDecryptJob extends ContextJob {
SignalServiceDataMessage message = content.getDataMessage().get();
boolean isMediaMessage = message.getAttachments().isPresent() || message.getQuote().isPresent() || message.getSharedContacts().isPresent();
if (message.isEndSession()) handleEndSessionMessage(envelope, message, smsMessageId);
else if (message.isGroupUpdate()) handleGroupMessage(envelope, message, smsMessageId);
else if (message.isExpirationUpdate()) handleExpirationUpdate(envelope, message, smsMessageId);
else if (isMediaMessage) handleMediaMessage(envelope, message, smsMessageId);
else if (message.getBody().isPresent()) handleTextMessage(envelope, message, smsMessageId);
if (message.isEndSession()) handleEndSessionMessage(content, smsMessageId);
else if (message.isGroupUpdate()) handleGroupMessage(content, message, smsMessageId);
else if (message.isExpirationUpdate()) handleExpirationUpdate(content, message, smsMessageId);
else if (isMediaMessage) handleMediaMessage(content, message, smsMessageId);
else if (message.getBody().isPresent()) handleTextMessage(content, message, smsMessageId);
if (message.getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId(), false))) {
handleUnknownGroupMessage(envelope, message.getGroupInfo().get());
handleUnknownGroupMessage(content, message.getGroupInfo().get());
}
if (message.getProfileKey().isPresent() && message.getProfileKey().get().length == 32) {
handleProfileKey(envelope, message);
handleProfileKey(content, message);
}
if (content.isNeedsReceipt()) {
handleNeedsDeliveryReceipt(content, message);
}
} else if (content.getSyncMessage().isPresent()) {
SignalServiceSyncMessage syncMessage = content.getSyncMessage().get();
if (syncMessage.getSent().isPresent()) handleSynchronizeSentMessage(envelope, syncMessage.getSent().get());
if (syncMessage.getSent().isPresent()) handleSynchronizeSentMessage(content, syncMessage.getSent().get());
else if (syncMessage.getRequest().isPresent()) handleSynchronizeRequestMessage(syncMessage.getRequest().get());
else if (syncMessage.getRead().isPresent()) handleSynchronizeReadMessage(syncMessage.getRead().get(), envelope.getTimestamp());
else if (syncMessage.getRead().isPresent()) handleSynchronizeReadMessage(syncMessage.getRead().get(), content.getTimestamp());
else if (syncMessage.getVerified().isPresent()) handleSynchronizeVerifiedMessage(syncMessage.getVerified().get());
else Log.w(TAG, "Contains no known sync types...");
} else if (content.getCallMessage().isPresent()) {
Log.i(TAG, "Got call message...");
SignalServiceCallMessage message = content.getCallMessage().get();
if (message.getOfferMessage().isPresent()) handleCallOfferMessage(envelope, message.getOfferMessage().get(), smsMessageId);
else if (message.getAnswerMessage().isPresent()) handleCallAnswerMessage(envelope, message.getAnswerMessage().get());
else if (message.getIceUpdateMessages().isPresent()) handleCallIceUpdateMessage(envelope, message.getIceUpdateMessages().get());
else if (message.getHangupMessage().isPresent()) handleCallHangupMessage(envelope, message.getHangupMessage().get(), smsMessageId);
else if (message.getBusyMessage().isPresent()) handleCallBusyMessage(envelope, message.getBusyMessage().get());
if (message.getOfferMessage().isPresent()) handleCallOfferMessage(content, message.getOfferMessage().get(), smsMessageId);
else if (message.getAnswerMessage().isPresent()) handleCallAnswerMessage(content, message.getAnswerMessage().get());
else if (message.getIceUpdateMessages().isPresent()) handleCallIceUpdateMessage(content, message.getIceUpdateMessages().get());
else if (message.getHangupMessage().isPresent()) handleCallHangupMessage(content, message.getHangupMessage().get(), smsMessageId);
else if (message.getBusyMessage().isPresent()) handleCallBusyMessage(content, message.getBusyMessage().get());
} else if (content.getReceiptMessage().isPresent()) {
SignalServiceReceiptMessage message = content.getReceiptMessage().get();
if (message.isReadReceipt()) handleReadReceipt(envelope, message);
else if (message.isDeliveryReceipt()) handleDeliveryReceipt(envelope, message);
if (message.isReadReceipt()) handleReadReceipt(content, message);
else if (message.isDeliveryReceipt()) handleDeliveryReceipt(content, message);
} else {
Log.w(TAG, "Got unrecognized message...");
}
@@ -267,28 +270,30 @@ public class PushDecryptJob extends ContextJob {
if (envelope.isPreKeySignalMessage()) {
ApplicationContext.getInstance(context).getJobManager().add(new RefreshPreKeysJob(context));
}
} catch (InvalidVersionException e) {
} catch (ProtocolInvalidVersionException e) {
Log.w(TAG, e);
handleInvalidVersionMessage(envelope, smsMessageId);
} catch (InvalidMessageException | InvalidKeyIdException | InvalidKeyException | MmsException e) {
handleInvalidVersionMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
} catch (ProtocolInvalidMessageException | ProtocolInvalidKeyIdException | ProtocolInvalidKeyException | ProtocolUntrustedIdentityException e) {
Log.w(TAG, e);
handleCorruptMessage(envelope, smsMessageId);
} catch (NoSessionException e) {
handleCorruptMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
} catch (StorageFailedException e) {
Log.w(TAG, e);
handleNoSessionMessage(envelope, smsMessageId);
} catch (LegacyMessageException e) {
handleCorruptMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
} catch (ProtocolNoSessionException e) {
Log.w(TAG, e);
handleLegacyMessage(envelope, smsMessageId);
} catch (DuplicateMessageException e) {
handleNoSessionMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
} catch (ProtocolLegacyMessageException e) {
Log.w(TAG, e);
handleDuplicateMessage(envelope, smsMessageId);
} catch (UntrustedIdentityException e) {
handleLegacyMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
} catch (ProtocolDuplicateMessageException e) {
Log.w(TAG, e);
handleDuplicateMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
} catch (InvalidMetadataVersionException | InvalidMetadataMessageException e) {
Log.w(TAG, e);
handleUntrustedIdentityMessage(envelope, smsMessageId);
}
}
private void handleCallOfferMessage(@NonNull SignalServiceEnvelope envelope,
private void handleCallOfferMessage(@NonNull SignalServiceContent content,
@NonNull OfferMessage message,
@NonNull Optional<Long> smsMessageId)
{
@@ -301,29 +306,29 @@ public class PushDecryptJob extends ContextJob {
Intent intent = new Intent(context, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_INCOMING_CALL);
intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromExternal(context, envelope.getSource()));
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromExternal(context, content.getSender()));
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_DESCRIPTION, message.getDescription());
intent.putExtra(WebRtcCallService.EXTRA_TIMESTAMP, envelope.getTimestamp());
intent.putExtra(WebRtcCallService.EXTRA_TIMESTAMP, content.getTimestamp());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) context.startForegroundService(intent);
else context.startService(intent);
}
}
private void handleCallAnswerMessage(@NonNull SignalServiceEnvelope envelope,
private void handleCallAnswerMessage(@NonNull SignalServiceContent content,
@NonNull AnswerMessage message)
{
Log.i(TAG, "handleCallAnswerMessage...");
Intent intent = new Intent(context, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_RESPONSE_MESSAGE);
intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromExternal(context, envelope.getSource()));
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromExternal(context, content.getSender()));
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_DESCRIPTION, message.getDescription());
context.startService(intent);
}
private void handleCallIceUpdateMessage(@NonNull SignalServiceEnvelope envelope,
private void handleCallIceUpdateMessage(@NonNull SignalServiceContent content,
@NonNull List<IceUpdateMessage> messages)
{
Log.w(TAG, "handleCallIceUpdateMessage... " + messages.size());
@@ -331,7 +336,7 @@ public class PushDecryptJob extends ContextJob {
Intent intent = new Intent(context, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_ICE_MESSAGE);
intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromExternal(context, envelope.getSource()));
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromExternal(context, content.getSender()));
intent.putExtra(WebRtcCallService.EXTRA_ICE_SDP, message.getSdp());
intent.putExtra(WebRtcCallService.EXTRA_ICE_SDP_MID, message.getSdpMid());
intent.putExtra(WebRtcCallService.EXTRA_ICE_SDP_LINE_INDEX, message.getSdpMLineIndex());
@@ -340,7 +345,7 @@ public class PushDecryptJob extends ContextJob {
}
}
private void handleCallHangupMessage(@NonNull SignalServiceEnvelope envelope,
private void handleCallHangupMessage(@NonNull SignalServiceContent content,
@NonNull HangupMessage message,
@NonNull Optional<Long> smsMessageId)
{
@@ -351,32 +356,32 @@ public class PushDecryptJob extends ContextJob {
Intent intent = new Intent(context, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_REMOTE_HANGUP);
intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromExternal(context, envelope.getSource()));
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromExternal(context, content.getSender()));
context.startService(intent);
}
}
private void handleCallBusyMessage(@NonNull SignalServiceEnvelope envelope,
private void handleCallBusyMessage(@NonNull SignalServiceContent content,
@NonNull BusyMessage message)
{
Intent intent = new Intent(context, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_REMOTE_BUSY);
intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId());
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromExternal(context, envelope.getSource()));
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromExternal(context, content.getSender()));
context.startService(intent);
}
private void handleEndSessionMessage(@NonNull SignalServiceEnvelope envelope,
@NonNull SignalServiceDataMessage message,
@NonNull Optional<Long> smsMessageId)
private void handleEndSessionMessage(@NonNull SignalServiceContent content,
@NonNull Optional<Long> smsMessageId)
{
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
IncomingTextMessage incomingTextMessage = new IncomingTextMessage(Address.fromExternal(context, envelope.getSource()),
envelope.getSourceDevice(),
message.getTimestamp(),
"", Optional.absent(), 0);
IncomingTextMessage incomingTextMessage = new IncomingTextMessage(Address.fromExternal(context, content.getSender()),
content.getSenderDevice(),
content.getTimestamp(),
"", Optional.absent(), 0,
content.isNeedsReceipt());
Long threadId;
@@ -393,7 +398,7 @@ public class PushDecryptJob extends ContextJob {
if (threadId != null) {
SessionStore sessionStore = new TextSecureSessionStore(context);
sessionStore.deleteAllSessions(envelope.getSource());
sessionStore.deleteAllSessions(content.getSender());
SecurityEvent.broadcastSecurityUpdateEvent(context);
MessageNotifier.updateNotification(context, threadId);
@@ -424,15 +429,15 @@ public class PushDecryptJob extends ContextJob {
return threadId;
}
private void handleGroupMessage(@NonNull SignalServiceEnvelope envelope,
private void handleGroupMessage(@NonNull SignalServiceContent content,
@NonNull SignalServiceDataMessage message,
@NonNull Optional<Long> smsMessageId)
throws MmsException
throws StorageFailedException
{
GroupMessageProcessor.process(context, envelope, message, false);
GroupMessageProcessor.process(context, content, message, false);
if (message.getExpiresInSeconds() != 0 && message.getExpiresInSeconds() != getMessageDestination(envelope, message).getExpireMessages()) {
handleExpirationUpdate(envelope, message, Optional.absent());
if (message.getExpiresInSeconds() != 0 && message.getExpiresInSeconds() != getMessageDestination(content, message).getExpireMessages()) {
handleExpirationUpdate(content, message, Optional.absent());
}
if (smsMessageId.isPresent()) {
@@ -440,36 +445,41 @@ public class PushDecryptJob extends ContextJob {
}
}
private void handleUnknownGroupMessage(@NonNull SignalServiceEnvelope envelope,
private void handleUnknownGroupMessage(@NonNull SignalServiceContent content,
@NonNull SignalServiceGroup group)
{
ApplicationContext.getInstance(context)
.getJobManager()
.add(new RequestGroupInfoJob(context, envelope.getSource(), group.getGroupId()));
.add(new RequestGroupInfoJob(context, content.getSender(), group.getGroupId()));
}
private void handleExpirationUpdate(@NonNull SignalServiceEnvelope envelope,
private void handleExpirationUpdate(@NonNull SignalServiceContent content,
@NonNull SignalServiceDataMessage message,
@NonNull Optional<Long> smsMessageId)
throws MmsException
throws StorageFailedException
{
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
Recipient recipient = getMessageDestination(envelope, message);
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(Address.fromExternal(context, envelope.getSource()),
message.getTimestamp(), -1,
message.getExpiresInSeconds() * 1000L, true,
Optional.fromNullable(envelope.getRelay()),
Optional.absent(), message.getGroupInfo(),
Optional.absent(), Optional.absent(), Optional.absent());
try {
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
Recipient recipient = getMessageDestination(content, message);
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(Address.fromExternal(context, content.getSender()),
message.getTimestamp(), -1,
message.getExpiresInSeconds() * 1000L, true,
content.isNeedsReceipt(),
Optional.absent(),
message.getGroupInfo(),
Optional.absent(),
Optional.absent(),
Optional.absent());
database.insertSecureDecryptedMessageInbox(mediaMessage, -1);
DatabaseFactory.getRecipientDatabase(context).setExpireMessages(recipient, message.getExpiresInSeconds());
database.insertSecureDecryptedMessageInbox(mediaMessage, -1);
DatabaseFactory.getRecipientDatabase(context).setExpireMessages(recipient, message.getExpiresInSeconds());
if (smsMessageId.isPresent()) {
DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get());
if (smsMessageId.isPresent()) {
DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get());
}
} catch (MmsException e) {
throw new StorageFailedException(e, content.getSender(), content.getSenderDevice());
}
}
@@ -477,48 +487,53 @@ public class PushDecryptJob extends ContextJob {
IdentityUtil.processVerifiedMessage(context, verifiedMessage);
}
private void handleSynchronizeSentMessage(@NonNull SignalServiceEnvelope envelope,
private void handleSynchronizeSentMessage(@NonNull SignalServiceContent content,
@NonNull SentTranscriptMessage message)
throws MmsException
throws StorageFailedException
{
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
try {
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
Long threadId;
Long threadId;
if (message.getMessage().isEndSession()) {
threadId = handleSynchronizeSentEndSessionMessage(message);
} else if (message.getMessage().isGroupUpdate()) {
threadId = GroupMessageProcessor.process(context, envelope, message.getMessage(), true);
} else if (message.getMessage().isExpirationUpdate()) {
threadId = handleSynchronizeSentExpirationUpdate(message);
} else if (message.getMessage().getAttachments().isPresent() || message.getMessage().getQuote().isPresent()) {
threadId = handleSynchronizeSentMediaMessage(message);
} else {
threadId = handleSynchronizeSentTextMessage(message);
}
if (message.getMessage().getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get().getGroupId(), false))) {
handleUnknownGroupMessage(envelope, message.getMessage().getGroupInfo().get());
}
if (message.getMessage().getProfileKey().isPresent()) {
Recipient recipient = null;
if (message.getDestination().isPresent()) recipient = Recipient.from(context, Address.fromExternal(context, message.getDestination().get()), false);
else if (message.getMessage().getGroupInfo().isPresent()) recipient = Recipient.from(context, Address.fromSerialized(GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get().getGroupId(), false)), false);
if (recipient != null && !recipient.isSystemContact() && !recipient.isProfileSharing()) {
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true);
if (message.getMessage().isEndSession()) {
threadId = handleSynchronizeSentEndSessionMessage(message);
} else if (message.getMessage().isGroupUpdate()) {
threadId = GroupMessageProcessor.process(context, content, message.getMessage(), true);
} else if (message.getMessage().isExpirationUpdate()) {
threadId = handleSynchronizeSentExpirationUpdate(message);
} else if (message.getMessage().getAttachments().isPresent() || message.getMessage().getQuote().isPresent()) {
threadId = handleSynchronizeSentMediaMessage(message);
} else {
threadId = handleSynchronizeSentTextMessage(message);
}
}
if (threadId != null) {
DatabaseFactory.getThreadDatabase(getContext()).setRead(threadId, true);
MessageNotifier.updateNotification(getContext());
}
if (message.getMessage().getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get().getGroupId(), false))) {
handleUnknownGroupMessage(content, message.getMessage().getGroupInfo().get());
}
MessageNotifier.setLastDesktopActivityTimestamp(message.getTimestamp());
if (message.getMessage().getProfileKey().isPresent()) {
Recipient recipient = null;
if (message.getDestination().isPresent()) recipient = Recipient.from(context, Address.fromExternal(context, message.getDestination().get()), false);
else if (message.getMessage().getGroupInfo().isPresent()) recipient = Recipient.from(context, Address.fromSerialized(GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get().getGroupId(), false)), false);
if (recipient != null && !recipient.isSystemContact() && !recipient.isProfileSharing()) {
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true);
}
}
if (threadId != null) {
DatabaseFactory.getThreadDatabase(getContext()).setRead(threadId, true);
MessageNotifier.updateNotification(getContext());
}
MessageNotifier.setLastDesktopActivityTimestamp(message.getTimestamp());
} catch (MmsException e) {
throw new StorageFailedException(e, content.getSender(), content.getSenderDevice());
}
}
private void handleSynchronizeRequestMessage(@NonNull RequestMessage message)
@@ -572,51 +587,48 @@ public class PushDecryptJob extends ContextJob {
MessageNotifier.updateNotification(context);
}
private void handleMediaMessage(@NonNull SignalServiceEnvelope envelope,
private void handleMediaMessage(@NonNull SignalServiceContent content,
@NonNull SignalServiceDataMessage message,
@NonNull Optional<Long> smsMessageId)
throws MmsException
throws StorageFailedException
{
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
Recipient recipient = getMessageDestination(envelope, message);
Optional<QuoteModel> quote = getValidatedQuote(message.getQuote());
Optional<List<Contact>> sharedContacts = getContacts(message.getSharedContacts());
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(Address.fromExternal(context, envelope.getSource()),
message.getTimestamp(), -1,
message.getExpiresInSeconds() * 1000L, false,
Optional.fromNullable(envelope.getRelay()),
message.getBody(),
message.getGroupInfo(),
message.getAttachments(),
quote,
sharedContacts);
try {
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
Optional<QuoteModel> quote = getValidatedQuote(message.getQuote());
Optional<List<Contact>> sharedContacts = getContacts(message.getSharedContacts());
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(Address.fromExternal(context, content.getSender()),
message.getTimestamp(), -1,
message.getExpiresInSeconds() * 1000L, false,
content.isNeedsReceipt(),
message.getBody(),
message.getGroupInfo(),
message.getAttachments(),
quote,
sharedContacts);
if (message.getExpiresInSeconds() != recipient.getExpireMessages()) {
handleExpirationUpdate(envelope, message, Optional.absent());
}
Optional<InsertResult> insertResult = database.insertSecureDecryptedMessageInbox(mediaMessage, -1);
Optional<InsertResult> insertResult = database.insertSecureDecryptedMessageInbox(mediaMessage, -1);
if (insertResult.isPresent()) {
List<DatabaseAttachment> attachments = DatabaseFactory.getAttachmentDatabase(context).getAttachmentsForMessage(insertResult.get().getMessageId());
if (insertResult.isPresent()) {
List<DatabaseAttachment> attachments = DatabaseFactory.getAttachmentDatabase(context).getAttachmentsForMessage(insertResult.get().getMessageId());
for (DatabaseAttachment attachment : attachments) {
ApplicationContext.getInstance(context)
.getJobManager()
.add(new AttachmentDownloadJob(context, insertResult.get().getMessageId(), attachment.getAttachmentId(), false));
}
for (DatabaseAttachment attachment : attachments) {
ApplicationContext.getInstance(context)
.getJobManager()
.add(new AttachmentDownloadJob(context, insertResult.get().getMessageId(), attachment.getAttachmentId(), false));
if (smsMessageId.isPresent()) {
DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get());
}
MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
}
if (smsMessageId.isPresent()) {
DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get());
}
MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
} catch (MmsException e) {
throw new StorageFailedException(e, content.getSender(), content.getSenderDevice());
}
}
private long handleSynchronizeSentExpirationUpdate(@NonNull SentTranscriptMessage message)
throws MmsException
{
private long handleSynchronizeSentExpirationUpdate(@NonNull SentTranscriptMessage message) throws MmsException {
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
Recipient recipient = getSyncMessageDestination(message);
@@ -646,7 +658,8 @@ public class PushDecryptJob extends ContextJob {
message.getTimestamp(), -1,
message.getMessage().getExpiresInSeconds() * 1000,
ThreadDatabase.DistributionTypes.DEFAULT, quote.orNull(),
sharedContacts.or(Collections.emptyList()));
sharedContacts.or(Collections.emptyList()),
Collections.emptyList(), Collections.emptyList());
mediaMessage = new OutgoingSecureMediaMessage(mediaMessage);
@@ -677,17 +690,17 @@ public class PushDecryptJob extends ContextJob {
return threadId;
}
private void handleTextMessage(@NonNull SignalServiceEnvelope envelope,
private void handleTextMessage(@NonNull SignalServiceContent content,
@NonNull SignalServiceDataMessage message,
@NonNull Optional<Long> smsMessageId)
throws MmsException
throws StorageFailedException
{
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
String body = message.getBody().isPresent() ? message.getBody().get() : "";
Recipient recipient = getMessageDestination(envelope, message);
Recipient recipient = getMessageDestination(content, message);
if (message.getExpiresInSeconds() != recipient.getExpireMessages()) {
handleExpirationUpdate(envelope, message, Optional.absent());
handleExpirationUpdate(content, message, Optional.absent());
}
Long threadId;
@@ -695,11 +708,12 @@ public class PushDecryptJob extends ContextJob {
if (smsMessageId.isPresent() && !message.getGroupInfo().isPresent()) {
threadId = database.updateBundleMessageBody(smsMessageId.get(), body).second;
} else {
IncomingTextMessage textMessage = new IncomingTextMessage(Address.fromExternal(context, envelope.getSource()),
envelope.getSourceDevice(),
IncomingTextMessage textMessage = new IncomingTextMessage(Address.fromExternal(context, content.getSender()),
content.getSenderDevice(),
message.getTimestamp(), body,
message.getGroupInfo(),
message.getExpiresInSeconds() * 1000L);
message.getExpiresInSeconds() * 1000L,
content.isNeedsReceipt());
textMessage = new IncomingEncryptedMessage(textMessage, body);
Optional<InsertResult> insertResult = database.insertMessageInbox(textMessage);
@@ -758,13 +772,13 @@ public class PushDecryptJob extends ContextJob {
return threadId;
}
private void handleInvalidVersionMessage(@NonNull SignalServiceEnvelope envelope,
private void handleInvalidVersionMessage(@NonNull String sender, int senderDevice, long timestamp,
@NonNull Optional<Long> smsMessageId)
{
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
if (!smsMessageId.isPresent()) {
Optional<InsertResult> insertResult = insertPlaceholder(envelope);
Optional<InsertResult> insertResult = insertPlaceholder(sender, senderDevice, timestamp);
if (insertResult.isPresent()) {
smsDatabase.markAsInvalidVersionKeyExchange(insertResult.get().getMessageId());
@@ -775,13 +789,13 @@ public class PushDecryptJob extends ContextJob {
}
}
private void handleCorruptMessage(@NonNull SignalServiceEnvelope envelope,
private void handleCorruptMessage(@NonNull String sender, int senderDevice, long timestamp,
@NonNull Optional<Long> smsMessageId)
{
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
if (!smsMessageId.isPresent()) {
Optional<InsertResult> insertResult = insertPlaceholder(envelope);
Optional<InsertResult> insertResult = insertPlaceholder(sender, senderDevice, timestamp);
if (insertResult.isPresent()) {
smsDatabase.markAsDecryptFailed(insertResult.get().getMessageId());
@@ -792,13 +806,13 @@ public class PushDecryptJob extends ContextJob {
}
}
private void handleNoSessionMessage(@NonNull SignalServiceEnvelope envelope,
private void handleNoSessionMessage(@NonNull String sender, int senderDevice, long timestamp,
@NonNull Optional<Long> smsMessageId)
{
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
if (!smsMessageId.isPresent()) {
Optional<InsertResult> insertResult = insertPlaceholder(envelope);
Optional<InsertResult> insertResult = insertPlaceholder(sender, senderDevice, timestamp);
if (insertResult.isPresent()) {
smsDatabase.markAsNoSession(insertResult.get().getMessageId());
@@ -809,13 +823,13 @@ public class PushDecryptJob extends ContextJob {
}
}
private void handleLegacyMessage(@NonNull SignalServiceEnvelope envelope,
private void handleLegacyMessage(@NonNull String sender, int senderDevice, long timestamp,
@NonNull Optional<Long> smsMessageId)
{
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
if (!smsMessageId.isPresent()) {
Optional<InsertResult> insertResult = insertPlaceholder(envelope);
Optional<InsertResult> insertResult = insertPlaceholder(sender, senderDevice, timestamp);
if (insertResult.isPresent()) {
smsDatabase.markAsLegacyVersion(insertResult.get().getMessageId());
@@ -827,7 +841,7 @@ public class PushDecryptJob extends ContextJob {
}
@SuppressWarnings("unused")
private void handleDuplicateMessage(@NonNull SignalServiceEnvelope envelope,
private void handleDuplicateMessage(@NonNull String sender, int senderDeviceId, long timestamp,
@NonNull Optional<Long> smsMessageId)
{
// Let's start ignoring these now
@@ -842,45 +856,11 @@ public class PushDecryptJob extends ContextJob {
// }
}
private void handleUntrustedIdentityMessage(@NonNull SignalServiceEnvelope envelope,
@NonNull Optional<Long> smsMessageId)
{
try {
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
Address sourceAddress = Address.fromExternal(context, envelope.getSource());
byte[] serialized = envelope.hasLegacyMessage() ? envelope.getLegacyMessage() : envelope.getContent();
PreKeySignalMessage whisperMessage = new PreKeySignalMessage(serialized);
IdentityKey identityKey = whisperMessage.getIdentityKey();
String encoded = Base64.encodeBytes(serialized);
IncomingTextMessage textMessage = new IncomingTextMessage(sourceAddress,
envelope.getSourceDevice(),
envelope.getTimestamp(), encoded,
Optional.absent(), 0);
if (!smsMessageId.isPresent()) {
IncomingPreKeyBundleMessage bundleMessage = new IncomingPreKeyBundleMessage(textMessage, encoded, envelope.hasLegacyMessage());
Optional<InsertResult> insertResult = database.insertMessageInbox(bundleMessage);
if (insertResult.isPresent()) {
database.setMismatchedIdentity(insertResult.get().getMessageId(), sourceAddress, identityKey);
MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
}
} else {
database.updateMessageBody(smsMessageId.get(), encoded);
database.markAsPreKeyBundle(smsMessageId.get());
database.setMismatchedIdentity(smsMessageId.get(), sourceAddress, identityKey);
}
} catch (InvalidMessageException | InvalidVersionException e) {
throw new AssertionError(e);
}
}
private void handleProfileKey(@NonNull SignalServiceEnvelope envelope,
private void handleProfileKey(@NonNull SignalServiceContent content,
@NonNull SignalServiceDataMessage message)
{
RecipientDatabase database = DatabaseFactory.getRecipientDatabase(context);
Address sourceAddress = Address.fromExternal(context, envelope.getSource());
Address sourceAddress = Address.fromExternal(context, content.getSender());
Recipient recipient = Recipient.from(context, sourceAddress, false);
if (recipient.getProfileKey() == null || !MessageDigest.isEqual(recipient.getProfileKey(), message.getProfileKey().get())) {
@@ -889,17 +869,27 @@ public class PushDecryptJob extends ContextJob {
}
}
private void handleDeliveryReceipt(@NonNull SignalServiceEnvelope envelope,
private void handleNeedsDeliveryReceipt(@NonNull SignalServiceContent content,
@NonNull SignalServiceDataMessage message)
{
ApplicationContext.getInstance(context)
.getJobManager()
.add(new SendDeliveryReceiptJob(context, Address.fromExternal(context, content.getSender()), message.getTimestamp()));
}
@SuppressLint("DefaultLocale")
private void handleDeliveryReceipt(@NonNull SignalServiceContent content,
@NonNull SignalServiceReceiptMessage message)
{
for (long timestamp : message.getTimestamps()) {
Log.i(TAG, String.format("Received encrypted delivery receipt: (XXXXX, %d)", timestamp));
DatabaseFactory.getMmsSmsDatabase(context)
.incrementDeliveryReceiptCount(new SyncMessageId(Address.fromExternal(context, envelope.getSource()), timestamp), System.currentTimeMillis());
.incrementDeliveryReceiptCount(new SyncMessageId(Address.fromExternal(context, content.getSender()), timestamp), System.currentTimeMillis());
}
}
private void handleReadReceipt(@NonNull SignalServiceEnvelope envelope,
@SuppressLint("DefaultLocale")
private void handleReadReceipt(@NonNull SignalServiceContent content,
@NonNull SignalServiceReceiptMessage message)
{
if (TextSecurePreferences.isReadReceiptsEnabled(context)) {
@@ -907,7 +897,7 @@ public class PushDecryptJob extends ContextJob {
Log.i(TAG, String.format("Received encrypted read receipt: (XXXXX, %d)", timestamp));
DatabaseFactory.getMmsSmsDatabase(context)
.incrementReadReceiptCount(new SyncMessageId(Address.fromExternal(context, envelope.getSource()), timestamp), envelope.getTimestamp());
.incrementReadReceiptCount(new SyncMessageId(Address.fromExternal(context, content.getSender()), timestamp), content.getTimestamp());
}
}
}
@@ -960,12 +950,11 @@ public class PushDecryptJob extends ContextJob {
return Optional.of(contacts);
}
private Optional<InsertResult> insertPlaceholder(@NonNull SignalServiceEnvelope envelope) {
private Optional<InsertResult> insertPlaceholder(@NonNull String sender, int senderDevice, long timestamp) {
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
IncomingTextMessage textMessage = new IncomingTextMessage(Address.fromExternal(context, envelope.getSource()),
envelope.getSourceDevice(),
envelope.getTimestamp(), "",
Optional.absent(), 0);
IncomingTextMessage textMessage = new IncomingTextMessage(Address.fromExternal(context, sender),
senderDevice, timestamp, "",
Optional.absent(), 0, false);
textMessage = new IncomingEncryptedMessage(textMessage, "");
return database.insertMessageInbox(textMessage);
@@ -979,21 +968,20 @@ public class PushDecryptJob extends ContextJob {
}
}
private Recipient getMessageDestination(SignalServiceEnvelope envelope, SignalServiceDataMessage message) {
private Recipient getMessageDestination(SignalServiceContent content, SignalServiceDataMessage message) {
if (message.getGroupInfo().isPresent()) {
return Recipient.from(context, Address.fromExternal(context, GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId(), false)), false);
} else {
return Recipient.from(context, Address.fromExternal(context, envelope.getSource()), false);
return Recipient.from(context, Address.fromExternal(context, content.getSender()), false);
}
}
private boolean shouldIgnore(@NonNull SignalServiceEnvelope envelope, @NonNull SignalServiceContent content) {
Recipient sender = Recipient.from(context, Address.fromExternal(context, envelope.getSource()), false);
private boolean shouldIgnore(@NonNull SignalServiceContent content) {
Recipient sender = Recipient.from(context, Address.fromExternal(context, content.getSender()), false);
if (content.getDataMessage().isPresent()) {
SignalServiceDataMessage message = content.getDataMessage().get();
Recipient conversation = getMessageDestination(envelope, message);
Recipient conversation = getMessageDestination(content, message);
if (conversation.isGroupRecipient() && conversation.isBlocked()) {
return true;
@@ -1023,4 +1011,25 @@ public class PushDecryptJob extends ContextJob {
return false;
}
@SuppressWarnings("WeakerAccess")
private static class StorageFailedException extends Exception {
private final String sender;
private final int senderDevice;
private StorageFailedException(Exception e, String sender, int senderDevice) {
super(e);
this.sender = sender;
this.senderDevice = senderDevice;
}
public String getSender() {
return sender;
}
public int getSenderDevice() {
return senderDevice;
}
}
}

View File

@@ -4,15 +4,18 @@ import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.annimon.stream.Collectors;
import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupReceiptDatabase.GroupReceiptInfo;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.database.documents.NetworkFailure;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
@@ -24,25 +27,27 @@ import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.SendMessageResult;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage.Quote;
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
import org.whispersystems.signalservice.api.messages.shared.SharedContact;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.EncapsulatedExceptions;
import org.whispersystems.signalservice.api.push.exceptions.NetworkFailureException;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
import java.io.IOException;
import java.util.LinkedList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
@@ -96,47 +101,56 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
@Override
public void onPushSend()
throws MmsException, IOException, NoSuchMessageException
throws IOException, MmsException, NoSuchMessageException, RetryLaterException
{
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
OutgoingMediaMessage message = database.getOutgoingMessage(messageId);
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
OutgoingMediaMessage message = database.getOutgoingMessage(messageId);
List<NetworkFailure> existingNetworkFailures = message.getNetworkFailures();
List<IdentityKeyMismatch> existingIdentityMismatches = message.getIdentityKeyMismatches();
try {
Log.i(TAG, "Sending message: " + messageId);
List<Address> target;
deliver(message, filterAddress == null ? null : Address.fromSerialized(filterAddress));
if (filterAddress != null) target = Collections.singletonList(Address.fromSerialized(filterAddress));
else if (!existingNetworkFailures.isEmpty()) target = Stream.of(existingNetworkFailures).map(NetworkFailure::getAddress).toList();
else target = getGroupMessageRecipients(message.getRecipient().getAddress().toGroupString(), messageId);
database.markAsSent(messageId, true);
markAttachmentsUploaded(messageId, message.getAttachments());
List<SendMessageResult> results = deliver(message, target);
List<NetworkFailure> networkFailures = Stream.of(results).filter(SendMessageResult::isNetworkFailure).map(result -> new NetworkFailure(Address.fromSerialized(result.getAddress().getNumber()))).toList();
List<IdentityKeyMismatch> identityMismatches = Stream.of(results).filter(result -> result.getIdentityFailure() != null).map(result -> new IdentityKeyMismatch(Address.fromSerialized(result.getAddress().getNumber()), result.getIdentityFailure().getIdentityKey())).toList();
Set<Address> successAddresses = Stream.of(results).filter(result -> result.getSuccess() != null).map(result -> Address.fromSerialized(result.getAddress().getNumber())).collect(Collectors.toSet());
List<NetworkFailure> resolvedNetworkFailures = Stream.of(existingNetworkFailures).filter(failure -> successAddresses.contains(failure.getAddress())).toList();
List<IdentityKeyMismatch> resolvedIdentityFailures = Stream.of(existingIdentityMismatches).filter(failure -> successAddresses.contains(failure.getAddress())).toList();
List<SendMessageResult> successes = Stream.of(results).filter(result -> result.getSuccess() != null).toList();
if (message.getExpiresIn() > 0 && !message.isExpirationUpdate()) {
database.markExpireStarted(messageId);
ApplicationContext.getInstance(context)
.getExpiringMessageManager()
.scheduleDeletion(messageId, true, message.getExpiresIn());
for (NetworkFailure resolvedFailure : resolvedNetworkFailures) {
database.removeFailure(messageId, resolvedFailure);
existingNetworkFailures.remove(resolvedFailure);
}
Log.i(TAG, "Sent message: " + messageId);
} catch (InvalidNumberException | RecipientFormattingException | UndeliverableMessageException e) {
Log.w(TAG, e);
database.markAsSentFailed(messageId);
notifyMediaMessageDeliveryFailed(context, messageId);
} catch (EncapsulatedExceptions e) {
Log.w(TAG, e);
List<NetworkFailure> failures = new LinkedList<>();
for (NetworkFailureException nfe : e.getNetworkExceptions()) {
failures.add(new NetworkFailure(Address.fromSerialized(nfe.getE164number())));
for (IdentityKeyMismatch resolvedIdentity : resolvedIdentityFailures) {
database.removeMismatchedIdentity(messageId, resolvedIdentity.getAddress(), resolvedIdentity.getIdentityKey());
existingIdentityMismatches.remove(resolvedIdentity);
}
for (UntrustedIdentityException uie : e.getUntrustedIdentityExceptions()) {
database.addMismatchedIdentity(messageId, Address.fromSerialized(uie.getE164Number()), uie.getIdentityKey());
if (!networkFailures.isEmpty()) {
database.addFailures(messageId, networkFailures);
}
database.addFailures(messageId, failures);
for (IdentityKeyMismatch mismatch : identityMismatches) {
database.addMismatchedIdentity(messageId, mismatch.getAddress(), mismatch.getIdentityKey());
}
if (e.getNetworkExceptions().isEmpty() && e.getUntrustedIdentityExceptions().isEmpty()) {
for (SendMessageResult success : successes) {
DatabaseFactory.getGroupReceiptDatabase(context).setUnidentified(Address.fromSerialized(success.getAddress().getNumber()),
messageId,
success.getSuccess().isUnidentified());
}
if (existingNetworkFailures.isEmpty() && networkFailures.isEmpty() && identityMismatches.isEmpty() && existingIdentityMismatches.isEmpty()) {
database.markAsSent(messageId, true);
markAttachmentsUploaded(messageId, message.getAttachments());
if (message.getExpiresIn() > 0 && !message.isExpirationUpdate()) {
@@ -145,16 +159,28 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
.getExpiringMessageManager()
.scheduleDeletion(messageId, true, message.getExpiresIn());
}
} else {
} else if (!networkFailures.isEmpty()) {
throw new RetryLaterException();
} else if (!identityMismatches.isEmpty()) {
database.markAsSentFailed(messageId);
notifyMediaMessageDeliveryFailed(context, messageId);
}
} catch (InvalidNumberException | RecipientFormattingException | UndeliverableMessageException e) {
Log.w(TAG, e);
database.markAsSentFailed(messageId);
notifyMediaMessageDeliveryFailed(context, messageId);
} catch (UntrustedIdentityException e) {
Log.w(TAG, e);
database.markAsSentFailed(messageId);
notifyMediaMessageDeliveryFailed(context, messageId);
}
}
@Override
public boolean onShouldRetryThrowable(Exception exception) {
if (exception instanceof IOException) return true;
if (exception instanceof IOException) return true;
if (exception instanceof RetryLaterException) return true;
return false;
}
@@ -163,23 +189,24 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageId);
}
private void deliver(OutgoingMediaMessage message, @Nullable Address filterAddress)
private List<SendMessageResult> deliver(OutgoingMediaMessage message, @NonNull List<Address> destinations)
throws IOException, RecipientFormattingException, InvalidNumberException,
EncapsulatedExceptions, UndeliverableMessageException
UndeliverableMessageException, UntrustedIdentityException
{
String groupId = message.getRecipient().getAddress().toGroupString();
Optional<byte[]> profileKey = getProfileKey(message.getRecipient());
List<Address> recipients = getGroupMessageRecipients(groupId, messageId);
MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints();
List<Attachment> scaledAttachments = scaleAndStripExifFromAttachments(mediaConstraints, message.getAttachments());
List<SignalServiceAttachment> attachmentStreams = getAttachmentsFor(scaledAttachments);
Optional<Quote> quote = getQuoteFor(message);
List<SharedContact> sharedContacts = getSharedContactsFor(message);
List<SignalServiceAddress> addresses = Stream.of(destinations).map(this::getPushAddress).toList();
List<SignalServiceAddress> addresses;
if (filterAddress != null) addresses = getPushAddresses(filterAddress);
else addresses = getPushAddresses(recipients);
List<Optional<UnidentifiedAccessPair>> unidentifiedAccess = Stream.of(addresses)
.map(address -> Address.fromSerialized(address.getNumber()))
.map(address -> Recipient.from(context, address, false))
.map(recipient -> UnidentifiedAccessUtil.getAccessFor(context, recipient))
.toList();
if (message.isGroup()) {
OutgoingGroupMediaMessage groupMessage = (OutgoingGroupMediaMessage) message;
@@ -193,7 +220,7 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
.asGroupMessage(group)
.build();
messageSender.sendMessage(addresses, groupDataMessage);
return messageSender.sendMessage(addresses, unidentifiedAccess, groupDataMessage);
} else {
SignalServiceGroup group = new SignalServiceGroup(GroupUtil.getDecodedId(groupId));
SignalServiceDataMessage groupMessage = SignalServiceDataMessage.newBuilder()
@@ -208,20 +235,10 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
.withSharedContacts(sharedContacts)
.build();
messageSender.sendMessage(addresses, groupMessage);
return messageSender.sendMessage(addresses, unidentifiedAccess, groupMessage);
}
}
private List<SignalServiceAddress> getPushAddresses(Address address) {
List<SignalServiceAddress> addresses = new LinkedList<>();
addresses.add(getPushAddress(address));
return addresses;
}
private List<SignalServiceAddress> getPushAddresses(List<Address> addresses) {
return Stream.of(addresses).map(this::getPushAddress).toList();
}
private @NonNull List<Address> getGroupMessageRecipients(String groupId, long messageId) {
List<GroupReceiptInfo> destinations = DatabaseFactory.getGroupReceiptDatabase(context).getGroupReceiptInfo(messageId);
if (!destinations.isEmpty()) return Stream.of(destinations).map(GroupReceiptInfo::getAddress).toList();

View File

@@ -4,15 +4,15 @@ package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.libsignal.util.guava.Optional;
@@ -122,7 +122,9 @@ public class PushGroupUpdateJob extends ContextJob implements InjectableType {
.withExpiration(groupRecipient.getExpireMessages())
.build();
messageSender.sendMessage(new SignalServiceAddress(source), message);
messageSender.sendMessage(new SignalServiceAddress(source),
UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(source), false)),
message);
}
@Override

View File

@@ -3,16 +3,16 @@ package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.MediaConstraints;
import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
@@ -80,9 +80,10 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
try {
Log.i(TAG, "Sending message: " + messageId);
deliver(message);
boolean unidentified = deliver(message);
database.markAsSent(messageId, true);
markAttachmentsUploaded(messageId, message.getAttachments());
database.markUnidentified(messageId, unidentified);
if (message.getExpiresIn() > 0 && !message.isExpirationUpdate()) {
database.markExpireStarted(messageId);
@@ -117,7 +118,7 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
notifyMediaMessageDeliveryFailed(context, messageId);
}
private void deliver(OutgoingMediaMessage message)
private boolean deliver(OutgoingMediaMessage message)
throws RetryLaterException, InsecureFallbackApprovalException, UntrustedIdentityException,
UndeliverableMessageException
{
@@ -144,7 +145,7 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
.asExpirationUpdate(message.isExpirationUpdate())
.build();
messageSender.sendMessage(address, mediaMessage);
return messageSender.sendMessage(address, UnidentifiedAccessUtil.getAccessFor(context, message.getRecipient()), mediaMessage).getSuccess().isUnidentified();
} catch (UnregisteredUserException e) {
Log.w(TAG, e);
throw new InsecureFallbackApprovalException(e);

View File

@@ -4,15 +4,12 @@ import android.content.Context;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import java.io.IOException;

View File

@@ -1,5 +1,6 @@
package org.thoughtcrime.securesms.jobs;
import android.annotation.SuppressLint;
import android.content.Context;
import android.support.annotation.NonNull;
@@ -25,17 +26,19 @@ public abstract class PushReceivedJob extends ContextJob {
public void processEnvelope(@NonNull SignalServiceEnvelope envelope) {
synchronized (RECEIVE_LOCK) {
Address source = Address.fromExternal(context, envelope.getSource());
Recipient recipient = Recipient.from(context, source, false);
if (envelope.hasSource()) {
Address source = Address.fromExternal(context, envelope.getSource());
Recipient recipient = Recipient.from(context, source, false);
if (!isActiveNumber(recipient)) {
DatabaseFactory.getRecipientDatabase(context).setRegistered(recipient, RecipientDatabase.RegisteredState.REGISTERED);
ApplicationContext.getInstance(context).getJobManager().add(new DirectoryRefreshJob(context, recipient, false));
if (!isActiveNumber(recipient)) {
DatabaseFactory.getRecipientDatabase(context).setRegistered(recipient, RecipientDatabase.RegisteredState.REGISTERED);
ApplicationContext.getInstance(context).getJobManager().add(new DirectoryRefreshJob(context, recipient, false));
}
}
if (envelope.isReceipt()) {
handleReceipt(envelope);
} else if (envelope.isPreKeySignalMessage() || envelope.isSignalMessage()) {
} else if (envelope.isPreKeySignalMessage() || envelope.isSignalMessage() || envelope.isUnidentifiedSender()) {
handleMessage(envelope);
} else {
Log.w(TAG, "Received envelope of unknown type: " + envelope.getType());
@@ -47,6 +50,7 @@ public abstract class PushReceivedJob extends ContextJob {
new PushDecryptJob(context).processMessage(envelope);
}
@SuppressLint("DefaultLocale")
private void handleReceipt(SignalServiceEnvelope envelope) {
Log.i(TAG, String.format("Received receipt: (XXXXX, %d)", envelope.getTimestamp()));
DatabaseFactory.getMmsSmsDatabase(context).incrementDeliveryReceiptCount(new SyncMessageId(Address.fromExternal(context, envelope.getSource()),

View File

@@ -92,7 +92,6 @@ public abstract class PushSendJob extends SendJob {
}
protected SignalServiceAddress getPushAddress(Address address) {
// String relay = TextSecureDirectory.getInstance(context).getRelay(address.toPhoneString());
String relay = null;
return new SignalServiceAddress(address.toPhoneString(), Optional.fromNullable(relay));
}

View File

@@ -7,6 +7,7 @@ import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.NoSuchMessageException;
@@ -20,7 +21,9 @@ import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.SendMessageResult;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
@@ -76,8 +79,9 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
try {
Log.i(TAG, "Sending message: " + messageId);
deliver(record);
boolean unidentified = deliver(record);
database.markAsSent(messageId, true);
database.markUnidentified(messageId, unidentified);
if (record.getExpiresIn() > 0) {
database.markExpireStarted(messageId);
@@ -118,22 +122,25 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
}
}
private void deliver(SmsMessageRecord message)
private boolean deliver(SmsMessageRecord message)
throws UntrustedIdentityException, InsecureFallbackApprovalException, RetryLaterException
{
try {
SignalServiceAddress address = getPushAddress(message.getIndividualRecipient().getAddress());
Optional<byte[]> profileKey = getProfileKey(message.getIndividualRecipient());
SignalServiceDataMessage textSecureMessage = SignalServiceDataMessage.newBuilder()
.withTimestamp(message.getDateSent())
.withBody(message.getBody())
.withExpiration((int)(message.getExpiresIn() / 1000))
.withProfileKey(profileKey.orNull())
.asEndSessionMessage(message.isEndSession())
.build();
SignalServiceAddress address = getPushAddress(message.getIndividualRecipient().getAddress());
Optional<byte[]> profileKey = getProfileKey(message.getIndividualRecipient());
Optional<UnidentifiedAccessPair> unidentifiedAccess = UnidentifiedAccessUtil.getAccessFor(context, message.getIndividualRecipient());
Log.w(TAG, "Have access key to use: " + unidentifiedAccess.isPresent());
messageSender.sendMessage(address, textSecureMessage);
SignalServiceDataMessage textSecureMessage = SignalServiceDataMessage.newBuilder()
.withTimestamp(message.getDateSent())
.withBody(message.getBody())
.withExpiration((int)(message.getExpiresIn() / 1000))
.withProfileKey(profileKey.orNull())
.asEndSessionMessage(message.isEndSession())
.build();
return messageSender.sendMessage(address, unidentifiedAccess, textSecureMessage).getSuccess().isUnidentified();
} catch (UnregisteredUserException e) {
Log.w(TAG, e);
throw new InsecureFallbackApprovalException(e);

View File

@@ -8,6 +8,12 @@ import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import org.whispersystems.signalservice.api.push.exceptions.NetworkFailureException;
@@ -48,12 +54,15 @@ public class RefreshAttributesJob extends ContextJob implements InjectableType {
@Override
public void onRun() throws IOException {
String signalingKey = TextSecurePreferences.getSignalingKey(context);
int registrationId = TextSecurePreferences.getLocalRegistrationId(context);
boolean fetchesMessages = TextSecurePreferences.isGcmDisabled(context);
String pin = TextSecurePreferences.getRegistrationLockPin(context);
String signalingKey = TextSecurePreferences.getSignalingKey(context);
int registrationId = TextSecurePreferences.getLocalRegistrationId(context);
boolean fetchesMessages = TextSecurePreferences.isGcmDisabled(context);
String pin = TextSecurePreferences.getRegistrationLockPin(context);
byte[] unidentifiedAccessKey = UnidentifiedAccessUtil.getSelfUnidentifiedAccessKey(context);
boolean universalUnidentifiedAccess = TextSecurePreferences.isUniversalUnidentifiedAccess(context);
signalAccountManager.setAccountAttributes(signalingKey, registrationId, fetchesMessages, pin);
signalAccountManager.setAccountAttributes(signalingKey, registrationId, fetchesMessages, pin,
unidentifiedAccessKey, universalUnidentifiedAccess);
}
@Override

View File

@@ -3,10 +3,14 @@ package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
@@ -23,6 +27,7 @@ import androidx.work.Data;
public class RequestGroupInfoJob extends ContextJob implements InjectableType {
@SuppressWarnings("unused")
private static final String TAG = RequestGroupInfoJob.class.getSimpleName();
private static final long serialVersionUID = 0L;
@@ -77,7 +82,9 @@ public class RequestGroupInfoJob extends ContextJob implements InjectableType {
.withTimestamp(System.currentTimeMillis())
.build();
messageSender.sendMessage(new SignalServiceAddress(source), message);
messageSender.sendMessage(new SignalServiceAddress(source),
UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromExternal(context, source), false)),
message);
}
@Override

View File

@@ -5,14 +5,16 @@ import android.content.Context;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.IncomingMessageObserver;
import org.thoughtcrime.securesms.util.Base64;
@@ -20,11 +22,15 @@ import org.thoughtcrime.securesms.util.IdentityUtil;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.InvalidKeyException;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessagePipe;
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
import org.whispersystems.signalservice.api.crypto.ProfileCipher;
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
import org.whispersystems.signalservice.api.profiles.SignalServiceProfile;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
import java.io.IOException;
@@ -88,12 +94,26 @@ public class RetrieveProfileJob extends ContextJob implements InjectableType {
private void handleIndividualRecipient(Recipient recipient)
throws IOException, InvalidKeyException, InvalidNumberException
{
String number = recipient.getAddress().toPhoneString();
SignalServiceProfile profile = retrieveProfile(number);
String number = recipient.getAddress().toPhoneString();
Optional<UnidentifiedAccess> unidentifiedAccess = getUnidentifiedAccess(recipient);
SignalServiceProfile profile;
try {
profile = retrieveProfile(number, unidentifiedAccess);
} catch (AuthorizationFailedException e) {
if (unidentifiedAccess.isPresent()) {
// XXX Update UI
profile = retrieveProfile(number, Optional.absent());
} else {
throw e;
}
}
setIdentityKey(recipient, profile.getIdentityKey());
setProfileName(recipient, profile.getName());
setProfileAvatar(recipient, profile.getAvatar());
setUnidentifiedAccessMode(recipient, profile.getUnidentifiedAccess(), profile.isUnrestrictedUnidentifiedAccess());
}
private void handleGroupRecipient(Recipient group)
@@ -106,18 +126,20 @@ public class RetrieveProfileJob extends ContextJob implements InjectableType {
}
}
private SignalServiceProfile retrieveProfile(@NonNull String number) throws IOException {
private SignalServiceProfile retrieveProfile(@NonNull String number, Optional<UnidentifiedAccess> unidentifiedAccess)
throws IOException
{
SignalServiceMessagePipe pipe = IncomingMessageObserver.getPipe();
if (pipe != null) {
try {
return pipe.getProfile(new SignalServiceAddress(number));
return pipe.getProfile(new SignalServiceAddress(number), unidentifiedAccess);
} catch (IOException e) {
Log.w(TAG, e);
}
}
return receiver.retrieveProfile(new SignalServiceAddress(number));
return receiver.retrieveProfile(new SignalServiceAddress(number), unidentifiedAccess);
}
private void setIdentityKey(Recipient recipient, String identityKeyValue) {
@@ -143,6 +165,30 @@ public class RetrieveProfileJob extends ContextJob implements InjectableType {
}
}
private void setUnidentifiedAccessMode(Recipient recipient, String unidentifiedAccessVerifier, boolean unrestrictedUnidentifiedAccess) {
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
byte[] profileKey = recipient.getProfileKey();
// XXX Update UI
if (unrestrictedUnidentifiedAccess) {
recipientDatabase.setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.UNRESTRICTED);
} else if (profileKey == null || unidentifiedAccessVerifier == null) {
recipientDatabase.setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.DISABLED);
} else {
ProfileCipher profileCipher = new ProfileCipher(profileKey);
boolean verifiedUnidentifiedAccess;
try {
verifiedUnidentifiedAccess = profileCipher.verifyUnidentifiedAccess(Base64.decode(unidentifiedAccessVerifier));
} catch (IOException e) {
Log.w(TAG, e);
verifiedUnidentifiedAccess = false;
}
recipientDatabase.setUnidentifiedAccessMode(recipient, verifiedUnidentifiedAccess ? UnidentifiedAccessMode.ENABLED : UnidentifiedAccessMode.DISABLED);
}
}
private void setProfileName(Recipient recipient, String profileName) {
try {
byte[] profileKey = recipient.getProfileKey();
@@ -172,4 +218,14 @@ public class RetrieveProfileJob extends ContextJob implements InjectableType {
.add(new RetrieveProfileAvatarJob(context, recipient, profileAvatar));
}
}
private Optional<UnidentifiedAccess> getUnidentifiedAccess(@NonNull Recipient recipient) {
Optional<UnidentifiedAccessPair> unidentifiedAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient);
if (unidentifiedAccess.isPresent()) {
return unidentifiedAccess.get().getTargetUnidentifiedAccess();
}
return Optional.absent();
}
}

View File

@@ -0,0 +1,71 @@
package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import android.util.Log;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import java.io.IOException;
import javax.inject.Inject;
import androidx.work.Data;
@SuppressWarnings("WeakerAccess")
public class RotateCertificateJob extends ContextJob implements InjectableType {
private static final long serialVersionUID = 1L;
private static final String TAG = RotateCertificateJob.class.getName();
@Inject transient SignalServiceAccountManager accountManager;
public RotateCertificateJob() {
super(null, null);
}
public RotateCertificateJob(Context context) {
super(context, JobParameters.newBuilder()
.withGroupId("__ROTATE_SENDER_CERTIFICATE__")
.withNetworkRequirement()
.create());
}
@NonNull
@Override
protected Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.build();
}
@Override
protected void initialize(@NonNull SafeData data) {
}
@Override
public void onAdded() {}
@Override
public void onRun() throws IOException {
byte[] certificate = accountManager.getSenderCertificate();
TextSecurePreferences.setUnidentifiedAccessCertificate(context, certificate);
}
@Override
public boolean onShouldRetry(Exception e) {
return e instanceof PushNetworkException;
}
@Override
public void onCanceled() {
Log.w(TAG, "Failed to rotate sender certificate!");
}
}

View File

@@ -0,0 +1,100 @@
package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import android.util.Log;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import java.io.IOException;
import java.util.Collections;
import javax.inject.Inject;
import androidx.work.Data;
public class SendDeliveryReceiptJob extends ContextJob implements InjectableType {
private static final long serialVersionUID = 1L;
private static final String KEY_ADDRESS = "address";
private static final String KEY_MESSAGE_ID = "message_id";
private static final String KEY_TIMESTAMP = "timestamp";
private static final String TAG = SendReadReceiptJob.class.getSimpleName();
@Inject
transient SignalServiceMessageSender messageSender;
private String address;
private long messageId;
private long timestamp;
public SendDeliveryReceiptJob() {
super(null, null);
}
public SendDeliveryReceiptJob(Context context, Address address, long messageId) {
super(context, JobParameters.newBuilder()
.withNetworkRequirement()
.create());
this.address = address.serialize();
this.messageId = messageId;
this.timestamp = System.currentTimeMillis();
}
@Override
public void onAdded() {}
@NonNull
@Override
protected Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.putString(KEY_ADDRESS, address)
.putLong(KEY_MESSAGE_ID, messageId)
.putLong(KEY_TIMESTAMP, timestamp)
.build();
}
@Override
protected void initialize(@NonNull SafeData data) {
this.address = data.getString(KEY_ADDRESS);
this.messageId = data.getLong(KEY_MESSAGE_ID);
this.timestamp = data.getLong(KEY_TIMESTAMP);
}
@Override
public void onRun() throws IOException, UntrustedIdentityException {
SignalServiceAddress remoteAddress = new SignalServiceAddress(address);
SignalServiceReceiptMessage receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.DELIVERY,
Collections.singletonList(messageId),
timestamp);
messageSender.sendReceipt(remoteAddress,
UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(address), false)),
receiptMessage);
}
@Override
public boolean onShouldRetry(Exception e) {
if (e instanceof PushNetworkException) return true;
return false;
}
@Override
public void onCanceled() {
Log.w(TAG, "Failed to send delivery receipt to: " + address);
}
}

View File

@@ -4,11 +4,13 @@ package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
@@ -86,7 +88,9 @@ public class SendReadReceiptJob extends ContextJob implements InjectableType {
SignalServiceAddress remoteAddress = new SignalServiceAddress(address);
SignalServiceReceiptMessage receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.READ, messageIds, timestamp);
messageSender.sendReceipt(remoteAddress, receiptMessage);
messageSender.sendReceipt(remoteAddress,
UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(address), false)),
receiptMessage);
}
@Override