mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-27 20:15:21 +00:00
commit
42eb8ef9d1
@ -6,6 +6,7 @@ import android.support.annotation.NonNull;
|
|||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.annotation.WorkerThread;
|
import android.support.annotation.WorkerThread;
|
||||||
|
|
||||||
|
import org.signal.libsignal.metadata.SignalProtos;
|
||||||
import org.signal.libsignal.metadata.certificate.CertificateValidator;
|
import org.signal.libsignal.metadata.certificate.CertificateValidator;
|
||||||
import org.signal.libsignal.metadata.certificate.InvalidCertificateException;
|
import org.signal.libsignal.metadata.certificate.InvalidCertificateException;
|
||||||
import network.loki.messenger.BuildConfig;
|
import network.loki.messenger.BuildConfig;
|
||||||
@ -20,6 +21,7 @@ import org.whispersystems.libsignal.ecc.ECPublicKey;
|
|||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
|
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
|
||||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
|
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
|
||||||
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
@ -28,12 +30,7 @@ public class UnidentifiedAccessUtil {
|
|||||||
private static final String TAG = UnidentifiedAccessUtil.class.getSimpleName();
|
private static final String TAG = UnidentifiedAccessUtil.class.getSimpleName();
|
||||||
|
|
||||||
public static CertificateValidator getCertificateValidator() {
|
public static CertificateValidator getCertificateValidator() {
|
||||||
try {
|
return new CertificateValidator();
|
||||||
ECPublicKey unidentifiedSenderTrustRoot = Curve.decodePoint(Base64.decode(BuildConfig.UNIDENTIFIED_SENDER_TRUST_ROOT), 0);
|
|
||||||
return new CertificateValidator(unidentifiedSenderTrustRoot);
|
|
||||||
} catch (InvalidKeyException | IOException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
@ -48,7 +45,7 @@ public class UnidentifiedAccessUtil {
|
|||||||
try {
|
try {
|
||||||
byte[] theirUnidentifiedAccessKey = getTargetUnidentifiedAccessKey(recipient);
|
byte[] theirUnidentifiedAccessKey = getTargetUnidentifiedAccessKey(recipient);
|
||||||
byte[] ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(context);
|
byte[] ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(context);
|
||||||
byte[] ourUnidentifiedAccessCertificate = TextSecurePreferences.getUnidentifiedAccessCertificate(context);
|
byte[] ourUnidentifiedAccessCertificate = getUnidentifiedAccessCertificate(context);
|
||||||
|
|
||||||
if (TextSecurePreferences.isUniversalUnidentifiedAccess(context)) {
|
if (TextSecurePreferences.isUniversalUnidentifiedAccess(context)) {
|
||||||
ourUnidentifiedAccessKey = Util.getSecretBytes(16);
|
ourUnidentifiedAccessKey = Util.getSecretBytes(16);
|
||||||
@ -83,7 +80,7 @@ public class UnidentifiedAccessUtil {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
byte[] ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(context);
|
byte[] ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(context);
|
||||||
byte[] ourUnidentifiedAccessCertificate = TextSecurePreferences.getUnidentifiedAccessCertificate(context);
|
byte[] ourUnidentifiedAccessCertificate = getUnidentifiedAccessCertificate(context);
|
||||||
|
|
||||||
if (TextSecurePreferences.isUniversalUnidentifiedAccess(context)) {
|
if (TextSecurePreferences.isUniversalUnidentifiedAccess(context)) {
|
||||||
ourUnidentifiedAccessKey = Util.getSecretBytes(16);
|
ourUnidentifiedAccessKey = Util.getSecretBytes(16);
|
||||||
@ -125,4 +122,17 @@ public class UnidentifiedAccessUtil {
|
|||||||
throw new AssertionError("Unknown mode: " + recipient.getUnidentifiedAccessMode().getMode());
|
throw new AssertionError("Unknown mode: " + recipient.getUnidentifiedAccessMode().getMode());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static @Nullable byte[] getUnidentifiedAccessCertificate(Context context) {
|
||||||
|
String ourNumber = TextSecurePreferences.getLocalNumber(context);
|
||||||
|
if (ourNumber != null) {
|
||||||
|
SignalProtos.SenderCertificate certificate = SignalProtos.SenderCertificate.newBuilder()
|
||||||
|
.setSender(ourNumber)
|
||||||
|
.setSenderDevice(SignalServiceAddress.DEFAULT_DEVICE_ID)
|
||||||
|
.build();
|
||||||
|
return certificate.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,8 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
|||||||
import org.whispersystems.libsignal.IdentityKey;
|
import org.whispersystems.libsignal.IdentityKey;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||||
|
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
|
||||||
|
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
|
||||||
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
|
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream;
|
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream;
|
||||||
@ -285,9 +287,12 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy
|
|||||||
.build();
|
.build();
|
||||||
|
|
||||||
SignalServiceAddress messageRecipient = recipient != null ? new SignalServiceAddress(recipient) : null;
|
SignalServiceAddress messageRecipient = recipient != null ? new SignalServiceAddress(recipient) : null;
|
||||||
|
Address address = recipient != null ? Address.fromSerialized(recipient) : null;
|
||||||
|
|
||||||
|
Optional<UnidentifiedAccessPair> unidentifiedAccess = address != null ? UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, address, false)) : Optional.absent();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
messageSender.sendMessage(0, SignalServiceSyncMessage.forContacts(new ContactsMessage(attachmentStream, complete)), messageRecipient);
|
messageSender.sendMessage(0, SignalServiceSyncMessage.forContacts(new ContactsMessage(attachmentStream, complete)), unidentifiedAccess, Optional.fromNullable(messageRecipient));
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
throw new NetworkException(ioe);
|
throw new NetworkException(ioe);
|
||||||
}
|
}
|
||||||
|
@ -274,18 +274,14 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
SignalServiceAddress localAddress = new SignalServiceAddress(TextSecurePreferences.getLocalNumber(context));
|
SignalServiceAddress localAddress = new SignalServiceAddress(TextSecurePreferences.getLocalNumber(context));
|
||||||
LokiServiceCipher cipher = new LokiServiceCipher(localAddress, axolotlStore, lokiThreadDatabase, lokiPreKeyRecordDatabase, UnidentifiedAccessUtil.getCertificateValidator());
|
LokiServiceCipher cipher = new LokiServiceCipher(localAddress, axolotlStore, lokiThreadDatabase, lokiPreKeyRecordDatabase, UnidentifiedAccessUtil.getCertificateValidator());
|
||||||
|
|
||||||
// Loki - Handle session reset logic
|
SignalServiceContent content = cipher.decrypt(envelope);
|
||||||
if (!envelope.isFriendRequest() && cipher.getSessionStatus(envelope) == null && envelope.isPreKeySignalMessage()) {
|
|
||||||
cipher.validateBackgroundMessage(envelope, envelope.getContent());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loki - Ignore any friend requests that we got before restoration
|
// Loki - Ignore any friend requests that we got before restoration
|
||||||
if (envelope.isFriendRequest() && envelope.getTimestamp() < TextSecurePreferences.getRestorationTime(context)) {
|
if (content.isFriendRequest() && content.getTimestamp() < TextSecurePreferences.getRestorationTime(context)) {
|
||||||
Log.d("Loki", "Ignoring friend request received before restoration.");
|
Log.d("Loki", "Ignoring friend request received before restoration.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SignalServiceContent content = cipher.decrypt(envelope);
|
|
||||||
|
|
||||||
if (shouldIgnore(content)) {
|
if (shouldIgnore(content)) {
|
||||||
Log.i(TAG, "Ignoring message.");
|
Log.i(TAG, "Ignoring message.");
|
||||||
@ -293,12 +289,12 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Loki - Handle friend request acceptance if needed
|
// Loki - Handle friend request acceptance if needed
|
||||||
acceptFriendRequestIfNeeded(envelope, content);
|
acceptFriendRequestIfNeeded(content);
|
||||||
|
|
||||||
// Loki - Store pre key bundle
|
// Loki - Store pre key bundle
|
||||||
// We shouldn't store it if it's a pairing message
|
// We shouldn't store it if it's a pairing message
|
||||||
if (!content.getPairingAuthorisation().isPresent()) {
|
if (!content.getPairingAuthorisation().isPresent()) {
|
||||||
storePreKeyBundleIfNeeded(envelope, content);
|
storePreKeyBundleIfNeeded(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (content.lokiServiceMessage.isPresent()) {
|
if (content.lokiServiceMessage.isPresent()) {
|
||||||
@ -313,24 +309,24 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
if (rawSenderDisplayName.isPresent() && rawSenderDisplayName.get().length() > 0) {
|
if (rawSenderDisplayName.isPresent() && rawSenderDisplayName.get().length() > 0) {
|
||||||
// If we got a name from our primary device then we set our profile name to match it
|
// If we got a name from our primary device then we set our profile name to match it
|
||||||
String ourPrimaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
|
String ourPrimaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
|
||||||
if (ourPrimaryDevice != null && envelope.getSource().equals(ourPrimaryDevice)) {
|
if (ourPrimaryDevice != null && content.getSender().equals(ourPrimaryDevice)) {
|
||||||
TextSecurePreferences.setProfileName(context, rawSenderDisplayName.get());
|
TextSecurePreferences.setProfileName(context, rawSenderDisplayName.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we receive a message from our device then don't set the display name in the database (as we probably have a alias set for them)
|
// If we receive a message from our device then don't set the display name in the database (as we probably have a alias set for them)
|
||||||
MultiDeviceUtilities.isOneOfOurDevices(context, Address.fromSerialized(content.getSender())).success(isOneOfOurDevice -> {
|
MultiDeviceUtilities.isOneOfOurDevices(context, Address.fromSerialized(content.getSender())).success(isOneOfOurDevice -> {
|
||||||
if (!isOneOfOurDevice) { setDisplayName(envelope.getSource(), rawSenderDisplayName.get()); }
|
if (!isOneOfOurDevice) { setDisplayName(content.getSender(), rawSenderDisplayName.get()); }
|
||||||
return Unit.INSTANCE;
|
return Unit.INSTANCE;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (content.getPairingAuthorisation().isPresent()) {
|
if (content.getPairingAuthorisation().isPresent()) {
|
||||||
handlePairingMessage(content.getPairingAuthorisation().get(), envelope, content);
|
handlePairingMessage(content.getPairingAuthorisation().get(), content);
|
||||||
} else if (content.getDataMessage().isPresent()) {
|
} else if (content.getDataMessage().isPresent()) {
|
||||||
SignalServiceDataMessage message = content.getDataMessage().get();
|
SignalServiceDataMessage message = content.getDataMessage().get();
|
||||||
boolean isMediaMessage = message.getAttachments().isPresent() || message.getQuote().isPresent() || message.getSharedContacts().isPresent() || message.getPreviews().isPresent() || message.getSticker().isPresent();
|
boolean isMediaMessage = message.getAttachments().isPresent() || message.getQuote().isPresent() || message.getSharedContacts().isPresent() || message.getPreviews().isPresent() || message.getSticker().isPresent();
|
||||||
|
|
||||||
if (!envelope.isFriendRequest() && message.isUnpairingRequest()) {
|
if (!content.isFriendRequest() && message.isUnpairingRequest()) {
|
||||||
// Make sure we got the request from our primary device
|
// Make sure we got the request from our primary device
|
||||||
String ourPrimaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
|
String ourPrimaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
|
||||||
if (ourPrimaryDevice != null && ourPrimaryDevice.equals(content.getSender())) {
|
if (ourPrimaryDevice != null && ourPrimaryDevice.equals(content.getSender())) {
|
||||||
@ -363,7 +359,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Loki - Handle friend request logic if needed
|
// Loki - Handle friend request logic if needed
|
||||||
updateFriendRequestStatusIfNeeded(envelope, content, message);
|
updateFriendRequestStatusIfNeeded(content, message);
|
||||||
}
|
}
|
||||||
} else if (content.getSyncMessage().isPresent()) {
|
} else if (content.getSyncMessage().isPresent()) {
|
||||||
TextSecurePreferences.setMultiDevice(context, true);
|
TextSecurePreferences.setMultiDevice(context, true);
|
||||||
@ -404,8 +400,8 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Loki - Handle session reset logic
|
// Loki - Handle session reset logic
|
||||||
if (!envelope.isFriendRequest()) {
|
if (!content.isFriendRequest()) {
|
||||||
cipher.handleSessionResetRequestIfNeeded(envelope, cipher.getSessionStatus(envelope));
|
cipher.handleSessionResetRequestIfNeeded(content, cipher.getSessionStatus(content));
|
||||||
}
|
}
|
||||||
} catch (ProtocolInvalidVersionException e) {
|
} catch (ProtocolInvalidVersionException e) {
|
||||||
Log.w(TAG, e);
|
Log.w(TAG, e);
|
||||||
@ -1109,26 +1105,26 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
ApplicationContext.getInstance(context).getJobManager().add(new RetrieveProfileAvatarJob(primaryDevice, url));
|
ApplicationContext.getInstance(context).getJobManager().add(new RetrieveProfileAvatarJob(primaryDevice, url));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handlePairingMessage(@NonNull PairingAuthorisation authorisation, @NonNull SignalServiceEnvelope envelope, @NonNull SignalServiceContent content) {
|
private void handlePairingMessage(@NonNull PairingAuthorisation authorisation, @NonNull SignalServiceContent content) {
|
||||||
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context);
|
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context);
|
||||||
if (authorisation.getType() == PairingAuthorisation.Type.REQUEST) {
|
if (authorisation.getType() == PairingAuthorisation.Type.REQUEST) {
|
||||||
handlePairingRequestMessage(authorisation, envelope, content);
|
handlePairingRequestMessage(authorisation, content);
|
||||||
} else if (authorisation.getSecondaryDevicePublicKey().equals(userHexEncodedPublicKey)) {
|
} else if (authorisation.getSecondaryDevicePublicKey().equals(userHexEncodedPublicKey)) {
|
||||||
handlePairingAuthorisationMessage(authorisation, envelope, content);
|
handlePairingAuthorisationMessage(authorisation, content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handlePairingRequestMessage(@NonNull PairingAuthorisation authorisation, @NonNull SignalServiceEnvelope envelope, @NonNull SignalServiceContent content) {
|
private void handlePairingRequestMessage(@NonNull PairingAuthorisation authorisation, @NonNull SignalServiceContent content) {
|
||||||
boolean isValid = isValidPairingMessage(authorisation);
|
boolean isValid = isValidPairingMessage(authorisation);
|
||||||
DeviceLinkingSession linkingSession = DeviceLinkingSession.Companion.getShared();
|
DeviceLinkingSession linkingSession = DeviceLinkingSession.Companion.getShared();
|
||||||
if (isValid && linkingSession.isListeningForLinkingRequests()) {
|
if (isValid && linkingSession.isListeningForLinkingRequests()) {
|
||||||
// Loki - If we successfully received a request then we should store the PreKeyBundle
|
// Loki - If we successfully received a request then we should store the PreKeyBundle
|
||||||
storePreKeyBundleIfNeeded(envelope, content);
|
storePreKeyBundleIfNeeded(content);
|
||||||
linkingSession.processLinkingRequest(authorisation);
|
linkingSession.processLinkingRequest(authorisation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handlePairingAuthorisationMessage(@NonNull PairingAuthorisation authorisation, @NonNull SignalServiceEnvelope envelope, @NonNull SignalServiceContent content) {
|
private void handlePairingAuthorisationMessage(@NonNull PairingAuthorisation authorisation, @NonNull SignalServiceContent content) {
|
||||||
// Prepare
|
// Prepare
|
||||||
boolean isSecondaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context) != null;
|
boolean isSecondaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context) != null;
|
||||||
if (isSecondaryDevice) {
|
if (isSecondaryDevice) {
|
||||||
@ -1147,7 +1143,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
if (authorisation.getType() != PairingAuthorisation.Type.GRANT) { return; }
|
if (authorisation.getType() != PairingAuthorisation.Type.GRANT) { return; }
|
||||||
Log.d("Loki", "Received pairing authorisation message from: " + authorisation.getPrimaryDevicePublicKey() + ".");
|
Log.d("Loki", "Received pairing authorisation message from: " + authorisation.getPrimaryDevicePublicKey() + ".");
|
||||||
// Save PreKeyBundle if for whatever reason we got one
|
// Save PreKeyBundle if for whatever reason we got one
|
||||||
storePreKeyBundleIfNeeded(envelope, content);
|
storePreKeyBundleIfNeeded(content);
|
||||||
// Process
|
// Process
|
||||||
DeviceLinkingSession.Companion.getShared().processLinkingAuthorization(authorisation);
|
DeviceLinkingSession.Companion.getShared().processLinkingAuthorization(authorisation);
|
||||||
// Store the primary device's public key
|
// Store the primary device's public key
|
||||||
@ -1188,7 +1184,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void storePreKeyBundleIfNeeded(@NonNull SignalServiceEnvelope envelope, @NonNull SignalServiceContent content) {
|
private void storePreKeyBundleIfNeeded(@NonNull SignalServiceContent content) {
|
||||||
Recipient sender = Recipient.from(context, Address.fromSerialized(content.getSender()), false);
|
Recipient sender = Recipient.from(context, Address.fromSerialized(content.getSender()), false);
|
||||||
if (!sender.isGroupRecipient() && content.lokiServiceMessage.isPresent()) {
|
if (!sender.isGroupRecipient() && content.lokiServiceMessage.isPresent()) {
|
||||||
LokiServiceMessage lokiMessage = content.lokiServiceMessage.get();
|
LokiServiceMessage lokiMessage = content.lokiServiceMessage.get();
|
||||||
@ -1205,7 +1201,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
lokiPreKeyBundleDatabase.setPreKeyBundle(content.getSender(), preKeyBundle);
|
lokiPreKeyBundleDatabase.setPreKeyBundle(content.getSender(), preKeyBundle);
|
||||||
|
|
||||||
// Loki - If we received a friend request, but we were already friends with this user, then reset the session
|
// Loki - If we received a friend request, but we were already friends with this user, then reset the session
|
||||||
if (envelope.isFriendRequest()) {
|
if (content.isFriendRequest()) {
|
||||||
long threadID = threadDatabase.getThreadIdIfExistsFor(sender);
|
long threadID = threadDatabase.getThreadIdIfExistsFor(sender);
|
||||||
if (lokiThreadDatabase.getFriendRequestStatus(threadID) == LokiThreadFriendRequestStatus.FRIENDS) {
|
if (lokiThreadDatabase.getFriendRequestStatus(threadID) == LokiThreadFriendRequestStatus.FRIENDS) {
|
||||||
resetSession(content.getSender(), threadID);
|
resetSession(content.getSender(), threadID);
|
||||||
@ -1218,9 +1214,9 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void acceptFriendRequestIfNeeded(@NonNull SignalServiceEnvelope envelope, @NonNull SignalServiceContent content) {
|
private void acceptFriendRequestIfNeeded(@NonNull SignalServiceContent content) {
|
||||||
// If we get anything other than a friend request, we can assume that we have a session with the other user
|
// If we get anything other than a friend request, we can assume that we have a session with the other user
|
||||||
if (envelope.isFriendRequest() || isGroupChatMessage(content)) { return; }
|
if (content.isFriendRequest() || isGroupChatMessage(content)) { return; }
|
||||||
becomeFriendsWithContact(content.getSender(), true);
|
becomeFriendsWithContact(content.getSender(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1251,8 +1247,8 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateFriendRequestStatusIfNeeded(@NonNull SignalServiceEnvelope envelope, @NonNull SignalServiceContent content, @NonNull SignalServiceDataMessage message) {
|
private void updateFriendRequestStatusIfNeeded(@NonNull SignalServiceContent content, @NonNull SignalServiceDataMessage message) {
|
||||||
if (!envelope.isFriendRequest() || message.isGroupUpdate()) { return; }
|
if (!content.isFriendRequest() || message.isGroupUpdate()) { return; }
|
||||||
// This handles the case where another user sends us a regular message without authorisation
|
// This handles the case where another user sends us a regular message without authorisation
|
||||||
Promise<Boolean, Exception> promise = PromiseUtil.timeout(MultiDeviceUtilities.shouldAutomaticallyBecomeFriendsWithDevice(content.getSender(), context), 8000);
|
Promise<Boolean, Exception> promise = PromiseUtil.timeout(MultiDeviceUtilities.shouldAutomaticallyBecomeFriendsWithDevice(content.getSender(), context), 8000);
|
||||||
boolean shouldBecomeFriends = PromiseUtil.get(promise, false);
|
boolean shouldBecomeFriends = PromiseUtil.get(promise, false);
|
||||||
|
@ -280,26 +280,7 @@ public abstract class PushSendJob extends SendJob {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void rotateSenderCertificateIfNecessary() throws IOException {
|
protected void rotateSenderCertificateIfNecessary() throws IOException {
|
||||||
try {
|
// Loki - We don't need verification on sender certificates
|
||||||
byte[] certificateBytes = TextSecurePreferences.getUnidentifiedAccessCertificate(context);
|
|
||||||
|
|
||||||
if (certificateBytes == null) {
|
|
||||||
throw new InvalidCertificateException("No certificate was present.");
|
|
||||||
}
|
|
||||||
|
|
||||||
SenderCertificate certificate = new SenderCertificate(certificateBytes);
|
|
||||||
|
|
||||||
if (System.currentTimeMillis() > (certificate.getExpiration() - CERTIFICATE_EXPIRATION_BUFFER)) {
|
|
||||||
throw new InvalidCertificateException("Certificate is expired, or close to it. Expires on: " + certificate.getExpiration() + ", currently: " + System.currentTimeMillis());
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.d(TAG, "Certificate is valid.");
|
|
||||||
} catch (InvalidCertificateException e) {
|
|
||||||
Log.w(TAG, "Certificate was invalid at send time. Fetching a new one.", e);
|
|
||||||
RotateCertificateJob certificateJob = new RotateCertificateJob(context);
|
|
||||||
ApplicationContext.getInstance(context).injectDependencies(certificateJob);
|
|
||||||
certificateJob.onRun();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected SignalServiceSyncMessage buildSelfSendSyncMessage(@NonNull Context context, @NonNull SignalServiceDataMessage message, Optional<UnidentifiedAccessPair> syncAccess) {
|
protected SignalServiceSyncMessage buildSelfSendSyncMessage(@NonNull Context context, @NonNull SignalServiceDataMessage message, Optional<UnidentifiedAccessPair> syncAccess) {
|
||||||
|
@ -65,7 +65,7 @@ class LokiRSSFeedPoller(private val context: Context, private val feed: LokiRSSF
|
|||||||
val id = feed.id.toByteArray()
|
val id = feed.id.toByteArray()
|
||||||
val x1 = SignalServiceGroup(SignalServiceGroup.Type.UPDATE, id, null, null, null)
|
val x1 = SignalServiceGroup(SignalServiceGroup.Type.UPDATE, id, null, null, null)
|
||||||
val x2 = SignalServiceDataMessage(timestamp, x1, null, body)
|
val x2 = SignalServiceDataMessage(timestamp, x1, null, body)
|
||||||
val x3 = SignalServiceContent(x2, "Loki", SignalServiceAddress.DEFAULT_DEVICE_ID, timestamp, false)
|
val x3 = SignalServiceContent(x2, "Loki", SignalServiceAddress.DEFAULT_DEVICE_ID, timestamp, false, false)
|
||||||
PushDecryptJob(context).handleTextMessage(x3, x2, Optional.absent(), Optional.absent())
|
PushDecryptJob(context).handleTextMessage(x3, x2, Optional.absent(), Optional.absent())
|
||||||
}
|
}
|
||||||
}.fail { exception ->
|
}.fail { exception ->
|
||||||
|
@ -13,6 +13,7 @@ import org.thoughtcrime.securesms.ApplicationContext
|
|||||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
|
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
|
||||||
import org.thoughtcrime.securesms.crypto.PreKeyUtil
|
import org.thoughtcrime.securesms.crypto.PreKeyUtil
|
||||||
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil
|
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil
|
||||||
|
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil
|
||||||
import org.thoughtcrime.securesms.database.Address
|
import org.thoughtcrime.securesms.database.Address
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.thoughtcrime.securesms.logging.Log
|
import org.thoughtcrime.securesms.logging.Log
|
||||||
@ -128,7 +129,8 @@ fun sendPairingAuthorisationMessage(context: Context, contactHexEncodedPublicKey
|
|||||||
|
|
||||||
return try {
|
return try {
|
||||||
Log.d("Loki", "Sending authorisation message to: $contactHexEncodedPublicKey.")
|
Log.d("Loki", "Sending authorisation message to: $contactHexEncodedPublicKey.")
|
||||||
val result = messageSender.sendMessage(0, address, Optional.absent<UnidentifiedAccessPair>(), message.build())
|
val udAccess = UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(contactHexEncodedPublicKey), false))
|
||||||
|
val result = messageSender.sendMessage(0, address, udAccess, message.build())
|
||||||
if (result.success == null) {
|
if (result.success == null) {
|
||||||
val exception = when {
|
val exception = when {
|
||||||
result.isNetworkFailure -> "Failed to send authorisation message due to a network error."
|
result.isNetworkFailure -> "Failed to send authorisation message due to a network error."
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
package org.thoughtcrime.securesms.loki
|
package org.thoughtcrime.securesms.loki
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.ApplicationContext
|
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.DatabaseFactory
|
||||||
import org.thoughtcrime.securesms.jobmanager.Data
|
import org.thoughtcrime.securesms.jobmanager.Data
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job
|
import org.thoughtcrime.securesms.jobmanager.Job
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
|
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
|
||||||
import org.thoughtcrime.securesms.jobs.BaseJob
|
import org.thoughtcrime.securesms.jobs.BaseJob
|
||||||
import org.thoughtcrime.securesms.logging.Log
|
import org.thoughtcrime.securesms.logging.Log
|
||||||
|
import org.thoughtcrime.securesms.recipients.Recipient
|
||||||
import org.whispersystems.libsignal.util.guava.Optional
|
import org.whispersystems.libsignal.util.guava.Optional
|
||||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair
|
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage
|
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage
|
||||||
@ -99,7 +102,8 @@ class PushBackgroundMessageSendJob private constructor(
|
|||||||
val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender()
|
val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender()
|
||||||
val address = SignalServiceAddress(recipient)
|
val address = SignalServiceAddress(recipient)
|
||||||
try {
|
try {
|
||||||
messageSender.sendMessage(-1, address, Optional.absent<UnidentifiedAccessPair>(), dataMessage.build()) // The message ID doesn't matter
|
val udAccess = UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(recipient), false))
|
||||||
|
messageSender.sendMessage(-1, address, udAccess, dataMessage.build()) // The message ID doesn't matter
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.d("Loki", "Failed to send background message to: ${recipient}.")
|
Log.d("Loki", "Failed to send background message to: ${recipient}.")
|
||||||
throw e
|
throw e
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
package org.thoughtcrime.securesms.loki
|
package org.thoughtcrime.securesms.loki
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil
|
||||||
import org.thoughtcrime.securesms.database.Address
|
import org.thoughtcrime.securesms.database.Address
|
||||||
import org.thoughtcrime.securesms.dependencies.InjectableType
|
import org.thoughtcrime.securesms.dependencies.InjectableType
|
||||||
import org.thoughtcrime.securesms.jobmanager.Data
|
import org.thoughtcrime.securesms.jobmanager.Data
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job
|
import org.thoughtcrime.securesms.jobmanager.Job
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
|
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
|
||||||
import org.thoughtcrime.securesms.jobs.BaseJob
|
import org.thoughtcrime.securesms.jobs.BaseJob
|
||||||
|
import org.thoughtcrime.securesms.recipients.Recipient
|
||||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender
|
import org.whispersystems.signalservice.api.SignalServiceMessageSender
|
||||||
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException
|
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
||||||
@ -63,7 +65,8 @@ class PushMessageSyncSendJob private constructor(
|
|||||||
public override fun onRun() {
|
public override fun onRun() {
|
||||||
// Don't send sync messages to a group
|
// Don't send sync messages to a group
|
||||||
if (recipient.isGroup || recipient.isEmail) { return }
|
if (recipient.isGroup || recipient.isEmail) { return }
|
||||||
messageSender.lokiSendSyncMessage(messageID, SignalServiceAddress(recipient.toPhoneString()), timestamp, message, ttl)
|
val unidentifiedAccess = UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, recipient, false))
|
||||||
|
messageSender.lokiSendSyncMessage(messageID, SignalServiceAddress(recipient.toPhoneString()), unidentifiedAccess, timestamp, message, ttl)
|
||||||
}
|
}
|
||||||
|
|
||||||
public override fun onShouldRetry(e: Exception): Boolean {
|
public override fun onShouldRetry(e: Exception): Boolean {
|
||||||
|
@ -164,7 +164,7 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki
|
|||||||
|
|
||||||
val senderPublicKey = primaryDevice ?: message.hexEncodedPublicKey
|
val senderPublicKey = primaryDevice ?: message.hexEncodedPublicKey
|
||||||
val serviceDataMessage = getDataMessage(message)
|
val serviceDataMessage = getDataMessage(message)
|
||||||
val serviceContent = SignalServiceContent(serviceDataMessage, senderPublicKey, SignalServiceAddress.DEFAULT_DEVICE_ID, message.timestamp, false)
|
val serviceContent = SignalServiceContent(serviceDataMessage, senderPublicKey, SignalServiceAddress.DEFAULT_DEVICE_ID, message.timestamp, false, false)
|
||||||
if (serviceDataMessage.quote.isPresent || (serviceDataMessage.attachments.isPresent && serviceDataMessage.attachments.get().size > 0) || serviceDataMessage.previews.isPresent) {
|
if (serviceDataMessage.quote.isPresent || (serviceDataMessage.attachments.isPresent && serviceDataMessage.attachments.get().size > 0) || serviceDataMessage.previews.isPresent) {
|
||||||
PushDecryptJob(context).handleMediaMessage(serviceContent, serviceDataMessage, Optional.absent(), Optional.of(message.serverID))
|
PushDecryptJob(context).handleMediaMessage(serviceContent, serviceDataMessage, Optional.absent(), Optional.of(message.serverID))
|
||||||
} else {
|
} else {
|
||||||
|
@ -101,7 +101,7 @@ public class Recipient implements RecipientModifiedListener {
|
|||||||
private String notificationChannel;
|
private String notificationChannel;
|
||||||
private boolean forceSmsSelection;
|
private boolean forceSmsSelection;
|
||||||
|
|
||||||
private @NonNull UnidentifiedAccessMode unidentifiedAccessMode = UnidentifiedAccessMode.DISABLED;
|
private @NonNull UnidentifiedAccessMode unidentifiedAccessMode = UnidentifiedAccessMode.ENABLED;
|
||||||
|
|
||||||
@SuppressWarnings("ConstantConditions")
|
@SuppressWarnings("ConstantConditions")
|
||||||
public static @NonNull Recipient from(@NonNull Context context, @NonNull Address address, boolean asynchronous) {
|
public static @NonNull Recipient from(@NonNull Context context, @NonNull Address address, boolean asynchronous) {
|
||||||
|
@ -601,7 +601,9 @@ public class TextSecurePreferences {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isUnidentifiedDeliveryEnabled(Context context) {
|
public static boolean isUnidentifiedDeliveryEnabled(Context context) {
|
||||||
return getBooleanPreference(context, UNIDENTIFIED_DELIVERY_ENABLED, true);
|
// Loki - Always enable unidentified sender
|
||||||
|
return true;
|
||||||
|
// return getBooleanPreference(context, UNIDENTIFIED_DELIVERY_ENABLED, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long getSignedPreKeyRotationTime(Context context) {
|
public static long getSignedPreKeyRotationTime(Context context) {
|
||||||
|
Loading…
Reference in New Issue
Block a user