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/messages/Destination.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/Destination.kt index a0573bd19c..07c2e726af 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/Destination.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/Destination.kt @@ -2,9 +2,9 @@ package org.session.libsession.messaging.messages sealed class Destination { - class Contact(val publicKey: String) - class ClosedGroup(val groupPublicKey: String) - class OpenGroup(val channel: Long, val server: String) + class Contact(val publicKey: String) : Destination() + class ClosedGroup(val groupPublicKey: String) : Destination() + class OpenGroup(val channel: Long, val server: String) : Destination() companion object { //TODO need to implement the equivalent to TSThread and then implement from(...) diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/Message.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/Message.kt index 3e92077689..338b8e0855 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/Message.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/Message.kt @@ -12,11 +12,7 @@ abstract class Message { var sender: String? = null var groupPublicKey: String? = null var openGroupServerMessageID: Long? = null - - companion object { - @JvmStatic - val ttl = 2 * 24 * 60 * 60 * 1000 //TODO not sure about that declaration - } + val ttl: Long = 2 * 24 * 60 * 60 * 1000 // validation open fun isValid(): Boolean { diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Attachment.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Attachment.kt index 7a37ef4849..90a3ebbaca 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Attachment.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Attachment.kt @@ -4,10 +4,8 @@ import android.util.Size import android.webkit.MimeTypeMap import org.session.libsignal.service.internal.push.SignalServiceProtos import java.io.File -import java.net.URL -import kotlin.math.absoluteValue -class Attachment : VisibleMessage() { +class Attachment : VisibleMessageProto() { var fileName: String? = null var contentType: String? = null diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/BaseVisibleMessage.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/BaseVisibleMessage.kt deleted file mode 100644 index 5e3fab2d1c..0000000000 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/BaseVisibleMessage.kt +++ /dev/null @@ -1,102 +0,0 @@ -package org.session.libsession.messaging.messages.visible - -import org.session.libsignal.libsignal.logging.Log -import org.session.libsignal.service.internal.push.SignalServiceProtos - -class BaseVisibleMessage() : VisibleMessage() { - - var text: String? = null - var attachmentIDs = ArrayList() - var quote: Quote? = null - var linkPreview: LinkPreview? = null - var contact: Contact? = null - var profile: Profile? = null - - companion object { - const val TAG = "BaseVisibleMessage" - - fun fromProto(proto: SignalServiceProtos.Content): BaseVisibleMessage? { - val dataMessage = proto.dataMessage ?: return null - val result = BaseVisibleMessage() - result.text = dataMessage.body - // Attachments are handled in MessageReceiver - val quoteProto = dataMessage.quote - quoteProto?.let { - val quote = Quote.fromProto(quoteProto) - quote?.let { result.quote = quote } - } - val linkPreviewProto = dataMessage.previewList.first() - linkPreviewProto?.let { - val linkPreview = LinkPreview.fromProto(linkPreviewProto) - linkPreview?.let { result.linkPreview = linkPreview } - } - // TODO Contact - val profile = Profile.fromProto(dataMessage) - profile?.let { result.profile = profile } - return result - } - } - - // validation - override fun isValid(): Boolean { - if (!super.isValid()) return false - if (attachmentIDs.isNotEmpty()) return true - val text = text?.trim() ?: return false - if (text.isNotEmpty()) return true - return false - } - - override fun toProto(transaction: String): SignalServiceProtos.Content? { - val proto = SignalServiceProtos.Content.newBuilder() - var attachmentIDs = this.attachmentIDs - val dataMessage: SignalServiceProtos.DataMessage.Builder - // Profile - val profile = profile - val profileProto = profile?.toSSProto() - if (profileProto != null) { - dataMessage = profileProto.toBuilder() - } else { - dataMessage = SignalServiceProtos.DataMessage.newBuilder() - } - // Text - text?.let { dataMessage.body = text } - // Quote - val quotedAttachmentID = quote?.attachmentID - quotedAttachmentID?.let { - val index = attachmentIDs.indexOf(quotedAttachmentID) - if (index >= 0) { attachmentIDs.removeAt(index) } - } - val quote = quote - quote?.let { - val quoteProto = quote.toProto(transaction) - if (quoteProto != null) dataMessage.quote = quoteProto - } - //Link preview - val linkPreviewAttachmentID = linkPreview?.attachmentID - linkPreviewAttachmentID?.let { - val index = attachmentIDs.indexOf(quotedAttachmentID) - if (index >= 0) { attachmentIDs.removeAt(index) } - } - val linkPreview = linkPreview - linkPreview?.let { - val linkPreviewProto = linkPreview.toProto(transaction) - linkPreviewProto?.let { - dataMessage.addAllPreview(listOf(linkPreviewProto)) - } - } - //Attachments - // TODO I'm blocking on that one... - //swift: let attachments = attachmentIDs.compactMap { TSAttachmentStream.fetch(uniqueId: $0, transaction: transaction) } - - // TODO Contact - // Build - try { - proto.dataMessage = dataMessage.build() - return proto.build() - } catch (e: Exception) { - Log.w(TAG, "Couldn't construct visible message proto from: $this") - return null - } - } - -} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Contact.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Contact.kt index cbca11cd2d..96af6e8bbd 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Contact.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Contact.kt @@ -2,7 +2,7 @@ package org.session.libsession.messaging.messages.visible import org.session.libsignal.service.internal.push.SignalServiceProtos -class Contact : VisibleMessage() { +class Contact : VisibleMessageProto() { companion object { fun fromProto(proto: SignalServiceProtos.Content): Contact? { diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/LinkPreview.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/LinkPreview.kt index 7806e9fbe0..87779f26e3 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/LinkPreview.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/LinkPreview.kt @@ -1,10 +1,9 @@ package org.session.libsession.messaging.messages.visible -import org.session.libsession.messaging.messages.control.TypingIndicator import org.session.libsignal.libsignal.logging.Log import org.session.libsignal.service.internal.push.SignalServiceProtos -class LinkPreview() : VisibleMessage(){ +class LinkPreview() : VisibleMessageProto(){ var title: String? = null var url: String? = null diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Profile.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Profile.kt index 5a2590b56d..10bc6ba350 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Profile.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Profile.kt @@ -4,7 +4,7 @@ import com.google.protobuf.ByteString import org.session.libsignal.libsignal.logging.Log import org.session.libsignal.service.internal.push.SignalServiceProtos -class Profile() : VisibleMessage() { +class Profile() : VisibleMessageProto() { var displayName: String? = null var profileKey: ByteArray? = null diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Quote.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Quote.kt index 0d07821e40..4fb54f70a6 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Quote.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Quote.kt @@ -3,7 +3,7 @@ package org.session.libsession.messaging.messages.visible import org.session.libsignal.libsignal.logging.Log import org.session.libsignal.service.internal.push.SignalServiceProtos -class Quote() : VisibleMessage() { +class Quote() : VisibleMessageProto() { var timestamp: Long? = 0 var publicKey: String? = null diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/VisibleMessage.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/VisibleMessage.kt index dd1068cc45..89486f7481 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/VisibleMessage.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/VisibleMessage.kt @@ -1,15 +1,102 @@ package org.session.libsession.messaging.messages.visible -import org.session.libsession.messaging.messages.Message +import org.session.libsignal.libsignal.logging.Log import org.session.libsignal.service.internal.push.SignalServiceProtos -abstract class VisibleMessage : Message() { +class VisibleMessage() : VisibleMessageProto() { - abstract fun toProto(transaction: String): T + var text: String? = null + var attachmentIDs = ArrayList() + var quote: Quote? = null + var linkPreview: LinkPreview? = null + var contact: Contact? = null + var profile: Profile? = null - final override fun toProto(): SignalServiceProtos.Content? { - //we don't need to implement this method in subclasses - //TODO it just needs an equivalent to swift: preconditionFailure("Use toProto(using:) if that exists... - TODO("Not implemented") + companion object { + const val TAG = "BaseVisibleMessage" + + fun fromProto(proto: SignalServiceProtos.Content): VisibleMessage? { + val dataMessage = proto.dataMessage ?: return null + val result = VisibleMessage() + result.text = dataMessage.body + // Attachments are handled in MessageReceiver + val quoteProto = dataMessage.quote + quoteProto?.let { + val quote = Quote.fromProto(quoteProto) + quote?.let { result.quote = quote } + } + val linkPreviewProto = dataMessage.previewList.first() + linkPreviewProto?.let { + val linkPreview = LinkPreview.fromProto(linkPreviewProto) + linkPreview?.let { result.linkPreview = linkPreview } + } + // TODO Contact + val profile = Profile.fromProto(dataMessage) + profile?.let { result.profile = profile } + return result + } + } + + // validation + override fun isValid(): Boolean { + if (!super.isValid()) return false + if (attachmentIDs.isNotEmpty()) return true + val text = text?.trim() ?: return false + if (text.isNotEmpty()) return true + return false } + + override fun toProto(transaction: String): SignalServiceProtos.Content? { + val proto = SignalServiceProtos.Content.newBuilder() + var attachmentIDs = this.attachmentIDs + val dataMessage: SignalServiceProtos.DataMessage.Builder + // Profile + val profile = profile + val profileProto = profile?.toSSProto() + if (profileProto != null) { + dataMessage = profileProto.toBuilder() + } else { + dataMessage = SignalServiceProtos.DataMessage.newBuilder() + } + // Text + text?.let { dataMessage.body = text } + // Quote + val quotedAttachmentID = quote?.attachmentID + quotedAttachmentID?.let { + val index = attachmentIDs.indexOf(quotedAttachmentID) + if (index >= 0) { attachmentIDs.removeAt(index) } + } + val quote = quote + quote?.let { + val quoteProto = quote.toProto(transaction) + if (quoteProto != null) dataMessage.quote = quoteProto + } + //Link preview + val linkPreviewAttachmentID = linkPreview?.attachmentID + linkPreviewAttachmentID?.let { + val index = attachmentIDs.indexOf(quotedAttachmentID) + if (index >= 0) { attachmentIDs.removeAt(index) } + } + val linkPreview = linkPreview + linkPreview?.let { + val linkPreviewProto = linkPreview.toProto(transaction) + linkPreviewProto?.let { + dataMessage.addAllPreview(listOf(linkPreviewProto)) + } + } + //Attachments + // TODO I'm blocking on that one... + //swift: let attachments = attachmentIDs.compactMap { TSAttachmentStream.fetch(uniqueId: $0, transaction: transaction) } + + // TODO Contact + // Build + try { + proto.dataMessage = dataMessage.build() + return proto.build() + } catch (e: Exception) { + Log.w(TAG, "Couldn't construct visible message proto from: $this") + return null + } + } + } \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/VisibleMessageProto.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/VisibleMessageProto.kt new file mode 100644 index 0000000000..d8dcd7090f --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/VisibleMessageProto.kt @@ -0,0 +1,15 @@ +package org.session.libsession.messaging.messages.visible + +import org.session.libsession.messaging.messages.Message +import org.session.libsignal.service.internal.push.SignalServiceProtos + +abstract class VisibleMessageProto : Message() { + + abstract fun toProto(transaction: String): T + + final override fun toProto(): SignalServiceProtos.Content? { + //we don't need to implement this method in subclasses + //TODO it just needs an equivalent to swift: preconditionFailure("Use toProto(using:) if that exists... + TODO("Not implemented") + } +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/opengroups/OpenGroupMessage.kt b/libsession/src/main/java/org/session/libsession/messaging/opengroups/OpenGroupMessage.kt index 51eb95b2f3..6472a9f1b1 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/opengroups/OpenGroupMessage.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/opengroups/OpenGroupMessage.kt @@ -27,13 +27,13 @@ public data class OpenGroupMessage( val storage = Configuration.shared.storage val userPublicKey = storage.getUserPublicKey() ?: return null // Validation - if (!message.isValid) { return null } // Should be valid at this point + if (!message.isValid()) { return null } // Should be valid at this point // Quote - val quote: OpenGroupMessage.Quote? = { + val quote: Quote? = { val quote = message.quote - if (quote != null && quote.isValid) { + if (quote != null && quote.isValid()) { val quotedMessageServerID = storage.getQuoteServerID(quote.id, quote.publicKey) - OpenGroupMessage.Quote(quote.timestamp, quote.publicKey, quote.text, quotedMessageServerID) + Quote(quote.timestamp!!, quote.publicKey!!, quote.text!!, quotedMessageServerID) } else { null } @@ -45,10 +45,10 @@ public data class OpenGroupMessage( // Link preview val linkPreview = message.linkPreview linkPreview?.let { - if (!linkPreview.isValid) { return@let } + if (!linkPreview.isValid()) { return@let } val attachment = linkPreview.getImage() ?: return@let - val openGroupLinkPreview = OpenGroupMessage.Attachment( - OpenGroupMessage.Attachment.Kind.LinkPreview, + val openGroupLinkPreview = Attachment( + Attachment.Kind.LinkPreview, server, attachment.getId(), attachment.getContentType(), @@ -59,14 +59,14 @@ public data class OpenGroupMessage( attachment.getHeight(), attachment.getCaption(), attachment.getUrl(), - linkPreview.getUrl(), - linkPreview.getTitle()) + linkPreview.url, + linkPreview.title) result.attachments.add(openGroupLinkPreview) } // Attachments val attachments = message.getAttachemnts().forEach { - val attachement = OpenGroupMessage.Attachment( - OpenGroupMessage.Attachment.Kind.Attachment, + val attachement = Attachment( + Attachment.Kind.Attachment, server, it.getId(), it.getContentType(), diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt index f4f1048ca1..773cb50dfc 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt @@ -69,7 +69,7 @@ object MessageSender { is Destination.OpenGroup -> throw preconditionFailure } // Validate the message - message.isValid ?: throw Error.InvalidMessage + if (!message.isValid()) { throw Error.InvalidMessage } // Convert it to protobuf val proto = message.toProto() ?: throw Error.ProtoConversionFailed // Serialize the protobuf @@ -162,7 +162,7 @@ object MessageSender { } } // Validate the message - if (message !is VisibleMessage || !message.isValid) { + if (message !is VisibleMessage || !message.isValid()) { throw Error.InvalidMessage } // Convert the message to an open group message 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