refactoring signing keys

This commit is contained in:
Sergey Chernov 2023-11-22 11:52:43 +03:00
parent e619b45485
commit 7042e41d70
23 changed files with 221 additions and 76 deletions

View File

@ -82,7 +82,7 @@ kotlin {
implementation("io.ktor:ktor-server-core-jvm:$ktor_version") implementation("io.ktor:ktor-server-core-jvm:$ktor_version")
implementation("io.ktor:ktor-server-websockets-jvm:$ktor_version") implementation("io.ktor:ktor-server-websockets-jvm:$ktor_version")
implementation("io.ktor:ktor-server-netty:$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 val jvmTest by getting

View File

@ -1,4 +1,4 @@
package net.sergeych.crypto package net.sergeych.crypto2
import com.ionspin.kotlin.crypto.LibsodiumInitializer import com.ionspin.kotlin.crypto.LibsodiumInitializer
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex

View File

@ -1,4 +1,4 @@
package net.sergeych.crypto package net.sergeych.crypto2
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient 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 * key, or return unchanged (same) object if it is already signed by this key; you
* _can't assume it always returns a copied object!_ * _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 if (key.verifying in this) this
else SignedBox(message, seals + Seal.create(key, message), false) else SignedBox(message, seals + Seal.create(key, message), false)
/** /**
* Check that it is signed with a specified key. * 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 } return seals.any { it.key == verifyingKey }
} }
@ -53,12 +53,12 @@ class SignedBox(
* add seals. * add seals.
*/ */
@Serializable @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) fun verify(message: UByteArray): Boolean = key.verify(signature, message)
companion object { 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)) 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. * @param keys a list of keys to sign with, should be at least one key.
* @throws IllegalArgumentException if keys are not specified. * @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) SignedBox(data, keys.map { Seal.create(it, data) }, false)
} }
} }

View File

@ -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.InvalidSignatureException
import com.ionspin.kotlin.crypto.signature.Signature import com.ionspin.kotlin.crypto.signature.Signature
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable 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 in general: public, secret and later symmetric too.
* Keys could be compared to each other for equality and used * Keys could be compared to each other for equality and used
* as a Map keys (not sure about js). * as a Map keys (not sure about js).
* *
* Use [Signing.pair] to create new keys. * Use [Secret.pair] to create new keys.
*/ */
@Serializable @Serializable
sealed class Key { sealed class SigningKey {
abstract val packed: UByteArray abstract val packed: UByteArray
override fun equals(other: Any?): Boolean { 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 { override fun hashCode(): Int {
@ -31,8 +31,8 @@ sealed class Key {
* Public key to verify signatures only * Public key to verify signatures only
*/ */
@Serializable @Serializable
@SerialName("pvk") @SerialName("p")
class Verifying(override val packed: UByteArray) : Key() { class Public(override val packed: UByteArray) : SigningKey() {
/** /**
* Verify the signature and return true if it is correct. * Verify the signature and return true if it is correct.
*/ */
@ -51,22 +51,22 @@ sealed class Key {
* Secret key to sign only * Secret key to sign only
*/ */
@Serializable @Serializable
@SerialName("ssk") @SerialName("s")
class Signing(override val packed: UByteArray) : Key() { class Secret(override val packed: UByteArray) : SigningKey() {
val verifying: Verifying by lazy { val verifying: Public by lazy {
Verifying(Signature.ed25519SkToPk(packed)) Public(Signature.ed25519SkToPk(packed))
} }
fun sign(message: UByteArray): UByteArray = Signature.detached(message, packed) fun sign(message: UByteArray): UByteArray = Signature.detached(message, packed)
override fun toString(): String = "Sct:${super.toString()}" override fun toString(): String = "Sct:${super.toString()}"
companion object { companion object {
data class Pair(val signing: Signing, val verifying: Verifying) data class Pair(val signing: Secret, val aPublic: Public)
fun pair(): Pair { fun pair(): Pair {
val p = Signature.keypair() val p = Signature.keypair()
return Pair(Signing(p.secretKey), Verifying(p.publicKey)) return Pair(Secret(p.secretKey), Public(p.publicKey))
} }
} }
} }

View File

@ -1,4 +1,4 @@
package net.sergeych.crypto package net.sergeych.crypto2
import net.sergeych.bintools.CRC import net.sergeych.bintools.CRC

View File

@ -1,6 +1,6 @@
@file:Suppress("unused") @file:Suppress("unused")
package net.sergeych.crypto package net.sergeych.crypto2
import com.ionspin.kotlin.crypto.secretbox.SecretBox import com.ionspin.kotlin.crypto.secretbox.SecretBox
import com.ionspin.kotlin.crypto.secretbox.crypto_secretbox_NONCEBYTES import com.ionspin.kotlin.crypto.secretbox.crypto_secretbox_NONCEBYTES

View File

@ -1,4 +1,4 @@
package net.sergeych.crypto package net.sergeych.crypto2
import net.sergeych.bintools.toDump import net.sergeych.bintools.toDump
import net.sergeych.mp_tools.encodeToBase64Url import net.sergeych.mp_tools.encodeToBase64Url

View File

@ -6,7 +6,7 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.isActive 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.LogTag
import net.sergeych.mp_logger.Loggable import net.sergeych.mp_logger.Loggable
import net.sergeych.mp_logger.debug import net.sergeych.mp_logger.debug
@ -21,7 +21,7 @@ import net.sergeych.mp_tools.globalLaunch
*/ */
class KiloClient<S>( class KiloClient<S>(
localInterface: KiloInterface<S>, localInterface: KiloInterface<S>,
secretKey: Key.Signing? = null, secretKey: SigningKey.Secret? = null,
connectionDataFactory: ConnectionDataFactory<S>, connectionDataFactory: ConnectionDataFactory<S>,
) : RemoteInterface, ) : RemoteInterface,
Loggable by LogTag("CLIF") { Loggable by LogTag("CLIF") {
@ -80,7 +80,7 @@ class KiloClient<S>(
suspend fun token() = deferredClient.await().token() 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. * there is no active MITM attack.
* *
* Non-null value means the key was successfully authenticated, null means remote party did not provide * Non-null value means the key was successfully authenticated, null means remote party did not provide
@ -99,7 +99,7 @@ class KiloClient<S>(
} }
private var connectionBuilder: (suspend () -> Transport.Device)? = null 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 * Build local command implementations (remotely callable ones), exception

View File

@ -2,7 +2,7 @@ package net.sergeych.kiloparsec
import com.ionspin.kotlin.crypto.keyexchange.KeyExchange import com.ionspin.kotlin.crypto.keyexchange.KeyExchange
import kotlinx.coroutines.* import kotlinx.coroutines.*
import net.sergeych.crypto.Key import net.sergeych.crypto2.SigningKey
import net.sergeych.mp_logger.LogTag import net.sergeych.mp_logger.LogTag
import net.sergeych.mp_logger.Loggable import net.sergeych.mp_logger.Loggable
import net.sergeych.mp_logger.debug import net.sergeych.mp_logger.debug
@ -15,17 +15,17 @@ class KiloClientConnection<S>(
private val clientInterface: KiloInterface<S>, private val clientInterface: KiloInterface<S>,
private val device: Transport.Device, private val device: Transport.Device,
private val session: S, private val session: S,
private val secretIdKey: Key.Signing? = null, private val secretIdKey: SigningKey.Secret? = null,
) : RemoteInterface, Loggable by LogTag("KPC:${++clientIds}") { ) : RemoteInterface, Loggable by LogTag("KPC:${++clientIds}") {
constructor(localInterface: KiloInterface<S>, connection: KiloConnectionData<S>, secretIdKey: Key.Signing? = null) constructor(localInterface: KiloInterface<S>, connection: KiloConnectionData<S>, secretIdKey: SigningKey.Secret? = null)
: this(localInterface, connection.device, connection.session, secretIdKey) : this(localInterface, connection.device, connection.session, secretIdKey)
private val kiloRemoteInterface = CompletableDeferred<KiloRemoteInterface<S>>() private val kiloRemoteInterface = CompletableDeferred<KiloRemoteInterface<S>>()
private val deferredParams = CompletableDeferred<KiloParams<S>>() private val deferredParams = CompletableDeferred<KiloParams<S>>()
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 * Run the client, blocking until the device is closed, or some critical exception
@ -59,7 +59,7 @@ class KiloClientConnection<S>(
var params = KiloParams(false, transport, sk, session, null, this@KiloClientConnection) var params = KiloParams(false, transport, sk, session, null, this@KiloClientConnection)
// Check ID if any // Check ID if any
serverHe.serverSharedKey?.let { k -> serverHe.serverPublicKey?.let { k ->
if (serverHe.signature == null) if (serverHe.signature == null)
throw RemoteInterface.SecurityException("missing signature") throw RemoteInterface.SecurityException("missing signature")
if (!k.verify(serverHe.signature, params.token)) if (!k.verify(serverHe.signature, params.token))

View File

@ -9,10 +9,10 @@ import kotlinx.serialization.Serializable
import net.sergeych.bintools.toDataSource import net.sergeych.bintools.toDataSource
import net.sergeych.bipack.BipackDecoder import net.sergeych.bipack.BipackDecoder
import net.sergeych.bipack.BipackEncoder import net.sergeych.bipack.BipackEncoder
import net.sergeych.crypto.DecryptionFailedException import net.sergeych.crypto2.DecryptionFailedException
import net.sergeych.crypto.Key import net.sergeych.crypto2.SigningKey
import net.sergeych.crypto.randomBytes import net.sergeych.crypto2.randomBytes
import net.sergeych.crypto.randomUInt import net.sergeych.crypto2.randomUInt
import net.sergeych.tools.ProtectedOp import net.sergeych.tools.ProtectedOp
import net.sergeych.utools.pack import net.sergeych.utools.pack
import net.sergeych.utools.unpack import net.sergeych.utools.unpack
@ -34,7 +34,7 @@ data class KiloParams<S>(
val transport: RemoteInterface, val transport: RemoteInterface,
val sessionKeyPair: KeyExchangeSessionKeyPair, val sessionKeyPair: KeyExchangeSessionKeyPair,
val scopeSession: S, val scopeSession: S,
val remoteIdentity: Key.Verifying?, val remoteIdentity: SigningKey.Public?,
val remoteTransport: RemoteInterface val remoteTransport: RemoteInterface
) { ) {
@Serializable @Serializable
@ -56,7 +56,7 @@ data class KiloParams<S>(
override val session = scopeSession override val session = scopeSession
override val remote: RemoteInterface = remoteTransport override val remote: RemoteInterface = remoteTransport
override val sessionToken: UByteArray = token override val sessionToken: UByteArray = token
override val remoteIdentity: Key.Verifying? = this@KiloParams.remoteIdentity override val remoteIdentity: SigningKey.Public? = this@KiloParams.remoteIdentity
} }
} }

View File

@ -1,6 +1,6 @@
package net.sergeych.kiloparsec 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 * Scope for Kiloparsec client/server commands execution, contain per-connection specific data. The scope
@ -26,8 +26,8 @@ interface KiloScope<S> {
val sessionToken: UByteArray val sessionToken: UByteArray
/** /**
* If the remote part has provided a secret key, e.g., gave non-null [Key.Signing] on construction, * 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 [Key.Verifying] shared key here. * 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 * 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. * to this party with no middle point intruders.
* *
@ -37,6 +37,6 @@ interface KiloScope<S> {
* In spite of the above said, which means, non-null value in this field means the key is authorized, but * 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. * It is up to the caller to ensure it is expected key of the remote party.
*/ */
val remoteIdentity: Key.Verifying? val remoteIdentity: SigningKey.Public?
} }

View File

@ -3,7 +3,7 @@ package net.sergeych.kiloparsec
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import net.sergeych.crypto.Key import net.sergeych.crypto2.SigningKey
import net.sergeych.kiloparsec.adapter.InetTransportDevice import net.sergeych.kiloparsec.adapter.InetTransportDevice
import net.sergeych.mp_logger.LogTag import net.sergeych.mp_logger.LogTag
import net.sergeych.mp_logger.debug import net.sergeych.mp_logger.debug
@ -17,7 +17,7 @@ private val instances = AtomicCounter()
class KiloServer<S>( class KiloServer<S>(
private val clientInterface: KiloInterface<S>, private val clientInterface: KiloInterface<S>,
private val connections: Flow<InetTransportDevice>, private val connections: Flow<InetTransportDevice>,
private val serverSigningKey: Key.Signing? = null, private val serverSecretKey: SigningKey.Secret? = null,
private val sessionBuilder: ()->S, private val sessionBuilder: ()->S,
): LogTag("KS:${instances.incrementAndGet()}") { ): LogTag("KS:${instances.incrementAndGet()}") {
@ -26,7 +26,7 @@ class KiloServer<S>(
launch { launch {
try { try {
info { "connected ${device}" } info { "connected ${device}" }
KiloServerConnection(clientInterface, device, sessionBuilder(), serverSigningKey) KiloServerConnection(clientInterface, device, sessionBuilder(), serverSecretKey)
.apply { debug { "server connection is ready" }} .apply { debug { "server connection is ready" }}
.run() .run()
} }

View File

@ -2,7 +2,7 @@ package net.sergeych.kiloparsec
import com.ionspin.kotlin.crypto.keyexchange.KeyExchange import com.ionspin.kotlin.crypto.keyexchange.KeyExchange
import kotlinx.coroutines.CompletableDeferred 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.LogTag
import net.sergeych.mp_logger.Loggable import net.sergeych.mp_logger.Loggable
import net.sergeych.mp_logger.debug import net.sergeych.mp_logger.debug
@ -23,15 +23,15 @@ class KiloServerConnection<S>(
private val clientInterface: KiloInterface<S>, private val clientInterface: KiloInterface<S>,
private val device: Transport.Device, private val device: Transport.Device,
private val session: S, private val session: S,
private val serverSigningKey: Key.Signing? = null private val serverSigningKey: SigningKey.Secret? = null
) : RemoteInterface, Loggable by LogTag("SRV${++serverIds}") { ) : RemoteInterface, Loggable by LogTag("SRV${++serverIds}") {
/** /**
* Shortcut to construct with [KiloConnectionData] intance * Shortcut to construct with [KiloConnectionData] intance
*/ */
@Suppress("unused") @Suppress("unused")
constructor(localInterface: KiloInterface<S>, connection: KiloConnectionData<S>, serverSigningKey: Key.Signing? = null) constructor(localInterface: KiloInterface<S>, connection: KiloConnectionData<S>, serverSecretKey: SigningKey.Secret? = null)
: this(localInterface, connection.device, connection.session, serverSigningKey) : this(localInterface, connection.device, connection.session, serverSecretKey)
private val kiloRemoteInterface = CompletableDeferred<KiloRemoteInterface<S>>() private val kiloRemoteInterface = CompletableDeferred<KiloRemoteInterface<S>>()
@ -60,13 +60,13 @@ class KiloServerConnection<S>(
this@KiloServerConnection this@KiloServerConnection
) )
var verifying: Key.Verifying? = null var public: SigningKey.Public? = null
var signature: UByteArray? = null var signature: UByteArray? = null
if( serverSigningKey != null ) { if( serverSigningKey != null ) {
verifying = serverSigningKey.verifying public = serverSigningKey.verifying
signature = serverSigningKey.sign(params!!.token) signature = serverSigningKey.sign(params!!.token)
} }
Handshake(1u, pair.publicKey, verifying, signature) Handshake(1u, pair.publicKey, public, signature)
} }
on(L0ClientId) { on(L0ClientId) {
var p = params ?: throw RemoteInterface.ClosedException("wrong handshake sequence") var p = params ?: throw RemoteInterface.ClosedException("wrong handshake sequence")

View File

@ -13,7 +13,7 @@ import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.serializer import kotlinx.serialization.serializer
import net.sergeych.crypto.toDump import net.sergeych.crypto2.toDump
import net.sergeych.kiloparsec.Transport.Device import net.sergeych.kiloparsec.Transport.Device
import net.sergeych.mp_logger.* import net.sergeych.mp_logger.*
import net.sergeych.utools.pack import net.sergeych.utools.pack

View File

@ -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 <S>websocketClient(
path: String,
clientInterface: KiloInterface<S> = KiloInterface(),
client: HttpClient = HttpClient { install(WebSockets) },
secretKey: SigningKey.Secret? = null,
sessionMaker: () -> S,
): KiloClient<S> {
var u = Url(path)
if (u.encodedPath.length <= 1)
u = URLBuilder(u).apply {
encodedPath = "/kp"
}.build()
return KiloClient(clientInterface, secretKey) {
val input = Channel<UByteArray>()
val output = Channel<UByteArray>()
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())
}
}

View File

@ -1,16 +1,16 @@
package net.sergeych.kiloparsec package net.sergeych.kiloparsec
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import net.sergeych.crypto.Key import net.sergeych.crypto2.SigningKey
// L0 commands - key exchange and check: // L0 commands - key exchange and check:
@Serializable @Serializable
data class Handshake(val version: UInt, val publicKey: UByteArray, data class Handshake(val version: UInt, val publicKey: UByteArray,
val serverSharedKey: Key.Verifying? = null, val serverPublicKey: SigningKey.Public? = null,
val signature: UByteArray? = null) val signature: UByteArray? = null)
@Serializable @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 // Level 0 command: request key exchange
internal val L0Request by command<Handshake, Handshake>() internal val L0Request by command<Handshake, Handshake>()

View File

@ -2,7 +2,7 @@ import com.ionspin.kotlin.crypto.secretbox.SecretBox
import com.ionspin.kotlin.crypto.util.decodeFromUByteArray import com.ionspin.kotlin.crypto.util.decodeFromUByteArray
import com.ionspin.kotlin.crypto.util.encodeToUByteArray import com.ionspin.kotlin.crypto.util.encodeToUByteArray
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import net.sergeych.crypto.* import net.sergeych.crypto2.*
import net.sergeych.utools.pack import net.sergeych.utools.pack
import net.sergeych.utools.unpack import net.sergeych.utools.unpack
import kotlin.test.* import kotlin.test.*
@ -11,11 +11,11 @@ class KeysTest {
@Test @Test
fun testCreationAndMap() = runTest { fun testCreationAndMap() = runTest {
initCrypto() initCrypto()
val (stk,pbk) = Key.Signing.pair() val (stk,pbk) = SigningKey.Secret.pair()
val x = mapOf( stk to "STK!", pbk to "PBK!") val x = mapOf( stk to "STK!", pbk to "PBK!")
assertEquals("STK!", x[stk]) assertEquals("STK!", x[stk])
val s1 = Key.Signing(stk.packed) val s1 = SigningKey.Secret(stk.packed)
assertEquals(stk, s1) assertEquals(stk, s1)
assertEquals("STK!", x[s1]) assertEquals("STK!", x[s1])
assertEquals("PBK!", x[pbk]) assertEquals("PBK!", x[pbk])
@ -27,8 +27,8 @@ class KeysTest {
data1[0] = 0x01u data1[0] = 0x01u
assertFalse(s.verify(data1)) assertFalse(s.verify(data1))
val p2 = Key.Signing.pair() val p2 = SigningKey.Secret.pair()
val p3 = Key.Signing.pair() val p3 = SigningKey.Secret.pair()
val ms = SignedBox(data, s1) + p2.signing val ms = SignedBox(data, s1) + p2.signing
@ -36,8 +36,8 @@ class KeysTest {
val ms1 = unpack<SignedBox>(pack(ms)) val ms1 = unpack<SignedBox>(pack(ms))
assertContentEquals(data, ms1.message) assertContentEquals(data, ms1.message)
assertTrue(pbk in ms1) assertTrue(pbk in ms1)
assertTrue(p2.verifying in ms1) assertTrue(p2.aPublic in ms1)
assertTrue(p3.verifying !in ms1) assertTrue(p3.aPublic !in ms1)
assertThrows<IllegalSignatureException> { assertThrows<IllegalSignatureException> {
unpack<SignedBox>(pack(ms).also { it[3] = 1u }) unpack<SignedBox>(pack(ms).also { it[3] = 1u })

View File

@ -1,6 +1,6 @@
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import kotlinx.datetime.Instant import kotlinx.datetime.Instant
import net.sergeych.crypto.initCrypto import net.sergeych.crypto2.initCrypto
import net.sergeych.kiloparsec.Transport import net.sergeych.kiloparsec.Transport
import net.sergeych.utools.nowToSeconds import net.sergeych.utools.nowToSeconds
import net.sergeych.utools.pack import net.sergeych.utools.pack

View File

@ -1,7 +1,7 @@
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import net.sergeych.crypto.createContrail import net.sergeych.crypto2.createContrail
import net.sergeych.crypto.initCrypto import net.sergeych.crypto2.initCrypto
import net.sergeych.crypto.isValidContrail import net.sergeych.crypto2.isValidContrail
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFalse import kotlin.test.assertFalse

View File

@ -4,8 +4,8 @@ import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.ReceiveChannel import kotlinx.coroutines.channels.ReceiveChannel
import kotlinx.coroutines.channels.SendChannel import kotlinx.coroutines.channels.SendChannel
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import net.sergeych.crypto.Key import net.sergeych.crypto2.SigningKey
import net.sergeych.crypto.initCrypto import net.sergeych.crypto2.initCrypto
import net.sergeych.kiloparsec.* import net.sergeych.kiloparsec.*
import net.sergeych.mp_logger.Log import net.sergeych.mp_logger.Log
import kotlin.test.* import kotlin.test.*
@ -162,7 +162,7 @@ class TransportTest {
val cmdPing by command<String, String>() val cmdPing by command<String, String>()
val cmdPush by command<String, String>() val cmdPush by command<String, String>()
val cmdGetToken by command<Unit, UByteArray>() val cmdGetToken by command<Unit, UByteArray>()
val cmdGetClientId by command<Unit, Key.Verifying?>() val cmdGetClientId by command<Unit, SigningKey.Public?>()
val cmdChainCallServer1 by command<String, String>() val cmdChainCallServer1 by command<String, String>()
val cmdChainCallClient1 by command<String, String>() val cmdChainCallClient1 by command<String, String>()
val cmdChainCallServer2 by command<String, String>() val cmdChainCallServer2 by command<String, String>()
@ -171,8 +171,8 @@ class TransportTest {
// Log.defaultLevel = Log.Level.DEBUG // Log.defaultLevel = Log.Level.DEBUG
val (d1, d2) = createTestDevice() val (d1, d2) = createTestDevice()
val serverId = Key.Signing.pair() val serverId = SigningKey.Secret.pair()
val clientId = Key.Signing.pair() val clientId = SigningKey.Secret.pair()
val serverInterface = KiloInterface<String>().apply { val serverInterface = KiloInterface<String>().apply {
on(cmdPing) { on(cmdPing) {

View File

@ -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 <S> Application.setupWebsocketServer(
localInterface: KiloInterface<S>,
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<UByteArray>(256)
val output = Channel<UByteArray>(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()
}
}
}

View File

@ -4,8 +4,8 @@ import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.ClosedReceiveChannelException import kotlinx.coroutines.channels.ClosedReceiveChannelException
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import net.sergeych.crypto.encodeVarUnsigned import net.sergeych.crypto2.encodeVarUnsigned
import net.sergeych.crypto.readVarUnsigned import net.sergeych.crypto2.readVarUnsigned
import net.sergeych.kiloparsec.Transport import net.sergeych.kiloparsec.Transport
import net.sergeych.mp_logger.LogTag import net.sergeych.mp_logger.LogTag
import net.sergeych.mp_logger.warning import net.sergeych.mp_logger.warning

View File

@ -1,7 +1,7 @@
package net.sergeych.kiloparsec package net.sergeych.kiloparsec
import kotlinx.coroutines.test.runTest 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.acceptTcpDevice
import net.sergeych.kiloparsec.adapter.connectTcpDevice import net.sergeych.kiloparsec.adapter.connectTcpDevice
import net.sergeych.mp_logger.Log import net.sergeych.mp_logger.Log
@ -49,4 +49,9 @@ class ClientTest {
// client.close() // client.close()
// Todo // Todo
} }
@Test
fun webSocketTest() = runTest {
// val server = setupWebsoketServer()
}
} }