diff --git a/build.gradle.kts b/build.gradle.kts index 6ab9563..5a4a226 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -82,7 +82,7 @@ kotlin { implementation("io.ktor:ktor-server-core-jvm:$ktor_version") implementation("io.ktor:ktor-server-websockets-jvm:$ktor_version") implementation("io.ktor:ktor-server-netty:$ktor_version") - implementation("io.ktor:ktor-client-netty:$ktor_version") + implementation("io.ktor:ktor-client-cio:$ktor_version") } } val jvmTest by getting diff --git a/src/commonMain/kotlin/net/sergeych/crypto/InitCrypto.kt b/src/commonMain/kotlin/net/sergeych/crypto2/InitCrypto.kt similarity index 95% rename from src/commonMain/kotlin/net/sergeych/crypto/InitCrypto.kt rename to src/commonMain/kotlin/net/sergeych/crypto2/InitCrypto.kt index ef32a0a..21199de 100644 --- a/src/commonMain/kotlin/net/sergeych/crypto/InitCrypto.kt +++ b/src/commonMain/kotlin/net/sergeych/crypto2/InitCrypto.kt @@ -1,4 +1,4 @@ -package net.sergeych.crypto +package net.sergeych.crypto2 import com.ionspin.kotlin.crypto.LibsodiumInitializer import kotlinx.coroutines.sync.Mutex diff --git a/src/commonMain/kotlin/net/sergeych/crypto/SignedBox.kt b/src/commonMain/kotlin/net/sergeych/crypto2/SignedBox.kt similarity index 86% rename from src/commonMain/kotlin/net/sergeych/crypto/SignedBox.kt rename to src/commonMain/kotlin/net/sergeych/crypto2/SignedBox.kt index d0cb7e7..522a846 100644 --- a/src/commonMain/kotlin/net/sergeych/crypto/SignedBox.kt +++ b/src/commonMain/kotlin/net/sergeych/crypto2/SignedBox.kt @@ -1,4 +1,4 @@ -package net.sergeych.crypto +package net.sergeych.crypto2 import kotlinx.serialization.Serializable import kotlinx.serialization.Transient @@ -29,14 +29,14 @@ class SignedBox( * key, or return unchanged (same) object if it is already signed by this key; you * _can't assume it always returns a copied object!_ */ - operator fun plus(key: Key.Signing): SignedBox = + operator fun plus(key: SigningKey.Secret): SignedBox = if (key.verifying in this) this else SignedBox(message, seals + Seal.create(key, message), false) /** * Check that it is signed with a specified key. */ - operator fun contains(verifyingKey: Key.Verifying): Boolean { + operator fun contains(verifyingKey: SigningKey.Public): Boolean { return seals.any { it.key == verifyingKey } } @@ -53,12 +53,12 @@ class SignedBox( * add seals. */ @Serializable - data class Seal(val key: Key.Verifying, val signature: UByteArray) { + data class Seal(val key: SigningKey.Public, val signature: UByteArray) { fun verify(message: UByteArray): Boolean = key.verify(signature, message) companion object { - fun create(key: Key.Signing, message: UByteArray): Seal { + fun create(key: SigningKey.Secret, message: UByteArray): Seal { return Seal(key.verifying, key.sign(message)) } } @@ -75,7 +75,7 @@ class SignedBox( * @param keys a list of keys to sign with, should be at least one key. * @throws IllegalArgumentException if keys are not specified. */ - operator fun invoke(data: UByteArray, vararg keys: Key.Signing): SignedBox = + operator fun invoke(data: UByteArray, vararg keys: SigningKey.Secret): SignedBox = SignedBox(data, keys.map { Seal.create(it, data) }, false) } } \ No newline at end of file diff --git a/src/commonMain/kotlin/net/sergeych/crypto/keys.kt b/src/commonMain/kotlin/net/sergeych/crypto2/SigningKey.kt similarity index 71% rename from src/commonMain/kotlin/net/sergeych/crypto/keys.kt rename to src/commonMain/kotlin/net/sergeych/crypto2/SigningKey.kt index 48b0633..0dd033b 100644 --- a/src/commonMain/kotlin/net/sergeych/crypto/keys.kt +++ b/src/commonMain/kotlin/net/sergeych/crypto2/SigningKey.kt @@ -1,24 +1,24 @@ -package net.sergeych.crypto +package net.sergeych.crypto2 import com.ionspin.kotlin.crypto.signature.InvalidSignatureException import com.ionspin.kotlin.crypto.signature.Signature import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import net.sergeych.crypto.Key.Signing +import net.sergeych.crypto2.SigningKey.Secret /** * Keys in general: public, secret and later symmetric too. * Keys could be compared to each other for equality and used * as a Map keys (not sure about js). * - * Use [Signing.pair] to create new keys. + * Use [Secret.pair] to create new keys. */ @Serializable -sealed class Key { +sealed class SigningKey { abstract val packed: UByteArray override fun equals(other: Any?): Boolean { - return other is Key && other.packed contentEquals packed + return other is SigningKey && other.packed contentEquals packed } override fun hashCode(): Int { @@ -31,8 +31,8 @@ sealed class Key { * Public key to verify signatures only */ @Serializable - @SerialName("pvk") - class Verifying(override val packed: UByteArray) : Key() { + @SerialName("p") + class Public(override val packed: UByteArray) : SigningKey() { /** * Verify the signature and return true if it is correct. */ @@ -51,22 +51,22 @@ sealed class Key { * Secret key to sign only */ @Serializable - @SerialName("ssk") - class Signing(override val packed: UByteArray) : Key() { + @SerialName("s") + class Secret(override val packed: UByteArray) : SigningKey() { - val verifying: Verifying by lazy { - Verifying(Signature.ed25519SkToPk(packed)) + val verifying: Public by lazy { + Public(Signature.ed25519SkToPk(packed)) } fun sign(message: UByteArray): UByteArray = Signature.detached(message, packed) override fun toString(): String = "Sct:${super.toString()}" companion object { - data class Pair(val signing: Signing, val verifying: Verifying) + data class Pair(val signing: Secret, val aPublic: Public) fun pair(): Pair { val p = Signature.keypair() - return Pair(Signing(p.secretKey), Verifying(p.publicKey)) + return Pair(Secret(p.secretKey), Public(p.publicKey)) } } } diff --git a/src/commonMain/kotlin/net/sergeych/crypto/contrail.kt b/src/commonMain/kotlin/net/sergeych/crypto2/contrail.kt similarity index 79% rename from src/commonMain/kotlin/net/sergeych/crypto/contrail.kt rename to src/commonMain/kotlin/net/sergeych/crypto2/contrail.kt index 108c726..abfd8d9 100644 --- a/src/commonMain/kotlin/net/sergeych/crypto/contrail.kt +++ b/src/commonMain/kotlin/net/sergeych/crypto2/contrail.kt @@ -1,4 +1,4 @@ -package net.sergeych.crypto +package net.sergeych.crypto2 import net.sergeych.bintools.CRC diff --git a/src/commonMain/kotlin/net/sergeych/crypto/tools.kt b/src/commonMain/kotlin/net/sergeych/crypto2/tools.kt similarity index 99% rename from src/commonMain/kotlin/net/sergeych/crypto/tools.kt rename to src/commonMain/kotlin/net/sergeych/crypto2/tools.kt index 3e970b2..cded97d 100644 --- a/src/commonMain/kotlin/net/sergeych/crypto/tools.kt +++ b/src/commonMain/kotlin/net/sergeych/crypto2/tools.kt @@ -1,6 +1,6 @@ @file:Suppress("unused") -package net.sergeych.crypto +package net.sergeych.crypto2 import com.ionspin.kotlin.crypto.secretbox.SecretBox import com.ionspin.kotlin.crypto.secretbox.crypto_secretbox_NONCEBYTES diff --git a/src/commonMain/kotlin/net/sergeych/crypto/utools.kt b/src/commonMain/kotlin/net/sergeych/crypto2/utools.kt similarity index 83% rename from src/commonMain/kotlin/net/sergeych/crypto/utools.kt rename to src/commonMain/kotlin/net/sergeych/crypto2/utools.kt index 8ae980a..c0bddea 100644 --- a/src/commonMain/kotlin/net/sergeych/crypto/utools.kt +++ b/src/commonMain/kotlin/net/sergeych/crypto2/utools.kt @@ -1,4 +1,4 @@ -package net.sergeych.crypto +package net.sergeych.crypto2 import net.sergeych.bintools.toDump import net.sergeych.mp_tools.encodeToBase64Url diff --git a/src/commonMain/kotlin/net/sergeych/kiloparsec/KiloClient.kt b/src/commonMain/kotlin/net/sergeych/kiloparsec/KiloClient.kt index 27952d1..1a2b63f 100644 --- a/src/commonMain/kotlin/net/sergeych/kiloparsec/KiloClient.kt +++ b/src/commonMain/kotlin/net/sergeych/kiloparsec/KiloClient.kt @@ -6,7 +6,7 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.isActive -import net.sergeych.crypto.Key +import net.sergeych.crypto2.SigningKey import net.sergeych.mp_logger.LogTag import net.sergeych.mp_logger.Loggable import net.sergeych.mp_logger.debug @@ -21,7 +21,7 @@ import net.sergeych.mp_tools.globalLaunch */ class KiloClient( localInterface: KiloInterface, - secretKey: Key.Signing? = null, + secretKey: SigningKey.Secret? = null, connectionDataFactory: ConnectionDataFactory, ) : RemoteInterface, Loggable by LogTag("CLIF") { @@ -80,7 +80,7 @@ class KiloClient( suspend fun token() = deferredClient.await().token() /** - * Remote party shared key ([Key.Verifying]]), could be used ti ensure server is what we expected and + * Remote party shared key ([SigningKey.Public]]), could be used ti ensure server is what we expected and * there is no active MITM attack. * * Non-null value means the key was successfully authenticated, null means remote party did not provide @@ -99,7 +99,7 @@ class KiloClient( } private var connectionBuilder: (suspend () -> Transport.Device)? = null - var secretIdKey: Key.Signing? = null + var secretIdKey: SigningKey.Secret? = null /** * Build local command implementations (remotely callable ones), exception diff --git a/src/commonMain/kotlin/net/sergeych/kiloparsec/KiloClientConnection.kt b/src/commonMain/kotlin/net/sergeych/kiloparsec/KiloClientConnection.kt index ad4cf83..c60e50f 100644 --- a/src/commonMain/kotlin/net/sergeych/kiloparsec/KiloClientConnection.kt +++ b/src/commonMain/kotlin/net/sergeych/kiloparsec/KiloClientConnection.kt @@ -2,7 +2,7 @@ package net.sergeych.kiloparsec import com.ionspin.kotlin.crypto.keyexchange.KeyExchange import kotlinx.coroutines.* -import net.sergeych.crypto.Key +import net.sergeych.crypto2.SigningKey import net.sergeych.mp_logger.LogTag import net.sergeych.mp_logger.Loggable import net.sergeych.mp_logger.debug @@ -15,17 +15,17 @@ class KiloClientConnection( private val clientInterface: KiloInterface, private val device: Transport.Device, private val session: S, - private val secretIdKey: Key.Signing? = null, + private val secretIdKey: SigningKey.Secret? = null, ) : RemoteInterface, Loggable by LogTag("KPC:${++clientIds}") { - constructor(localInterface: KiloInterface, connection: KiloConnectionData, secretIdKey: Key.Signing? = null) + constructor(localInterface: KiloInterface, connection: KiloConnectionData, secretIdKey: SigningKey.Secret? = null) : this(localInterface, connection.device, connection.session, secretIdKey) private val kiloRemoteInterface = CompletableDeferred>() private val deferredParams = CompletableDeferred>() - suspend fun remoteId(): Key.Verifying? = deferredParams.await().remoteIdentity + suspend fun remoteId(): SigningKey.Public? = deferredParams.await().remoteIdentity /** * Run the client, blocking until the device is closed, or some critical exception @@ -59,7 +59,7 @@ class KiloClientConnection( var params = KiloParams(false, transport, sk, session, null, this@KiloClientConnection) // Check ID if any - serverHe.serverSharedKey?.let { k -> + serverHe.serverPublicKey?.let { k -> if (serverHe.signature == null) throw RemoteInterface.SecurityException("missing signature") if (!k.verify(serverHe.signature, params.token)) diff --git a/src/commonMain/kotlin/net/sergeych/kiloparsec/KiloParams.kt b/src/commonMain/kotlin/net/sergeych/kiloparsec/KiloParams.kt index a709e66..c18e359 100644 --- a/src/commonMain/kotlin/net/sergeych/kiloparsec/KiloParams.kt +++ b/src/commonMain/kotlin/net/sergeych/kiloparsec/KiloParams.kt @@ -9,10 +9,10 @@ import kotlinx.serialization.Serializable import net.sergeych.bintools.toDataSource import net.sergeych.bipack.BipackDecoder import net.sergeych.bipack.BipackEncoder -import net.sergeych.crypto.DecryptionFailedException -import net.sergeych.crypto.Key -import net.sergeych.crypto.randomBytes -import net.sergeych.crypto.randomUInt +import net.sergeych.crypto2.DecryptionFailedException +import net.sergeych.crypto2.SigningKey +import net.sergeych.crypto2.randomBytes +import net.sergeych.crypto2.randomUInt import net.sergeych.tools.ProtectedOp import net.sergeych.utools.pack import net.sergeych.utools.unpack @@ -34,7 +34,7 @@ data class KiloParams( val transport: RemoteInterface, val sessionKeyPair: KeyExchangeSessionKeyPair, val scopeSession: S, - val remoteIdentity: Key.Verifying?, + val remoteIdentity: SigningKey.Public?, val remoteTransport: RemoteInterface ) { @Serializable @@ -56,7 +56,7 @@ data class KiloParams( override val session = scopeSession override val remote: RemoteInterface = remoteTransport override val sessionToken: UByteArray = token - override val remoteIdentity: Key.Verifying? = this@KiloParams.remoteIdentity + override val remoteIdentity: SigningKey.Public? = this@KiloParams.remoteIdentity } } diff --git a/src/commonMain/kotlin/net/sergeych/kiloparsec/KiloScope.kt b/src/commonMain/kotlin/net/sergeych/kiloparsec/KiloScope.kt index 6280f33..3e1eda2 100644 --- a/src/commonMain/kotlin/net/sergeych/kiloparsec/KiloScope.kt +++ b/src/commonMain/kotlin/net/sergeych/kiloparsec/KiloScope.kt @@ -1,6 +1,6 @@ package net.sergeych.kiloparsec -import net.sergeych.crypto.Key +import net.sergeych.crypto2.SigningKey /** * Scope for Kiloparsec client/server commands execution, contain per-connection specific data. The scope @@ -26,8 +26,8 @@ interface KiloScope { val sessionToken: UByteArray /** - * If the remote part has provided a secret key, e.g., gave non-null [Key.Signing] on construction, - * the kiloparsec checks it in the MITM-safe way and provides its [Key.Verifying] shared key here. + * If the remote part has provided a secret key, e.g., gave non-null [SigningKey.Secret] on construction, + * the kiloparsec checks it in the MITM-safe way and provides its [SigningKey.Public] shared key here. * Knowing a remote party shared key, it is possible to be sure that the connection is made directly * to this party with no middle point intruders. * @@ -37,6 +37,6 @@ interface KiloScope { * In spite of the above said, which means, non-null value in this field means the key is authorized, but * It is up to the caller to ensure it is expected key of the remote party. */ - val remoteIdentity: Key.Verifying? + val remoteIdentity: SigningKey.Public? } diff --git a/src/commonMain/kotlin/net/sergeych/kiloparsec/KiloServer.kt b/src/commonMain/kotlin/net/sergeych/kiloparsec/KiloServer.kt index c96b87a..51f76e3 100644 --- a/src/commonMain/kotlin/net/sergeych/kiloparsec/KiloServer.kt +++ b/src/commonMain/kotlin/net/sergeych/kiloparsec/KiloServer.kt @@ -3,7 +3,7 @@ package net.sergeych.kiloparsec import kotlinx.coroutines.CancellationException import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.launch -import net.sergeych.crypto.Key +import net.sergeych.crypto2.SigningKey import net.sergeych.kiloparsec.adapter.InetTransportDevice import net.sergeych.mp_logger.LogTag import net.sergeych.mp_logger.debug @@ -17,7 +17,7 @@ private val instances = AtomicCounter() class KiloServer( private val clientInterface: KiloInterface, private val connections: Flow, - private val serverSigningKey: Key.Signing? = null, + private val serverSecretKey: SigningKey.Secret? = null, private val sessionBuilder: ()->S, ): LogTag("KS:${instances.incrementAndGet()}") { @@ -26,7 +26,7 @@ class KiloServer( launch { try { info { "connected ${device}" } - KiloServerConnection(clientInterface, device, sessionBuilder(), serverSigningKey) + KiloServerConnection(clientInterface, device, sessionBuilder(), serverSecretKey) .apply { debug { "server connection is ready" }} .run() } diff --git a/src/commonMain/kotlin/net/sergeych/kiloparsec/KiloServerConnection.kt b/src/commonMain/kotlin/net/sergeych/kiloparsec/KiloServerConnection.kt index b1e0056..83d3bbb 100644 --- a/src/commonMain/kotlin/net/sergeych/kiloparsec/KiloServerConnection.kt +++ b/src/commonMain/kotlin/net/sergeych/kiloparsec/KiloServerConnection.kt @@ -2,7 +2,7 @@ package net.sergeych.kiloparsec import com.ionspin.kotlin.crypto.keyexchange.KeyExchange import kotlinx.coroutines.CompletableDeferred -import net.sergeych.crypto.Key +import net.sergeych.crypto2.SigningKey import net.sergeych.mp_logger.LogTag import net.sergeych.mp_logger.Loggable import net.sergeych.mp_logger.debug @@ -23,15 +23,15 @@ class KiloServerConnection( private val clientInterface: KiloInterface, private val device: Transport.Device, private val session: S, - private val serverSigningKey: Key.Signing? = null + private val serverSigningKey: SigningKey.Secret? = null ) : RemoteInterface, Loggable by LogTag("SRV${++serverIds}") { /** * Shortcut to construct with [KiloConnectionData] intance */ @Suppress("unused") - constructor(localInterface: KiloInterface, connection: KiloConnectionData, serverSigningKey: Key.Signing? = null) - : this(localInterface, connection.device, connection.session, serverSigningKey) + constructor(localInterface: KiloInterface, connection: KiloConnectionData, serverSecretKey: SigningKey.Secret? = null) + : this(localInterface, connection.device, connection.session, serverSecretKey) private val kiloRemoteInterface = CompletableDeferred>() @@ -60,13 +60,13 @@ class KiloServerConnection( this@KiloServerConnection ) - var verifying: Key.Verifying? = null + var public: SigningKey.Public? = null var signature: UByteArray? = null if( serverSigningKey != null ) { - verifying = serverSigningKey.verifying + public = serverSigningKey.verifying signature = serverSigningKey.sign(params!!.token) } - Handshake(1u, pair.publicKey, verifying, signature) + Handshake(1u, pair.publicKey, public, signature) } on(L0ClientId) { var p = params ?: throw RemoteInterface.ClosedException("wrong handshake sequence") diff --git a/src/commonMain/kotlin/net/sergeych/kiloparsec/Transport.kt b/src/commonMain/kotlin/net/sergeych/kiloparsec/Transport.kt index be4024d..59e125b 100644 --- a/src/commonMain/kotlin/net/sergeych/kiloparsec/Transport.kt +++ b/src/commonMain/kotlin/net/sergeych/kiloparsec/Transport.kt @@ -13,7 +13,7 @@ import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.serializer -import net.sergeych.crypto.toDump +import net.sergeych.crypto2.toDump import net.sergeych.kiloparsec.Transport.Device import net.sergeych.mp_logger.* import net.sergeych.utools.pack diff --git a/src/commonMain/kotlin/net/sergeych/kiloparsec/adapter/websocketClient.kt b/src/commonMain/kotlin/net/sergeych/kiloparsec/adapter/websocketClient.kt new file mode 100644 index 0000000..265363e --- /dev/null +++ b/src/commonMain/kotlin/net/sergeych/kiloparsec/adapter/websocketClient.kt @@ -0,0 +1,76 @@ +package net.sergeych.kiloparsec.adapter + +import io.ktor.client.* +import io.ktor.client.plugins.websocket.* +import io.ktor.http.* +import io.ktor.websocket.* +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.cancel +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.launch +import net.sergeych.crypto2.SigningKey +import net.sergeych.kiloparsec.KiloClient +import net.sergeych.kiloparsec.KiloConnectionData +import net.sergeych.kiloparsec.KiloInterface +import net.sergeych.mp_logger.LogTag +import net.sergeych.mp_logger.exception +import net.sergeych.mp_logger.info +import net.sergeych.mp_logger.warning +import net.sergeych.mp_tools.globalLaunch +import net.sergeych.tools.AtomicCounter + +private val counter = AtomicCounter() + +fun websocketClient( + path: String, + clientInterface: KiloInterface = KiloInterface(), + client: HttpClient = HttpClient { install(WebSockets) }, + secretKey: SigningKey.Secret? = null, + sessionMaker: () -> S, +): KiloClient { + var u = Url(path) + if (u.encodedPath.length <= 1) + u = URLBuilder(u).apply { + encodedPath = "/kp" + }.build() + + return KiloClient(clientInterface, secretKey) { + val input = Channel() + val output = Channel() + globalLaunch { + val log = LogTag("KC:${counter.incrementAndGet()}:$u") + client.webSocket({ + url.protocol = u.protocol + url.host = u.host + url.encodedPath = u.encodedPath + url.parameters.appendAll(u.parameters) + }) { + try { + log.info { "connected to server" } + launch { + for (block in output) { + send(block.toByteArray()) + } + log.info { "input is closed, closing the websocket" } + cancel() + } + for (f in incoming) { + if (f is Frame.Binary) { + input.send(f.readBytes().toUByteArray()) + } else { + log.warning { "ignoring unexpected frame of type ${f.frameType}" } + } + } + } + catch(_:CancellationException) { + } + catch(t: Throwable) { + log.exception { "unexpected error" to t } + } + log.info { "closing connection" } + } + } + val device = ProxyDevice(input,output) { input.close() } + KiloConnectionData(device, sessionMaker()) + } +} diff --git a/src/commonMain/kotlin/net/sergeych/kiloparsec/commands.kt b/src/commonMain/kotlin/net/sergeych/kiloparsec/commands.kt index 577831f..4f97842 100644 --- a/src/commonMain/kotlin/net/sergeych/kiloparsec/commands.kt +++ b/src/commonMain/kotlin/net/sergeych/kiloparsec/commands.kt @@ -1,16 +1,16 @@ package net.sergeych.kiloparsec import kotlinx.serialization.Serializable -import net.sergeych.crypto.Key +import net.sergeych.crypto2.SigningKey // L0 commands - key exchange and check: @Serializable data class Handshake(val version: UInt, val publicKey: UByteArray, - val serverSharedKey: Key.Verifying? = null, + val serverPublicKey: SigningKey.Public? = null, val signature: UByteArray? = null) @Serializable -data class ClientIdentity(val clientIdKey: Key.Verifying?, val signature: UByteArray?) +data class ClientIdentity(val clientIdKey: SigningKey.Public?, val signature: UByteArray?) // Level 0 command: request key exchange internal val L0Request by command() diff --git a/src/commonTest/kotlin/KeysTest.kt b/src/commonTest/kotlin/KeysTest.kt index 7570847..5b3437d 100644 --- a/src/commonTest/kotlin/KeysTest.kt +++ b/src/commonTest/kotlin/KeysTest.kt @@ -2,7 +2,7 @@ import com.ionspin.kotlin.crypto.secretbox.SecretBox import com.ionspin.kotlin.crypto.util.decodeFromUByteArray import com.ionspin.kotlin.crypto.util.encodeToUByteArray import kotlinx.coroutines.test.runTest -import net.sergeych.crypto.* +import net.sergeych.crypto2.* import net.sergeych.utools.pack import net.sergeych.utools.unpack import kotlin.test.* @@ -11,11 +11,11 @@ class KeysTest { @Test fun testCreationAndMap() = runTest { initCrypto() - val (stk,pbk) = Key.Signing.pair() + val (stk,pbk) = SigningKey.Secret.pair() val x = mapOf( stk to "STK!", pbk to "PBK!") assertEquals("STK!", x[stk]) - val s1 = Key.Signing(stk.packed) + val s1 = SigningKey.Secret(stk.packed) assertEquals(stk, s1) assertEquals("STK!", x[s1]) assertEquals("PBK!", x[pbk]) @@ -27,8 +27,8 @@ class KeysTest { data1[0] = 0x01u assertFalse(s.verify(data1)) - val p2 = Key.Signing.pair() - val p3 = Key.Signing.pair() + val p2 = SigningKey.Secret.pair() + val p3 = SigningKey.Secret.pair() val ms = SignedBox(data, s1) + p2.signing @@ -36,8 +36,8 @@ class KeysTest { val ms1 = unpack(pack(ms)) assertContentEquals(data, ms1.message) assertTrue(pbk in ms1) - assertTrue(p2.verifying in ms1) - assertTrue(p3.verifying !in ms1) + assertTrue(p2.aPublic in ms1) + assertTrue(p3.aPublic !in ms1) assertThrows { unpack(pack(ms).also { it[3] = 1u }) diff --git a/src/commonTest/kotlin/PackTest.kt b/src/commonTest/kotlin/PackTest.kt index 42522f4..1495c06 100644 --- a/src/commonTest/kotlin/PackTest.kt +++ b/src/commonTest/kotlin/PackTest.kt @@ -1,6 +1,6 @@ import kotlinx.coroutines.test.runTest import kotlinx.datetime.Instant -import net.sergeych.crypto.initCrypto +import net.sergeych.crypto2.initCrypto import net.sergeych.kiloparsec.Transport import net.sergeych.utools.nowToSeconds import net.sergeych.utools.pack diff --git a/src/commonTest/kotlin/ToolsTest.kt b/src/commonTest/kotlin/ToolsTest.kt index c01e476..ae19541 100644 --- a/src/commonTest/kotlin/ToolsTest.kt +++ b/src/commonTest/kotlin/ToolsTest.kt @@ -1,7 +1,7 @@ import kotlinx.coroutines.test.runTest -import net.sergeych.crypto.createContrail -import net.sergeych.crypto.initCrypto -import net.sergeych.crypto.isValidContrail +import net.sergeych.crypto2.createContrail +import net.sergeych.crypto2.initCrypto +import net.sergeych.crypto2.isValidContrail import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFalse diff --git a/src/commonTest/kotlin/TransportTest.kt b/src/commonTest/kotlin/TransportTest.kt index 1d216f6..60ca099 100644 --- a/src/commonTest/kotlin/TransportTest.kt +++ b/src/commonTest/kotlin/TransportTest.kt @@ -4,8 +4,8 @@ import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.ReceiveChannel import kotlinx.coroutines.channels.SendChannel import kotlinx.coroutines.test.runTest -import net.sergeych.crypto.Key -import net.sergeych.crypto.initCrypto +import net.sergeych.crypto2.SigningKey +import net.sergeych.crypto2.initCrypto import net.sergeych.kiloparsec.* import net.sergeych.mp_logger.Log import kotlin.test.* @@ -162,7 +162,7 @@ class TransportTest { val cmdPing by command() val cmdPush by command() val cmdGetToken by command() - val cmdGetClientId by command() + val cmdGetClientId by command() val cmdChainCallServer1 by command() val cmdChainCallClient1 by command() val cmdChainCallServer2 by command() @@ -171,8 +171,8 @@ class TransportTest { // Log.defaultLevel = Log.Level.DEBUG val (d1, d2) = createTestDevice() - val serverId = Key.Signing.pair() - val clientId = Key.Signing.pair() + val serverId = SigningKey.Secret.pair() + val clientId = SigningKey.Secret.pair() val serverInterface = KiloInterface().apply { on(cmdPing) { diff --git a/src/jvmMain/kotlin/net/sergeych/kiloparsec/adapter/WebsocketServer.kt b/src/jvmMain/kotlin/net/sergeych/kiloparsec/adapter/WebsocketServer.kt new file mode 100644 index 0000000..b73dc63 --- /dev/null +++ b/src/jvmMain/kotlin/net/sergeych/kiloparsec/adapter/WebsocketServer.kt @@ -0,0 +1,64 @@ +package net.sergeych.kiloparsec.adapter + +import io.ktor.server.application.* +import io.ktor.server.routing.* +import io.ktor.server.websocket.* +import io.ktor.websocket.* +import kotlinx.coroutines.cancel +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch +import net.sergeych.crypto2.SigningKey +import net.sergeych.kiloparsec.KiloInterface +import net.sergeych.kiloparsec.KiloServerConnection +import net.sergeych.mp_logger.LogTag +import net.sergeych.mp_logger.debug +import net.sergeych.mp_logger.warning +import net.sergeych.tools.AtomicCounter +import java.time.Duration + +fun Application.setupWebsocketServer( + localInterface: KiloInterface, + path: String = "/kp", + serverKey: SigningKey.Secret? = null, + createSession: () -> S, +) { + + install(Routing) + install(WebSockets) { + pingPeriod = Duration.ofSeconds(15) + timeout = Duration.ofSeconds(15) + maxFrameSize = Long.MAX_VALUE + masking = false + } + val counter = AtomicCounter() + routing { + webSocket(path) { + val log = LogTag("KWS:${counter.incrementAndGet()}") + log.debug { "opening the connection" } + val input = Channel(256) + val output = Channel(256) + launch { + while (isActive) { + send(output.receive().toByteArray()) + } + } + val server = KiloServerConnection( + localInterface, + ProxyDevice(input, output) { input.close() }, + createSession(), + serverKey + ) + launch { server.run() } + for( f in incoming) { + if (f is Frame.Binary) + input.send(f.readBytes().toUByteArray()) + else + log.warning { "unknown frame type ${f.frameType}, ignoring" } + + } + log.debug { "closing the server" } + cancel() + } + } +} diff --git a/src/jvmMain/kotlin/net/sergeych/kiloparsec/adapter/asyncSocketToDevice.kt b/src/jvmMain/kotlin/net/sergeych/kiloparsec/adapter/asyncSocketToDevice.kt index 58c012e..70793bf 100644 --- a/src/jvmMain/kotlin/net/sergeych/kiloparsec/adapter/asyncSocketToDevice.kt +++ b/src/jvmMain/kotlin/net/sergeych/kiloparsec/adapter/asyncSocketToDevice.kt @@ -4,8 +4,8 @@ import kotlinx.coroutines.* import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.ClosedReceiveChannelException import kotlinx.coroutines.flow.MutableStateFlow -import net.sergeych.crypto.encodeVarUnsigned -import net.sergeych.crypto.readVarUnsigned +import net.sergeych.crypto2.encodeVarUnsigned +import net.sergeych.crypto2.readVarUnsigned import net.sergeych.kiloparsec.Transport import net.sergeych.mp_logger.LogTag import net.sergeych.mp_logger.warning diff --git a/src/jvmTest/kotlin/net/sergeych/kiloparsec/ClientTest.kt b/src/jvmTest/kotlin/net/sergeych/kiloparsec/ClientTest.kt index 058bb92..67fbbac 100644 --- a/src/jvmTest/kotlin/net/sergeych/kiloparsec/ClientTest.kt +++ b/src/jvmTest/kotlin/net/sergeych/kiloparsec/ClientTest.kt @@ -1,7 +1,7 @@ package net.sergeych.kiloparsec import kotlinx.coroutines.test.runTest -import net.sergeych.crypto.initCrypto +import net.sergeych.crypto2.initCrypto import net.sergeych.kiloparsec.adapter.acceptTcpDevice import net.sergeych.kiloparsec.adapter.connectTcpDevice import net.sergeych.mp_logger.Log @@ -49,4 +49,9 @@ class ClientTest { // client.close() // Todo } + + @Test + fun webSocketTest() = runTest { +// val server = setupWebsoketServer() + } } \ No newline at end of file