From a18355adae42aab00058ada056ee7d73bc91cba1 Mon Sep 17 00:00:00 2001 From: Ugljesa Jovanovic Date: Sun, 11 Oct 2020 19:47:23 +0200 Subject: [PATCH] Implemeting _stream_ primitives, at elast the ones available in lazy sodium --- .../stream/Stream.kt | 21 +++ .../kotlin/crypto/stream/StreamTest.kt | 62 +++++++ .../ionspin/kotlin/crypto/stream/Stream.kt | 152 ++++++++++++++++++ supported_bindings_list.md | 14 +- 4 files changed, 242 insertions(+), 7 deletions(-) create mode 100644 multiplatform-crypto-libsodium-bindings/src/commonMain/kotlin/com.ionspin.kotlin.crypto/stream/Stream.kt create mode 100644 multiplatform-crypto-libsodium-bindings/src/commonTest/kotlin/com/ionspin/kotlin/crypto/stream/StreamTest.kt create mode 100644 multiplatform-crypto-libsodium-bindings/src/nativeMain/kotlin/com/ionspin/kotlin/crypto/stream/Stream.kt diff --git a/multiplatform-crypto-libsodium-bindings/src/commonMain/kotlin/com.ionspin.kotlin.crypto/stream/Stream.kt b/multiplatform-crypto-libsodium-bindings/src/commonMain/kotlin/com.ionspin.kotlin.crypto/stream/Stream.kt new file mode 100644 index 0000000..1f4ab72 --- /dev/null +++ b/multiplatform-crypto-libsodium-bindings/src/commonMain/kotlin/com.ionspin.kotlin.crypto/stream/Stream.kt @@ -0,0 +1,21 @@ +package com.ionspin.kotlin.crypto.stream + +/** + * Created by Ugljesa Jovanovic + * ugljesa.jovanovic@ionspin.com + * on 11-Oct-2020 + */ +const val crypto_stream_chacha20_KEYBYTES = 32 +const val crypto_stream_chacha20_NONCEBYTES = 8 +const val crypto_stream_chacha20_ietf_KEYBYTES = 32 +const val crypto_stream_chacha20_ietf_NONCEBYTES = 12 + + +expect object Stream { + fun chacha20(clen: Int, nonce: UByteArray, key: UByteArray) : UByteArray + fun chacha20IetfXor(message : UByteArray, nonce: UByteArray, key: UByteArray) : UByteArray + fun chacha20IetfXorIc(message : UByteArray, nonce: UByteArray, initialCounter: ULong, key: UByteArray) : UByteArray + fun chacha20Keygen() : UByteArray + fun chacha20Xor(message : UByteArray, nonce: UByteArray, key: UByteArray) : UByteArray + fun chacha20XorIc(message : UByteArray, nonce: UByteArray, initialCounter: ULong, key: UByteArray) : UByteArray +} diff --git a/multiplatform-crypto-libsodium-bindings/src/commonTest/kotlin/com/ionspin/kotlin/crypto/stream/StreamTest.kt b/multiplatform-crypto-libsodium-bindings/src/commonTest/kotlin/com/ionspin/kotlin/crypto/stream/StreamTest.kt new file mode 100644 index 0000000..9afe16c --- /dev/null +++ b/multiplatform-crypto-libsodium-bindings/src/commonTest/kotlin/com/ionspin/kotlin/crypto/stream/StreamTest.kt @@ -0,0 +1,62 @@ +package com.ionspin.kotlin.crypto.stream + +import com.ionspin.kotlin.crypto.util.LibsodiumRandom +import com.ionspin.kotlin.crypto.util.encodeToUByteArray +import com.ionspin.kotlin.crypto.util.randombytes_SEEDBYTES +import com.ionspin.kotlin.crypto.util.toHexString +import kotlin.random.Random +import kotlin.random.nextUBytes +import kotlin.test.Test +import kotlin.test.assertTrue + +/** + * Created by Ugljesa Jovanovic + * ugljesa.jovanovic@ionspin.com + * on 11-Oct-2020 + */ +class StreamTest { + + val seed = UByteArray(randombytes_SEEDBYTES) { 0U } + + @Test + fun testChaCha20Stream() { + val message = "This is a cha cha message".encodeToUByteArray() + val nonce = LibsodiumRandom.bufDeterministic(crypto_stream_chacha20_NONCEBYTES, seed) + val key = Stream.chacha20Keygen() + val stream = Stream.chacha20(message.size, nonce, key) + val encryptedManually = message.mapIndexed { index, it -> it xor stream[index] }.toUByteArray() + val encryptedUsingLibsodium = Stream.chacha20Xor(message, nonce, key) + val encryptedUsingLibsodiumWithInitialCounter = Stream.chacha20XorIc(message, nonce, 0U, key) + println(encryptedManually.toHexString()) + println(encryptedUsingLibsodium.toHexString()) + println(encryptedUsingLibsodiumWithInitialCounter.toHexString()) + assertTrue { + encryptedManually.contentEquals(encryptedUsingLibsodium) + } + assertTrue { + encryptedManually.contentEquals(encryptedUsingLibsodiumWithInitialCounter) + } + val decryptedUsingLibsodium = Stream.chacha20Xor(encryptedUsingLibsodium, nonce, key) + assertTrue { + decryptedUsingLibsodium.contentEquals(message) + } + } + + @Test + fun testChaCha20IetfStream() { + val message = "This is a cha cha message".encodeToUByteArray() + val nonce = LibsodiumRandom.bufDeterministic(crypto_stream_chacha20_NONCEBYTES, seed) + val key = Stream.chacha20Keygen() + val encryptedUsingLibsodium = Stream.chacha20IetfXor(message, nonce, key) + val encryptedUsingLibsodiumWithInitialCounter = Stream.chacha20IetfXorIc(message, nonce, 0U, key) + println(encryptedUsingLibsodium.toHexString()) + println(encryptedUsingLibsodiumWithInitialCounter.toHexString()) + assertTrue { + encryptedUsingLibsodium.contentEquals(encryptedUsingLibsodiumWithInitialCounter) + } + val decryptedUsingLibsodium = Stream.chacha20IetfXor(encryptedUsingLibsodium, nonce, key) + assertTrue { + decryptedUsingLibsodium.contentEquals(message) + } + } +} diff --git a/multiplatform-crypto-libsodium-bindings/src/nativeMain/kotlin/com/ionspin/kotlin/crypto/stream/Stream.kt b/multiplatform-crypto-libsodium-bindings/src/nativeMain/kotlin/com/ionspin/kotlin/crypto/stream/Stream.kt new file mode 100644 index 0000000..ff37390 --- /dev/null +++ b/multiplatform-crypto-libsodium-bindings/src/nativeMain/kotlin/com/ionspin/kotlin/crypto/stream/Stream.kt @@ -0,0 +1,152 @@ +package com.ionspin.kotlin.crypto.stream + +import com.ionspin.kotlin.crypto.util.toPtr +import kotlinx.cinterop.convert +import kotlinx.cinterop.pin +import libsodium.crypto_stream_KEYBYTES +import libsodium.crypto_stream_chacha20 +import libsodium.crypto_stream_chacha20_ietf_xor +import libsodium.crypto_stream_chacha20_ietf_xor_ic +import libsodium.crypto_stream_chacha20_keygen +import libsodium.crypto_stream_chacha20_xor +import libsodium.crypto_stream_chacha20_xor_ic + +actual object Stream { + actual fun chacha20(clen: Int, nonce: UByteArray, key: UByteArray): UByteArray { + val result = UByteArray(clen) + val resultPinned = result.pin() + val noncePinned = nonce.pin() + val keyPinned = key.pin() + + crypto_stream_chacha20(resultPinned.toPtr(), clen.convert(), noncePinned.toPtr(), keyPinned.toPtr()) + + resultPinned.unpin() + noncePinned.unpin() + keyPinned.unpin() + + return result + } + + actual fun chacha20IetfXor( + message: UByteArray, + nonce: UByteArray, + key: UByteArray + ): UByteArray { + val result = UByteArray(message.size) + val messagePinned = message.pin() + val resultPinned = result.pin() + val noncePinned = nonce.pin() + val keyPinned = key.pin() + + crypto_stream_chacha20_ietf_xor( + resultPinned.toPtr(), + messagePinned.toPtr(), + message.size.convert(), + noncePinned.toPtr(), + keyPinned.toPtr() + ) + + messagePinned.unpin() + resultPinned.unpin() + noncePinned.unpin() + keyPinned.unpin() + + return result + } + + actual fun chacha20IetfXorIc( + message: UByteArray, + nonce: UByteArray, + initialCounter: ULong, + key: UByteArray + ): UByteArray { + val result = UByteArray(message.size) + val messagePinned = message.pin() + val resultPinned = result.pin() + val noncePinned = nonce.pin() + val keyPinned = key.pin() + + crypto_stream_chacha20_ietf_xor_ic( + resultPinned.toPtr(), + messagePinned.toPtr(), + message.size.convert(), + noncePinned.toPtr(), + initialCounter.convert(), + keyPinned.toPtr() + ) + + messagePinned.unpin() + resultPinned.unpin() + noncePinned.unpin() + keyPinned.unpin() + + return result + } + + actual fun chacha20Keygen(): UByteArray { + val result = UByteArray(crypto_stream_chacha20_KEYBYTES) + val resultPinned = result.pin() + + crypto_stream_chacha20_keygen(resultPinned.toPtr()) + + resultPinned.unpin() + + return result + } + + actual fun chacha20Xor( + message: UByteArray, + nonce: UByteArray, + key: UByteArray + ): UByteArray { + val result = UByteArray(message.size) + val messagePinned = message.pin() + val resultPinned = result.pin() + val noncePinned = nonce.pin() + val keyPinned = key.pin() + + crypto_stream_chacha20_xor( + resultPinned.toPtr(), + messagePinned.toPtr(), + message.size.convert(), + noncePinned.toPtr(), + keyPinned.toPtr() + ) + + messagePinned.unpin() + resultPinned.unpin() + noncePinned.unpin() + keyPinned.unpin() + + return result + } + + actual fun chacha20XorIc( + message: UByteArray, + nonce: UByteArray, + initialCounter: ULong, + key: UByteArray + ): UByteArray { + val result = UByteArray(message.size) + val messagePinned = message.pin() + val resultPinned = result.pin() + val noncePinned = nonce.pin() + val keyPinned = key.pin() + + crypto_stream_chacha20_xor_ic( + resultPinned.toPtr(), + messagePinned.toPtr(), + message.size.convert(), + noncePinned.toPtr(), + initialCounter.convert(), + keyPinned.toPtr() + ) + + messagePinned.unpin() + resultPinned.unpin() + noncePinned.unpin() + keyPinned.unpin() + + return result + } +} diff --git a/supported_bindings_list.md b/supported_bindings_list.md index 6ff6e71..6847dcc 100644 --- a/supported_bindings_list.md +++ b/supported_bindings_list.md @@ -79,10 +79,10 @@ | crypto_hash_sha512_update | :heavy_check_mark: | | crypto_kdf_derive_from_key | :heavy_check_mark: | | crypto_kdf_keygen | :heavy_check_mark: | -| crypto_kx_client_session_keys | | -| crypto_kx_keypair | | -| crypto_kx_seed_keypair | | -| crypto_kx_server_session_keys | | +| crypto_kx_client_session_keys | :heavy_check_mark: | +| crypto_kx_keypair | :heavy_check_mark: | +| crypto_kx_seed_keypair | :heavy_check_mark: | +| crypto_kx_server_session_keys | :heavy_check_mark: | | crypto_onetimeauth | not present in LazySodium | | crypto_onetimeauth_final | not present in LazySodium | | crypto_onetimeauth_init | not present in LazySodium | @@ -99,8 +99,8 @@ | crypto_pwhash_str_verify | :heavy_check_mark: | | crypto_scalarmult | | | crypto_scalarmult_base | | -| crypto_scalarmult_ristretto255 | | -| crypto_scalarmult_ristretto255_base | | +| crypto_scalarmult_ristretto255 | not present in LazySodium | +| crypto_scalarmult_ristretto255_base | not present in LazySodium | | crypto_secretbox_detached | :heavy_check_mark: | | crypto_secretbox_easy | :heavy_check_mark: | | crypto_secretbox_keygen | :heavy_check_mark: | @@ -135,7 +135,7 @@ | crypto_stream_chacha20_keygen | | | crypto_stream_chacha20_xor | | | crypto_stream_chacha20_xor_ic | | -| crypto_stream_keygen | | +| crypto_stream_keygen | Other XSalsa20 primitives are not available, so I'm leaving this out as well| | crypto_stream_xchacha20_keygen | not present in LazySodium Android | | crypto_stream_xchacha20_xor | not present in LazySodium Android| | crypto_stream_xchacha20_xor_ic | not present in LazySodium Android |