adopted crypto2 0.4.*

This commit is contained in:
Sergey Chernov 2024-06-25 10:39:00 +07:00
parent 38fbca955c
commit 5df6143c75
14 changed files with 46 additions and 57 deletions

View File

@ -44,6 +44,7 @@ kotlin {
// iosArm64() // iosArm64()
// iosSimulatorArm64() // iosSimulatorArm64()
linuxX64() linuxX64()
linuxArm64()
// macosX64() // macosX64()
@ -66,7 +67,7 @@ kotlin {
// api("net.sergeych:mp_bintools:0.1.1") // api("net.sergeych:mp_bintools:0.1.1")
// api("net.sergeych:mp_stools:1.4.7") // api("net.sergeych:mp_stools:1.4.7")
api("net.sergeych:crypto2:0.2.2-SNAPSHOT") api("net.sergeych:crypto2:0.4.1-SNAPSHOT")
} }
} }
val commonTest by getting { val commonTest by getting {

View File

@ -9,6 +9,7 @@ import kotlinx.coroutines.isActive
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import net.sergeych.crypto2.SigningKey import net.sergeych.crypto2.SigningKey
import net.sergeych.crypto2.VerifyingPublicKey
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,7 +24,7 @@ import net.sergeych.mp_tools.globalLaunch
*/ */
class KiloClient<S>( class KiloClient<S>(
localInterface: KiloInterface<S>, localInterface: KiloInterface<S>,
secretKey: SigningKey.Secret? = null, secretKey: SigningKey? = null,
connectionDataFactory: ConnectionDataFactory<S>, connectionDataFactory: ConnectionDataFactory<S>,
) : RemoteInterface, ) : RemoteInterface,
Loggable by LogTag("CLIF") { Loggable by LogTag("CLIF") {
@ -101,14 +102,14 @@ class KiloClient<S>(
suspend fun token() = deferredClient.await().token() suspend fun token() = deferredClient.await().token()
/** /**
* Remote party shared key ([SigningKey.Public]]), could be used ti ensure server is what we expected and * Remote party shared key ([VerifyingPublicKey]), 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
* a key. Connection is established either with a properly authenticated key or no key at all. * a key. Connection is established either with a properly authenticated key or no key at all.
*/ */
@Suppress("unused") @Suppress("unused")
suspend fun remoteId() = deferredClient.await().remoteId() suspend fun remoteId(): VerifyingPublicKey? = deferredClient.await().remoteId()
companion object { companion object {
class Builder<S>() { class Builder<S>() {
@ -120,7 +121,7 @@ class KiloClient<S>(
} }
private var connectionBuilder: (suspend () -> Transport.Device)? = null private var connectionBuilder: (suspend () -> Transport.Device)? = null
var secretIdKey: SigningKey.Secret? = null var secretIdKey: SigningKey? = null
/** /**
* Build local command implementations (remotely callable ones), exception * Build local command implementations (remotely callable ones), exception

View File

@ -3,6 +3,7 @@ package net.sergeych.kiloparsec
import kotlinx.coroutines.* import kotlinx.coroutines.*
import net.sergeych.crypto2.SafeKeyExchange import net.sergeych.crypto2.SafeKeyExchange
import net.sergeych.crypto2.SigningKey import net.sergeych.crypto2.SigningKey
import net.sergeych.crypto2.VerifyingPublicKey
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 +16,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: SigningKey.Secret? = null, private val secretIdKey: SigningKey? = null,
) : RemoteInterface, Loggable by LogTag("KPC:${++clientIds}") { ) : RemoteInterface, Loggable by LogTag("KPC:${++clientIds}") {
constructor(localInterface: KiloInterface<S>, connection: KiloConnectionData<S>, secretIdKey: SigningKey.Secret? = null) constructor(localInterface: KiloInterface<S>, connection: KiloConnectionData<S>, secretIdKey: SigningKey? = 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(): SigningKey.Public? = deferredParams.await().remoteIdentity suspend fun remoteId(): VerifyingPublicKey? = 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
@ -69,7 +70,7 @@ class KiloClientConnection<S>(
L0ClientId, params.encrypt( L0ClientId, params.encrypt(
pack( pack(
ClientIdentity( ClientIdentity(
secretIdKey?.publicKey, secretIdKey?.verifyingKey,
secretIdKey?.sign(params.token) secretIdKey?.sign(params.token)
) )
) )

View File

@ -24,7 +24,7 @@ data class KiloParams<S>(
val transport: RemoteInterface, val transport: RemoteInterface,
val sessionKey: SafeKeyExchange.SessionKey, val sessionKey: SafeKeyExchange.SessionKey,
val scopeSession: S, val scopeSession: S,
val remoteIdentity: SigningKey.Public?, val remoteIdentity: VerifyingPublicKey?,
val remoteTransport: RemoteInterface, val remoteTransport: RemoteInterface,
) { ) {
@Serializable @Serializable
@ -41,12 +41,12 @@ 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: SigningKey.Public? = this@KiloParams.remoteIdentity override val remoteIdentity: VerifyingPublicKey? = this@KiloParams.remoteIdentity
} }
} }
val token: UByteArray by lazy { val token: UByteArray by lazy {
blake2b("token_".encodeToUByteArray() + sessionKey.sessionTag).sliceArray(0..<SymmetricKey.nonceByteLength) blake2b("token_".encodeToUByteArray() + sessionKey.sessionTag).sliceArray(0..<SymmetricKey.nonceLength)
} }
private val numericNonce = NumericNonce(token) private val numericNonce = NumericNonce(token)

View File

@ -1,6 +1,7 @@
package net.sergeych.kiloparsec package net.sergeych.kiloparsec
import net.sergeych.crypto2.SigningKey import net.sergeych.crypto2.SigningKey
import net.sergeych.crypto2.VerifyingPublicKey
/** /**
* 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 +27,8 @@ interface KiloScope<S> {
val sessionToken: UByteArray val sessionToken: UByteArray
/** /**
* If the remote part has provided a secret key, e.g., gave non-null [SigningKey.Secret] on construction, * If the remote part has provided a secret key, e.g., gave non-null [SigningKey] on construction,
* the kiloparsec checks it in the MITM-safe way and provides its [SigningKey.Public] shared key here. * the kiloparsec checks it in the MITM-safe way and provides its [VerifyingPublicKey] 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 +38,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: SigningKey.Public? val remoteIdentity: VerifyingPublicKey?
} }

View File

@ -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 serverSecretKey: SigningKey.Secret? = null, private val serverSecretKey: SigningKey? = null,
private val sessionBuilder: ()->S, private val sessionBuilder: ()->S,
): LogTag("KS:${instances.incrementAndGet()}") { ): LogTag("KS:${instances.incrementAndGet()}") {

View File

@ -23,14 +23,14 @@ 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: SigningKey.Secret? = null private val serverSigningKey: SigningKey? = 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>, serverSecretKey: SigningKey.Secret? = null) constructor(localInterface: KiloInterface<S>, connection: KiloConnectionData<S>, serverSecretKey: SigningKey? = null)
: this(localInterface, connection.device, connection.session, serverSecretKey) : this(localInterface, connection.device, connection.session, serverSecretKey)
private val kiloRemoteInterface = CompletableDeferred<KiloRemoteInterface<S>>() private val kiloRemoteInterface = CompletableDeferred<KiloRemoteInterface<S>>()

View File

@ -28,7 +28,7 @@ fun <S> websocketClient(
path: String, path: String,
clientInterface: KiloInterface<S> = KiloInterface(), clientInterface: KiloInterface<S> = KiloInterface(),
client: HttpClient = HttpClient { install(WebSockets) }, client: HttpClient = HttpClient { install(WebSockets) },
secretKey: SigningKey.Secret? = null, secretKey: SigningKey? = null,
sessionMaker: () -> S = { sessionMaker: () -> S = {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
Unit as S Unit as S

View File

@ -3,7 +3,7 @@ package net.sergeych.kiloparsec
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import net.sergeych.crypto2.SafeKeyExchange import net.sergeych.crypto2.SafeKeyExchange
import net.sergeych.crypto2.Seal import net.sergeych.crypto2.Seal
import net.sergeych.crypto2.SigningKey import net.sergeych.crypto2.VerifyingPublicKey
// L0 commands - key exchange and check: // L0 commands - key exchange and check:
@Serializable @Serializable
@ -11,7 +11,7 @@ data class Handshake(val version: UInt, val publicKey: SafeKeyExchange.PublicKey
val signature: Seal? = null) val signature: Seal? = null)
@Serializable @Serializable
data class ClientIdentity(val clientIdKey: SigningKey.Public?, val signature: UByteArray?) data class ClientIdentity(val clientIdKey: VerifyingPublicKey?, 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

@ -1,5 +1,8 @@
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import net.sergeych.crypto2.* import net.sergeych.crypto2.IllegalSignatureException
import net.sergeych.crypto2.SealedBox
import net.sergeych.crypto2.SigningSecretKey
import net.sergeych.crypto2.initCrypto
import net.sergeych.kiloparsec.encodeToUByteArray import net.sergeych.kiloparsec.encodeToUByteArray
import net.sergeych.utools.pack import net.sergeych.utools.pack
import net.sergeych.utools.unpack import net.sergeych.utools.unpack
@ -9,11 +12,11 @@ class KeysTest {
@Test @Test
fun testCreationAndMap() = runTest { fun testCreationAndMap() = runTest {
initCrypto() initCrypto()
val (stk,pbk) = SigningKey.pair() val (stk,pbk) = SigningSecretKey.generatePair()
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 = SigningKey.Secret(stk.packed) val s1 = SigningSecretKey(stk.keyBytes)
assertEquals(stk, s1) assertEquals(stk, s1)
assertEquals("STK!", x[s1]) assertEquals("STK!", x[s1])
assertEquals("PBK!", x[pbk]) assertEquals("PBK!", x[pbk])
@ -25,8 +28,8 @@ class KeysTest {
data1[0] = 0x01u data1[0] = 0x01u
assertFalse(s.isValid(data1)) assertFalse(s.isValid(data1))
val p2 = SigningKey.pair() val p2 = SigningSecretKey.generatePair()
val p3 = SigningKey.pair() val p3 = SigningSecretKey.generatePair()
val ms = SealedBox(data, s1) + p2.secretKey val ms = SealedBox(data, s1) + p2.secretKey

View File

@ -1,23 +1,4 @@
import kotlinx.coroutines.test.runTest
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
import kotlin.test.assertTrue
class ToolsTest { class ToolsTest {
@Test
fun testContrails() = runTest {
initCrypto()
val c = createContrail(ubyteArrayOf(1u, 2u, 3u, 4u, 5u))
assertEquals(134u, c[0])
assertTrue { isValidContrail(c) }
c[2] = 11u
assertFalse { isValidContrail(c) }
}
// @Test // @Test
// fun testRemoceCmd() { // fun testRemoceCmd() {
// assertEquals("lalala", removeCmd("lalala")) // assertEquals("lalala", removeCmd("lalala"))

View File

@ -3,7 +3,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.crypto2.SigningKey import net.sergeych.crypto2.SigningSecretKey
import net.sergeych.crypto2.VerifyingPublicKey
import net.sergeych.crypto2.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
@ -167,7 +168,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, SigningKey.Public?>() val cmdGetClientId by command<Unit, VerifyingPublicKey?>()
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>()
@ -176,8 +177,8 @@ class TransportTest {
// Log.defaultLevel = Log.Level.DEBUG // Log.defaultLevel = Log.Level.DEBUG
val (d1, d2) = createTestDevice() val (d1, d2) = createTestDevice()
val serverId = SigningKey.pair() val serverId = SigningSecretKey.generatePair()
val clientId = SigningKey.pair() val clientId = SigningSecretKey.generatePair()
val cmdException by command<Unit, Unit>() val cmdException by command<Unit, Unit>()
val cmdRemoteExceptionTest by command<Unit, String>() val cmdRemoteExceptionTest by command<Unit, String>()

View File

@ -21,7 +21,7 @@ import java.time.Duration
fun <S> Application.setupWebsocketServer( fun <S> Application.setupWebsocketServer(
localInterface: KiloInterface<S>, localInterface: KiloInterface<S>,
path: String = "/kp", path: String = "/kp",
serverKey: SigningKey.Secret? = null, serverKey: SigningKey? = null,
createSession: () -> S, createSession: () -> S,
) { ) {

View File

@ -4,12 +4,12 @@ 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.crypto2.Contrail
import net.sergeych.crypto2.encodeVarUnsigned import net.sergeych.crypto2.encodeVarUnsigned
import net.sergeych.crypto2.readVarUnsigned import net.sergeych.crypto2.readVarUnsigned
import net.sergeych.kiloparsec.RemoteInterface import net.sergeych.kiloparsec.RemoteInterface
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.info
import net.sergeych.mp_logger.warning import net.sergeych.mp_logger.warning
import net.sergeych.mp_tools.globalLaunch import net.sergeych.mp_tools.globalLaunch
import net.sergeych.tools.waitFor import net.sergeych.tools.waitFor
@ -25,7 +25,10 @@ private val log = LogTag("ASTD")
/** /**
* Prepend block with its size, varint-encoded * Prepend block with its size, varint-encoded
*/ */
private fun encode(block: UByteArray): ByteArray = (encodeVarUnsigned(block.size.toUInt()) + block).toByteArray() private fun encode(block: UByteArray): ByteArray {
val c = Contrail.create(block)
return (encodeVarUnsigned(c.size.toUInt()) + c).toByteArray()
}
/** /**
* Convert asynchronous socket to a [Transport.Device] using non-blocking nio, * Convert asynchronous socket to a [Transport.Device] using non-blocking nio,
@ -122,7 +125,8 @@ suspend fun asyncSocketToDevice(socket: AsynchronousSocketChannel): InetTranspor
for (i in 0..<size.toInt()) { for (i in 0..<size.toInt()) {
block[i] = input.receive() block[i] = input.receive()
} }
inputBlocks.send(block) Contrail.unpack(block)?.let { inputBlocks.send(it) }
?: log.warning { "skipping bad block ${block.size} bytes" }
} }
} }
} catch (_: CancellationException) { } catch (_: CancellationException) {
@ -135,17 +139,13 @@ suspend fun asyncSocketToDevice(socket: AsynchronousSocketChannel): InetTranspor
val addr = socket.remoteAddress as InetSocketAddress val addr = socket.remoteAddress as InetSocketAddress
deferredDevice.complete( deferredDevice.complete(
InetTransportDevice(inputBlocks, outputBlocks, JvmNetworkAddress(addr.address, addr.port), { InetTransportDevice(inputBlocks, outputBlocks, JvmNetworkAddress(addr.address, addr.port), {
val log = LogTag("S:${addr.address}:${addr.port}")
log.info { "ASTD is waitig to close" }
yield() yield()
// wait until all received data are parsed, but not too long // wait until all received data are parsed, but not too long
withTimeoutOrNull(500) { withTimeoutOrNull(500) {
receiving.waitFor { !it } receiving.waitFor { !it }
} }
// then stop it // then stop it
log.info { "ASTd is calling STOP" }
stop() stop()
log.info { "STopped" }
}) })
) )
} }