forked from sergeych/crypto2
		
	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