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 | @Serializable | ||||||
| class SymmetricKey( | class SymmetricKey( | ||||||
|     val keyBytes: UByteArray |     val keyBytes: UByteArray | ||||||
| ): EncryptingKey, DecryptingKey { | ): CipherKey { | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @suppress |      * @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