x25519 key exchange
This commit is contained in:
parent
aad44c5af5
commit
382c3277af
@ -0,0 +1,80 @@
|
||||
package net.sergeych.crypto2
|
||||
|
||||
import com.ionspin.kotlin.crypto.keyexchange.KeyExchange
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.sergeych.crypto2.SafeKeyExchange.SessionKey
|
||||
|
||||
/**
|
||||
* Bidirectional key, not necessarily symmetric. It could be asymmetric too. Examples:
|
||||
*
|
||||
* - [SafeKeyExchange] provides _asymmetric_ [SafeKeyExchange.SessionKey]
|
||||
* - [SymmetricKey] is the symmetric cipher key
|
||||
*/
|
||||
interface CipherKey : EncryptingKey, DecryptingKey
|
||||
|
||||
/**
|
||||
* Safe (and typesafe) key exchange based on the x25519 protocol.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* 1. Create [SafeKeyExchange] on both server and client sides
|
||||
* 2. Exchange [publicKey] instances
|
||||
* 3. Create [serverSessionKey] and [clientSessionKey] respectively
|
||||
* 4. Use [SessionKey.sendingKey] and [SessionKey.receivingKey] to send and receive encrypted data.
|
||||
*
|
||||
* Some important rules to keep security level high:
|
||||
*
|
||||
* - do not reuse [SafeKeyExchange] after session keys are generated, at least on the client. Create new
|
||||
* instances as often as performance considerations allow.
|
||||
* - while it is possible to generate several keys "ahead", the care should be taken when storing them,
|
||||
* encrypt it with some other key to maintain safety.
|
||||
* - do not use [publicKey] for anything but creating session keys.
|
||||
*/
|
||||
class SafeKeyExchange {
|
||||
private val pair = KeyExchange.keypair()
|
||||
|
||||
/**
|
||||
* The session key. It uses a pair of keys to encrypt and decrypt messages to maintain high
|
||||
* security level and allow using counters as nonce with no extra precautions.
|
||||
*/
|
||||
@Serializable
|
||||
class SessionKey internal constructor(
|
||||
val sendingKey: EncryptingKey,
|
||||
val receivingKey: DecryptingKey,
|
||||
): CipherKey, EncryptingKey by sendingKey, DecryptingKey by receivingKey
|
||||
|
||||
/**
|
||||
* The public key; it should be transmitted to the other party, this is serializable.
|
||||
* Do not use it except to get [SessionKey] with [clientSessionKey] or [serverSessionKey]. Storing and reusing
|
||||
* it is a great danger.
|
||||
*/
|
||||
@Serializable
|
||||
class PublicKey(val keyBytes: UByteArray)
|
||||
|
||||
/**
|
||||
* The public key to be sent to the other party. When received, get the session keys with [clientSessionKey]
|
||||
* and [serverSessionKey] respectively. Do not reuse public key, do not use it for any other reason than
|
||||
* to create a single session keys instance. For a new session, create new [SafeKeyExchange] at least on the
|
||||
* client side, and get new keys. It is recommended to change server-side [SafeKeyExchange]
|
||||
* instance every time or often enough.
|
||||
*/
|
||||
val publicKey = PublicKey(pair.publicKey)
|
||||
|
||||
/**
|
||||
* Create asymmetric [SessionKey] to encrypt the session. It can't decrypt what is encrypted by it
|
||||
* but can decrypt what was encrypted with the [serverSessionKey] on the another side.
|
||||
*/
|
||||
fun clientSessionKey(serverPublicKey: PublicKey): SessionKey =
|
||||
KeyExchange.clientSessionKeys(pair.publicKey, pair.secretKey, serverPublicKey.keyBytes)
|
||||
.let { SessionKey(SymmetricKey(it.sendKey), SymmetricKey(it.receiveKey))}
|
||||
|
||||
/**
|
||||
* Create an asymmetric [SessionKey] instance to work with [clientSessionKey] on the other side.
|
||||
* Note that asymmetric means it can't decrypt what it encrypted.
|
||||
*/
|
||||
fun serverSessionKey(clientPublicKey: PublicKey): SessionKey =
|
||||
KeyExchange.serverSessionKeys(pair.publicKey, pair.secretKey, clientPublicKey.keyBytes)
|
||||
.let { SessionKey(SymmetricKey(it.sendKey), SymmetricKey(it.receiveKey))}
|
||||
|
||||
|
||||
}
|
@ -24,7 +24,7 @@ import kotlin.random.nextUBytes
|
||||
@Serializable
|
||||
class SymmetricKey(
|
||||
val keyBytes: UByteArray
|
||||
): EncryptingKey, DecryptingKey {
|
||||
): CipherKey {
|
||||
|
||||
/**
|
||||
* @suppress
|
||||
|
@ -83,4 +83,26 @@ class KeysTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun keyExchangeTest() = runTest {
|
||||
initCrypto()
|
||||
val ske = SafeKeyExchange()
|
||||
val cke = SafeKeyExchange()
|
||||
|
||||
val clientSessionKey = cke.clientSessionKey(ske.publicKey)
|
||||
val serverSessionKey = ske.serverSessionKey(cke.publicKey)
|
||||
|
||||
val src = "Hello, Dolly!"
|
||||
assertEquals(src, serverSessionKey.decryptString(clientSessionKey.encrypt(src)))
|
||||
assertEquals(src, serverSessionKey.decryptString(clientSessionKey.encrypt(src)))
|
||||
assertEquals(src, serverSessionKey.decryptString(clientSessionKey.encrypt(src)))
|
||||
assertEquals(src, serverSessionKey.decryptString(clientSessionKey.encrypt(src)))
|
||||
|
||||
assertEquals(src, clientSessionKey.decryptString(serverSessionKey.encrypt(src)))
|
||||
assertEquals(src, clientSessionKey.decryptString(serverSessionKey.encrypt(src)))
|
||||
assertEquals(src, clientSessionKey.decryptString(serverSessionKey.encrypt(src)))
|
||||
assertEquals(src, clientSessionKey.decryptString(serverSessionKey.encrypt(src)))
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user