From c7445376cadf8cd9e9043d6cbf139031fc88a287 Mon Sep 17 00:00:00 2001 From: Ugljesa Jovanovic Date: Tue, 16 Jun 2020 16:07:01 +0200 Subject: [PATCH] Completed xsalsa20, adding chacha20 --- .../kotlin/crypto/symmetric/ChaCha20Pure.kt | 38 +++++++ .../crypto/symmetric/CubanDancesCommon.kt | 45 ++++++++ .../symmetric/{Salsa20.kt => Salsa20Pure.kt} | 64 +++-------- .../kotlin/crypto/symmetric/XSalsa20Pure.kt | 106 ++++++++++++++++++ .../com/ionspin/kotlin/crypto/util/Util.kt | 3 - .../kotlin/crypto/symmetric/Salsa20Test.kt | 50 ++++----- .../kotlin/crypto/symmetric/XSalsa20Test.kt | 44 ++++++++ 7 files changed, 275 insertions(+), 75 deletions(-) create mode 100644 multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/ChaCha20Pure.kt create mode 100644 multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/CubanDancesCommon.kt rename multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/{Salsa20.kt => Salsa20Pure.kt} (68%) create mode 100644 multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/XSalsa20Pure.kt create mode 100644 multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/XSalsa20Test.kt diff --git a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/ChaCha20Pure.kt b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/ChaCha20Pure.kt new file mode 100644 index 0000000..345c953 --- /dev/null +++ b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/ChaCha20Pure.kt @@ -0,0 +1,38 @@ +package com.ionspin.kotlin.crypto.symmetric + +import com.ionspin.kotlin.crypto.util.rotateLeft + +/** + * Created by Ugljesa Jovanovic + * ugljesa.jovanovic@ionspin.com + * on 16-Jun-2020 + */ +class ChaCha20Pure { + companion object { + fun quarterRound(input: UIntArray, aPosition: Int, bPosition: Int, cPosition: Int, dPosition: Int) { + input[aPosition] += input[bPosition]; input[dPosition] = input[dPosition] xor input[aPosition]; input[dPosition] = input[dPosition] rotateLeft 16 + input[cPosition] += input[dPosition]; input[bPosition] = input[bPosition] xor input[cPosition]; input[bPosition] = input[bPosition] rotateLeft 12 + input[aPosition] += input[bPosition]; input[dPosition] = input[dPosition] xor input[aPosition]; input[dPosition] = input[dPosition] rotateLeft 8 + input[cPosition] += input[dPosition]; input[bPosition] = input[bPosition] xor input[cPosition]; input[bPosition] = input[bPosition] rotateLeft 7 + } + + fun rowRound(input: UIntArray) { + Salsa20Pure.quarterRound(input, 0, 1, 2, 3) + Salsa20Pure.quarterRound(input, 5, 6, 7, 4) + Salsa20Pure.quarterRound(input, 10, 11, 8, 9) + Salsa20Pure.quarterRound(input, 15, 12, 13, 14) + } + + fun columnRound(input: UIntArray) { + Salsa20Pure.quarterRound(input, 0, 4, 8, 12) + Salsa20Pure.quarterRound(input, 5, 9, 13, 1) + Salsa20Pure.quarterRound(input, 10, 14, 2, 6) + Salsa20Pure.quarterRound(input, 15, 3, 7, 11) + } + + fun doubleRound(input: UIntArray) { + columnRound(input) + rowRound(input) + } + } +} \ No newline at end of file diff --git a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/CubanDancesCommon.kt b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/CubanDancesCommon.kt new file mode 100644 index 0000000..006f7b6 --- /dev/null +++ b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/CubanDancesCommon.kt @@ -0,0 +1,45 @@ +package com.ionspin.kotlin.crypto.symmetric + +/** + * Created by Ugljesa Jovanovic + * ugljesa.jovanovic@ionspin.com + * on 16-Jun-2020 + */ +fun littleEndian( + input: UByteArray, + byte0Position: Int, + byte1Position: Int, + byte2Position: Int, + byte3Position: Int +): UInt { + var uint = 0U + uint = input[byte0Position].toUInt() + uint = uint or (input[byte1Position].toUInt() shl 8) + uint = uint or (input[byte2Position].toUInt() shl 16) + uint = uint or (input[byte3Position].toUInt() shl 24) + + return uint +} + +fun littleEndianInverted( + input: UIntArray, + startingPosition: Int, + output: UByteArray, + outputPosition: Int +) { + output[outputPosition] = (input[startingPosition] and 0xFFU).toUByte() + output[outputPosition + 1] = ((input[startingPosition] shr 8) and 0xFFU).toUByte() + output[outputPosition + 2] = ((input[startingPosition] shr 16) and 0xFFU).toUByte() + output[outputPosition + 3] = ((input[startingPosition] shr 24) and 0xFFU).toUByte() +} + +fun littleEndianInverted( + input: UInt, + output: UByteArray, + outputPosition: Int +) { + output[outputPosition] = (input and 0xFFU).toUByte() + output[outputPosition + 1] = ((input shr 8) and 0xFFU).toUByte() + output[outputPosition + 2] = ((input shr 16) and 0xFFU).toUByte() + output[outputPosition + 3] = ((input shr 24) and 0xFFU).toUByte() +} \ No newline at end of file diff --git a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/Salsa20.kt b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/Salsa20Pure.kt similarity index 68% rename from multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/Salsa20.kt rename to multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/Salsa20Pure.kt index dd2d0e2..e3648b4 100644 --- a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/Salsa20.kt +++ b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/Salsa20Pure.kt @@ -1,6 +1,5 @@ package com.ionspin.kotlin.crypto.symmetric -import com.ionspin.kotlin.crypto.keyderivation.argon2.xorWithBlock import com.ionspin.kotlin.crypto.util.* /** @@ -8,7 +7,7 @@ import com.ionspin.kotlin.crypto.util.* * ugljesa.jovanovic@ionspin.com * on 14-Jun-2020 */ -class Salsa20 { +class Salsa20Pure { companion object { fun quarterRound(input: UIntArray, y0position: Int, y1position: Int, y2position: Int, y3position: Int) { input[y1position] = input[y1position] xor ((input[y0position] + input[y3position]) rotateLeft 7) @@ -36,44 +35,7 @@ class Salsa20 { rowRound(input) } - fun littleEndian( - input: UByteArray, - byte0Position: Int, - byte1Position: Int, - byte2Position: Int, - byte3Position: Int - ): UInt { - var uint = 0U - uint = input[byte0Position].toUInt() - uint = uint or (input[byte1Position].toUInt() shl 8) - uint = uint or (input[byte2Position].toUInt() shl 16) - uint = uint or (input[byte3Position].toUInt() shl 24) - return uint - } - - fun littleEndianInverted( - input: UIntArray, - startingPosition: Int, - output: UByteArray, - outputPosition: Int - ) { - output[outputPosition] = (input[startingPosition] and 0xFFU).toUByte() - output[outputPosition + 1] = ((input[startingPosition] shr 8) and 0xFFU).toUByte() - output[outputPosition + 2] = ((input[startingPosition] shr 16) and 0xFFU).toUByte() - output[outputPosition + 3] = ((input[startingPosition] shr 24) and 0xFFU).toUByte() - } - - fun littleEndianInverted( - input: UInt, - output: UByteArray, - outputPosition: Int - ) { - output[outputPosition] = (input and 0xFFU).toUByte() - output[outputPosition + 1] = ((input shr 8) and 0xFFU).toUByte() - output[outputPosition + 2] = ((input shr 16) and 0xFFU).toUByte() - output[outputPosition + 3] = ((input shr 24) and 0xFFU).toUByte() - } fun hash(initialState: UIntArray): UByteArray { val state = initialState.copyOf() @@ -128,23 +90,31 @@ class Salsa20 { else -> 0U } } + val blocks = message.size / 64 val remainder = message.size % 64 - for (i in 0 until message.size - 64 step 64) { - hash(state).xorWithPositionsAndInsertIntoArray(0, 64, message, i, ciphertext, i) + for (i in 0 until blocks) { + hash(state).xorWithPositionsAndInsertIntoArray(0, 64, message, i * 64, ciphertext, i * 64) state[8] += 1U if (state[8] == 0U) { state[9] += 1U } } - for ( i in message.size - (64 - remainder) until message.size step 64) { - hash(state).xorWithPositionsAndInsertIntoArray(0, (64 - remainder), message, i, ciphertext, i) - state[8] += 1U - if (state[8] == 0U) { - state[9] += 1U - } + + hash(state).xorWithPositionsAndInsertIntoArray( + 0, remainder, + message, (blocks - 1) * 64, + ciphertext, (blocks - 1) * 64) + state[8] += 1U + if (state[8] == 0U) { + state[9] += 1U } + return ciphertext } + + fun decrypt(key : UByteArray, nonce: UByteArray, ciphertext: UByteArray) : UByteArray { + return encrypt(key, nonce, ciphertext) + } } } \ No newline at end of file diff --git a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/XSalsa20Pure.kt b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/XSalsa20Pure.kt new file mode 100644 index 0000000..9144e28 --- /dev/null +++ b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/XSalsa20Pure.kt @@ -0,0 +1,106 @@ +package com.ionspin.kotlin.crypto.symmetric + +import com.ionspin.kotlin.crypto.util.fromLittleEndianArrayToUInt +import com.ionspin.kotlin.crypto.util.fromLittleEndianArrayToUIntWithPosition +import com.ionspin.kotlin.crypto.util.xorWithPositionsAndInsertIntoArray + +/** + * Created by Ugljesa Jovanovic + * ugljesa.jovanovic@ionspin.com + * on 16-Jun-2020 + */ +class XSalsa20Pure { + companion object { + fun hSalsa(key: UByteArray, nonce: UByteArray): UIntArray { + val state = UIntArray(16) { + when (it) { + 0 -> Salsa20Pure.sigma0_32.fromLittleEndianArrayToUInt() + 1 -> key.fromLittleEndianArrayToUIntWithPosition(0) + 2 -> key.fromLittleEndianArrayToUIntWithPosition(4) + 3 -> key.fromLittleEndianArrayToUIntWithPosition(8) + 4 -> key.fromLittleEndianArrayToUIntWithPosition(12) + 5 -> Salsa20Pure.sigma1_32.fromLittleEndianArrayToUInt() + 6 -> nonce.fromLittleEndianArrayToUIntWithPosition(0) + 7 -> nonce.fromLittleEndianArrayToUIntWithPosition(4) + 8 -> nonce.fromLittleEndianArrayToUIntWithPosition(8) + 9 -> nonce.fromLittleEndianArrayToUIntWithPosition(12) + 10 -> Salsa20Pure.sigma2_32.fromLittleEndianArrayToUInt() + 11 -> key.fromLittleEndianArrayToUIntWithPosition(16) + 12 -> key.fromLittleEndianArrayToUIntWithPosition(20) + 13 -> key.fromLittleEndianArrayToUIntWithPosition(24) + 14 -> key.fromLittleEndianArrayToUIntWithPosition(28) + 15 -> Salsa20Pure.sigma3_32.fromLittleEndianArrayToUInt() + else -> throw RuntimeException("Invalid index $it") + } + } + for (i in 0 until 10) { + Salsa20Pure.doubleRound(state) + } + val result = UIntArray(8) { + when (it) { + 0 -> state[0] + 1 -> state[5] + 2 -> state[10] + 3 -> state[15] + 4 -> state[6] + 5 -> state[7] + 6 -> state[8] + 7 -> state[9] + else -> throw RuntimeException("Invalid index $it") + } + } + return result + + } + + fun encrypt(key: UByteArray, nonce: UByteArray, message: UByteArray): UByteArray { + if (nonce.size != 24) { + throw RuntimeException("Invalid nonce size. required 192 bits, got ${nonce.size * 8}") + } + val ciphertext = UByteArray(message.size) + val hSalsaKey = hSalsa(key, nonce) + val state = UIntArray(16) { + when (it) { + 0 -> Salsa20Pure.sigma0_32.fromLittleEndianArrayToUInt() + 1 -> hSalsaKey[0] + 2 -> hSalsaKey[1] + 3 -> hSalsaKey[2] + 4 -> hSalsaKey[3] + 5 -> Salsa20Pure.sigma1_32.fromLittleEndianArrayToUInt() + 6 -> nonce.fromLittleEndianArrayToUIntWithPosition(16) //Last 63 bit of 192 bit nonce + 7 -> nonce.fromLittleEndianArrayToUIntWithPosition(20) + 8 -> 0U + 9 -> 0U + 10 -> Salsa20Pure.sigma2_32.fromLittleEndianArrayToUInt() + 11 -> hSalsaKey[4] + 12 -> hSalsaKey[5] + 13 -> hSalsaKey[6] + 14 -> hSalsaKey[7] + 15 -> Salsa20Pure.sigma3_32.fromLittleEndianArrayToUInt() + else -> 0U + } + } + val blocks = message.size / 64 + val remainder = message.size % 64 + for (i in 0 until blocks) { + Salsa20Pure.hash(state).xorWithPositionsAndInsertIntoArray(0, 64, message, i * 64, ciphertext, i * 64) + state[8] += 1U + if (state[8] == 0U) { + state[9] += 1U + } + } + + Salsa20Pure.hash(state).xorWithPositionsAndInsertIntoArray( + 0, remainder, + message, (blocks - 1).coerceAtLeast(0) * 64, + ciphertext, (blocks - 1).coerceAtLeast(0) * 64) + state[8] += 1U + if (state[8] == 0U) { + state[9] += 1U + } + return ciphertext + } + + } + +} \ No newline at end of file diff --git a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/util/Util.kt b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/util/Util.kt index d2ea19c..8878a47 100644 --- a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/util/Util.kt +++ b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/util/Util.kt @@ -108,9 +108,6 @@ fun UByteArray.xorWithPositionsAndInsertIntoArray( other : UByteArray, otherStart: Int, targetArray: UByteArray, targetStart : Int) { for (i in start until end) { - if (targetStart + i == 131071) { - println("stop") - } targetArray[targetStart + i] = this[start + i] xor other[otherStart + i] } } diff --git a/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/Salsa20Test.kt b/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/Salsa20Test.kt index ff284e0..027a4bd 100644 --- a/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/Salsa20Test.kt +++ b/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/Salsa20Test.kt @@ -28,7 +28,7 @@ class Salsa20Test { assertTrue { val input = uintArrayOf(0U, 0U, 0U, 0U) val expected = uintArrayOf(0U, 0U, 0U, 0U) - Salsa20.quarterRound(input, 0, 1, 2, 3) + Salsa20Pure.quarterRound(input, 0, 1, 2, 3) println("Result ${input.joinToString { it.toString(16).padStart(2, '0') }}") expected.contentEquals(input) @@ -37,7 +37,7 @@ class Salsa20Test { assertTrue { val input = uintArrayOf(1U, 0U, 0U, 0U) val expected = uintArrayOf(0x08008145U, 0x00000080U, 0x00010200U, 0x20500000U) - Salsa20.quarterRound(input, 0, 1, 2, 3) + Salsa20Pure.quarterRound(input, 0, 1, 2, 3) println("Result ${input.joinToString { it.toString(16).padStart(2, '0') }}") expected.contentEquals(input) @@ -46,7 +46,7 @@ class Salsa20Test { assertTrue { val input = uintArrayOf(0U, 1U, 0U, 0U) val expected = uintArrayOf(0x88000100U, 0x00000001U, 0x00000200U, 0x00402000U) - Salsa20.quarterRound(input, 0, 1, 2, 3) + Salsa20Pure.quarterRound(input, 0, 1, 2, 3) println("Result ${input.joinToString { it.toString(16).padStart(2, '0') }}") expected.contentEquals(input) @@ -56,14 +56,14 @@ class Salsa20Test { val input = uintArrayOf(0U, 0U, 1U, 0U) val expected = uintArrayOf(0x80040000U, 0x00000000U, 0x00000001U, 0x00002000U) println("Result ${input.joinToString { it.toString(16).padStart(2, '0') }}") - Salsa20.quarterRound(input, 0, 1, 2, 3) + Salsa20Pure.quarterRound(input, 0, 1, 2, 3) expected.contentEquals(input) } assertTrue { val input = uintArrayOf(0U, 0U, 0U, 1U) val expected = uintArrayOf(0x00048044U, 0x00000080U, 0x00010000U, 0x20100001U) - Salsa20.quarterRound(input, 0, 1, 2, 3) + Salsa20Pure.quarterRound(input, 0, 1, 2, 3) println("Result ${input.joinToString { it.toString(16).padStart(2, '0') }}") expected.contentEquals(input) @@ -73,7 +73,7 @@ class Salsa20Test { assertTrue { val input = uintArrayOf(0xd3917c5bU, 0x55f1c407U, 0x52a58a7aU, 0x8f887a3bU) val expected = uintArrayOf(0x3e2f308cU, 0xd90a8f36U, 0x6ab2a923U, 0x2883524cU) - Salsa20.quarterRound(input, 0, 1, 2, 3) + Salsa20Pure.quarterRound(input, 0, 1, 2, 3) println("Result ${input.joinToString { it.toString(16).padStart(2, '0') }}") expected.contentEquals(input) } @@ -95,7 +95,7 @@ class Salsa20Test { 0x00000001U, 0x00000200U, 0x00402000U, 0x88000100U ) - Salsa20.rowRound(input) + Salsa20Pure.rowRound(input) println("Result ${input.joinToString { it.toString(16).padStart(2, '0') }}") expected.contentEquals(input) } @@ -113,7 +113,7 @@ class Salsa20Test { 0x3402e183U, 0x3c3af432U, 0x50669f96U, 0xd89ef0a8U, 0x0040ede5U, 0xb545fbceU, 0xd257ed4fU, 0x1818882dU ) - Salsa20.rowRound(input) + Salsa20Pure.rowRound(input) println("Result ${input.joinToString { it.toString(16).padStart(2, '0') }}") expected.contentEquals(input) } @@ -135,7 +135,7 @@ class Salsa20Test { 0x40a04001U, 0x00000000U, 0x00000000U, 0x00000000U ) - Salsa20.columnRound(input) + Salsa20Pure.columnRound(input) println("Result ${input.joinToString { it.toString(16).padStart(2, '0') }}") expected.contentEquals(input) } @@ -153,7 +153,7 @@ class Salsa20Test { 0x789b010cU, 0xd195a681U, 0xeb7d5504U, 0xa774135cU, 0x481c2027U, 0x53a8e4b5U, 0x4c1f89c5U, 0x3f78c9c8U ) - Salsa20.columnRound(input) + Salsa20Pure.columnRound(input) println("Result ${input.joinToString { it.toString(16).padStart(2, '0') }}") expected.contentEquals(input) } @@ -175,7 +175,7 @@ class Salsa20Test { 0x20500000U, 0xa0000040U, 0x0008180aU, 0x612a8020U ) - Salsa20.doubleRound(input) + Salsa20Pure.doubleRound(input) println("Result ${input.joinToString { it.toString(16).padStart(2, '0') }}") expected.contentEquals(input) } @@ -193,7 +193,7 @@ class Salsa20Test { 0xca531c29U, 0x8e7943dbU, 0xac1680cdU, 0xd503ca00U, 0xa74b2ad6U, 0xbc331c5cU, 0x1dda24c7U, 0xee928277U ) - Salsa20.doubleRound(input) + Salsa20Pure.doubleRound(input) println("Result ${input.joinToString { it.toString(16).padStart(2, '0') }}") expected.contentEquals(input) } @@ -204,14 +204,14 @@ class Salsa20Test { assertTrue { val input = ubyteArrayOf(86U, 75U, 30U, 9U) val expected = 0x091e4b56U - val result = Salsa20.littleEndian(input, 0, 1, 2, 3) + val result = littleEndian(input, 0, 1, 2, 3) result == expected } assertTrue { val input = ubyteArrayOf(255U, 255U, 255U, 250U) val expected = 0xFAFFFFFFU - val result = Salsa20.littleEndian(input, 0, 1, 2, 3) + val result = littleEndian(input, 0, 1, 2, 3) result == expected } } @@ -222,7 +222,7 @@ class Salsa20Test { val expected = ubyteArrayOf(86U, 75U, 30U, 9U) val input = uintArrayOf(0x091e4b56U) val result = UByteArray(4) - Salsa20.littleEndianInverted(input, 0, result, 0) + littleEndianInverted(input, 0, result, 0) result.contentEquals(expected) } @@ -230,7 +230,7 @@ class Salsa20Test { val expected = ubyteArrayOf(255U, 255U, 255U, 250U) val input = uintArrayOf(0xFAFFFFFFU) val result = UByteArray(4) - Salsa20.littleEndianInverted(input, 0, result, 0) + littleEndianInverted(input, 0, result, 0) result.contentEquals(expected) } } @@ -241,7 +241,7 @@ class Salsa20Test { val expected = ubyteArrayOf(86U, 75U, 30U, 9U) val input = 0x091e4b56U val result = UByteArray(4) - Salsa20.littleEndianInverted(input, result, 0) + littleEndianInverted(input, result, 0) result.contentEquals(expected) } @@ -249,7 +249,7 @@ class Salsa20Test { val expected = ubyteArrayOf(255U, 255U, 255U, 250U) val input = 0xFAFFFFFFU val result = UByteArray(4) - Salsa20.littleEndianInverted(input, result, 0) + littleEndianInverted(input, result, 0) result.contentEquals(expected) } } @@ -269,7 +269,7 @@ class Salsa20Test { 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U ) - val result = Salsa20.hash(input.fromLittleEndianToUInt()) + val result = Salsa20Pure.hash(input.fromLittleEndianToUInt()) input.contentEquals(expected) } @@ -286,7 +286,7 @@ class Salsa20Test { 118U, 40U, 152U, 157U, 180U, 57U, 27U, 94U, 107U, 42U, 236U, 35U, 27U, 111U, 114U, 114U, 219U, 236U, 232U, 135U, 111U, 155U, 110U, 18U, 24U, 232U, 95U, 158U, 179U, 19U, 48U, 202U ) - val result = Salsa20.hash(input.fromLittleEndianToUInt()) + val result = Salsa20Pure.hash(input.fromLittleEndianToUInt()) result.contentEquals(expected) } @@ -304,7 +304,7 @@ class Salsa20Test { 122U, 127U, 195U, 185U, 185U, 204U, 188U, 90U, 245U, 9U, 183U, 248U, 226U, 85U, 245U, 104U ) val result = (0 until 1_000_000).fold(input) { acc, _ -> - Salsa20.hash(acc.fromLittleEndianToUInt()) + Salsa20Pure.hash(acc.fromLittleEndianToUInt()) } result.contentEquals(expected) } @@ -326,7 +326,7 @@ class Salsa20Test { 104U, 197U, 7U, 225U, 197U, 153U, 31U, 2U, 102U, 78U, 76U, 176U, 84U, 245U, 246U, 184U, 177U, 160U, 133U, 130U, 6U, 72U, 149U, 119U, 192U, 195U, 132U, 236U, 234U, 103U, 246U, 74U ) - val result = Salsa20.expansion32(k0 + k1, n) + val result = Salsa20Pure.expansion32(k0 + k1, n) result.contentEquals(expected) } @@ -337,7 +337,7 @@ class Salsa20Test { 134U, 85U, 110U, 246U, 161U, 163U, 43U, 235U, 231U, 94U, 171U, 51U, 145U, 214U, 112U, 29U, 14U, 232U, 5U, 16U, 151U, 140U, 183U, 141U, 171U, 9U, 122U, 181U, 104U, 182U, 177U, 193U ) - val result = Salsa20.expansion16(k0, n) + val result = Salsa20Pure.expansion16(k0, n) result.contentEquals(expected) } } @@ -359,7 +359,7 @@ class Salsa20Test { "CC32B15B93784A36E56A76CC64BC8477" ).toLowerCase() - val ciphertext = Salsa20.encrypt(key, nonce, UByteArray(512) { 0U }) + val ciphertext = Salsa20Pure.encrypt(key, nonce, UByteArray(512) { 0U }) ciphertext.toHexString().toLowerCase().startsWith(expectedStartsWith) && ciphertext.toHexString().toLowerCase().endsWith(endsWith) } @@ -379,7 +379,7 @@ class Salsa20Test { "529C4158B7FF41EE854B1235373988C8" ).toLowerCase() - val ciphertext = Salsa20.encrypt(key, nonce, UByteArray(131072) { 0U }) + val ciphertext = Salsa20Pure.encrypt(key, nonce, UByteArray(131072) { 0U }) println(ciphertext.slice(0 until 64).toTypedArray().toHexString()) println(ciphertext.slice(131008 until 131072).toTypedArray().toHexString()) ciphertext.slice(0 until 64).toTypedArray().toHexString().toLowerCase().startsWith(expectedStartsWith) && diff --git a/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/XSalsa20Test.kt b/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/XSalsa20Test.kt new file mode 100644 index 0000000..4f74b04 --- /dev/null +++ b/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/XSalsa20Test.kt @@ -0,0 +1,44 @@ +package com.ionspin.kotlin.crypto.symmetric + +import com.ionspin.kotlin.crypto.hash.encodeToUByteArray +import kotlin.test.Test +import kotlin.test.assertTrue + +/** + * Created by Ugljesa Jovanovic + * ugljesa.jovanovic@ionspin.com + * on 16-Jun-2020 + */ +class XSalsa20Test { + @Test + fun testXSalsa20() { + // values from https://go.googlesource.com/crypto/+/master/salsa20/salsa20_test.go xSalsa20TestData + assertTrue { + val message = "Hello world!".encodeToUByteArray() + val nonce = "24-byte nonce for xsalsa".encodeToUByteArray() + val key = "this is 32-byte key for xsalsa20".encodeToUByteArray() + val expected = ubyteArrayOf(0x00U, 0x2dU, 0x45U, 0x13U, 0x84U, 0x3fU, 0xc2U, 0x40U, 0xc4U, 0x01U, 0xe5U, 0x41U) + val result = XSalsa20Pure.encrypt(key, nonce, message) + result.contentEquals(expected) + } + + assertTrue { + val message = UByteArray(64) { 0U } + val nonce = "24-byte nonce for xsalsa".encodeToUByteArray() + val key = "this is 32-byte key for xsalsa20".encodeToUByteArray() + val expected = ubyteArrayOf( + 0x48U, 0x48U, 0x29U, 0x7fU, 0xebU, 0x1fU, 0xb5U, 0x2fU, 0xb6U, + 0x6dU, 0x81U, 0x60U, 0x9bU, 0xd5U, 0x47U, 0xfaU, 0xbcU, 0xbeU, 0x70U, + 0x26U, 0xedU, 0xc8U, 0xb5U, 0xe5U, 0xe4U, 0x49U, 0xd0U, 0x88U, 0xbfU, + 0xa6U, 0x9cU, 0x08U, 0x8fU, 0x5dU, 0x8dU, 0xa1U, 0xd7U, 0x91U, 0x26U, + 0x7cU, 0x2cU, 0x19U, 0x5aU, 0x7fU, 0x8cU, 0xaeU, 0x9cU, 0x4bU, 0x40U, + 0x50U, 0xd0U, 0x8cU, 0xe6U, 0xd3U, 0xa1U, 0x51U, 0xecU, 0x26U, 0x5fU, + 0x3aU, 0x58U, 0xe4U, 0x76U, 0x48U + ) + val result = XSalsa20Pure.encrypt(key, nonce, message) + result.contentEquals(expected) + } + + + } +} \ No newline at end of file