WIP: clean up signal protocol

pull/451/head
Ryan ZHAO 3 years ago
parent 8cc78e8f4c
commit b34809f4d5

@ -7,35 +7,22 @@ import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread; import androidx.annotation.WorkerThread;
import org.session.libsignal.metadata.SignalProtos; import org.session.libsignal.metadata.SignalProtos;
import org.session.libsignal.metadata.certificate.CertificateValidator;
import org.session.libsignal.metadata.certificate.InvalidCertificateException;
import org.session.libsignal.utilities.logging.Log; import org.session.libsignal.utilities.logging.Log;
import org.session.libsession.messaging.threads.recipients.Recipient; import org.session.libsession.messaging.threads.recipients.Recipient;
import org.session.libsession.utilities.TextSecurePreferences; import org.session.libsession.utilities.TextSecurePreferences;
import org.session.libsession.utilities.Util; import org.session.libsession.utilities.Util;
import org.session.libsignal.libsignal.util.guava.Optional; import org.session.libsignal.libsignal.util.guava.Optional;
import org.session.libsignal.service.api.crypto.UnidentifiedAccess; import org.session.libsignal.service.api.crypto.UnidentifiedAccess;
import org.session.libsignal.service.api.crypto.UnidentifiedAccessPair;
import org.session.libsignal.service.api.push.SignalServiceAddress; import org.session.libsignal.service.api.push.SignalServiceAddress;
public class UnidentifiedAccessUtil { public class UnidentifiedAccessUtil {
private static final String TAG = UnidentifiedAccessUtil.class.getSimpleName(); private static final String TAG = UnidentifiedAccessUtil.class.getSimpleName();
public static CertificateValidator getCertificateValidator() {
return new CertificateValidator();
}
@WorkerThread @WorkerThread
public static Optional<UnidentifiedAccessPair> getAccessFor(@NonNull Context context, public static Optional<UnidentifiedAccess> getAccessFor(@NonNull Context context,
@NonNull Recipient recipient) @NonNull Recipient recipient)
{ {
if (!TextSecurePreferences.isUnidentifiedDeliveryEnabled(context)) {
Log.i(TAG, "Unidentified delivery is disabled. [other]");
return Optional.absent();
}
try {
byte[] theirUnidentifiedAccessKey = getTargetUnidentifiedAccessKey(recipient); byte[] theirUnidentifiedAccessKey = getTargetUnidentifiedAccessKey(recipient);
byte[] ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(context); byte[] ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(context);
byte[] ourUnidentifiedAccessCertificate = getUnidentifiedAccessCertificate(context); byte[] ourUnidentifiedAccessCertificate = getUnidentifiedAccessCertificate(context);
@ -52,26 +39,13 @@ public class UnidentifiedAccessUtil {
ourUnidentifiedAccessKey != null && ourUnidentifiedAccessKey != null &&
ourUnidentifiedAccessCertificate != null) ourUnidentifiedAccessCertificate != null)
{ {
return Optional.of(new UnidentifiedAccessPair(new UnidentifiedAccess(theirUnidentifiedAccessKey, return Optional.of(new UnidentifiedAccess(theirUnidentifiedAccessKey));
ourUnidentifiedAccessCertificate),
new UnidentifiedAccess(ourUnidentifiedAccessKey,
ourUnidentifiedAccessCertificate)));
}
return Optional.absent();
} catch (InvalidCertificateException e) {
Log.w(TAG, e);
return Optional.absent();
}
} }
public static Optional<UnidentifiedAccessPair> getAccessForSync(@NonNull Context context) {
if (!TextSecurePreferences.isUnidentifiedDeliveryEnabled(context)) {
Log.i(TAG, "Unidentified delivery is disabled. [self]");
return Optional.absent(); return Optional.absent();
} }
try { public static Optional<UnidentifiedAccess> getAccessForSync(@NonNull Context context) {
byte[] ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(context); byte[] ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(context);
byte[] ourUnidentifiedAccessCertificate = getUnidentifiedAccessCertificate(context); byte[] ourUnidentifiedAccessCertificate = getUnidentifiedAccessCertificate(context);
@ -80,17 +54,10 @@ public class UnidentifiedAccessUtil {
} }
if (ourUnidentifiedAccessKey != null && ourUnidentifiedAccessCertificate != null) { if (ourUnidentifiedAccessKey != null && ourUnidentifiedAccessCertificate != null) {
return Optional.of(new UnidentifiedAccessPair(new UnidentifiedAccess(ourUnidentifiedAccessKey, return Optional.of(new UnidentifiedAccess(ourUnidentifiedAccessKey));
ourUnidentifiedAccessCertificate),
new UnidentifiedAccess(ourUnidentifiedAccessKey,
ourUnidentifiedAccessCertificate)));
} }
return Optional.absent(); return Optional.absent();
} catch (InvalidCertificateException e) {
Log.w(TAG, e);
return Optional.absent();
}
} }
public static @NonNull byte[] getSelfUnidentifiedAccessKey(@NonNull Context context) { public static @NonNull byte[] getSelfUnidentifiedAccessKey(@NonNull Context context) {

@ -16,6 +16,7 @@ import org.session.libsession.messaging.threads.Address;
import org.session.libsession.utilities.GroupUtil; import org.session.libsession.utilities.GroupUtil;
import org.session.libsession.utilities.TextSecurePreferences; import org.session.libsession.utilities.TextSecurePreferences;
import org.session.libsignal.service.api.crypto.UnidentifiedAccess;
import org.session.libsignal.service.internal.push.SignalServiceProtos; import org.session.libsignal.service.internal.push.SignalServiceProtos;
import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
@ -37,7 +38,6 @@ import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException; import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import org.session.libsignal.libsignal.util.guava.Optional; import org.session.libsignal.libsignal.util.guava.Optional;
import org.session.libsignal.service.api.SignalServiceMessageSender; import org.session.libsignal.service.api.SignalServiceMessageSender;
import org.session.libsignal.service.api.crypto.UnidentifiedAccessPair;
import org.session.libsignal.service.api.crypto.UntrustedIdentityException; import org.session.libsignal.service.api.crypto.UntrustedIdentityException;
import org.session.libsignal.service.api.messages.SendMessageResult; import org.session.libsignal.service.api.messages.SendMessageResult;
import org.session.libsignal.service.api.messages.SignalServiceAttachment; import org.session.libsignal.service.api.messages.SignalServiceAttachment;
@ -236,7 +236,7 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
Address address = message.getRecipient().getAddress(); Address address = message.getRecipient().getAddress();
List<SignalServiceAddress> addresses = Stream.of(destinations).map(this::getPushAddress).toList(); List<SignalServiceAddress> addresses = Stream.of(destinations).map(this::getPushAddress).toList();
List<Optional<UnidentifiedAccessPair>> unidentifiedAccess = Stream.of(addresses) List<Optional<UnidentifiedAccess>> unidentifiedAccess = Stream.of(addresses)
.map(a -> Address.fromSerialized(a.getNumber())) .map(a -> Address.fromSerialized(a.getNumber()))
.map(a -> Recipient.from(context, a, false)) .map(a -> Recipient.from(context, a, false))
.map(recipient -> UnidentifiedAccessUtil.getAccessFor(context, recipient)) .map(recipient -> UnidentifiedAccessUtil.getAccessFor(context, recipient))

@ -13,6 +13,7 @@ import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAt
import org.session.libsession.messaging.threads.recipients.Recipient.UnidentifiedAccessMode; import org.session.libsession.messaging.threads.recipients.Recipient.UnidentifiedAccessMode;
import org.session.libsession.utilities.TextSecurePreferences; import org.session.libsession.utilities.TextSecurePreferences;
import org.session.libsignal.service.api.crypto.UnidentifiedAccess;
import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
@ -33,7 +34,6 @@ import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException; import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import org.session.libsignal.libsignal.util.guava.Optional; import org.session.libsignal.libsignal.util.guava.Optional;
import org.session.libsignal.service.api.SignalServiceMessageSender; import org.session.libsignal.service.api.SignalServiceMessageSender;
import org.session.libsignal.service.api.crypto.UnidentifiedAccessPair;
import org.session.libsignal.service.api.crypto.UntrustedIdentityException; import org.session.libsignal.service.api.crypto.UntrustedIdentityException;
import org.session.libsignal.service.api.messages.SendMessageResult; import org.session.libsignal.service.api.messages.SendMessageResult;
import org.session.libsignal.service.api.messages.SignalServiceAttachment; import org.session.libsignal.service.api.messages.SignalServiceAttachment;
@ -181,7 +181,6 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
DatabaseFactory.getMmsSmsDatabase(context).incrementReadReceiptCount(id, System.currentTimeMillis()); DatabaseFactory.getMmsSmsDatabase(context).incrementReadReceiptCount(id, System.currentTimeMillis());
} }
if (TextSecurePreferences.isUnidentifiedDeliveryEnabled(context)) {
if (unidentified && accessMode == UnidentifiedAccessMode.UNKNOWN && profileKey == null) { if (unidentified && accessMode == UnidentifiedAccessMode.UNKNOWN && profileKey == null) {
log(TAG, "Marking recipient as UD-unrestricted following a UD send."); log(TAG, "Marking recipient as UD-unrestricted following a UD send.");
DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.UNRESTRICTED); DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.UNRESTRICTED);
@ -192,7 +191,6 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
log(TAG, "Marking recipient as UD-disabled following a non-UD send."); log(TAG, "Marking recipient as UD-disabled following a non-UD send.");
DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.DISABLED); DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.DISABLED);
} }
}
if (messageId > 0 && message.getExpiresIn() > 0 && !message.isExpirationUpdate()) { if (messageId > 0 && message.getExpiresIn() > 0 && !message.isExpirationUpdate()) {
database.markExpireStarted(messageId); database.markExpireStarted(messageId);
@ -207,12 +205,6 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
database.markAsPendingInsecureSmsFallback(messageId); database.markAsPendingInsecureSmsFallback(messageId);
notifyMediaMessageDeliveryFailed(context, messageId); notifyMediaMessageDeliveryFailed(context, messageId);
} }
} catch (UntrustedIdentityException uie) {
warn(TAG, "Failure", uie);
if (messageId >= 0) {
database.addMismatchedIdentity(messageId, Address.fromSerialized(uie.getE164Number()), uie.getIdentityKey());
database.markAsSentFailed(messageId);
}
} catch (SnodeAPI.Error e) { } catch (SnodeAPI.Error e) {
Log.d("Loki", "Couldn't send message due to error: " + e.getDescription()); Log.d("Loki", "Couldn't send message due to error: " + e.getDescription());
if (messageId >= 0) { if (messageId >= 0) {
@ -238,8 +230,7 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
} }
private boolean deliver(OutgoingMediaMessage message) private boolean deliver(OutgoingMediaMessage message)
throws RetryLaterException, InsecureFallbackApprovalException, UntrustedIdentityException, throws RetryLaterException, InsecureFallbackApprovalException, UndeliverableMessageException, SnodeAPI.Error
UndeliverableMessageException, SnodeAPI.Error
{ {
try { try {
Recipient recipient = Recipient.from(context, destination, false); Recipient recipient = Recipient.from(context, destination, false);
@ -250,11 +241,10 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
List<SignalServiceAttachment> serviceAttachments = getAttachmentPointersFor(attachments); List<SignalServiceAttachment> serviceAttachments = getAttachmentPointersFor(attachments);
Optional<byte[]> profileKey = getProfileKey(message.getRecipient()); Optional<byte[]> profileKey = getProfileKey(message.getRecipient());
Optional<SignalServiceDataMessage.Quote> quote = getQuoteFor(message); Optional<SignalServiceDataMessage.Quote> quote = getQuoteFor(message);
Optional<SignalServiceDataMessage.Sticker> sticker = getStickerFor(message);
List<SharedContact> sharedContacts = getSharedContactsFor(message); List<SharedContact> sharedContacts = getSharedContactsFor(message);
List<Preview> previews = getPreviewsFor(message); List<Preview> previews = getPreviewsFor(message);
Optional<UnidentifiedAccessPair> unidentifiedAccessPair = UnidentifiedAccessUtil.getAccessFor(context, recipient); Optional<UnidentifiedAccess> unidentifiedAccessPair = UnidentifiedAccessUtil.getAccessFor(context, recipient);
SignalServiceDataMessage mediaMessage = SignalServiceDataMessage.newBuilder() SignalServiceDataMessage mediaMessage = SignalServiceDataMessage.newBuilder()
.withBody(message.getBody()) .withBody(message.getBody())
@ -298,7 +288,7 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
try { try {
// send to ourselves to sync multi-device // send to ourselves to sync multi-device
Optional<UnidentifiedAccessPair> syncAccess = UnidentifiedAccessUtil.getAccessForSync(context); Optional<UnidentifiedAccess> syncAccess = UnidentifiedAccessUtil.getAccessForSync(context);
SendMessageResult selfSendResult = messageSender.sendMessage(messageId, localAddress, syncAccess, mediaSelfSendMessage); SendMessageResult selfSendResult = messageSender.sendMessage(messageId, localAddress, syncAccess, mediaSelfSendMessage);
if (selfSendResult.getLokiAPIError() != null) { if (selfSendResult.getLokiAPIError() != null) {
throw selfSendResult.getLokiAPIError(); throw selfSendResult.getLokiAPIError();

@ -26,7 +26,6 @@ import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
import org.thoughtcrime.securesms.transport.RetryLaterException; import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.session.libsignal.libsignal.util.guava.Optional; import org.session.libsignal.libsignal.util.guava.Optional;
import org.session.libsignal.service.api.SignalServiceMessageSender; import org.session.libsignal.service.api.SignalServiceMessageSender;
import org.session.libsignal.service.api.crypto.UnidentifiedAccessPair;
import org.session.libsignal.service.api.crypto.UntrustedIdentityException; import org.session.libsignal.service.api.crypto.UntrustedIdentityException;
import org.session.libsignal.service.api.messages.SendMessageResult; import org.session.libsignal.service.api.messages.SendMessageResult;
import org.session.libsignal.service.api.messages.SignalServiceDataMessage; import org.session.libsignal.service.api.messages.SignalServiceDataMessage;
@ -124,7 +123,6 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
DatabaseFactory.getMmsSmsDatabase(context).incrementReadReceiptCount(id, System.currentTimeMillis()); DatabaseFactory.getMmsSmsDatabase(context).incrementReadReceiptCount(id, System.currentTimeMillis());
} }
if (TextSecurePreferences.isUnidentifiedDeliveryEnabled(context)) {
if (unidentified && accessMode == UnidentifiedAccessMode.UNKNOWN && profileKey == null) { if (unidentified && accessMode == UnidentifiedAccessMode.UNKNOWN && profileKey == null) {
log(TAG, "Marking recipient as UD-unrestricted following a UD send."); log(TAG, "Marking recipient as UD-unrestricted following a UD send.");
DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.UNRESTRICTED); DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.UNRESTRICTED);
@ -135,7 +133,6 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
log(TAG, "Marking recipient as UD-disabled following a non-UD send."); log(TAG, "Marking recipient as UD-disabled following a non-UD send.");
DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.DISABLED); DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.DISABLED);
} }
}
if (record.getExpiresIn() > 0 && messageId >= 0) { if (record.getExpiresIn() > 0 && messageId >= 0) {
database.markExpireStarted(messageId); database.markExpireStarted(messageId);
@ -196,15 +193,10 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
SignalServiceAddress address = getPushAddress(recipient.getAddress()); SignalServiceAddress address = getPushAddress(recipient.getAddress());
SignalServiceAddress localAddress = new SignalServiceAddress(userPublicKey); SignalServiceAddress localAddress = new SignalServiceAddress(userPublicKey);
Optional<byte[]> profileKey = getProfileKey(recipient); Optional<byte[]> profileKey = getProfileKey(recipient);
Optional<UnidentifiedAccessPair> unidentifiedAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient); Optional<UnidentifiedAccess> unidentifiedAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient);
log(TAG, "Have access key to use: " + unidentifiedAccess.isPresent()); log(TAG, "Have access key to use: " + unidentifiedAccess.isPresent());
// PreKeyBundle preKeyBundle = null;
// if (message.isEndSession()) {
// preKeyBundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(destination.serialize());
// }
SignalServiceDataMessage textSecureMessage = SignalServiceDataMessage.newBuilder() SignalServiceDataMessage textSecureMessage = SignalServiceDataMessage.newBuilder()
.withTimestamp(message.getDateSent()) .withTimestamp(message.getDateSent())
.withBody(message.getBody()) .withBody(message.getBody())
@ -237,7 +229,7 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
try { try {
// send to ourselves to sync multi-device // send to ourselves to sync multi-device
Optional<UnidentifiedAccessPair> syncAccess = UnidentifiedAccessUtil.getAccessForSync(context); Optional<UnidentifiedAccess> syncAccess = UnidentifiedAccessUtil.getAccessForSync(context);
SendMessageResult selfSendResult = messageSender.sendMessage(messageId, localAddress, syncAccess, textSecureSelfSendMessage); SendMessageResult selfSendResult = messageSender.sendMessage(messageId, localAddress, syncAccess, textSecureSelfSendMessage);
if (selfSendResult.getLokiAPIError() != null) { if (selfSendResult.getLokiAPIError() != null) {
throw selfSendResult.getLokiAPIError(); throw selfSendResult.getLokiAPIError();

@ -5,20 +5,19 @@ import androidx.annotation.NonNull;
import com.annimon.stream.Stream; import com.annimon.stream.Stream;
import org.session.libsession.messaging.jobs.Data; import org.session.libsession.messaging.jobs.Data;
import org.session.libsignal.libsignal.util.guava.Optional;
import org.session.libsignal.service.api.crypto.UnidentifiedAccess;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.Job;
import org.session.libsignal.utilities.logging.Log; import org.session.libsignal.utilities.logging.Log;
import org.session.libsignal.libsignal.util.guava.Optional;
import org.session.libsignal.service.api.SignalServiceMessageSender; import org.session.libsignal.service.api.SignalServiceMessageSender;
import org.session.libsignal.service.api.crypto.UnidentifiedAccessPair;
import org.session.libsignal.service.api.messages.SignalServiceTypingMessage; import org.session.libsignal.service.api.messages.SignalServiceTypingMessage;
import org.session.libsignal.service.api.messages.SignalServiceTypingMessage.Action; import org.session.libsignal.service.api.messages.SignalServiceTypingMessage.Action;
import org.session.libsignal.service.api.push.SignalServiceAddress; import org.session.libsignal.service.api.push.SignalServiceAddress;
import org.session.libsession.messaging.threads.recipients.Recipient; import org.session.libsession.messaging.threads.recipients.Recipient;
import org.session.libsession.utilities.GroupUtil;
import org.session.libsession.utilities.TextSecurePreferences; import org.session.libsession.utilities.TextSecurePreferences;
import java.util.Collections; import java.util.Collections;
@ -93,7 +92,7 @@ public class TypingSendJob extends BaseJob implements InjectableType {
} }
List<SignalServiceAddress> addresses = Stream.of(recipients).map(r -> new SignalServiceAddress(r.getAddress().serialize())).toList(); List<SignalServiceAddress> addresses = Stream.of(recipients).map(r -> new SignalServiceAddress(r.getAddress().serialize())).toList();
List<Optional<UnidentifiedAccessPair>> unidentifiedAccess = Stream.of(recipients).map(r -> UnidentifiedAccessUtil.getAccessFor(context, r)).toList(); List<Optional<UnidentifiedAccess>> unidentifiedAccess = Stream.of(recipients).map(r -> UnidentifiedAccessUtil.getAccessFor(context, r)).toList();
SignalServiceTypingMessage typingMessage = new SignalServiceTypingMessage(typing ? Action.STARTED : Action.STOPPED, System.currentTimeMillis()); SignalServiceTypingMessage typingMessage = new SignalServiceTypingMessage(typing ? Action.STARTED : Action.STOPPED, System.currentTimeMillis());
messageSender.sendTyping(addresses, unidentifiedAccess, typingMessage); messageSender.sendTyping(addresses, unidentifiedAccess, typingMessage);

@ -223,7 +223,7 @@ class ClosedGroupUpdateMessageSendJobV2 private constructor(parameters: Paramete
} }
try { try {
// isClosedGroup can always be false as it's only used in the context of legacy closed groups // isClosedGroup can always be false as it's only used in the context of legacy closed groups
messageSender.sendMessage(0, address, udAccess.get().targetUnidentifiedAccess, messageSender.sendMessage(0, address, udAccess,
sentTime, serializedContentMessage, false, ttl, sentTime, serializedContentMessage, false, ttl,
true, false, false, Optional.absent()) true, false, false, Optional.absent())
} catch (e: Exception) { } catch (e: Exception) {

@ -34,7 +34,7 @@ object MultiDeviceProtocol {
val recipient = recipient(context, userPublicKey) val recipient = recipient(context, userPublicKey)
val udAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient) val udAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient)
try { try {
messageSender.sendMessage(0, address, udAccess.get().targetUnidentifiedAccess, messageSender.sendMessage(0, address, udAccess,
Date().time, serializedMessage, false, configurationMessage.ttl.toInt(), Date().time, serializedMessage, false, configurationMessage.ttl.toInt(),
true, false, true, Optional.absent()) true, false, true, Optional.absent())
TextSecurePreferences.setLastConfigurationSyncTime(context, now) TextSecurePreferences.setLastConfigurationSyncTime(context, now)
@ -53,7 +53,7 @@ object MultiDeviceProtocol {
val recipient = recipient(context, userPublicKey) val recipient = recipient(context, userPublicKey)
val udAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient) val udAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient)
try { try {
messageSender.sendMessage(0, address, udAccess.get().targetUnidentifiedAccess, messageSender.sendMessage(0, address, udAccess,
Date().time, serializedMessage, false, configurationMessage.ttl.toInt(), Date().time, serializedMessage, false, configurationMessage.ttl.toInt(),
true, false, true, Optional.absent()) true, false, true, Optional.absent())
} catch (e: Exception) { } catch (e: Exception) {

@ -1,22 +1,20 @@
package org.session.libsession.messaging.utilities package org.session.libsession.messaging.utilities
import android.content.Context
import com.goterl.lazycode.lazysodium.LazySodiumAndroid import com.goterl.lazycode.lazysodium.LazySodiumAndroid
import com.goterl.lazycode.lazysodium.SodiumAndroid import com.goterl.lazycode.lazysodium.SodiumAndroid
import org.session.libsession.messaging.MessagingConfiguration import org.session.libsession.messaging.MessagingConfiguration
import org.session.libsession.utilities.TextSecurePreferences.isUniversalUnidentifiedAccess
import org.session.libsignal.utilities.logging.Log import org.session.libsession.utilities.Util.getSecretBytes
import org.session.libsignal.metadata.SignalProtos import org.session.libsignal.metadata.SignalProtos
import org.session.libsignal.metadata.certificate.InvalidCertificateException
import org.session.libsignal.service.api.crypto.UnidentifiedAccess import org.session.libsignal.service.api.crypto.UnidentifiedAccess
import org.session.libsignal.service.api.crypto.UnidentifiedAccessPair import org.session.libsignal.utilities.logging.Log
object UnidentifiedAccessUtil { object UnidentifiedAccessUtil {
private val TAG = UnidentifiedAccessUtil::class.simpleName private val TAG = UnidentifiedAccessUtil::class.simpleName
private val sodium by lazy { LazySodiumAndroid(SodiumAndroid()) } private val sodium by lazy { LazySodiumAndroid(SodiumAndroid()) }
fun getAccessFor(recipientPublicKey: String): UnidentifiedAccessPair? { fun getAccessFor(recipientPublicKey: String): UnidentifiedAccess? {
try {
val theirUnidentifiedAccessKey = getTargetUnidentifiedAccessKey(recipientPublicKey) val theirUnidentifiedAccessKey = getTargetUnidentifiedAccessKey(recipientPublicKey)
val ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey() val ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey()
val ourUnidentifiedAccessCertificate = getUnidentifiedAccessCertificate() val ourUnidentifiedAccessCertificate = getUnidentifiedAccessCertificate()
@ -25,15 +23,20 @@ object UnidentifiedAccessUtil {
" | Our access key present? " + (ourUnidentifiedAccessKey != null) + " | Our access key present? " + (ourUnidentifiedAccessKey != null) +
" | Our certificate present? " + (ourUnidentifiedAccessCertificate != null)) " | Our certificate present? " + (ourUnidentifiedAccessCertificate != null))
if (theirUnidentifiedAccessKey != null && ourUnidentifiedAccessKey != null && ourUnidentifiedAccessCertificate != null) { return if (theirUnidentifiedAccessKey != null && ourUnidentifiedAccessKey != null && ourUnidentifiedAccessCertificate != null) {
return UnidentifiedAccessPair(UnidentifiedAccess(theirUnidentifiedAccessKey, ourUnidentifiedAccessCertificate), UnidentifiedAccess(theirUnidentifiedAccessKey)
UnidentifiedAccess(ourUnidentifiedAccessKey, ourUnidentifiedAccessCertificate)) } else null
} }
return null
} catch (e: InvalidCertificateException) { fun getAccessForSync(context: Context): UnidentifiedAccess? {
Log.w(TAG, e) var ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey()
return null val ourUnidentifiedAccessCertificate = getUnidentifiedAccessCertificate()
if (isUniversalUnidentifiedAccess(context)) {
ourUnidentifiedAccessKey = getSecretBytes(16)
} }
return if (ourUnidentifiedAccessKey != null && ourUnidentifiedAccessCertificate != null) {
UnidentifiedAccess(ourUnidentifiedAccessKey)
} else null
} }
private fun getTargetUnidentifiedAccessKey(recipientPublicKey: String): ByteArray? { private fun getTargetUnidentifiedAccessKey(recipientPublicKey: String): ByteArray? {

@ -448,13 +448,6 @@ object TextSecurePreferences {
return getBooleanPreference(context, UNIVERSAL_UNIDENTIFIED_ACCESS, false) return getBooleanPreference(context, UNIVERSAL_UNIDENTIFIED_ACCESS, false)
} }
@JvmStatic
fun isUnidentifiedDeliveryEnabled(context: Context): Boolean {
// Loki - Always enable unidentified sender
return true
// return getBooleanPreference(context, UNIDENTIFIED_DELIVERY_ENABLED, true);
}
@JvmStatic @JvmStatic
fun getUpdateApkRefreshTime(context: Context): Long { fun getUpdateApkRefreshTime(context: Context): Long {
return getLongPreference(context, UPDATE_APK_REFRESH_TIME_PREF, 0L) return getLongPreference(context, UPDATE_APK_REFRESH_TIME_PREF, 0L)

@ -1,50 +0,0 @@
/**
* Copyright (C) 2014-2016 Open Whisper Systems
*
* Licensed according to the LICENSE file in this repository.
*/
package org.session.libsignal.libsignal.kdf;
import org.session.libsignal.libsignal.util.ByteUtil;
import java.text.ParseException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class DerivedMessageSecrets {
public static final int SIZE = 80;
private static final int CIPHER_KEY_LENGTH = 32;
private static final int MAC_KEY_LENGTH = 32;
private static final int IV_LENGTH = 16;
private final SecretKeySpec cipherKey;
private final SecretKeySpec macKey;
private final IvParameterSpec iv;
public DerivedMessageSecrets(byte[] okm) {
try {
byte[][] keys = ByteUtil.split(okm, CIPHER_KEY_LENGTH, MAC_KEY_LENGTH, IV_LENGTH);
this.cipherKey = new SecretKeySpec(keys[0], "AES");
this.macKey = new SecretKeySpec(keys[1], "HmacSHA256");
this.iv = new IvParameterSpec(keys[2]);
} catch (ParseException e) {
throw new AssertionError(e);
}
}
public SecretKeySpec getCipherKey() {
return cipherKey;
}
public SecretKeySpec getMacKey() {
return macKey;
}
public IvParameterSpec getIv() {
return iv;
}
}

@ -1,31 +0,0 @@
/**
* Copyright (C) 2014-2016 Open Whisper Systems
*
* Licensed according to the LICENSE file in this repository.
*/
package org.session.libsignal.libsignal.kdf;
import org.session.libsignal.libsignal.util.ByteUtil;
public class DerivedRootSecrets {
public static final int SIZE = 64;
private final byte[] rootKey;
private final byte[] chainKey;
public DerivedRootSecrets(byte[] okm) {
byte[][] keys = ByteUtil.split(okm, 32, 32);
this.rootKey = keys[0];
this.chainKey = keys[1];
}
public byte[] getRootKey() {
return rootKey;
}
public byte[] getChainKey() {
return chainKey;
}
}

@ -6,9 +6,6 @@
package org.session.libsignal.libsignal.kdf; package org.session.libsignal.libsignal.kdf;
import org.session.libsignal.libsignal.kdf.HKDFv2;
import org.session.libsignal.libsignal.kdf.HKDFv3;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
@ -22,7 +19,6 @@ public abstract class HKDF {
public static HKDF createFor(int messageVersion) { public static HKDF createFor(int messageVersion) {
switch (messageVersion) { switch (messageVersion) {
case 2: return new HKDFv2();
case 3: return new HKDFv3(); case 3: return new HKDFv3();
default: throw new AssertionError("Unknown version: " + messageVersion); default: throw new AssertionError("Unknown version: " + messageVersion);
} }

@ -1,13 +0,0 @@
/**
* Copyright (C) 2014-2016 Open Whisper Systems
*
* Licensed according to the LICENSE file in this repository.
*/
package org.session.libsignal.libsignal.kdf;
public class HKDFv2 extends HKDF {
@Override
protected int getIterationStartOffset() {
return 0;
}
}

@ -1,24 +0,0 @@
package org.session.libsignal.metadata.certificate;
import java.util.HashSet;
import java.util.Set;
public class CertificateValidator {
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
private static final Set<Integer> REVOKED = new HashSet<Integer>() {{
}};
public void validate(SenderCertificate certificate, long validationTime) throws InvalidCertificateException {
if (certificate.getSender() == null || certificate.getSenderDeviceId() <= 0) {
throw new InvalidCertificateException("Sender or sender device id is invalid");
}
}
// VisibleForTesting
void validate(ServerCertificate certificate) throws InvalidCertificateException {
}
}

@ -1,12 +0,0 @@
package org.session.libsignal.metadata.certificate;
public class InvalidCertificateException extends Exception {
public InvalidCertificateException(String s) {
super(s);
}
public InvalidCertificateException(Exception e) {
super(e);
}
}

@ -1,51 +0,0 @@
package org.session.libsignal.metadata.certificate;
import com.google.protobuf.InvalidProtocolBufferException;
import org.session.libsignal.metadata.SignalProtos;
public class SenderCertificate {
private final int senderDeviceId;
private final String sender;
private final byte[] serialized;
private final byte[] certificate;
public SenderCertificate(byte[] serialized) throws InvalidCertificateException {
try {
SignalProtos.SenderCertificate certificate = SignalProtos.SenderCertificate.parseFrom(serialized);
if (!certificate.hasSenderDevice() || !certificate.hasSender()) {
throw new InvalidCertificateException("Missing fields");
}
this.sender = certificate.getSender();
this.senderDeviceId = certificate.getSenderDevice();
this.serialized = serialized;
this.certificate = certificate.toByteArray();
} catch (InvalidProtocolBufferException e) {
throw new InvalidCertificateException(e);
}
}
public int getSenderDeviceId() {
return senderDeviceId;
}
public String getSender() {
return sender;
}
public byte[] getSerialized() {
return serialized;
}
public byte[] getCertificate() {
return certificate;
}
}

@ -1,66 +0,0 @@
package org.session.libsignal.metadata.certificate;
import com.google.protobuf.InvalidProtocolBufferException;
import org.session.libsignal.metadata.SignalProtos;
import org.session.libsignal.libsignal.InvalidKeyException;
import org.session.libsignal.libsignal.ecc.Curve;
import org.session.libsignal.libsignal.ecc.ECPublicKey;
public class ServerCertificate {
private final int keyId;
private final ECPublicKey key;
private final byte[] serialized;
private final byte[] certificate;
private final byte[] signature;
public ServerCertificate(byte[] serialized) throws InvalidCertificateException {
try {
SignalProtos.ServerCertificate wrapper = SignalProtos.ServerCertificate.parseFrom(serialized);
if (!wrapper.hasCertificate() || !wrapper.hasSignature()) {
throw new InvalidCertificateException("Missing fields");
}
SignalProtos.ServerCertificate.Certificate certificate = SignalProtos.ServerCertificate.Certificate.parseFrom(wrapper.getCertificate());
if (!certificate.hasId() || !certificate.hasKey()) {
throw new InvalidCertificateException("Missing fields");
}
this.keyId = certificate.getId();
this.key = Curve.decodePoint(certificate.getKey().toByteArray(), 0);
this.serialized = serialized;
this.certificate = wrapper.getCertificate().toByteArray();
this.signature = wrapper.getSignature().toByteArray();
} catch (InvalidProtocolBufferException e) {
throw new InvalidCertificateException(e);
} catch (InvalidKeyException e) {
throw new InvalidCertificateException(e);
}
}
public int getKeyId() {
return keyId;
}
public ECPublicKey getKey() {
return key;
}
public byte[] getSerialized() {
return serialized;
}
public byte[] getCertificate() {
return certificate;
}
public byte[] getSignature() {
return signature;
}
}

@ -14,7 +14,6 @@ import org.session.libsignal.utilities.logging.Log;
import org.session.libsignal.libsignal.util.guava.Optional; import org.session.libsignal.libsignal.util.guava.Optional;
import org.session.libsignal.service.api.crypto.AttachmentCipherOutputStream; import org.session.libsignal.service.api.crypto.AttachmentCipherOutputStream;
import org.session.libsignal.service.api.crypto.UnidentifiedAccess; import org.session.libsignal.service.api.crypto.UnidentifiedAccess;
import org.session.libsignal.service.api.crypto.UnidentifiedAccessPair;
import org.session.libsignal.service.api.crypto.UntrustedIdentityException; import org.session.libsignal.service.api.crypto.UntrustedIdentityException;
import org.session.libsignal.service.api.messages.SendMessageResult; import org.session.libsignal.service.api.messages.SendMessageResult;
import org.session.libsignal.service.api.messages.SignalServiceAttachment; import org.session.libsignal.service.api.messages.SignalServiceAttachment;
@ -165,21 +164,21 @@ public class SignalServiceMessageSender {
* @throws IOException * @throws IOException
*/ */
public void sendReceipt(SignalServiceAddress recipient, public void sendReceipt(SignalServiceAddress recipient,
Optional<UnidentifiedAccessPair> unidentifiedAccess, Optional<UnidentifiedAccess> unidentifiedAccess,
SignalServiceReceiptMessage message) SignalServiceReceiptMessage message)
throws IOException { throws IOException {
byte[] content = createReceiptContent(message); byte[] content = createReceiptContent(message);
boolean useFallbackEncryption = true; boolean useFallbackEncryption = true;
sendMessage(recipient, getTargetUnidentifiedAccess(unidentifiedAccess), message.getWhen(), content, false, message.getTTL(), useFallbackEncryption); sendMessage(recipient, unidentifiedAccess, message.getWhen(), content, false, message.getTTL(), useFallbackEncryption);
} }
public void sendTyping(List<SignalServiceAddress> recipients, public void sendTyping(List<SignalServiceAddress> recipients,
List<Optional<UnidentifiedAccessPair>> unidentifiedAccess, List<Optional<UnidentifiedAccess>> unidentifiedAccess,
SignalServiceTypingMessage message) SignalServiceTypingMessage message)
throws IOException throws IOException
{ {
byte[] content = createTypingContent(message); byte[] content = createTypingContent(message);
sendMessage(0, recipients, getTargetUnidentifiedAccess(unidentifiedAccess), message.getTimestamp(), content, true, message.getTTL(), false, false); sendMessage(0, recipients, unidentifiedAccess, message.getTimestamp(), content, true, message.getTTL(), false, false);
} }
/** /**
@ -191,14 +190,14 @@ public class SignalServiceMessageSender {
*/ */
public SendMessageResult sendMessage(long messageID, public SendMessageResult sendMessage(long messageID,
SignalServiceAddress recipient, SignalServiceAddress recipient,
Optional<UnidentifiedAccessPair> unidentifiedAccess, Optional<UnidentifiedAccess> unidentifiedAccess,
SignalServiceDataMessage message) SignalServiceDataMessage message)
throws IOException throws IOException
{ {
byte[] content = createMessageContent(message, recipient); byte[] content = createMessageContent(message, recipient);
long timestamp = message.getTimestamp(); long timestamp = message.getTimestamp();
boolean isClosedGroup = message.group.isPresent() && message.group.get().getGroupType() == SignalServiceGroup.GroupType.SIGNAL; boolean isClosedGroup = message.group.isPresent() && message.group.get().getGroupType() == SignalServiceGroup.GroupType.SIGNAL;
SendMessageResult result = sendMessage(messageID, recipient, getTargetUnidentifiedAccess(unidentifiedAccess), timestamp, content, false, message.getTTL(), true, isClosedGroup, message.hasVisibleContent(), message.getSyncTarget()); SendMessageResult result = sendMessage(messageID, recipient, unidentifiedAccess, timestamp, content, false, message.getTTL(), true, isClosedGroup, message.hasVisibleContent(), message.getSyncTarget());
return result; return result;
} }
@ -212,7 +211,7 @@ public class SignalServiceMessageSender {
*/ */
public List<SendMessageResult> sendMessage(long messageID, public List<SendMessageResult> sendMessage(long messageID,
List<SignalServiceAddress> recipients, List<SignalServiceAddress> recipients,
List<Optional<UnidentifiedAccessPair>> unidentifiedAccess, List<Optional<UnidentifiedAccess>> unidentifiedAccess,
SignalServiceDataMessage message) SignalServiceDataMessage message)
throws IOException { throws IOException {
// Loki - We only need the first recipient in the line below. This is because the recipient is only used to determine // Loki - We only need the first recipient in the line below. This is because the recipient is only used to determine
@ -221,7 +220,7 @@ public class SignalServiceMessageSender {
long timestamp = message.getTimestamp(); long timestamp = message.getTimestamp();
boolean isClosedGroup = message.group.isPresent() && message.group.get().getGroupType() == SignalServiceGroup.GroupType.SIGNAL; boolean isClosedGroup = message.group.isPresent() && message.group.get().getGroupType() == SignalServiceGroup.GroupType.SIGNAL;
return sendMessage(messageID, recipients, getTargetUnidentifiedAccess(unidentifiedAccess), timestamp, content, false, message.getTTL(), isClosedGroup, message.hasVisibleContent()); return sendMessage(messageID, recipients, unidentifiedAccess, timestamp, content, false, message.getTTL(), isClosedGroup, message.hasVisibleContent());
} }
public void setMessagePipe(SignalServiceMessagePipe pipe, SignalServiceMessagePipe unidentifiedPipe) { public void setMessagePipe(SignalServiceMessagePipe pipe, SignalServiceMessagePipe unidentifiedPipe) {
@ -885,25 +884,6 @@ public class SignalServiceMessageSender {
return new OutgoingPushMessageList(publicKey, timestamp, messages, false); return new OutgoingPushMessageList(publicKey, timestamp, messages, false);
} }
private Optional<UnidentifiedAccess> getTargetUnidentifiedAccess(Optional<UnidentifiedAccessPair> unidentifiedAccess) {
if (unidentifiedAccess.isPresent()) {
return unidentifiedAccess.get().getTargetUnidentifiedAccess();
}
return Optional.absent();
}
private List<Optional<UnidentifiedAccess>> getTargetUnidentifiedAccess(List<Optional<UnidentifiedAccessPair>> unidentifiedAccess) {
List<Optional<UnidentifiedAccess>> results = new LinkedList<>();
for (Optional<UnidentifiedAccessPair> item : unidentifiedAccess) {
if (item.isPresent()) results.add(item.get().getTargetUnidentifiedAccess());
else results.add(Optional.<UnidentifiedAccess>absent());
}
return results;
}
public static interface EventListener { public static interface EventListener {
public void onSecurityEvent(SignalServiceAddress address); public void onSecurityEvent(SignalServiceAddress address);

@ -1,8 +1,6 @@
package org.session.libsignal.service.api.crypto; package org.session.libsignal.service.api.crypto;
import org.session.libsignal.metadata.certificate.InvalidCertificateException;
import org.session.libsignal.metadata.certificate.SenderCertificate;
import org.session.libsignal.libsignal.util.ByteUtil; import org.session.libsignal.libsignal.util.ByteUtil;
import java.security.InvalidAlgorithmParameterException; import java.security.InvalidAlgorithmParameterException;
@ -19,23 +17,16 @@ import javax.crypto.spec.SecretKeySpec;
public class UnidentifiedAccess { public class UnidentifiedAccess {
private final byte[] unidentifiedAccessKey; private final byte[] unidentifiedAccessKey;
private final SenderCertificate unidentifiedCertificate;
public UnidentifiedAccess(byte[] unidentifiedAccessKey, byte[] unidentifiedCertificate) public UnidentifiedAccess(byte[] unidentifiedAccessKey)
throws InvalidCertificateException
{ {
this.unidentifiedAccessKey = unidentifiedAccessKey; this.unidentifiedAccessKey = unidentifiedAccessKey;
this.unidentifiedCertificate = new SenderCertificate(unidentifiedCertificate);
} }
public byte[] getUnidentifiedAccessKey() { public byte[] getUnidentifiedAccessKey() {
return unidentifiedAccessKey; return unidentifiedAccessKey;
} }
public SenderCertificate getUnidentifiedCertificate() {
return unidentifiedCertificate;
}
public static byte[] deriveAccessKeyFrom(byte[] profileKey) { public static byte[] deriveAccessKeyFrom(byte[] profileKey) {
try { try {
byte[] nonce = new byte[12]; byte[] nonce = new byte[12];
@ -47,17 +38,7 @@ public class UnidentifiedAccess {
byte[] ciphertext = cipher.doFinal(input); byte[] ciphertext = cipher.doFinal(input);
return ByteUtil.trim(ciphertext, 16); return ByteUtil.trim(ciphertext, 16);
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException | InvalidKeyException | NoSuchPaddingException | InvalidAlgorithmParameterException | BadPaddingException | IllegalBlockSizeException e) {
throw new AssertionError(e);
} catch (InvalidKeyException e) {
throw new AssertionError(e);
} catch (NoSuchPaddingException e) {
throw new AssertionError(e);
} catch (InvalidAlgorithmParameterException e) {
throw new AssertionError(e);
} catch (BadPaddingException e) {
throw new AssertionError(e);
} catch (IllegalBlockSizeException e) {
throw new AssertionError(e); throw new AssertionError(e);
} }
} }

@ -1,23 +0,0 @@
package org.session.libsignal.service.api.crypto;
import org.session.libsignal.libsignal.util.guava.Optional;
public class UnidentifiedAccessPair {
private final Optional<UnidentifiedAccess> targetUnidentifiedAccess;
private final Optional<UnidentifiedAccess> selfUnidentifiedAccess;
public UnidentifiedAccessPair(UnidentifiedAccess targetUnidentifiedAccess, UnidentifiedAccess selfUnidentifiedAccess) {
this.targetUnidentifiedAccess = Optional.of(targetUnidentifiedAccess);
this.selfUnidentifiedAccess = Optional.of(selfUnidentifiedAccess);
}
public Optional<UnidentifiedAccess> getTargetUnidentifiedAccess() {
return targetUnidentifiedAccess;
}
public Optional<UnidentifiedAccess> getSelfUnidentifiedAccess() {
return selfUnidentifiedAccess;
}
}
Loading…
Cancel
Save