diff --git a/README.md b/README.md index d0a8635..52f79ac 100644 --- a/README.md +++ b/README.md @@ -3,279 +3,10 @@ # Kotlin Multiplatform Crypto Library -#Note: -### Next stable release will be published after public release of Kotlin 1.4, until then API will change significantly - -Kotlin Multiplatform Crypto is a library for various cryptographic applications. - -The library comes in two flavors `multiplatform-crypto` and `multiplatform-crypto-delegated`. This project also provides -direct libsodium bindings under `multiplatform-crypto-libsodium-bindings`. - -* `multiplatform-crypto` contains pure kotlin implementations, is not reviewed, should be considered unsafe and only -for prototyping or experimentation purposes. - -* `multiplatform-crypto-delegated` relies on platform specific implementations, mostly libsodium, but care should still be taken that the kotlin code is not reviewed or proven safe. - -APIs of both variants are identical. - -* `multiplatform-crypto-libsodium-bindings` is a generated bindings library using `kotlin-multiplatform-libsodium-generator` - * Under HEAVY development at the moment - - - -### Table of contents -1. [Supported platforms](#supported-platforms-by-variant) -2. [API](#api) -3. TODO - -## Supported platforms by variant -|Platform|Pure variant| Delegated variant| -|--------|------------|------------------| -|Linux X86 64| :heavy_check_mark: | :heavy_check_mark: | -|Linux Arm 64| :heavy_check_mark: | :heavy_check_mark: | -|Linux Arm 32| :heavy_check_mark: | :x: | -|macOS X86 64| :heavy_check_mark: | :heavy_check_mark: | -|iOS x86 64 | :heavy_check_mark: | :heavy_check_mark: | -|iOS Arm 64 | :heavy_check_mark: | :heavy_check_mark: | -|iOS Arm 32 | :heavy_check_mark: | :heavy_check_mark: | -|watchOS X86 32 | :heavy_check_mark: | :heavy_check_mark: | -|watchOS Arm 64(_32) | :heavy_check_mark: | :heavy_check_mark: | -|watchos Arm 32 | :heavy_check_mark: | :heavy_check_mark: | -|tvOS X86 64 | :heavy_check_mark: | :heavy_check_mark: | -|tvOS Arm 64 | :heavy_check_mark: | :heavy_check_mark: | -|minGW X86 64| :heavy_check_mark: | :heavy_check_mark: | -|minGW X86 32| :x: | :x: | - -## Sample project -The library includes sample project that shows usage on different platforms -- NOTE: Currently only linux, macOs and windows are included. - -## Notes & Roadmap - -**The API will move fast and break often until v1.0** - -Next steps: -- Expand API (ECC, Signing ...) - -## Should I use this in production? -**NO.** -The library is under HEAVY development. - -## Why? - -This is an experimental implementation, mostly for expanding personal understanding of cryptography. -It's not peer reviewed, not guaranteed to be bug free, and not guaranteed to be secure. - -## API for Pure and Delegated flavourd - -### Hashing functions -* Blake2b -* SHA512 -* SHA256 - -### Key Derivation - -* Argon2 - -### Authenticated symmetric encryption (AEAD) - -* XChaCha20-Poly1305 - - -### Delegated flavor dependancy table -The following table describes which library is used for particular cryptographic primitive - -| Primitive | JVM | JS | Native | -| ----------|-----|----|--------| -| Blake2b | LazySodium | libsodium.js | libsodium | -| SHA256 | LazySodium | libsodium.js | libsodium | -| SHA512 | LazySodium | libsodium.js | libsodium | -| XChaCha20-Poly1305 | LazySodium | libsodium.js | libsodium | - - - -## Integration - -NOTE: Latest version of the library is built with Kotlin 1.4-M2 and therefore only SNAPSHOT variant is available. Next -stable version will be released when Kotlin 1.4. is released - -#### Gradle -Kotlin -```kotlin -implementation("com.ionspin.kotlin:multiplatform-crypto:0.1.0") - -or - -implementation("com.ionspin.kotlin:multiplatform-crypto-delegated:0.1.0") -``` - -#### Snapshot builds -```kotlin -repositories { - maven { - url = uri("https://oss.sonatype.org/content/repositories/snapshots") - } -} -implementation("com.ionspin.kotlin:multiplatform-crypto:0.1.0-SNAPSHOT") - -``` - -## Usage - -### Helper functions - -All API take `UByteArray` as message/key/nonce/etc parameter. For convenience when working with strings we provide -`String.enocdeToUbyteArray()` extensions function, and `UByteArray.toHexString` extension function. - -More convenience functions will be added. - -### Hashes - -Hashes are provided in two versions, "stateless", usually the companion object of the hash, -which takes the data to be hashed in one go, and "updatable" which can be fed data in chunks. - - -#### Blake2b - -You can use Blake 2b in two modes - -##### Stateless version -You need to deliver the complete data that is to be hashed in one go - -```kotlin -val input = "abc" -val result = Crypto.Blake2b.stateless(input.encodeToUByteArray()) -``` - -Result is returned as a `UByteArray` - -##### Updatable instance version -You can create an instance and feed the data by using `update(input : UByteArray)` call. Once all data is supplied, -you should call `digest()`. - -If you want to use Blake2b with a key, you should supply it when creating the `Blake2b` instance. - -```kotlin -val test = "abc" -val key = "key" -val blake2b = Crypto.Blake2b.updateable(key.encodeToUByteArray()) -blake2b.update(test.encodeToUByteArray()) -val result = blake2b.digest().toHexString() -``` - -After digest is called, the instance is reset and can be reused (Keep in mind key stays the same for the particular instance). -#### SHA2 (SHA256 and SHA512) - -##### Stateless version - -You need to deliver the complete data that is to be hashed in one go. You can either provide the `UByteArray` as input -or `String`. Result is always returned as `UByteArray` (At least in verision 0.0.1) - -```kotlin -val input = "abc" -val result = Crypto.Sha256.stateless(input.encodeToUByteArray()) -``` - -```kotlin -val input ="abc" -val result = Crypto.Sha512.stateless(input.encodeToUByteArray()) -``` - -Result is returned as a `UByteArray` - -##### Updateable version - -Or you can use the updatable instance version - -```kotlin -val sha256 = Crypto.Sha256.updateable() -sha256.update("abc".encodeToUByteArray()) -val result = sha256.digest() -``` - -```kotlin -val sha512 = Crypto.Sha512.updateable() -sha512.update("abc".encodeToUByteArray()) -val result = sha512.digest() -``` - -### Key derivation - -#### Argon2 - -NOTE: This implementation is tested against KAT generated by reference Argon2 implementation, which does not follow -specification completely. See this issue https://github.com/P-H-C/phc-winner-argon2/issues/183 - -```kotlin -val argon2Instance = Argon2( - password = "Password", - salt = "RandomSalt", - parallelism = 8, - tagLength = 64U, - requestedMemorySize = 256U, //4GB - numberOfIterations = 4U, - key = "", - associatedData = "", - argonType = ArgonType.Argon2id - ) -val tag = argon2Instance.derive() -val tagString = tag.map { it.toString(16).padStart(2, '0') }.joinToString(separator = "") -val expectedTagString = "c255e3e94305817d5e09a7c771e574e3a81cc78fef5da4a9644b6df0" + - "0ba1c9b424e3dd0ce7e600b1269b14c84430708186a8a60403e1bfbda935991592b9ff37" -println("Tag: ${tagString}") -assertEquals(tagString, expectedTagString) -``` - -### Symmetric encryption (OUTDATED, won't be exposed in next release, no counterpart in delegated flavor - 0.1.1) - -#### AES - -Aes is available with CBC and CTR mode through `AesCbc` and `AesCtr` classes/objects. -Similarly to hashes you can either use stateless or updateable version. - -Initialization vector, or counter states are chosen by the SDK automaticaly, and returned alongside encrypted data - -##### Stateless AesCbc and AesCtr - -AesCtr - -```kotlin -val keyString = "4278b840fb44aaa757c1bf04acbe1a3e" -val key = AesKey.Aes128Key(keyString) -val plainText = "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710" - -val encryptedDataAndInitializationVector = AesCtr.encrypt(key, plainText.hexStringToUByteArray()) -val decrypted = AesCtr.decrypt( - key, - encryptedDataAndInitializationVector.encryptedData, - encryptedDataAndInitializationVector.initialCounter -) -plainText == decrypted.toHexString() -``` - -AesCbc - -```kotlin - -val keyString = "4278b840fb44aaa757c1bf04acbe1a3e" -val key = AesKey.Aes128Key(keyString) - -val plainText = "3c888bbbb1a8eb9f3e9b87acaad986c466e2f7071c83083b8a557971918850e5" - -val encryptedDataAndInitializationVector = AesCbc.encrypt(key, plainText.hexStringToUByteArray()) -val decrypted = AesCbc.decrypt( - key, - encryptedDataAndInitializationVector.encryptedData, - encryptedDataAndInitializationVector.initilizationVector -) -plainText == decrypted.toHexString() - -``` - -## Libsodium bindings - -* Under development +This repository contains two crypto related projects: +1. Libsodium bindings for Kotiln Multiplatform +2. Pure/Delegated kotlin multiplatform crypto library written from scratch in pure form. diff --git a/buildSrc/src/main/kotlin/Deps.kt b/buildSrc/src/main/kotlin/Deps.kt index e647100..2d0fb60 100644 --- a/buildSrc/src/main/kotlin/Deps.kt +++ b/buildSrc/src/main/kotlin/Deps.kt @@ -25,7 +25,7 @@ object Versions { val kotlinBigNumVersion = "0.1.6-1.4.0-rc-SNAPSHOT" - val lazySodium = "4.2.6" + val lazySodium = "4.3.1-SNAPSHOT" val jna = "5.5.0" val kotlinPoet = "1.6.0" @@ -81,7 +81,10 @@ object Deps { val kotlinPoet = "com.squareup:kotlinpoet:${Versions.kotlinPoet}" object Delegated { - val lazysodium = "com.goterl.lazycode:lazysodium-java:${Versions.lazySodium}" + // Temporary until reported lazysodium issues are fixed. My snapshot build with + // And cause I registered com.ionspin.kotlin as maven central package root now I have to use + // that even though this is pure java library. :) + val lazysodium = "com.ionspin.kotlin:lazysodium-java:${Versions.lazySodium}" val jna = "net.java.dev.jna:jna:${Versions.jna}" } } diff --git a/multiplatform-crypto-api/README.md b/multiplatform-crypto-api/README.md new file mode 100644 index 0000000..8d19365 --- /dev/null +++ b/multiplatform-crypto-api/README.md @@ -0,0 +1,285 @@ +# Kotlin Multiplatform Crypto Library + + +Kotlin Multiplatform Crypto is a library for various cryptographic applications. + +The library comes in two flavors `multiplatform-crypto` and `multiplatform-crypto-delegated`. + +* `multiplatform-crypto` contains pure kotlin implementations, is not reviewed, should be considered unsafe and only +for prototyping or experimentation purposes. + +* `multiplatform-crypto-delegated` relies on platform specific implementations, mostly libsodium, but care should still be taken that the kotlin code is not reviewed or proven safe. + +APIs of both variants are identical. + + +### Table of contents +1. [Supported platforms](#supported-platforms-by-variant) +2. [API](#api) +3. TODO + +## Supported platforms by variant +|Platform|Pure variant| Delegated variant| +|--------|------------|------------------| +|Linux X86 64| :heavy_check_mark: | :heavy_check_mark: | +|Linux Arm 64| :heavy_check_mark: | :heavy_check_mark: | +|Linux Arm 32| :heavy_check_mark: | :x: | +|macOS X86 64| :heavy_check_mark: | :heavy_check_mark: | +|iOS x86 64 | :heavy_check_mark: | :heavy_check_mark: | +|iOS Arm 64 | :heavy_check_mark: | :heavy_check_mark: | +|iOS Arm 32 | :heavy_check_mark: | :heavy_check_mark: | +|watchOS X86 32 | :heavy_check_mark: | :heavy_check_mark: | +|watchOS Arm 64(_32) | :heavy_check_mark: | :heavy_check_mark: | +|watchos Arm 32 | :heavy_check_mark: | :heavy_check_mark: | +|tvOS X86 64 | :heavy_check_mark: | :heavy_check_mark: | +|tvOS Arm 64 | :heavy_check_mark: | :heavy_check_mark: | +|minGW X86 64| :heavy_check_mark: | :heavy_check_mark: | +|minGW X86 32| :x: | :x: | + +## Sample project +The library includes sample project that shows usage on different platforms +- NOTE: Currently only linux, macOs and windows are included. + +## Notes & Roadmap + +**The API will move fast and break often until v1.0** + +Next steps: +- Expand API (ECC, Signing ...) + +## Should I use this in production? +**NO.** +The library is under HEAVY development. Until development is done it will not be reviewed and therefore it shouldn't be used. +Contributions are still welcome! + +## Why? + +This is an experimental implementation, mostly for expanding personal understanding of cryptography. +It's not peer reviewed, not guaranteed to be bug free, and not guaranteed to be secure. + +## API for Pure and Delegated flavours + +### Hashing functions +* Blake2b +* SHA512 +* SHA256 + +### Key Derivation + +* Argon2 + +### Authenticated symmetric encryption (AEAD) + +* XChaCha20-Poly1305 + + +### Delegated flavor dependancy table +The following table describes which library is used for particular cryptographic primitive + +| Primitive | JVM | JS | Native | +| ----------|-----|----|--------| +| Blake2b | LazySodium | libsodium.js | libsodium | +| SHA256 | LazySodium | libsodium.js | libsodium | +| SHA512 | LazySodium | libsodium.js | libsodium | +| XChaCha20-Poly1305 | LazySodium | libsodium.js | libsodium | + + + +## Integration + +NOTE: Latest version of the library is built with Kotlin 1.4-M2 and therefore only SNAPSHOT variant is available. Next +stable version will be released when Kotlin 1.4. is released + +#### Gradle +Kotlin +```kotlin +implementation("com.ionspin.kotlin:multiplatform-crypto:0.1.0") + +or + +implementation("com.ionspin.kotlin:multiplatform-crypto-delegated:0.1.0") +``` + +#### Snapshot builds +```kotlin +repositories { + maven { + url = uri("https://oss.sonatype.org/content/repositories/snapshots") + } +} +implementation("com.ionspin.kotlin:multiplatform-crypto:0.1.0-SNAPSHOT") + +``` + +## Usage + +### Helper functions + +All API take `UByteArray` as message/key/nonce/etc parameter. For convenience when working with strings we provide +`String.enocdeToUbyteArray()` extensions function, and `UByteArray.toHexString` extension function. + +More convenience functions will be added. + +### Hashes + +Hashes are provided in two versions, "stateless", usually the companion object of the hash, +which takes the data to be hashed in one go, and "updatable" which can be fed data in chunks. + + +#### Blake2b + +You can use Blake 2b in two modes + +##### Stateless version +You need to deliver the complete data that is to be hashed in one go + +```kotlin +val input = "abc" +val result = Crypto.Blake2b.stateless(input.encodeToUByteArray()) +``` + +Result is returned as a `UByteArray` + +##### Updatable instance version +You can create an instance and feed the data by using `update(input : UByteArray)` call. Once all data is supplied, +you should call `digest()`. + +If you want to use Blake2b with a key, you should supply it when creating the `Blake2b` instance. + +```kotlin +val test = "abc" +val key = "key" +val blake2b = Crypto.Blake2b.updateable(key.encodeToUByteArray()) +blake2b.update(test.encodeToUByteArray()) +val result = blake2b.digest().toHexString() +``` + +After digest is called, the instance is reset and can be reused (Keep in mind key stays the same for the particular instance). +#### SHA2 (SHA256 and SHA512) + +##### Stateless version + +You need to deliver the complete data that is to be hashed in one go. You can either provide the `UByteArray` as input +or `String`. Result is always returned as `UByteArray` (At least in verision 0.0.1) + +```kotlin +val input = "abc" +val result = Crypto.Sha256.stateless(input.encodeToUByteArray()) +``` + +```kotlin +val input ="abc" +val result = Crypto.Sha512.stateless(input.encodeToUByteArray()) +``` + +Result is returned as a `UByteArray` + +##### Updateable version + +Or you can use the updatable instance version + +```kotlin +val sha256 = Crypto.Sha256.updateable() +sha256.update("abc".encodeToUByteArray()) +val result = sha256.digest() +``` + +```kotlin +val sha512 = Crypto.Sha512.updateable() +sha512.update("abc".encodeToUByteArray()) +val result = sha512.digest() +``` + +### Key derivation + +#### Argon2 + +NOTE: This implementation is tested against KAT generated by reference Argon2 implementation, which does not follow +specification completely. See this issue https://github.com/P-H-C/phc-winner-argon2/issues/183 + +```kotlin +val argon2Instance = Argon2( + password = "Password", + salt = "RandomSalt", + parallelism = 8, + tagLength = 64U, + requestedMemorySize = 256U, //4GB + numberOfIterations = 4U, + key = "", + associatedData = "", + argonType = ArgonType.Argon2id + ) +val tag = argon2Instance.derive() +val tagString = tag.map { it.toString(16).padStart(2, '0') }.joinToString(separator = "") +val expectedTagString = "c255e3e94305817d5e09a7c771e574e3a81cc78fef5da4a9644b6df0" + + "0ba1c9b424e3dd0ce7e600b1269b14c84430708186a8a60403e1bfbda935991592b9ff37" +println("Tag: ${tagString}") +assertEquals(tagString, expectedTagString) +``` + +### Symmetric encryption (OUTDATED, won't be exposed in next release, no counterpart in delegated flavor - 0.1.1) + +#### AES + +Aes is available with CBC and CTR mode through `AesCbc` and `AesCtr` classes/objects. +Similarly to hashes you can either use stateless or updateable version. + +Initialization vector, or counter states are chosen by the SDK automaticaly, and returned alongside encrypted data + +##### Stateless AesCbc and AesCtr + +AesCtr + +```kotlin +val keyString = "4278b840fb44aaa757c1bf04acbe1a3e" +val key = AesKey.Aes128Key(keyString) +val plainText = "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710" + +val encryptedDataAndInitializationVector = AesCtr.encrypt(key, plainText.hexStringToUByteArray()) +val decrypted = AesCtr.decrypt( + key, + encryptedDataAndInitializationVector.encryptedData, + encryptedDataAndInitializationVector.initialCounter +) +plainText == decrypted.toHexString() +``` + +AesCbc + +```kotlin + +val keyString = "4278b840fb44aaa757c1bf04acbe1a3e" +val key = AesKey.Aes128Key(keyString) + +val plainText = "3c888bbbb1a8eb9f3e9b87acaad986c466e2f7071c83083b8a557971918850e5" + +val encryptedDataAndInitializationVector = AesCbc.encrypt(key, plainText.hexStringToUByteArray()) +val decrypted = AesCbc.decrypt( + key, + encryptedDataAndInitializationVector.encryptedData, + encryptedDataAndInitializationVector.initilizationVector +) +plainText == decrypted.toHexString() + +``` + +## Libsodium bindings + +* Under development + + + + + + + + + + + + + + + + + diff --git a/multiplatform-crypto-libsodium-bindings/build.gradle.kts b/multiplatform-crypto-libsodium-bindings/build.gradle.kts index 91ed8ac..57c3ca5 100644 --- a/multiplatform-crypto-libsodium-bindings/build.gradle.kts +++ b/multiplatform-crypto-libsodium-bindings/build.gradle.kts @@ -47,6 +47,9 @@ repositories { mavenCentral() jcenter() maven("https://dl.bintray.com/terl/lazysodium-maven") + maven { + url = uri("https://oss.sonatype.org/content/repositories/snapshots") + } } group = ReleaseInfo.group diff --git a/multiplatform-crypto-libsodium-bindings/src/commonMain/kotlin/com.ionspin.kotlin.crypto/util/LibsodiumRandom.kt b/multiplatform-crypto-libsodium-bindings/src/commonMain/kotlin/com.ionspin.kotlin.crypto/util/LibsodiumRandom.kt index b653d90..83aa113 100644 --- a/multiplatform-crypto-libsodium-bindings/src/commonMain/kotlin/com.ionspin.kotlin.crypto/util/LibsodiumRandom.kt +++ b/multiplatform-crypto-libsodium-bindings/src/commonMain/kotlin/com.ionspin.kotlin.crypto/util/LibsodiumRandom.kt @@ -5,6 +5,8 @@ package com.ionspin.kotlin.crypto.util * ugljesa.jovanovic@ionspin.com * on 27-Sep-2020 */ +val randombytes_SEEDBYTES = 32 + expect object LibsodiumRandom { /** diff --git a/multiplatform-crypto-libsodium-bindings/src/commonTest/kotlin/com/ionspin/kotlin/crypto/util/LibsodiumRandomTest.kt b/multiplatform-crypto-libsodium-bindings/src/commonTest/kotlin/com/ionspin/kotlin/crypto/util/LibsodiumRandomTest.kt new file mode 100644 index 0000000..a515a32 --- /dev/null +++ b/multiplatform-crypto-libsodium-bindings/src/commonTest/kotlin/com/ionspin/kotlin/crypto/util/LibsodiumRandomTest.kt @@ -0,0 +1,56 @@ +package com.ionspin.kotlin.crypto.util + +import com.ionspin.kotlin.crypto.LibsodiumInitializer +import kotlin.test.Test +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +/** + * Created by Ugljesa Jovanovic + * ugljesa.jovanovic@ionspin.com + * on 03-Oct-2020 + */ +class LibsodiumRandomTest { + + @Test + fun testRandom() { + //This is just a sanity test, it should fail on occasion though, with probability of 1/2^32 + LibsodiumInitializer.initializeWithCallback { + val random = LibsodiumRandom.random() + assertTrue { random != 0U } + } + + } + + @Test + fun testRandomUniform() { + //This is just a sanity test, it should fail on occasion though, with probability of 1/2^31 + LibsodiumInitializer.initializeWithCallback { + val random = LibsodiumRandom.uniform(UInt.MAX_VALUE / 2U) + assertTrue { random != 0U && random < (UInt.MAX_VALUE / 2U)} + } + } + + @Test + fun testRandomBuffer() { + //This is just a sanity test, it should fail on occasion though, with probability of 1/2^52 + LibsodiumInitializer.initializeWithCallback { + val result = LibsodiumRandom.buf(20) + val lowProbability = UByteArray(20) { 0U } + assertFalse { result.contentEquals(lowProbability) } + } + } + + @Test + fun testRandomBufferDeterministic() { + //This is just a sanity test, it should fail on occasion though, with probability of 1/2^52 + LibsodiumInitializer.initializeWithCallback { + val seed = UByteArray(randombytes_SEEDBYTES) { 1U } + val result = LibsodiumRandom.bufDeterministic(20, seed) + val lowProbability = UByteArray(20) { 0U } + assertFalse { result.contentEquals(lowProbability) } + val secondResult = LibsodiumRandom.bufDeterministic(20, seed) + assertTrue { result.contentEquals(secondResult) } + } + } +} diff --git a/multiplatform-crypto-libsodium-bindings/src/jsMain/kotlin/com/ionspin/kotlin/crypto/JsSodiumInterface.kt b/multiplatform-crypto-libsodium-bindings/src/jsMain/kotlin/com/ionspin/kotlin/crypto/JsSodiumInterface.kt index ebe0be0..f216f00 100644 --- a/multiplatform-crypto-libsodium-bindings/src/jsMain/kotlin/com/ionspin/kotlin/crypto/JsSodiumInterface.kt +++ b/multiplatform-crypto-libsodium-bindings/src/jsMain/kotlin/com/ionspin/kotlin/crypto/JsSodiumInterface.kt @@ -207,9 +207,18 @@ interface JsSodiumInterface { fun from_hex(data : String): Uint8Array fun from_string(data : String): Uint8Array + // ---- > ---- Random ---- < ----- + + fun randombytes_buf(length: UInt) : Uint8Array + fun randombytes_buf_deterministic(length: UInt, seed : Uint8Array) : Uint8Array + fun randombytes_random() : UInt + fun randombytes_uniform(upper_bound: UInt) : UInt + // ---- Utils end ---- + + } diff --git a/multiplatform-crypto-libsodium-bindings/src/jsMain/kotlin/com/ionspin/kotlin/crypto/util/LibsodiumRandom.kt b/multiplatform-crypto-libsodium-bindings/src/jsMain/kotlin/com/ionspin/kotlin/crypto/util/LibsodiumRandom.kt new file mode 100644 index 0000000..1854710 --- /dev/null +++ b/multiplatform-crypto-libsodium-bindings/src/jsMain/kotlin/com/ionspin/kotlin/crypto/util/LibsodiumRandom.kt @@ -0,0 +1,48 @@ +package com.ionspin.kotlin.crypto.util + +import com.ionspin.kotlin.crypto.getSodium +import ext.libsodium.com.ionspin.kotlin.crypto.toUByteArray +import ext.libsodium.com.ionspin.kotlin.crypto.toUInt8Array + +/** + * Created by Ugljesa Jovanovic + * ugljesa.jovanovic@ionspin.com + * on 27-Sep-2020 + */ +actual object LibsodiumRandom { + /** + * The randombytes_buf() function fills size bytes starting at buf with an unpredictable sequence of bytes. + */ + actual fun buf(size: Int): UByteArray { + return getSodium().randombytes_buf(size).toUByteArray() + } + + /** + * The randombytes_buf_deterministic function stores size bytes into buf indistinguishable from random bytes without knowing seed. + * For a given seed, this function will always output the same sequence. size can be up to 2^31 (~8GB) because we use kotlin arrays + * and they are limited by Int primitive type + * seed is randombytes_SEEDBYTES bytes long. + * This function is mainly useful for writing tests, and was introduced in libsodium 1.0.12. Under the hood, it uses the ChaCha20 stream cipher. + * + */ + actual fun bufDeterministic(size: Int, seed: UByteArray): UByteArray { + return getSodium().randombytes_buf_deterministic(size.toUInt(), seed.toUInt8Array()).toUByteArray() + } + + /** + * The randombytes_random() function returns an unpredictable value between 0 and 0xffffffff (included). + */ + actual fun random(): UInt { + return getSodium().randombytes_random() + } + + /** + * The randombytes_uniform() function returns an unpredictable value between 0 and upper_bound (excluded). Unlike r + * andombytes_random() % upper_bound, it guarantees a uniform distribution of the possible output values even when + * upper_bound is not a power of 2. Note that an upper_bound < 2 leaves only a single element to be chosen, namely 0 + */ + actual fun uniform(upperBound: UInt): UInt { + return getSodium().randombytes_uniform(upperBound) + } + +} diff --git a/multiplatform-crypto-libsodium-bindings/src/jvmMain/kotlin/com/ionspin/kotlin/crypto/util/LibsodiumRandom.kt b/multiplatform-crypto-libsodium-bindings/src/jvmMain/kotlin/com/ionspin/kotlin/crypto/util/LibsodiumRandom.kt index df628b7..fbbcae3 100644 --- a/multiplatform-crypto-libsodium-bindings/src/jvmMain/kotlin/com/ionspin/kotlin/crypto/util/LibsodiumRandom.kt +++ b/multiplatform-crypto-libsodium-bindings/src/jvmMain/kotlin/com/ionspin/kotlin/crypto/util/LibsodiumRandom.kt @@ -27,7 +27,7 @@ actual object LibsodiumRandom { */ actual fun bufDeterministic(size: Int, seed: UByteArray): UByteArray { val result = ByteArray(size) - sodium.randombytes_buf(result, size, seed.asByteArray()) + sodium.randombytes_buf_deterministic(result, size, seed.asByteArray()) return result.asUByteArray() } @@ -36,7 +36,8 @@ actual object LibsodiumRandom { */ actual fun random(): UInt { //Broken in lazysodium-java https://github.com/terl/lazysodium-java/issues/86 - TODO() + //Using temporary forked and fixed build until pull request is accepted in original repo + return sodium.randombytes_random().toUInt() } @@ -48,7 +49,8 @@ actual object LibsodiumRandom { */ actual fun uniform(upperBound: UInt): UInt { //Broken in lazysodium-java https://github.com/terl/lazysodium-java/issues/86 - TODO("not implemented yet") + //Using temporary fixed build until pull request is accepted + return sodium.randombytes_uniform(upperBound.toInt()).toUInt() } } diff --git a/supported_bindings_list.md b/supported_bindings_list.md index 1038a64..a797b30 100644 --- a/supported_bindings_list.md +++ b/supported_bindings_list.md @@ -1,15 +1,16 @@ |Function name| Implemented | |-------------|-------------| | add | | -| memcmp | | -| memzero | | +| memcmp | :heavy_check_mark: | +| memzero | :heavy_check_mark: | | output_formats | | -| pad | | -| unpad | | +| pad | :heavy_check_mark: | +| unpad | :heavy_check_mark: | | symbols | | -| to_base64 | | -| to_hex | | -| to_string | | +| to_base64 | :heavy_check_mark: | +| to_hex | :heavy_check_mark: | +| from_base64 | :heavy_check_mark: | +| from_hex | :heavy_check_mark: | | crypto_aead_chacha20poly1305_decrypt | :heavy_check_mark: | | crypto_aead_chacha20poly1305_decrypt_detached | :heavy_check_mark: | | crypto_aead_chacha20poly1305_encrypt | :heavy_check_mark: | @@ -138,12 +139,12 @@ | 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 | -| randombytes_buf | | -| randombytes_buf_deterministic | | -| randombytes_close | | -| randombytes_random | | -| randombytes_stir | | -| randombytes_uniform | | +| randombytes_buf | :heavy_check_mark: | +| randombytes_buf_deterministic | :heavy_check_mark: | +| randombytes_close | not present in LazySodium | +| randombytes_random | :heavy_check_mark: | +| randombytes_stir | not present in LazySodium | +| randombytes_uniform | :heavy_check_mark: | | sodium_version_string | | | SODIUM_LIBRARY_VERSION_MAJOR | | | SODIUM_LIBRARY_VERSION_MINOR | |