Skip to content

Commit

Permalink
✨ Implementing signal protocol for desktop device
Browse files Browse the repository at this point in the history
  • Loading branch information
guiyanakuang committed Jan 31, 2024
1 parent ef2656b commit 52005ac
Show file tree
Hide file tree
Showing 30 changed files with 668 additions and 175 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.clipevery.dao.signal

import org.signal.libsignal.protocol.ecc.ECPrivateKey

interface SignalDao {
fun generatePreKeyPair(): ClipPreKey
fun generatesSignedPreKeyPair(privateKey: ECPrivateKey): ClipSignedPreKey
fun saveIdentities(identityKeys: List<ClipIdentityKey>)
fun saveIdentity(appInstanceId: String, serialized: ByteArray): Boolean
fun identity(appInstanceId: String): ByteArray?
fun loadPreKey(id: Int): ByteArray?
fun storePreKey(id: Int, serialized: ByteArray)
fun removePreKey(id: Int)
fun loadSession(appInstanceId: String): ByteArray?
fun loadExistingSessions(): List<ByteArray>
fun storeSession(appInstanceId: String, sessionRecord: ByteArray)
fun containSession(appInstanceId: String): Boolean
fun deleteSession(appInstanceId: String)
fun deleteAllSession()
fun loadSignedPreKey(id: Int): ByteArray?
fun loadSignedPreKeys(): List<ByteArray>
fun storeSignedPreKey(id: Int, serialized: ByteArray)
fun containsSignedPreKey(id: Int): Boolean
fun removeSignedPreKey(id: Int)
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class SyncRuntimeInfo: RealmObject {
var hostInfoList: RealmList<HostInfo> = realmListOf()
var port: Int = 0
var connectHostAddress: String? = null
var connectState: Int = 0
var connectState: Int = SyncState.DISCONNECTED
var allowSend: Boolean = true
var allowReceive: Boolean = true
var createTime: RealmInstant = RealmInstant.now()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ interface SyncRuntimeInfoDao {

fun getSyncRuntimeInfo(appInstanceId: String): SyncRuntimeInfo?

fun updateConnectState(appInstanceId: String, connectState: Int)
suspend fun getSyncRuntimeInfo(syncRuntimeInfo: SyncRuntimeInfo): SyncRuntimeInfo?

fun updateConnectInfo(appInstanceId: String, connectState: Int, connectHostAddress: String)
suspend fun updateConnectState(syncRuntimeInfo: SyncRuntimeInfo, connectState: Int)

suspend fun updateConnectInfo(syncRuntimeInfo: SyncRuntimeInfo, connectState: Int, connectHostAddress: String)

fun updateAllowSend(syncRuntimeInfo: SyncRuntimeInfo, allowSend: Boolean): SyncRuntimeInfo?

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,27 +1,12 @@
package com.clipevery.dto.sync

import com.clipevery.serializer.IdentityKeySerializer
import kotlinx.serialization.Serializable
import org.signal.libsignal.protocol.IdentityKey
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.DataInputStream
import java.io.DataOutputStream

data class RequestTrust(val identityKey: IdentityKey, val token: Int)
@Serializable
data class RequestTrust(
@Serializable(with = IdentityKeySerializer::class) val identityKey: IdentityKey,
val token: Int
)

fun decodeRequestTrust(encoded: ByteArray): RequestTrust {
val byteStream = ByteArrayInputStream(encoded)
val dataStream = DataInputStream(byteStream)
val token = dataStream.readInt()
val serialize = dataStream.readNBytes(encoded.size - 4)
val identityKey = IdentityKey(serialize)
return RequestTrust(identityKey, token)
}

fun encodeRequestTrust(requestTrust: RequestTrust): ByteArray {
val byteStream = ByteArrayOutputStream()
val dataStream = DataOutputStream(byteStream)
dataStream.writeInt(requestTrust.token)
val identityKeyBytes = requestTrust.identityKey.serialize()
dataStream.write(identityKeyBytes)
return byteStream.toByteArray()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.clipevery.dto.sync

import com.clipevery.serializer.IdentityKeySerializer
import kotlinx.serialization.Serializable
import org.signal.libsignal.protocol.IdentityKey

@Serializable
data class RequestTrustSyncInfo(
@Serializable(with = IdentityKeySerializer::class) val identityKey: IdentityKey,
val syncInfo: SyncInfo
)

Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.clipevery.net

import com.clipevery.dao.sync.HostInfo
import com.clipevery.net.clientapi.SyncClientApi
import org.signal.libsignal.protocol.SessionBuilder
import org.signal.libsignal.protocol.SessionCipher

interface ClientHandler {

fun getId(): String

fun getSyncClientApi(): SyncClientApi

fun createSessionBuilder(): SessionBuilder

fun getSessionCipher(): SessionCipher

fun getHostInfo(): HostInfo?

fun port(): Int

fun isConnected(): Boolean

suspend fun updateSyncStateWithHostInfo(syncState: Int, hostInfo: HostInfo, port: Int)

suspend fun updateSyncState(syncState: Int)

suspend fun checkHandler(checkAction: CheckAction): Boolean
}


enum class CheckAction {
CheckAll,
CheckNonConnected
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.clipevery.net

interface ClientHandlerManager {
fun start()
fun addHandler(id: String)
fun removeHandler(id: String)
fun stop()
suspend fun checkConnects(checkAction: CheckAction)
suspend fun checkConnect(id: String, checkAction: CheckAction): Boolean
}
11 changes: 11 additions & 0 deletions composeApp/src/commonMain/kotlin/com/clipevery/net/ClipClient.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
package com.clipevery.net

import io.ktor.client.statement.HttpResponse
import io.ktor.http.URLBuilder

interface ClipClient {

suspend fun post(
urlBuilder: URLBuilder.(URLBuilder) -> Unit,
message: ByteArray,
): HttpResponse

suspend fun get(
urlBuilder: URLBuilder.(URLBuilder) -> Unit
): HttpResponse
}
10 changes: 10 additions & 0 deletions composeApp/src/commonMain/kotlin/com/clipevery/net/ConnectState.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.clipevery.net

import com.clipevery.dao.sync.SyncRuntimeInfo

interface ConnectState {

suspend fun autoResolve(syncRuntimeInfo: SyncRuntimeInfo)

suspend fun next(): Boolean
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.clipevery.net

interface DeviceRefresher {
suspend fun refresh(checkAction: CheckAction)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.clipevery.net.clientapi

import io.ktor.http.URLBuilder
import org.signal.libsignal.protocol.SessionCipher
import org.signal.libsignal.protocol.state.PreKeyBundle

interface SyncClientApi {

suspend fun getPreKeyBundle(toUrl: URLBuilder.(URLBuilder) -> Unit): PreKeyBundle?

suspend fun exchangePreKey(
sessionCipher: SessionCipher,
toUrl: URLBuilder.(URLBuilder) -> Unit
): Boolean
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.clipevery.serializer

import com.clipevery.utils.base64Decode
import com.clipevery.utils.base64Encode
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import org.signal.libsignal.protocol.IdentityKey

object IdentityKeySerializer: KSerializer<IdentityKey> {
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("IdentityKey") {}

override fun deserialize(decoder: Decoder): IdentityKey {
val byteArray = base64Decode(decoder.decodeString())
return IdentityKey(byteArray)
}

override fun serialize(encoder: Encoder, value: IdentityKey) {
encoder.encodeString(base64Encode(value.serialize()))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,9 @@ package com.clipevery.utils
import kotlinx.coroutines.CoroutineDispatcher

expect val ioDispatcher: CoroutineDispatcher

expect val mainDispatcher: CoroutineDispatcher

expect val cpuDispatcher: CoroutineDispatcher

expect val unconfinedDispatcher: CoroutineDispatcher
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.clipevery.clip.TransferableConsumer
import com.clipevery.clip.getDesktopClipboardService
import com.clipevery.config.ConfigManager
import com.clipevery.config.DefaultConfigManager
import com.clipevery.dao.signal.SignalDao
import com.clipevery.dao.signal.SignalRealm
import com.clipevery.dao.sync.SyncRuntimeInfoDao
import com.clipevery.dao.sync.SyncRuntimeInfoRealm
Expand All @@ -19,12 +20,16 @@ import com.clipevery.endpoint.EndpointInfoFactory
import com.clipevery.i18n.GlobalCopywriter
import com.clipevery.i18n.GlobalCopywriterImpl
import com.clipevery.listen.GlobalListener
import com.clipevery.net.ClientHandlerManager
import com.clipevery.net.ClipBonjourService
import com.clipevery.net.ClipClient
import com.clipevery.net.ClipServer
import com.clipevery.net.DesktopClientHandlerManager
import com.clipevery.net.DesktopClipBonjourService
import com.clipevery.net.DesktopClipClient
import com.clipevery.net.DesktopClipServer
import com.clipevery.net.DesktopDeviceRefresher
import com.clipevery.net.DeviceRefresher
import com.clipevery.path.getPathProvider
import com.clipevery.presist.FilePersist
import com.clipevery.presist.getFilePersist
Expand Down Expand Up @@ -64,14 +69,16 @@ object Dependencies {

// realm component
single<RealmManager> { RealmManager.createRealmManager(pathProvider = pathProvider) }
single<SignalRealm> { SignalRealm(get<RealmManager>().realm) }
single<SignalDao> { SignalRealm(get<RealmManager>().realm) }
single<SyncRuntimeInfoDao> { SyncRuntimeInfoRealm(get<RealmManager>().realm) }

// net component
single<ClipServer> { DesktopClipServer().start() }
single<ClipClient> { DesktopClipClient() }
single<ClipClient> { DesktopClipClient(get<AppInfo>()) }
single<ClientHandlerManager> { DesktopClientHandlerManager(get(), get(), get(), get()) }
single<ClipServer> { DesktopClipServer(get<ClientHandlerManager>()).start() }
single<Lazy<ClipServer>> { lazy { get<ClipServer>() } }
single<ClipBonjourService> { DesktopClipBonjourService(get(), get()).registerService() }
single<DeviceRefresher> { DesktopDeviceRefresher(get<ClientHandlerManager>()) }

// signal component
single<IdentityKeyStore> { getClipIdentityKeyStoreFactory(get(), get()).createIdentityKeyStore() }
Expand Down
Loading

0 comments on commit 52005ac

Please sign in to comment.