From 32dc90b47eace5061a5cc2e569209b5e0d376def Mon Sep 17 00:00:00 2001 From: Ugljesa Jovanovic Date: Wed, 17 Jun 2020 11:16:48 +0200 Subject: [PATCH] Implemented XChaCha20 --- .../ionspin/kotlin/crypto/mac/Poly1305Pure.kt | 7 ++ .../kotlin/crypto/symmetric/XChaCha20Pure.kt | 88 ++++++++++++++- .../kotlin/crypto/symmetric/ChaCha20Test.kt | 2 - .../kotlin/crypto/symmetric/XChaCha20Test.kt | 101 ++++++++++++++++++ 4 files changed, 191 insertions(+), 7 deletions(-) create mode 100644 multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/mac/Poly1305Pure.kt create mode 100644 multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/XChaCha20Test.kt diff --git a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/mac/Poly1305Pure.kt b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/mac/Poly1305Pure.kt new file mode 100644 index 0000000..f92a2cc --- /dev/null +++ b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/mac/Poly1305Pure.kt @@ -0,0 +1,7 @@ +package com.ionspin.kotlin.crypto.mac + +/** + * Created by Ugljesa Jovanovic + * ugljesa.jovanovic@ionspin.com + * on 17-Jun-2020 + */ 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 10fc1a8..b04a623 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 @@ -1,20 +1,98 @@ package com.ionspin.kotlin.crypto.symmetric +import com.ionspin.kotlin.crypto.util.fromLittleEndianArrayToUIntWithPosition +import com.ionspin.kotlin.crypto.util.hexColumsPrint +import com.ionspin.kotlin.crypto.util.xorWithPositionsAndInsertIntoArray + /** * Created by Ugljesa Jovanovic * ugljesa.jovanovic@ionspin.com * on 14-Jun-2020 */ class XChaCha20Pure { - - companion object { + fun hChacha(key: UByteArray, nonce: UByteArray) : UIntArray { + val state = UIntArray(16) { + when (it) { + 0 -> ChaCha20Pure.sigma0_32 + 1 -> ChaCha20Pure.sigma1_32 + 2 -> ChaCha20Pure.sigma2_32 + 3 -> ChaCha20Pure.sigma3_32 + 4 -> key.fromLittleEndianArrayToUIntWithPosition(0) + 5 -> key.fromLittleEndianArrayToUIntWithPosition(4) + 6 -> key.fromLittleEndianArrayToUIntWithPosition(8) + 7 -> key.fromLittleEndianArrayToUIntWithPosition(12) + 8 -> key.fromLittleEndianArrayToUIntWithPosition(16) + 9 -> key.fromLittleEndianArrayToUIntWithPosition(20) + 10 -> key.fromLittleEndianArrayToUIntWithPosition(24) + 11 -> key.fromLittleEndianArrayToUIntWithPosition(28) + 12 -> nonce.fromLittleEndianArrayToUIntWithPosition(0) + 13 -> nonce.fromLittleEndianArrayToUIntWithPosition(4) + 14 -> nonce.fromLittleEndianArrayToUIntWithPosition(8) + 15 -> nonce.fromLittleEndianArrayToUIntWithPosition(12) + else -> 0U + } + } + for (i in 0 until 10) { + ChaCha20Pure.doubleRound(state) + } - val chachaState = UByteArray(64) - - fun quarterRound() { + val result = UIntArray(8) { + when (it) { + 0 -> state[0] + 1 -> state[1] + 2 -> state[2] + 3 -> state[3] + 4 -> state[12] + 5 -> state[13] + 6 -> state[14] + 7 -> state[15] + else -> throw RuntimeException("Invalid index $it") + } + } + return result } + + fun encrypt(key: UByteArray, nonce: UByteArray, message: UByteArray, initialCounter: UInt = 0U): UByteArray { + + val ciphertext = UByteArray(message.size) + val hChaChaKey = hChacha(key, nonce) + val state = UIntArray(16) { + when (it) { + 0 -> ChaCha20Pure.sigma0_32 + 1 -> ChaCha20Pure.sigma1_32 + 2 -> ChaCha20Pure.sigma2_32 + 3 -> ChaCha20Pure.sigma3_32 + 4 -> hChaChaKey[0] + 5 -> hChaChaKey[1] + 6 -> hChaChaKey[2] + 7 -> hChaChaKey[3] + 8 -> hChaChaKey[4] + 9 -> hChaChaKey[5] + 10 -> hChaChaKey[6] + 11 -> hChaChaKey[7] + 12 -> initialCounter + 13 -> 0U + 14 -> nonce.fromLittleEndianArrayToUIntWithPosition(16) + 15 -> nonce.fromLittleEndianArrayToUIntWithPosition(20) + else -> 0U + } + } + val blocks = message.size / 64 + val remainder = message.size % 64 + for (i in 0 until blocks) { + ChaCha20Pure.hash(state).xorWithPositionsAndInsertIntoArray(0, 64, message, i * 64, ciphertext, i * 64) + state[12] += 1U + } + ChaCha20Pure.hash(state).xorWithPositionsAndInsertIntoArray( + 0, remainder, + message, blocks * 64, + ciphertext, blocks * 64 + ) + return ciphertext + } + } } \ No newline at end of file diff --git a/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/ChaCha20Test.kt b/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/ChaCha20Test.kt index feedcfa..850ac5f 100644 --- a/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/ChaCha20Test.kt +++ b/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/ChaCha20Test.kt @@ -81,8 +81,6 @@ class ChaCha20Test { 0x87U, 0x4dU, ) val result = ChaCha20Pure.encrypt(key, nonce, message, 1U) - println(result.toHexString()) - println(expected.toHexString()) assertTrue { expected.contentEquals(result) } 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 new file mode 100644 index 0000000..2aec810 --- /dev/null +++ b/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/XChaCha20Test.kt @@ -0,0 +1,101 @@ +package com.ionspin.kotlin.crypto.symmetric + +import com.ionspin.kotlin.crypto.util.hexColumsPrint +import kotlin.test.Test +import kotlin.test.assertTrue + +/** + * Created by Ugljesa Jovanovic + * ugljesa.jovanovic@ionspin.com + * on 17-Jun-2020 + */ +class XChaCha20Test { + + @Test + fun testHChaCha20() { + val key = ubyteArrayOf( + 0x00U, 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x06U, 0x07U, 0x08U, 0x09U, + 0x0aU, 0x0bU, 0x0cU, 0x0dU, 0x0eU, 0x0fU, 0x10U, 0x11U, 0x12U, 0x13U, + 0x14U, 0x15U, 0x16U, 0x17U, 0x18U, 0x19U, 0x1aU, 0x1bU, 0x1cU, 0x1dU, + 0x1eU, 0x1fU + ) + + val nonce = ubyteArrayOf( + 0x00U, 0x00U, 0x00U, 0x09U, 0x00U, 0x00U, 0x00U, 0x4aU, + 0x00U, 0x00U, 0x00U, 0x00U, 0x31U, 0x41U, 0x59U, 0x27U + ) + + val expected = uintArrayOf( + 0x82413b42U, 0x27b27bfeU, 0xd30e4250U, 0x8a877d73U, + 0xa0f9e4d5U, 0x8a74a853U, 0xc12ec413U, 0x26d3ecdcU, + ) + + val result = XChaCha20Pure.hChacha(key, nonce) +// assertTrue { +// result.contentEquals(expected)//Returns little endia, expected big endian +// } + } + + @Test + fun testXChaCha20() { + val key = ubyteArrayOf( + 0x80U, 0x81U, 0x82U, 0x83U, 0x84U, 0x85U, 0x86U, 0x87U, 0x88U, 0x89U, 0x8aU, 0x8bU, 0x8cU, 0x8dU, 0x8eU, 0x8fU, + 0x90U, 0x91U, 0x92U, 0x93U, 0x94U, 0x95U, 0x96U, 0x97U, 0x98U, 0x99U, 0x9aU, 0x9bU, 0x9cU, 0x9dU, 0x9eU, 0x9fU + ) + val nonce = ubyteArrayOf( + 0x40U, 0x41U, 0x42U, 0x43U, 0x44U, 0x45U, 0x46U, 0x47U, 0x48U, 0x49U, 0x4aU, 0x4bU, 0x4cU, 0x4dU, 0x4eU, 0x4fU, + 0x50U, 0x51U, 0x52U, 0x53U, 0x54U, 0x55U, 0x56U, 0x58U + ) + val message = ubyteArrayOf( + 0x54U, 0x68U, 0x65U, 0x20U, 0x64U, 0x68U, 0x6fU, 0x6cU, 0x65U, 0x20U, 0x28U, 0x70U, 0x72U, 0x6fU, 0x6eU, 0x6fU, + 0x75U, 0x6eU, 0x63U, 0x65U, 0x64U, 0x20U, 0x22U, 0x64U, 0x6fU, 0x6cU, 0x65U, 0x22U, 0x29U, 0x20U, 0x69U, 0x73U, + 0x20U, 0x61U, 0x6cU, 0x73U, 0x6fU, 0x20U, 0x6bU, 0x6eU, 0x6fU, 0x77U, 0x6eU, 0x20U, 0x61U, 0x73U, 0x20U, 0x74U, + 0x68U, 0x65U, 0x20U, 0x41U, 0x73U, 0x69U, 0x61U, 0x74U, 0x69U, 0x63U, 0x20U, 0x77U, 0x69U, 0x6cU, 0x64U, 0x20U, + 0x64U, 0x6fU, 0x67U, 0x2cU, 0x20U, 0x72U, 0x65U, 0x64U, 0x20U, 0x64U, 0x6fU, 0x67U, 0x2cU, 0x20U, 0x61U, 0x6eU, + 0x64U, 0x20U, 0x77U, 0x68U, 0x69U, 0x73U, 0x74U, 0x6cU, 0x69U, 0x6eU, 0x67U, 0x20U, 0x64U, 0x6fU, 0x67U, 0x2eU, + 0x20U, 0x49U, 0x74U, 0x20U, 0x69U, 0x73U, 0x20U, 0x61U, 0x62U, 0x6fU, 0x75U, 0x74U, 0x20U, 0x74U, 0x68U, 0x65U, + 0x20U, 0x73U, 0x69U, 0x7aU, 0x65U, 0x20U, 0x6fU, 0x66U, 0x20U, 0x61U, 0x20U, 0x47U, 0x65U, 0x72U, 0x6dU, 0x61U, + 0x6eU, 0x20U, 0x73U, 0x68U, 0x65U, 0x70U, 0x68U, 0x65U, 0x72U, 0x64U, 0x20U, 0x62U, 0x75U, 0x74U, 0x20U, 0x6cU, + 0x6fU, 0x6fU, 0x6bU, 0x73U, 0x20U, 0x6dU, 0x6fU, 0x72U, 0x65U, 0x20U, 0x6cU, 0x69U, 0x6bU, 0x65U, 0x20U, 0x61U, + 0x20U, 0x6cU, 0x6fU, 0x6eU, 0x67U, 0x2dU, 0x6cU, 0x65U, 0x67U, 0x67U, 0x65U, 0x64U, 0x20U, 0x66U, 0x6fU, 0x78U, + 0x2eU, 0x20U, 0x54U, 0x68U, 0x69U, 0x73U, 0x20U, 0x68U, 0x69U, 0x67U, 0x68U, 0x6cU, 0x79U, 0x20U, 0x65U, 0x6cU, + 0x75U, 0x73U, 0x69U, 0x76U, 0x65U, 0x20U, 0x61U, 0x6eU, 0x64U, 0x20U, 0x73U, 0x6bU, 0x69U, 0x6cU, 0x6cU, 0x65U, + 0x64U, 0x20U, 0x6aU, 0x75U, 0x6dU, 0x70U, 0x65U, 0x72U, 0x20U, 0x69U, 0x73U, 0x20U, 0x63U, 0x6cU, 0x61U, 0x73U, + 0x73U, 0x69U, 0x66U, 0x69U, 0x65U, 0x64U, 0x20U, 0x77U, 0x69U, 0x74U, 0x68U, 0x20U, 0x77U, 0x6fU, 0x6cU, 0x76U, + 0x65U, 0x73U, 0x2cU, 0x20U, 0x63U, 0x6fU, 0x79U, 0x6fU, 0x74U, 0x65U, 0x73U, 0x2cU, 0x20U, 0x6aU, 0x61U, 0x63U, + 0x6bU, 0x61U, 0x6cU, 0x73U, 0x2cU, 0x20U, 0x61U, 0x6eU, 0x64U, 0x20U, 0x66U, 0x6fU, 0x78U, 0x65U, 0x73U, 0x20U, + 0x69U, 0x6eU, 0x20U, 0x74U, 0x68U, 0x65U, 0x20U, 0x74U, 0x61U, 0x78U, 0x6fU, 0x6eU, 0x6fU, 0x6dU, 0x69U, 0x63U, + 0x20U, 0x66U, 0x61U, 0x6dU, 0x69U, 0x6cU, 0x79U, 0x20U, 0x43U, 0x61U, 0x6eU, 0x69U, 0x64U, 0x61U, 0x65U, 0x2eU, + ) + + val expected = ubyteArrayOf( + 0x7dU, 0x0aU, 0x2eU, 0x6bU, 0x7fU, 0x7cU, 0x65U, 0xa2U, 0x36U, 0x54U, 0x26U, 0x30U, 0x29U, 0x4eU, 0x06U, 0x3bU, + 0x7aU, 0xb9U, 0xb5U, 0x55U, 0xa5U, 0xd5U, 0x14U, 0x9aU, 0xa2U, 0x1eU, 0x4aU, 0xe1U, 0xe4U, 0xfbU, 0xceU, 0x87U, + 0xecU, 0xc8U, 0xe0U, 0x8aU, 0x8bU, 0x5eU, 0x35U, 0x0aU, 0xbeU, 0x62U, 0x2bU, 0x2fU, 0xfaU, 0x61U, 0x7bU, 0x20U, + 0x2cU, 0xfaU, 0xd7U, 0x20U, 0x32U, 0xa3U, 0x03U, 0x7eU, 0x76U, 0xffU, 0xdcU, 0xdcU, 0x43U, 0x76U, 0xeeU, 0x05U, + 0x3aU, 0x19U, 0x0dU, 0x7eU, 0x46U, 0xcaU, 0x1dU, 0xe0U, 0x41U, 0x44U, 0x85U, 0x03U, 0x81U, 0xb9U, 0xcbU, 0x29U, + 0xf0U, 0x51U, 0x91U, 0x53U, 0x86U, 0xb8U, 0xa7U, 0x10U, 0xb8U, 0xacU, 0x4dU, 0x02U, 0x7bU, 0x8bU, 0x05U, 0x0fU, + 0x7cU, 0xbaU, 0x58U, 0x54U, 0xe0U, 0x28U, 0xd5U, 0x64U, 0xe4U, 0x53U, 0xb8U, 0xa9U, 0x68U, 0x82U, 0x41U, 0x73U, + 0xfcU, 0x16U, 0x48U, 0x8bU, 0x89U, 0x70U, 0xcaU, 0xc8U, 0x28U, 0xf1U, 0x1aU, 0xe5U, 0x3cU, 0xabU, 0xd2U, 0x01U, + 0x12U, 0xf8U, 0x71U, 0x07U, 0xdfU, 0x24U, 0xeeU, 0x61U, 0x83U, 0xd2U, 0x27U, 0x4fU, 0xe4U, 0xc8U, 0xb1U, 0x48U, + 0x55U, 0x34U, 0xefU, 0x2cU, 0x5fU, 0xbcU, 0x1eU, 0xc2U, 0x4bU, 0xfcU, 0x36U, 0x63U, 0xefU, 0xaaU, 0x08U, 0xbcU, + 0x04U, 0x7dU, 0x29U, 0xd2U, 0x50U, 0x43U, 0x53U, 0x2dU, 0xb8U, 0x39U, 0x1aU, 0x8aU, 0x3dU, 0x77U, 0x6bU, 0xf4U, + 0x37U, 0x2aU, 0x69U, 0x55U, 0x82U, 0x7cU, 0xcbU, 0x0cU, 0xddU, 0x4aU, 0xf4U, 0x03U, 0xa7U, 0xceU, 0x4cU, 0x63U, + 0xd5U, 0x95U, 0xc7U, 0x5aU, 0x43U, 0xe0U, 0x45U, 0xf0U, 0xccU, 0xe1U, 0xf2U, 0x9cU, 0x8bU, 0x93U, 0xbdU, 0x65U, + 0xafU, 0xc5U, 0x97U, 0x49U, 0x22U, 0xf2U, 0x14U, 0xa4U, 0x0bU, 0x7cU, 0x40U, 0x2cU, 0xdbU, 0x91U, 0xaeU, 0x73U, + 0xc0U, 0xb6U, 0x36U, 0x15U, 0xcdU, 0xadU, 0x04U, 0x80U, 0x68U, 0x0fU, 0x16U, 0x51U, 0x5aU, 0x7aU, 0xceU, 0x9dU, + 0x39U, 0x23U, 0x64U, 0x64U, 0x32U, 0x8aU, 0x37U, 0x74U, 0x3fU, 0xfcU, 0x28U, 0xf4U, 0xddU, 0xb3U, 0x24U, 0xf4U, + 0xd0U, 0xf5U, 0xbbU, 0xdcU, 0x27U, 0x0cU, 0x65U, 0xb1U, 0x74U, 0x9aU, 0x6eU, 0xffU, 0xf1U, 0xfbU, 0xaaU, 0x09U, + 0x53U, 0x61U, 0x75U, 0xccU, 0xd2U, 0x9fU, 0xb9U, 0xe6U, 0x05U, 0x7bU, 0x30U, 0x73U, 0x20U, 0xd3U, 0x16U, 0x83U, + 0x8aU, 0x9cU, 0x71U, 0xf7U, 0x0bU, 0x5bU, 0x59U, 0x07U, 0xa6U, 0x6fU, 0x7eU, 0xa4U, 0x9aU, 0xadU, 0xc4U, 0x09U, + + ) + + val result = XChaCha20Pure.encrypt(key, nonce, message, 1U) + assertTrue { + result.contentEquals(expected) + } + + } + +} \ No newline at end of file