mirror of
https://github.com/oxen-io/session-android.git
synced 2025-04-26 23:20:46 +00:00
Merge branch 'refactor' of https://github.com/RyanRory/loki-messenger-android into refactor
This commit is contained in:
commit
a53cc9bffb
@ -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'
|
||||||
|
@ -2,9 +2,9 @@ package org.session.libsession.messaging.messages
|
|||||||
|
|
||||||
sealed class Destination {
|
sealed class Destination {
|
||||||
|
|
||||||
class Contact(val publicKey: String)
|
class Contact(val publicKey: String) : Destination()
|
||||||
class ClosedGroup(val groupPublicKey: String)
|
class ClosedGroup(val groupPublicKey: String) : Destination()
|
||||||
class OpenGroup(val channel: Long, val server: String)
|
class OpenGroup(val channel: Long, val server: String) : Destination()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
//TODO need to implement the equivalent to TSThread and then implement from(...)
|
//TODO need to implement the equivalent to TSThread and then implement from(...)
|
||||||
|
@ -12,11 +12,7 @@ abstract class Message {
|
|||||||
var sender: String? = null
|
var sender: String? = null
|
||||||
var groupPublicKey: String? = null
|
var groupPublicKey: String? = null
|
||||||
var openGroupServerMessageID: Long? = null
|
var openGroupServerMessageID: Long? = null
|
||||||
|
val ttl: Long = 2 * 24 * 60 * 60 * 1000
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
val ttl = 2 * 24 * 60 * 60 * 1000 //TODO not sure about that declaration
|
|
||||||
}
|
|
||||||
|
|
||||||
// validation
|
// validation
|
||||||
open fun isValid(): Boolean {
|
open fun isValid(): Boolean {
|
||||||
|
@ -4,10 +4,8 @@ import android.util.Size
|
|||||||
import android.webkit.MimeTypeMap
|
import android.webkit.MimeTypeMap
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.URL
|
|
||||||
import kotlin.math.absoluteValue
|
|
||||||
|
|
||||||
class Attachment : VisibleMessage<SignalServiceProtos.AttachmentPointer?>() {
|
class Attachment : VisibleMessageProto<SignalServiceProtos.AttachmentPointer?>() {
|
||||||
|
|
||||||
var fileName: String? = null
|
var fileName: String? = null
|
||||||
var contentType: String? = null
|
var contentType: String? = 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<SignalServiceProtos.Content?>() {
|
|
||||||
|
|
||||||
var text: String? = null
|
|
||||||
var attachmentIDs = ArrayList<String>()
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -2,7 +2,7 @@ package org.session.libsession.messaging.messages.visible
|
|||||||
|
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
||||||
|
|
||||||
class Contact : VisibleMessage<SignalServiceProtos.DataMessage.Contact?>() {
|
class Contact : VisibleMessageProto<SignalServiceProtos.DataMessage.Contact?>() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun fromProto(proto: SignalServiceProtos.Content): Contact? {
|
fun fromProto(proto: SignalServiceProtos.Content): Contact? {
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
package org.session.libsession.messaging.messages.visible
|
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.libsignal.logging.Log
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
||||||
|
|
||||||
class LinkPreview() : VisibleMessage<SignalServiceProtos.DataMessage.Preview?>(){
|
class LinkPreview() : VisibleMessageProto<SignalServiceProtos.DataMessage.Preview?>(){
|
||||||
|
|
||||||
var title: String? = null
|
var title: String? = null
|
||||||
var url: String? = null
|
var url: String? = null
|
||||||
|
@ -4,7 +4,7 @@ import com.google.protobuf.ByteString
|
|||||||
import org.session.libsignal.libsignal.logging.Log
|
import org.session.libsignal.libsignal.logging.Log
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
||||||
|
|
||||||
class Profile() : VisibleMessage<SignalServiceProtos.DataMessage?>() {
|
class Profile() : VisibleMessageProto<SignalServiceProtos.DataMessage?>() {
|
||||||
|
|
||||||
var displayName: String? = null
|
var displayName: String? = null
|
||||||
var profileKey: ByteArray? = null
|
var profileKey: ByteArray? = null
|
||||||
|
@ -3,7 +3,7 @@ package org.session.libsession.messaging.messages.visible
|
|||||||
import org.session.libsignal.libsignal.logging.Log
|
import org.session.libsignal.libsignal.logging.Log
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
||||||
|
|
||||||
class Quote() : VisibleMessage<SignalServiceProtos.DataMessage.Quote?>() {
|
class Quote() : VisibleMessageProto<SignalServiceProtos.DataMessage.Quote?>() {
|
||||||
|
|
||||||
var timestamp: Long? = 0
|
var timestamp: Long? = 0
|
||||||
var publicKey: String? = null
|
var publicKey: String? = null
|
||||||
|
@ -1,15 +1,102 @@
|
|||||||
package org.session.libsession.messaging.messages.visible
|
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
|
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
||||||
|
|
||||||
abstract class VisibleMessage<out T: com.google.protobuf.MessageOrBuilder?> : Message() {
|
class VisibleMessage() : VisibleMessageProto<SignalServiceProtos.Content?>() {
|
||||||
|
|
||||||
abstract fun toProto(transaction: String): T
|
var text: String? = null
|
||||||
|
var attachmentIDs = ArrayList<String>()
|
||||||
|
var quote: Quote? = null
|
||||||
|
var linkPreview: LinkPreview? = null
|
||||||
|
var contact: Contact? = null
|
||||||
|
var profile: Profile? = null
|
||||||
|
|
||||||
final override fun toProto(): SignalServiceProtos.Content? {
|
companion object {
|
||||||
//we don't need to implement this method in subclasses
|
const val TAG = "BaseVisibleMessage"
|
||||||
//TODO it just needs an equivalent to swift: preconditionFailure("Use toProto(using:) if that exists...
|
|
||||||
TODO("Not implemented")
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -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<out T: com.google.protobuf.MessageOrBuilder?> : 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")
|
||||||
|
}
|
||||||
|
}
|
@ -27,13 +27,13 @@ public data class OpenGroupMessage(
|
|||||||
val storage = Configuration.shared.storage
|
val storage = Configuration.shared.storage
|
||||||
val userPublicKey = storage.getUserPublicKey() ?: return null
|
val userPublicKey = storage.getUserPublicKey() ?: return null
|
||||||
// Validation
|
// Validation
|
||||||
if (!message.isValid) { return null } // Should be valid at this point
|
if (!message.isValid()) { return null } // Should be valid at this point
|
||||||
// Quote
|
// Quote
|
||||||
val quote: OpenGroupMessage.Quote? = {
|
val quote: Quote? = {
|
||||||
val quote = message.quote
|
val quote = message.quote
|
||||||
if (quote != null && quote.isValid) {
|
if (quote != null && quote.isValid()) {
|
||||||
val quotedMessageServerID = storage.getQuoteServerID(quote.id, quote.publicKey)
|
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 {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
@ -45,10 +45,10 @@ public data class OpenGroupMessage(
|
|||||||
// Link preview
|
// Link preview
|
||||||
val linkPreview = message.linkPreview
|
val linkPreview = message.linkPreview
|
||||||
linkPreview?.let {
|
linkPreview?.let {
|
||||||
if (!linkPreview.isValid) { return@let }
|
if (!linkPreview.isValid()) { return@let }
|
||||||
val attachment = linkPreview.getImage() ?: return@let
|
val attachment = linkPreview.getImage() ?: return@let
|
||||||
val openGroupLinkPreview = OpenGroupMessage.Attachment(
|
val openGroupLinkPreview = Attachment(
|
||||||
OpenGroupMessage.Attachment.Kind.LinkPreview,
|
Attachment.Kind.LinkPreview,
|
||||||
server,
|
server,
|
||||||
attachment.getId(),
|
attachment.getId(),
|
||||||
attachment.getContentType(),
|
attachment.getContentType(),
|
||||||
@ -59,14 +59,14 @@ public data class OpenGroupMessage(
|
|||||||
attachment.getHeight(),
|
attachment.getHeight(),
|
||||||
attachment.getCaption(),
|
attachment.getCaption(),
|
||||||
attachment.getUrl(),
|
attachment.getUrl(),
|
||||||
linkPreview.getUrl(),
|
linkPreview.url,
|
||||||
linkPreview.getTitle())
|
linkPreview.title)
|
||||||
result.attachments.add(openGroupLinkPreview)
|
result.attachments.add(openGroupLinkPreview)
|
||||||
}
|
}
|
||||||
// Attachments
|
// Attachments
|
||||||
val attachments = message.getAttachemnts().forEach {
|
val attachments = message.getAttachemnts().forEach {
|
||||||
val attachement = OpenGroupMessage.Attachment(
|
val attachement = Attachment(
|
||||||
OpenGroupMessage.Attachment.Kind.Attachment,
|
Attachment.Kind.Attachment,
|
||||||
server,
|
server,
|
||||||
it.getId(),
|
it.getId(),
|
||||||
it.getContentType(),
|
it.getContentType(),
|
||||||
|
@ -69,7 +69,7 @@ object MessageSender {
|
|||||||
is Destination.OpenGroup -> throw preconditionFailure
|
is Destination.OpenGroup -> throw preconditionFailure
|
||||||
}
|
}
|
||||||
// Validate the message
|
// Validate the message
|
||||||
message.isValid ?: throw Error.InvalidMessage
|
if (!message.isValid()) { throw Error.InvalidMessage }
|
||||||
// Convert it to protobuf
|
// Convert it to protobuf
|
||||||
val proto = message.toProto() ?: throw Error.ProtoConversionFailed
|
val proto = message.toProto() ?: throw Error.ProtoConversionFailed
|
||||||
// Serialize the protobuf
|
// Serialize the protobuf
|
||||||
@ -162,7 +162,7 @@ object MessageSender {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Validate the message
|
// Validate the message
|
||||||
if (message !is VisibleMessage || !message.isValid) {
|
if (message !is VisibleMessage || !message.isValid()) {
|
||||||
throw Error.InvalidMessage
|
throw Error.InvalidMessage
|
||||||
}
|
}
|
||||||
// Convert the message to an open group message
|
// Convert the message to an open group message
|
||||||
|
@ -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