mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-23 18:15:22 +00:00
WIP: clean up signal protocol
This commit is contained in:
parent
8cc78e8f4c
commit
b34809f4d5
@ -7,90 +7,57 @@ 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)) {
|
byte[] theirUnidentifiedAccessKey = getTargetUnidentifiedAccessKey(recipient);
|
||||||
Log.i(TAG, "Unidentified delivery is disabled. [other]");
|
byte[] ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(context);
|
||||||
return Optional.absent();
|
byte[] ourUnidentifiedAccessCertificate = getUnidentifiedAccessCertificate(context);
|
||||||
|
|
||||||
|
if (TextSecurePreferences.isUniversalUnidentifiedAccess(context)) {
|
||||||
|
ourUnidentifiedAccessKey = Util.getSecretBytes(16);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
Log.i(TAG, "Their access key present? " + (theirUnidentifiedAccessKey != null) +
|
||||||
byte[] theirUnidentifiedAccessKey = getTargetUnidentifiedAccessKey(recipient);
|
" | Our access key present? " + (ourUnidentifiedAccessKey != null) +
|
||||||
byte[] ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(context);
|
" | Our certificate present? " + (ourUnidentifiedAccessCertificate != null));
|
||||||
byte[] ourUnidentifiedAccessCertificate = getUnidentifiedAccessCertificate(context);
|
|
||||||
|
|
||||||
if (TextSecurePreferences.isUniversalUnidentifiedAccess(context)) {
|
if (theirUnidentifiedAccessKey != null &&
|
||||||
ourUnidentifiedAccessKey = Util.getSecretBytes(16);
|
ourUnidentifiedAccessKey != null &&
|
||||||
}
|
ourUnidentifiedAccessCertificate != null)
|
||||||
|
{
|
||||||
Log.i(TAG, "Their access key present? " + (theirUnidentifiedAccessKey != null) +
|
return Optional.of(new UnidentifiedAccess(theirUnidentifiedAccessKey));
|
||||||
" | Our access key present? " + (ourUnidentifiedAccessKey != null) +
|
|
||||||
" | Our certificate present? " + (ourUnidentifiedAccessCertificate != null));
|
|
||||||
|
|
||||||
if (theirUnidentifiedAccessKey != null &&
|
|
||||||
ourUnidentifiedAccessKey != null &&
|
|
||||||
ourUnidentifiedAccessCertificate != null)
|
|
||||||
{
|
|
||||||
return Optional.of(new UnidentifiedAccessPair(new UnidentifiedAccess(theirUnidentifiedAccessKey,
|
|
||||||
ourUnidentifiedAccessCertificate),
|
|
||||||
new UnidentifiedAccess(ourUnidentifiedAccessKey,
|
|
||||||
ourUnidentifiedAccessCertificate)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Optional.absent();
|
|
||||||
} catch (InvalidCertificateException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
return Optional.absent();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Optional.absent();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Optional<UnidentifiedAccessPair> getAccessForSync(@NonNull Context context) {
|
public static Optional<UnidentifiedAccess> getAccessForSync(@NonNull Context context) {
|
||||||
if (!TextSecurePreferences.isUnidentifiedDeliveryEnabled(context)) {
|
byte[] ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(context);
|
||||||
Log.i(TAG, "Unidentified delivery is disabled. [self]");
|
byte[] ourUnidentifiedAccessCertificate = getUnidentifiedAccessCertificate(context);
|
||||||
return Optional.absent();
|
|
||||||
|
if (TextSecurePreferences.isUniversalUnidentifiedAccess(context)) {
|
||||||
|
ourUnidentifiedAccessKey = Util.getSecretBytes(16);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
if (ourUnidentifiedAccessKey != null && ourUnidentifiedAccessCertificate != null) {
|
||||||
byte[] ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(context);
|
return Optional.of(new UnidentifiedAccess(ourUnidentifiedAccessKey));
|
||||||
byte[] ourUnidentifiedAccessCertificate = getUnidentifiedAccessCertificate(context);
|
|
||||||
|
|
||||||
if (TextSecurePreferences.isUniversalUnidentifiedAccess(context)) {
|
|
||||||
ourUnidentifiedAccessKey = Util.getSecretBytes(16);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ourUnidentifiedAccessKey != null && ourUnidentifiedAccessCertificate != null) {
|
|
||||||
return Optional.of(new UnidentifiedAccessPair(new UnidentifiedAccess(ourUnidentifiedAccessKey,
|
|
||||||
ourUnidentifiedAccessCertificate),
|
|
||||||
new UnidentifiedAccess(ourUnidentifiedAccessKey,
|
|
||||||
ourUnidentifiedAccessCertificate)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Optional.absent();
|
|
||||||
} catch (InvalidCertificateException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
return Optional.absent();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,17 +181,15 @@ 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);
|
} else if (unidentified && accessMode == UnidentifiedAccessMode.UNKNOWN) {
|
||||||
} else if (unidentified && accessMode == UnidentifiedAccessMode.UNKNOWN) {
|
log(TAG, "Marking recipient as UD-enabled following a UD send.");
|
||||||
log(TAG, "Marking recipient as UD-enabled following a UD send.");
|
DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.ENABLED);
|
||||||
DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.ENABLED);
|
} else if (!unidentified && accessMode != UnidentifiedAccessMode.DISABLED) {
|
||||||
} else if (!unidentified && accessMode != UnidentifiedAccessMode.DISABLED) {
|
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()) {
|
||||||
@ -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,17 +123,15 @@ 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);
|
} else if (unidentified && accessMode == UnidentifiedAccessMode.UNKNOWN) {
|
||||||
} else if (unidentified && accessMode == UnidentifiedAccessMode.UNKNOWN) {
|
log(TAG, "Marking recipient as UD-enabled following a UD send.");
|
||||||
log(TAG, "Marking recipient as UD-enabled following a UD send.");
|
DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.ENABLED);
|
||||||
DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.ENABLED);
|
} else if (!unidentified && accessMode != UnidentifiedAccessMode.DISABLED) {
|
||||||
} else if (!unidentified && accessMode != UnidentifiedAccessMode.DISABLED) {
|
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) {
|
||||||
@ -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,39 +1,42 @@
|
|||||||
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()
|
|
||||||
|
|
||||||
Log.i(TAG, "Their access key present? " + (theirUnidentifiedAccessKey != null) +
|
Log.i(TAG, "Their access key present? " + (theirUnidentifiedAccessKey != null) +
|
||||||
" | 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…
Reference in New Issue
Block a user