From f51374ce15f05afee462669d68de8141c49fa849 Mon Sep 17 00:00:00 2001 From: Ugljesa Jovanovic Date: Thu, 28 May 2020 19:00:33 +0200 Subject: [PATCH] Use random bytes provided by libsodium on js, add with callback load for sodium, more progress --- .../build.gradle.kts | 2 +- .../com/ionspin/kotlin/crypto/Crypto.kt | 2 ++ .../com/ionspin/kotlin/crypto/Initializer.kt | 8 ++++++- .../kotlin/crypto/JsSodiumInterface.kt | 9 +++++++- .../ionspin/kotlin/crypto/JsSodiumLoader.kt | 23 +++++++++++++++---- .../kotlin/com/ionspin/kotlin/crypto/SRNG.kt | 19 ++------------- .../crypto/hash/blake2b/Blake2bDelegated.kt | 23 ++++++++++++++----- .../com/ionspin/kotlin/crypto/SRNGJsTest.kt | 9 +++++--- .../crypto/hash/blake2b/Blake2bJsTest.kt | 18 +++++++++++---- 9 files changed, 75 insertions(+), 38 deletions(-) diff --git a/multiplatform-crypto-delegated/build.gradle.kts b/multiplatform-crypto-delegated/build.gradle.kts index 2e2926c..7186584 100644 --- a/multiplatform-crypto-delegated/build.gradle.kts +++ b/multiplatform-crypto-delegated/build.gradle.kts @@ -74,7 +74,7 @@ kotlin { js { browser { testTask { - enabled = false //Until I sort out testing on travis + enabled = true //Until I sort out testing on travis useKarma { useChrome() } diff --git a/multiplatform-crypto-delegated/src/commonMain/kotlin/com/ionspin/kotlin/crypto/Crypto.kt b/multiplatform-crypto-delegated/src/commonMain/kotlin/com/ionspin/kotlin/crypto/Crypto.kt index 85f8a14..84489e6 100644 --- a/multiplatform-crypto-delegated/src/commonMain/kotlin/com/ionspin/kotlin/crypto/Crypto.kt +++ b/multiplatform-crypto-delegated/src/commonMain/kotlin/com/ionspin/kotlin/crypto/Crypto.kt @@ -23,5 +23,7 @@ object Crypto : CryptoProvider { expect object Initializer { suspend fun initialize() + + fun initializeWithCallback(done : () -> (Unit)) } diff --git a/multiplatform-crypto-delegated/src/jsMain/kotlin/com/ionspin/kotlin/crypto/Initializer.kt b/multiplatform-crypto-delegated/src/jsMain/kotlin/com/ionspin/kotlin/crypto/Initializer.kt index a2ef6a6..897a51f 100644 --- a/multiplatform-crypto-delegated/src/jsMain/kotlin/com/ionspin/kotlin/crypto/Initializer.kt +++ b/multiplatform-crypto-delegated/src/jsMain/kotlin/com/ionspin/kotlin/crypto/Initializer.kt @@ -6,7 +6,7 @@ import ext.libsodium.com.ionspin.kotlin.crypto.JsSodiumLoader lateinit var sodiumPointer : JsSodiumInterface var sodiumLoaded: Boolean = false -fun getSodiumPointer() : JsSodiumInterface = sodiumPointer +fun getSodium() : JsSodiumInterface = sodiumPointer fun setSodiumPointer(jsSodiumInterface: JsSodiumInterface) { js("sodiumPointer = jsSodiumInterface") @@ -22,4 +22,10 @@ actual object Initializer { actual suspend fun initialize() { JsSodiumLoader.load() } + + actual fun initializeWithCallback(done: () -> Unit) { + + } + + } \ No newline at end of file diff --git a/multiplatform-crypto-delegated/src/jsMain/kotlin/com/ionspin/kotlin/crypto/JsSodiumInterface.kt b/multiplatform-crypto-delegated/src/jsMain/kotlin/com/ionspin/kotlin/crypto/JsSodiumInterface.kt index 3a85c15..e2cf5d1 100644 --- a/multiplatform-crypto-delegated/src/jsMain/kotlin/com/ionspin/kotlin/crypto/JsSodiumInterface.kt +++ b/multiplatform-crypto-delegated/src/jsMain/kotlin/com/ionspin/kotlin/crypto/JsSodiumInterface.kt @@ -1,10 +1,17 @@ package ext.libsodium.com.ionspin.kotlin.crypto +import org.khronos.webgl.Uint8Array + /** * Created by Ugljesa Jovanovic * ugljesa.jovanovic@ionspin.com * on 27-May-2020 */ interface JsSodiumInterface { - fun crypto_generichash(hashLength: Int, inputMessage: String) : String + + fun crypto_generichash(hashLength: Int, inputMessage: String) : Uint8Array + + fun randombytes_buf(numberOfBytes: Int) : Uint8Array + + } \ No newline at end of file diff --git a/multiplatform-crypto-delegated/src/jsMain/kotlin/com/ionspin/kotlin/crypto/JsSodiumLoader.kt b/multiplatform-crypto-delegated/src/jsMain/kotlin/com/ionspin/kotlin/crypto/JsSodiumLoader.kt index d4bef22..d5a4000 100644 --- a/multiplatform-crypto-delegated/src/jsMain/kotlin/com/ionspin/kotlin/crypto/JsSodiumLoader.kt +++ b/multiplatform-crypto-delegated/src/jsMain/kotlin/com/ionspin/kotlin/crypto/JsSodiumLoader.kt @@ -1,7 +1,6 @@ package ext.libsodium.com.ionspin.kotlin.crypto import com.ionspin.kotlin.crypto.getSodiumLoaded -import com.ionspin.kotlin.crypto.getSodiumPointer import com.ionspin.kotlin.crypto.setSodiumPointer import com.ionspin.kotlin.crypto.sodiumLoaded import ext.libsodium._libsodiumPromise @@ -15,19 +14,33 @@ import kotlin.coroutines.suspendCoroutine */ object JsSodiumLoader { - fun storeSodium(promisedSodium: dynamic, continuation: Continuation) { + fun storeSodium(promisedSodium: dynamic, continuation: Continuation) { setSodiumPointer(promisedSodium) sodiumLoaded = true - continuation.resumeWith(Result.success(getSodiumPointer())) + continuation.resumeWith(Result.success(Unit)) } - suspend fun load(): JsSodiumInterface = suspendCoroutine { continuation -> + suspend fun load() = suspendCoroutine { continuation -> console.log(getSodiumLoaded()) if (!getSodiumLoaded()) { val libsodiumModule = js("\$module\$libsodium_wrappers_sumo") _libsodiumPromise.then { storeSodium(libsodiumModule, continuation) } } else { - continuation.resumeWith(Result.success(getSodiumPointer())) + continuation.resumeWith(Result.success(Unit)) + } + } + + fun loadWithCallback(doneCallback: () -> (Unit)) { + console.log(getSodiumLoaded()) + if (!getSodiumLoaded()) { + val libsodiumModule = js("\$module\$libsodium_wrappers_sumo") + _libsodiumPromise.then { + setSodiumPointer(libsodiumModule) + sodiumLoaded = true + doneCallback.invoke() + } + } else { + doneCallback.invoke() } } } \ No newline at end of file diff --git a/multiplatform-crypto-delegated/src/jsMain/kotlin/com/ionspin/kotlin/crypto/SRNG.kt b/multiplatform-crypto-delegated/src/jsMain/kotlin/com/ionspin/kotlin/crypto/SRNG.kt index b2de763..b12515c 100644 --- a/multiplatform-crypto-delegated/src/jsMain/kotlin/com/ionspin/kotlin/crypto/SRNG.kt +++ b/multiplatform-crypto-delegated/src/jsMain/kotlin/com/ionspin/kotlin/crypto/SRNG.kt @@ -16,7 +16,7 @@ package com.ionspin.kotlin.crypto -import kotlin.browser.window +import org.khronos.webgl.get /** * Created by Ugljesa Jovanovic @@ -27,22 +27,7 @@ actual object SRNG { var counter = 0 @ExperimentalUnsignedTypes actual fun getRandomBytes(amount: Int): UByteArray { - val runningOnNode = jsTypeOf(window) == "undefined" - val randomBytes = if (runningOnNode) { - println("Running on node") - js("require('crypto')").randomBytes(amount).toJSON().data - } else { - println("Running in browser") - js( - """ - var randomArray = new Uint8Array(amount); - var crypto = (self.crypto || self.msCrypto); - crypto.getRandomValues(randomArray); - """ - ) - var randomArrayResult = js("Array.prototype.slice.call(randomArray);") - randomArrayResult - } + val randomBytes = getSodium().randombytes_buf(amount) println("Random bytes: $randomBytes") print("Byte at ${randomBytes[0]}") val randomBytesUByteArray = UByteArray(amount) { diff --git a/multiplatform-crypto-delegated/src/jsMain/kotlin/com/ionspin/kotlin/crypto/hash/blake2b/Blake2bDelegated.kt b/multiplatform-crypto-delegated/src/jsMain/kotlin/com/ionspin/kotlin/crypto/hash/blake2b/Blake2bDelegated.kt index 590b770..f7ed9f2 100644 --- a/multiplatform-crypto-delegated/src/jsMain/kotlin/com/ionspin/kotlin/crypto/hash/blake2b/Blake2bDelegated.kt +++ b/multiplatform-crypto-delegated/src/jsMain/kotlin/com/ionspin/kotlin/crypto/hash/blake2b/Blake2bDelegated.kt @@ -1,9 +1,7 @@ package com.ionspin.kotlin.crypto.hash.blake2b +import com.ionspin.kotlin.crypto.getSodium import com.ionspin.kotlin.crypto.util.toHexString -import ext.libsodium.com.ionspin.kotlin.crypto.JsSodiumInterface -import ext.libsodium.com.ionspin.kotlin.crypto.JsSodiumLoader -import ext.libsodium.crypto_generichash /** * Created by Ugljesa Jovanovic @@ -39,8 +37,7 @@ actual object Blake2bStateless : Blake2bStatelessInterface { override val MAX_HASH_BYTES: Int = 64 override suspend fun digest(inputString: String, key: String?, hashLength: Int): UByteArray { - val sodium = JsSodiumLoader.load() - val hashed = sodium.crypto_generichash(64, inputString) + val hashed = getSodium().crypto_generichash(64, inputString) val hash = UByteArray(MAX_HASH_BYTES) for (i in 0 until MAX_HASH_BYTES) { js( @@ -50,7 +47,21 @@ actual object Blake2bStateless : Blake2bStatelessInterface { ) } println("Hash ${hash.toHexString()}") - return ubyteArrayOf(0U) + return hash + } + + fun digestBlocking(inputString: String, key: String?, hashLength: Int): UByteArray { + val hashed = getSodium().crypto_generichash(hashLength, inputString) + val hash = UByteArray(MAX_HASH_BYTES) + for (i in 0 until MAX_HASH_BYTES) { + js( + """ + hash[i] = hashed[i] + """ + ) + } + println("Hash ${hash.toHexString()}") + return hash } override fun digest(inputMessage: UByteArray, key: UByteArray, hashLength: Int): UByteArray { diff --git a/multiplatform-crypto-delegated/src/jsTest/kotlin/com/ionspin/kotlin/crypto/SRNGJsTest.kt b/multiplatform-crypto-delegated/src/jsTest/kotlin/com/ionspin/kotlin/crypto/SRNGJsTest.kt index 95d5291..761d25f 100644 --- a/multiplatform-crypto-delegated/src/jsTest/kotlin/com/ionspin/kotlin/crypto/SRNGJsTest.kt +++ b/multiplatform-crypto-delegated/src/jsTest/kotlin/com/ionspin/kotlin/crypto/SRNGJsTest.kt @@ -16,6 +16,8 @@ package com.ionspin.kotlin.crypto +import com.ionspin.kotlin.crypto.util.testBlocking +import ext.libsodium.com.ionspin.kotlin.crypto.JsSodiumLoader import kotlin.test.Test import kotlin.test.assertTrue @@ -28,14 +30,15 @@ import kotlin.test.assertTrue class SRNGJsTest { @Test - fun testJsSrng() { + fun testJsSrng() = testBlocking { + JsSodiumLoader.load() val bytes1 = SRNG.getRandomBytes(10) val bytes2 = SRNG.getRandomBytes(10) - println("BYTES1") + println("BYTES1\n") bytes1.forEach { print(it.toString(16).padStart(2, '0')) } - println("BYTES2") + println("BYTES2\n") bytes2.forEach { print(it.toString(16).padStart(2, '0')) } diff --git a/multiplatform-crypto-delegated/src/jsTest/kotlin/com/ionspin/kotlin/crypto/hash/blake2b/Blake2bJsTest.kt b/multiplatform-crypto-delegated/src/jsTest/kotlin/com/ionspin/kotlin/crypto/hash/blake2b/Blake2bJsTest.kt index d727566..42b51c9 100644 --- a/multiplatform-crypto-delegated/src/jsTest/kotlin/com/ionspin/kotlin/crypto/hash/blake2b/Blake2bJsTest.kt +++ b/multiplatform-crypto-delegated/src/jsTest/kotlin/com/ionspin/kotlin/crypto/hash/blake2b/Blake2bJsTest.kt @@ -2,8 +2,10 @@ package com.ionspin.kotlin.crypto.hash.blake2b import com.ionspin.kotlin.crypto.Crypto import com.ionspin.kotlin.crypto.util.testBlocking +import com.ionspin.kotlin.crypto.util.toHexString import kotlin.test.BeforeTest import kotlin.test.Test +import kotlin.test.assertEquals /** * Created by Ugljesa Jovanovic @@ -14,13 +16,21 @@ import kotlin.test.Test @ExperimentalStdlibApi class Blake2bJsTest { - @BeforeTest - fun setup() = testBlocking { + @Test + fun testBlake2BSodiumInterop() = testBlocking { Crypto.initialize() + val hash = Blake2bStateless.digest("test") + assertEquals(hash.toHexString(), "a71079d42853dea26e453004338670a53814b78137ffbed07603a41d76a4" + + "83aa9bc33b582f77d30a65e6f29a896c0411f38312e1d66e0bf16386c86a89bea572") } @Test - fun testBlake2BSodiumInterop() = testBlocking { - Blake2bStateless.digest("test") + fun testBlake2BSodiumBlockingInterop() = testBlocking { + Crypto.initialize() + val hash = Blake2bStateless.digestBlocking("test", null, 64) + assertEquals(hash.toHexString(), "a71079d42853dea26e453004338670a53814b78137ffbed07603a41d76a4" + + "83aa9bc33b582f77d30a65e6f29a896c0411f38312e1d66e0bf16386c86a89bea572") + + } } \ No newline at end of file