mirror of
https://github.com/oxen-io/session-android.git
synced 2025-02-20 05:38:26 +00:00
refactor unidentified access (sealed sender)
This commit is contained in:
parent
c758619f13
commit
5924d90b12
@ -36,6 +36,7 @@ dependencies {
|
|||||||
// Local:
|
// Local:
|
||||||
implementation project(":libsignal")
|
implementation project(":libsignal")
|
||||||
// Remote:
|
// Remote:
|
||||||
|
implementation "com.goterl.lazycode:lazysodium-android:4.2.0@aar"
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
|
||||||
implementation 'androidx.core:core-ktx:1.3.2'
|
implementation 'androidx.core:core-ktx:1.3.2'
|
||||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||||
|
@ -17,6 +17,8 @@ interface StorageProtocol {
|
|||||||
fun getUserProfileKey(): ByteArray?
|
fun getUserProfileKey(): ByteArray?
|
||||||
fun getUserProfilePictureURL(): String?
|
fun getUserProfilePictureURL(): String?
|
||||||
|
|
||||||
|
fun getProfileKeyForRecipient(recipientPublicKey: String): ByteArray?
|
||||||
|
|
||||||
// Shared Sender Keys
|
// Shared Sender Keys
|
||||||
fun getClosedGroupPrivateKey(publicKey: String): ECPrivateKey?
|
fun getClosedGroupPrivateKey(publicKey: String): ECPrivateKey?
|
||||||
fun isClosedGroup(publicKey: String): Boolean
|
fun isClosedGroup(publicKey: String): Boolean
|
||||||
|
@ -6,9 +6,11 @@ import org.session.libsession.messaging.messages.Message
|
|||||||
import org.session.libsession.messaging.sending_receiving.MessageSender.Error
|
import org.session.libsession.messaging.sending_receiving.MessageSender.Error
|
||||||
import org.session.libsession.messaging.utilities.UnidentifiedAccessUtil
|
import org.session.libsession.messaging.utilities.UnidentifiedAccessUtil
|
||||||
import org.session.libsession.utilities.AESGCM
|
import org.session.libsession.utilities.AESGCM
|
||||||
|
|
||||||
import org.session.libsignal.libsignal.SignalProtocolAddress
|
import org.session.libsignal.libsignal.SignalProtocolAddress
|
||||||
import org.session.libsignal.libsignal.loki.ClosedGroupCiphertextMessage
|
import org.session.libsignal.libsignal.loki.ClosedGroupCiphertextMessage
|
||||||
import org.session.libsignal.libsignal.util.Hex
|
import org.session.libsignal.libsignal.util.Hex
|
||||||
|
import org.session.libsignal.libsignal.util.guava.Optional
|
||||||
import org.session.libsignal.service.api.crypto.SignalServiceCipher
|
import org.session.libsignal.service.api.crypto.SignalServiceCipher
|
||||||
import org.session.libsignal.service.api.push.SignalServiceAddress
|
import org.session.libsignal.service.api.push.SignalServiceAddress
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
||||||
@ -26,8 +28,9 @@ object MessageSenderEncryption {
|
|||||||
val certificateValidator = Configuration.shared.certificateValidator
|
val certificateValidator = Configuration.shared.certificateValidator
|
||||||
val cipher = SignalServiceCipher(localAddress, storage, sskDatabase, sessionResetImp, certificateValidator)
|
val cipher = SignalServiceCipher(localAddress, storage, sskDatabase, sessionResetImp, certificateValidator)
|
||||||
val signalProtocolAddress = SignalProtocolAddress(recipientPublicKey, 1)
|
val signalProtocolAddress = SignalProtocolAddress(recipientPublicKey, 1)
|
||||||
val unidentifiedAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient)
|
val unidentifiedAccessPair = UnidentifiedAccessUtil.getAccessFor(recipientPublicKey)
|
||||||
val encryptedMessage = cipher.encrypt(signalProtocolAddress, unidentifiedAccess,plaintext)
|
val unidentifiedAccess = if (unidentifiedAccessPair != null) unidentifiedAccessPair.targetUnidentifiedAccess else Optional.absent()
|
||||||
|
val encryptedMessage = cipher.encrypt(signalProtocolAddress, unidentifiedAccess, plaintext)
|
||||||
return Base64.decode(encryptedMessage.content)
|
return Base64.decode(encryptedMessage.content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,121 +0,0 @@
|
|||||||
package org.session.libsession.messaging.utilities;
|
|
||||||
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.annotation.WorkerThread;
|
|
||||||
|
|
||||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
|
||||||
import org.session.libsignal.metadata.SignalProtos;
|
|
||||||
import org.session.libsignal.metadata.certificate.CertificateValidator;
|
|
||||||
import org.session.libsignal.metadata.certificate.InvalidCertificateException;
|
|
||||||
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.thoughtcrime.securesms.logging.Log;
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
|
||||||
|
|
||||||
public class UnidentifiedAccessUtil {
|
|
||||||
|
|
||||||
private static final String TAG = UnidentifiedAccessUtil.class.getSimpleName();
|
|
||||||
|
|
||||||
public static CertificateValidator getCertificateValidator() {
|
|
||||||
return new CertificateValidator();
|
|
||||||
}
|
|
||||||
|
|
||||||
@WorkerThread
|
|
||||||
public static Optional<UnidentifiedAccessPair> getAccessFor(@NonNull Context context,
|
|
||||||
@NonNull Recipient recipient)
|
|
||||||
{
|
|
||||||
if (!TextSecurePreferences.isUnidentifiedDeliveryEnabled(context)) {
|
|
||||||
Log.i(TAG, "Unidentified delivery is disabled. [other]");
|
|
||||||
return Optional.absent();
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
byte[] theirUnidentifiedAccessKey = getTargetUnidentifiedAccessKey(recipient);
|
|
||||||
byte[] ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(context);
|
|
||||||
byte[] ourUnidentifiedAccessCertificate = getUnidentifiedAccessCertificate(context);
|
|
||||||
|
|
||||||
if (TextSecurePreferences.isUniversalUnidentifiedAccess(context)) {
|
|
||||||
ourUnidentifiedAccessKey = Util.getSecretBytes(16);
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.i(TAG, "Their access key present? " + (theirUnidentifiedAccessKey != null) +
|
|
||||||
" | 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Optional<UnidentifiedAccessPair> getAccessForSync(@NonNull Context context) {
|
|
||||||
if (!TextSecurePreferences.isUnidentifiedDeliveryEnabled(context)) {
|
|
||||||
Log.i(TAG, "Unidentified delivery is disabled. [self]");
|
|
||||||
return Optional.absent();
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
byte[] ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(context);
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static @NonNull byte[] getSelfUnidentifiedAccessKey(@NonNull Context context) {
|
|
||||||
return UnidentifiedAccess.deriveAccessKeyFrom(ProfileKeyUtil.getProfileKey(context));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static @Nullable byte[] getTargetUnidentifiedAccessKey(@NonNull Recipient recipient) {
|
|
||||||
byte[] theirProfileKey = recipient.resolve().getProfileKey();
|
|
||||||
|
|
||||||
if (theirProfileKey == null) return Util.getSecretBytes(16);
|
|
||||||
else return UnidentifiedAccess.deriveAccessKeyFrom(theirProfileKey);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static @Nullable byte[] getUnidentifiedAccessCertificate(Context context) {
|
|
||||||
String ourNumber = TextSecurePreferences.getLocalNumber(context);
|
|
||||||
if (ourNumber != null) {
|
|
||||||
SignalProtos.SenderCertificate certificate = SignalProtos.SenderCertificate.newBuilder()
|
|
||||||
.setSender(ourNumber)
|
|
||||||
.setSenderDevice(SignalServiceAddress.DEFAULT_DEVICE_ID)
|
|
||||||
.build();
|
|
||||||
return certificate.toByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,60 @@
|
|||||||
|
package org.session.libsession.messaging.utilities
|
||||||
|
|
||||||
|
import com.goterl.lazycode.lazysodium.LazySodiumAndroid
|
||||||
|
import com.goterl.lazycode.lazysodium.SodiumAndroid
|
||||||
|
|
||||||
|
import org.session.libsession.messaging.Configuration
|
||||||
|
|
||||||
|
import org.session.libsignal.libsignal.logging.Log
|
||||||
|
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.UnidentifiedAccessPair
|
||||||
|
|
||||||
|
object UnidentifiedAccessUtil {
|
||||||
|
private val TAG = UnidentifiedAccessUtil::class.simpleName
|
||||||
|
private val sodium = LazySodiumAndroid(SodiumAndroid())
|
||||||
|
|
||||||
|
fun getAccessFor(recipientPublicKey: String): UnidentifiedAccessPair? {
|
||||||
|
try {
|
||||||
|
val theirUnidentifiedAccessKey = getTargetUnidentifiedAccessKey(recipientPublicKey)
|
||||||
|
val ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey()
|
||||||
|
val ourUnidentifiedAccessCertificate = getUnidentifiedAccessCertificate()
|
||||||
|
|
||||||
|
Log.i(TAG, "Their access key present? " + (theirUnidentifiedAccessKey != null) +
|
||||||
|
" | Our access key present? " + (ourUnidentifiedAccessKey != null) +
|
||||||
|
" | Our certificate present? " + (ourUnidentifiedAccessCertificate != null))
|
||||||
|
|
||||||
|
if (theirUnidentifiedAccessKey != null && ourUnidentifiedAccessKey != null && ourUnidentifiedAccessCertificate != null) {
|
||||||
|
return UnidentifiedAccessPair(UnidentifiedAccess(theirUnidentifiedAccessKey, ourUnidentifiedAccessCertificate),
|
||||||
|
UnidentifiedAccess(ourUnidentifiedAccessKey, ourUnidentifiedAccessCertificate))
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
} catch (e: InvalidCertificateException) {
|
||||||
|
Log.w(TAG, e)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getTargetUnidentifiedAccessKey(recipientPublicKey: String): ByteArray? {
|
||||||
|
val theirProfileKey = Configuration.shared.storage.getProfileKeyForRecipient(recipientPublicKey) ?: return sodium.randomBytesBuf(16)
|
||||||
|
return UnidentifiedAccess.deriveAccessKeyFrom(theirProfileKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getSelfUnidentifiedAccessKey(): ByteArray? {
|
||||||
|
val userPublicKey = Configuration.shared.storage.getUserPublicKey()
|
||||||
|
if (userPublicKey != null) {
|
||||||
|
return sodium.randomBytesBuf(16)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getUnidentifiedAccessCertificate(): ByteArray? {
|
||||||
|
val userPublicKey = Configuration.shared.storage.getUserPublicKey()
|
||||||
|
if (userPublicKey != null) {
|
||||||
|
val certificate = SignalProtos.SenderCertificate.newBuilder().setSender(userPublicKey).setSenderDevice(1).build()
|
||||||
|
return certificate.toByteArray()
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user