From 2f0f174b3386c0fa3352ca6f55b99396a971b7dc Mon Sep 17 00:00:00 2001 From: Ugljesa Jovanovic Date: Sun, 21 Jun 2020 21:52:17 +0200 Subject: [PATCH] Added decryption --- .../ionspin/kotlin/crypto/CryptoProvider.kt | 17 +++++++++-- .../authenticated/XChaCha20Poly1305Pure.kt | 30 +++++++++++++++++-- .../com/ionspin/kotlin/crypto/mac/Poly1305.kt | 3 ++ .../kotlin/crypto/symmetric/XChaCha20Pure.kt | 2 +- .../authenticated/XChaCha20Poly1305Test.kt | 14 +++++---- .../kotlin/crypto/symmetric/XChaCha20Test.kt | 3 +- 6 files changed, 57 insertions(+), 12 deletions(-) diff --git a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/CryptoProvider.kt b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/CryptoProvider.kt index bc0449e..712a138 100644 --- a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/CryptoProvider.kt +++ b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/CryptoProvider.kt @@ -1,5 +1,6 @@ package com.ionspin.kotlin.crypto +import com.ionspin.kotlin.crypto.authenticated.XChaCha20Poly1305Pure import com.ionspin.kotlin.crypto.hash.UpdatableHash import com.ionspin.kotlin.crypto.hash.blake2b.Blake2bProperties import com.ionspin.kotlin.crypto.hash.blake2b.Blake2bPure @@ -92,6 +93,14 @@ data class HashedData(val hash: UByteArray) { } } +data class SymmetricKey(val value : UByteArray) { + companion object { + fun randomKey() : SymmetricKey { + return SymmetricKey(SRNG.getRandomBytes(32)) + } + } +} + data class EncryptedData(val encrypted: UByteArray) object PublicApi { @@ -106,8 +115,12 @@ object PublicApi { } } object Symmetric { - fun encrypt(data : Encryptable) : EncryptedData { - TODO() + fun encrypt(key: SymmetricKey, data : Encryptable, additionalData : UByteArray = ubyteArrayOf()) : EncryptedData { + if (key.value.size != 32) { + throw RuntimeException("Invalid key size! Required 32, supplied ${key.value.size}") + } + val nonce = SRNG.getRandomBytes(24) + return EncryptedData(XChaCha20Poly1305Pure.encrypt(key.value, nonce, data.encryptableData(), additionalData) + nonce) } fun decrypt(encryptedData : EncryptedData) : T { diff --git a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/authenticated/XChaCha20Poly1305Pure.kt b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/authenticated/XChaCha20Poly1305Pure.kt index 3056d58..00ac080 100644 --- a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/authenticated/XChaCha20Poly1305Pure.kt +++ b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/authenticated/XChaCha20Poly1305Pure.kt @@ -3,7 +3,6 @@ package com.ionspin.kotlin.crypto.authenticated import com.ionspin.kotlin.crypto.mac.Poly1305 import com.ionspin.kotlin.crypto.symmetric.ChaCha20Pure import com.ionspin.kotlin.crypto.symmetric.XChaCha20Pure -import com.ionspin.kotlin.crypto.util.hexColumsPrint import com.ionspin.kotlin.crypto.util.toLittleEndianUByteArray /** @@ -26,7 +25,7 @@ class XChaCha20Poly1305Pure(val key: UByteArray, val nonce: UByteArray, val addi // at org.jetbrains.kotlin.ir.backend.js.lower.ConstTransformer$visitConst$1$3.invoke(ConstLowering.kt:28) // at org.jetbrains.kotlin.ir.backend.js.lower.ConstTransformer.lowerConst(ConstLowering.kt:38) ) - val cipherText = XChaCha20Pure.encrypt(key, nonce, message, 1U) + val cipherText = XChaCha20Pure.xorWithKeystream(key, nonce, message, 1U) val additionalDataPad = UByteArray(16 - additionalData.size % 16) { 0U } val cipherTextPad = UByteArray(16 - cipherText.size % 16) { 0U } val macData = additionalData + additionalDataPad + @@ -36,6 +35,33 @@ class XChaCha20Poly1305Pure(val key: UByteArray, val nonce: UByteArray, val addi val tag = Poly1305.poly1305Authenticate(authKey, macData) return cipherText + tag } + + fun decrypt(key: UByteArray, nonce: UByteArray, cipherText: UByteArray, additionalData: UByteArray) : UByteArray { + val subKey = XChaCha20Pure.hChacha(key, nonce) + val authKey = + ChaCha20Pure.encrypt( + subKey.toLittleEndianUByteArray(), + ubyteArrayOf(0U, 0U, 0U, 0U) + nonce.sliceArray(16 until 24), + UByteArray(64) { 0U }, + 0U + ) + //2. Get the tag + val tag = cipherText.sliceArray(cipherText.size - 16 until cipherText.size) + //3. Verify tag is valid + val cipherTextWithoutTag = cipherText.sliceArray(0 until cipherText.size - 16) + val additionalDataPad = UByteArray(16 - additionalData.size % 16) { 0U } + val cipherTextPad = UByteArray(16 - cipherTextWithoutTag.size % 16) { 0U } + val macData = additionalData + additionalDataPad + + cipherTextWithoutTag + cipherTextPad + + additionalData.size.toULong().toLittleEndianUByteArray() + + cipherTextWithoutTag.size.toULong().toLittleEndianUByteArray() + val calculatedTag = Poly1305.poly1305Authenticate(authKey, macData) + if (!calculatedTag.contentEquals(tag)) { + RuntimeException("Bad tag!") //TODO replace with specific exception + } + //4. Decrypt data + return XChaCha20Pure.xorWithKeystream(key, nonce, cipherTextWithoutTag, 1U) + } } private val updateableEncryptionPrimitive = XChaCha20Pure(key, nonce, initialCounter = 1U) diff --git a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/mac/Poly1305.kt b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/mac/Poly1305.kt index b1b464e..c328fb2 100644 --- a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/mac/Poly1305.kt +++ b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/mac/Poly1305.kt @@ -37,6 +37,9 @@ class Poly1305(key: UByteArray) { //Doesn't have to be every power, just divisible by 8 val twoToThe128 = BigInteger.ONE.shl(128) + /** + * Limit - stop poly calculating tag at desired index, ignored if 0 + */ fun poly1305Authenticate(key: UByteArray, message: UByteArray) : UByteArray { val r = clampR(UByteArray(16) { key[it] }) val s= UByteArray(16) { key[it + 16]} diff --git a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/XChaCha20Pure.kt b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/XChaCha20Pure.kt index 9101398..b2747f4 100644 --- a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/XChaCha20Pure.kt +++ b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/XChaCha20Pure.kt @@ -55,7 +55,7 @@ class XChaCha20Pure(key: UByteArray, nonce: UByteArray, initialCounter: UInt = 0 } - fun encrypt(key: UByteArray, nonce: UByteArray, message: UByteArray, initialCounter: UInt = 0U): UByteArray { + fun xorWithKeystream(key: UByteArray, nonce: UByteArray, message: UByteArray, initialCounter: UInt = 0U): UByteArray { val ciphertext = UByteArray(message.size) val hChaChaKey = hChacha(key, nonce) diff --git a/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/authenticated/XChaCha20Poly1305Test.kt b/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/authenticated/XChaCha20Poly1305Test.kt index f10a104..edc6239 100644 --- a/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/authenticated/XChaCha20Poly1305Test.kt +++ b/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/authenticated/XChaCha20Poly1305Test.kt @@ -53,8 +53,10 @@ class XChaCha20Poly1305Test { 0x98U, 0x79U, 0x47U, 0xdeU, 0xafU, 0xd8U, 0x78U, 0x0aU, 0xcfU, 0x49U ) - val result = XChaCha20Poly1305Pure.encrypt(key, nonce, message, additionalData) - result.contentEquals(expected) + val encrypted = XChaCha20Poly1305Pure.encrypt(key, nonce, message, additionalData) + val decrypted = XChaCha20Poly1305Pure.decrypt(key, nonce, encrypted, additionalData) + + encrypted.contentEquals(expected) && decrypted.contentEquals(message) } assertTrue { @@ -80,13 +82,15 @@ class XChaCha20Poly1305Test { 0xbdU, 0x3bU, 0x8aU, 0xd7U, 0xa1U, 0x9dU, 0xe8U, 0xc4U, 0x55U, 0x84U, 0x6fU, 0xfcU, 0x75U, 0x31U, 0xbfU, 0x0cU, 0x2dU ) - val result = XChaCha20Poly1305Pure.encrypt(key, nonce, message, additionalData) - result.contentEquals(expected) + val encrypted = XChaCha20Poly1305Pure.encrypt(key, nonce, message, additionalData) + val decrypted = XChaCha20Poly1305Pure.decrypt(key, nonce, encrypted, additionalData) + + encrypted.contentEquals(expected) && decrypted.contentEquals(message) } } - + @Test fun updateableXChaCha20Poly1305() { assertTrue { diff --git a/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/XChaCha20Test.kt b/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/XChaCha20Test.kt index d745e41..89b8f83 100644 --- a/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/XChaCha20Test.kt +++ b/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/XChaCha20Test.kt @@ -1,7 +1,6 @@ package com.ionspin.kotlin.crypto.symmetric import com.ionspin.kotlin.crypto.hash.encodeToUByteArray -import com.ionspin.kotlin.crypto.util.hexColumsPrint import kotlin.test.Test import kotlin.test.assertTrue @@ -133,7 +132,7 @@ class XChaCha20Test { ) - val result = XChaCha20Pure.encrypt(key, nonce, message, 1U) + val result = XChaCha20Pure.xorWithKeystream(key, nonce, message, 1U) assertTrue { result.contentEquals(expected) }