Completed xsalsa20, adding chacha20

This commit is contained in:
Ugljesa Jovanovic 2020-06-16 16:07:01 +02:00 committed by Ugljesa Jovanovic
parent 0143fe0080
commit c7445376ca
No known key found for this signature in database
GPG Key ID: 178E6DFCECCB0E0F
7 changed files with 275 additions and 75 deletions

View File

@ -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)
}
}
}

View File

@ -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()
}

View File

@ -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)
}
}
}

View File

@ -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
}
}
}

View File

@ -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]
}
}

View File

@ -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) &&

View File

@ -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)
}
}
}