From 5924d90b127006e143f33ffc9846846c60ccc044 Mon Sep 17 00:00:00 2001 From: Ryan ZHAO Date: Thu, 3 Dec 2020 14:20:49 +1100 Subject: [PATCH] refactor unidentified access (sealed sender) --- libsession/build.gradle | 1 + .../libsession/messaging/StorageProtocol.kt | 2 + .../MessageSenderEncryption.kt | 7 +- .../utilities/UnidentifiedAccessUtil.java | 121 ------------------ .../utilities/UnidentifiedAccessUtil.kt | 60 +++++++++ 5 files changed, 68 insertions(+), 123 deletions(-) delete mode 100644 libsession/src/main/java/org/session/libsession/messaging/utilities/UnidentifiedAccessUtil.java create mode 100644 libsession/src/main/java/org/session/libsession/messaging/utilities/UnidentifiedAccessUtil.kt diff --git a/libsession/build.gradle b/libsession/build.gradle index 2da1d4bc60..0ae0a9ca03 100644 --- a/libsession/build.gradle +++ b/libsession/build.gradle @@ -36,6 +36,7 @@ dependencies { // Local: implementation project(":libsignal") // Remote: + implementation "com.goterl.lazycode:lazysodium-android:4.2.0@aar" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion" implementation 'androidx.core:core-ktx:1.3.2' implementation 'androidx.appcompat:appcompat:1.2.0' diff --git a/libsession/src/main/java/org/session/libsession/messaging/StorageProtocol.kt b/libsession/src/main/java/org/session/libsession/messaging/StorageProtocol.kt index a2ac740bbf..ded75c0799 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/StorageProtocol.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/StorageProtocol.kt @@ -17,6 +17,8 @@ interface StorageProtocol { fun getUserProfileKey(): ByteArray? fun getUserProfilePictureURL(): String? + fun getProfileKeyForRecipient(recipientPublicKey: String): ByteArray? + // Shared Sender Keys fun getClosedGroupPrivateKey(publicKey: String): ECPrivateKey? fun isClosedGroup(publicKey: String): Boolean diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderEncryption.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderEncryption.kt index ef1a5ba8b9..f1c9dd2c88 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderEncryption.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderEncryption.kt @@ -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.utilities.UnidentifiedAccessUtil import org.session.libsession.utilities.AESGCM + import org.session.libsignal.libsignal.SignalProtocolAddress import org.session.libsignal.libsignal.loki.ClosedGroupCiphertextMessage 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.push.SignalServiceAddress import org.session.libsignal.service.internal.push.SignalServiceProtos @@ -26,8 +28,9 @@ object MessageSenderEncryption { val certificateValidator = Configuration.shared.certificateValidator val cipher = SignalServiceCipher(localAddress, storage, sskDatabase, sessionResetImp, certificateValidator) val signalProtocolAddress = SignalProtocolAddress(recipientPublicKey, 1) - val unidentifiedAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient) - val encryptedMessage = cipher.encrypt(signalProtocolAddress, unidentifiedAccess,plaintext) + val unidentifiedAccessPair = UnidentifiedAccessUtil.getAccessFor(recipientPublicKey) + val unidentifiedAccess = if (unidentifiedAccessPair != null) unidentifiedAccessPair.targetUnidentifiedAccess else Optional.absent() + val encryptedMessage = cipher.encrypt(signalProtocolAddress, unidentifiedAccess, plaintext) return Base64.decode(encryptedMessage.content) } diff --git a/libsession/src/main/java/org/session/libsession/messaging/utilities/UnidentifiedAccessUtil.java b/libsession/src/main/java/org/session/libsession/messaging/utilities/UnidentifiedAccessUtil.java deleted file mode 100644 index c66a9b4954..0000000000 --- a/libsession/src/main/java/org/session/libsession/messaging/utilities/UnidentifiedAccessUtil.java +++ /dev/null @@ -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 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 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; - } -} diff --git a/libsession/src/main/java/org/session/libsession/messaging/utilities/UnidentifiedAccessUtil.kt b/libsession/src/main/java/org/session/libsession/messaging/utilities/UnidentifiedAccessUtil.kt new file mode 100644 index 0000000000..de4e2db801 --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/utilities/UnidentifiedAccessUtil.kt @@ -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 + } +} \ No newline at end of file