diff --git a/kotlin-multiplatform-libsodium-generator/src/main/kotlin/com/ionspin/kotlin/crypto/generator/libsodium/generator/Coordinator.kt b/kotlin-multiplatform-libsodium-generator/src/main/kotlin/com/ionspin/kotlin/crypto/generator/libsodium/generator/Coordinator.kt index 30004bb..ae5966c 100644 --- a/kotlin-multiplatform-libsodium-generator/src/main/kotlin/com/ionspin/kotlin/crypto/generator/libsodium/generator/Coordinator.kt +++ b/kotlin-multiplatform-libsodium-generator/src/main/kotlin/com/ionspin/kotlin/crypto/generator/libsodium/generator/Coordinator.kt @@ -16,11 +16,16 @@ object Coordinator { val commonFileSpec = CommonLibsodiumGenerator.createCommonFile(packageName, LibSodiumDefinitions.testKotlinFile) val jvmFileSpec = JvmLibsodiumGenerator.createJvmFile(packageName, LibSodiumDefinitions.testKotlinFile) val nativeFileSpec = NativeLibsodiumGenerator.createNativeFile(packageName, LibSodiumDefinitions.testKotlinFile) + val jsFileSpec = JsLibsodiumGenerator.createJsFile(packageName, LibSodiumDefinitions.testKotlinFile) + val commonFile = File("../multiplatform-crypto-libsodium-bindings/src/commonMain/kotlin/") commonFileSpec.writeTo(commonFile) val jvmFile = File("../multiplatform-crypto-libsodium-bindings/src/jvmMain/kotlin/") jvmFileSpec.writeTo(jvmFile) val nativeFile = File("../multiplatform-crypto-libsodium-bindings/src/nativeMain/kotlin/") nativeFileSpec.writeTo(nativeFile) + val jsFile = File("../multiplatform-crypto-libsodium-bindings/src/jsMain/kotlin/") + jsFileSpec.writeTo(jsFile) + } } diff --git a/kotlin-multiplatform-libsodium-generator/src/main/kotlin/com/ionspin/kotlin/crypto/generator/libsodium/generator/JsLibsodiumGenerator.kt b/kotlin-multiplatform-libsodium-generator/src/main/kotlin/com/ionspin/kotlin/crypto/generator/libsodium/generator/JsLibsodiumGenerator.kt index 3eb9312..2df839c 100644 --- a/kotlin-multiplatform-libsodium-generator/src/main/kotlin/com/ionspin/kotlin/crypto/generator/libsodium/generator/JsLibsodiumGenerator.kt +++ b/kotlin-multiplatform-libsodium-generator/src/main/kotlin/com/ionspin/kotlin/crypto/generator/libsodium/generator/JsLibsodiumGenerator.kt @@ -13,9 +13,8 @@ object JsLibsodiumGenerator { fun createJsFile(packageName: String, fileDefinition: KotlinFileDefinition): FileSpec { val fileBuilder = FileSpec.builder(packageName, fileDefinition.name) - val sodiumProperty = PropertySpec.builder("sodium", ClassName.bestGuess("com.goterl.lazycode.lazysodium.SodiumJava")) - sodiumProperty.initializer(CodeBlock.of("SodiumJava()")) - fileBuilder.addProperty(sodiumProperty.build()) + fileBuilder.addImport("ext.libsodium.com.ionspin.kotlin.crypto", "toUInt8Array") + fileBuilder.addImport("com.ionspin.kotlin.crypto", "getSodium") for (commonClassDefinition in fileDefinition.commonClassList) { //Create type-aliases commonClassDefinition.innerClasses.forEach { @@ -38,7 +37,7 @@ object JsLibsodiumGenerator { innerClassDefinition: InnerClassDefinition, multiplatformModifier: MultiplatformModifier ): TypeAliasSpec { - val innerClassBuilder = TypeAliasSpec.builder(innerClassDefinition.name, ClassName.bestGuess(innerClassDefinition.javaName)) + val innerClassBuilder = TypeAliasSpec.builder(innerClassDefinition.name, Any::class.asTypeName()) innerClassBuilder.modifiers += multiplatformModifier.modifierList return innerClassBuilder.build() @@ -65,7 +64,7 @@ object JsLibsodiumGenerator { if (methodDefinition.returnType == TypeDefinition.ARRAY_OF_UBYTES) { methodBuilder.addStatement("println(\"Debug\")") val constructJvmCall = StringBuilder() - constructJvmCall.append("return sodium.${methodDefinition.javaName}") + constructJvmCall.append("return getSodium().${methodDefinition.javaName}") constructJvmCall.append(paramsToString(methodDefinition)) methodBuilder.addStatement(constructJvmCall.toString()) @@ -74,7 +73,7 @@ object JsLibsodiumGenerator { if (methodDefinition.returnType == TypeDefinition.INT) { methodBuilder.addStatement("println(\"Debug\")") val constructJvmCall = StringBuilder() - constructJvmCall.append("return sodium.${methodDefinition.javaName}") + constructJvmCall.append("return getSodium().${methodDefinition.javaName}") constructJvmCall.append(paramsToString(methodDefinition)) methodBuilder.addStatement(constructJvmCall.toString()) @@ -83,7 +82,7 @@ object JsLibsodiumGenerator { if (methodDefinition.returnType == TypeDefinition.UNIT) { methodBuilder.addStatement("println(\"Debug\")") val constructJvmCall = StringBuilder() - constructJvmCall.append("sodium.${methodDefinition.javaName}") + constructJvmCall.append("getSodium().${methodDefinition.javaName}") constructJvmCall.append(paramsToString(methodDefinition)) methodBuilder.addStatement(constructJvmCall.toString()) @@ -92,7 +91,7 @@ object JsLibsodiumGenerator { if (methodDefinition.returnType is CustomTypeDefinition) { methodBuilder.addStatement("println(\"Debug\")") val constructJvmCall = StringBuilder() - constructJvmCall.append("return sodium.${methodDefinition.javaName}") + constructJvmCall.append("return getSodium().${methodDefinition.javaName}") constructJvmCall.append(paramsToString(methodDefinition)) methodBuilder.addStatement(constructJvmCall.toString()) @@ -117,13 +116,13 @@ object JsLibsodiumGenerator { if (paramDefinition.parameterType is TypeDefinition) { when(paramDefinition.parameterType) { TypeDefinition.ARRAY_OF_UBYTES -> { - paramsBuilder.append(paramDefinition.parameterName + ".asByteArray(), " + paramDefinition.parameterName + ".size" + separator) + paramsBuilder.append(paramDefinition.parameterName + ".toUInt8Array(), " + paramDefinition.parameterName + ".size" + separator) } TypeDefinition.ARRAY_OF_UBYTES_LONG_SIZE -> { - paramsBuilder.append(paramDefinition.parameterName + ".asByteArray(), " + paramDefinition.parameterName + ".size.toLong()" + separator) + paramsBuilder.append(paramDefinition.parameterName + ".toUInt8Array(), " + paramDefinition.parameterName + ".size.toLong()" + separator) } TypeDefinition.ARRAY_OF_UBYTES_NO_SIZE -> { - paramsBuilder.append(paramDefinition.parameterName + ".asByteArray()" + separator) + paramsBuilder.append(paramDefinition.parameterName + ".toUInt8Array()" + separator) } TypeDefinition.LONG -> { paramsBuilder.append(paramDefinition.parameterName + separator) diff --git a/multiplatform-crypto-libsodium-bindings/src/commonMain/kotlin/com.ionspin.kotlin.crypto/Initializer.kt b/multiplatform-crypto-libsodium-bindings/src/commonMain/kotlin/com.ionspin.kotlin.crypto/Initializer.kt new file mode 100644 index 0000000..2ea2e2f --- /dev/null +++ b/multiplatform-crypto-libsodium-bindings/src/commonMain/kotlin/com.ionspin.kotlin.crypto/Initializer.kt @@ -0,0 +1,12 @@ +package com.ionspin.kotlin.crypto + +/** + * Created by Ugljesa Jovanovic (jovanovic.ugljesa@gmail.com) on 02/Aug/2020 + */ +expect object Initializer { + fun isInitialized() : Boolean + + suspend fun initialize() + + fun initializeWithCallback(done: () -> (Unit)) +} \ No newline at end of file diff --git a/multiplatform-crypto-libsodium-bindings/src/jsMain/kotlin/com/ionspin/kotlin/crypto/Initializer.kt b/multiplatform-crypto-libsodium-bindings/src/jsMain/kotlin/com/ionspin/kotlin/crypto/Initializer.kt new file mode 100644 index 0000000..9567243 --- /dev/null +++ b/multiplatform-crypto-libsodium-bindings/src/jsMain/kotlin/com/ionspin/kotlin/crypto/Initializer.kt @@ -0,0 +1,43 @@ +package com.ionspin.kotlin.crypto + +import ext.libsodium.com.ionspin.kotlin.crypto.JsSodiumInterface +import ext.libsodium.com.ionspin.kotlin.crypto.JsSodiumLoader +/* 1.4-M1 has some weirdness with static/objects, or I'm misusing something, not sure */ +lateinit var sodiumPointer : JsSodiumInterface +var sodiumLoaded: Boolean = false + +fun getSodium() : JsSodiumInterface = sodiumPointer + +//fun getSodiumAdvanced() : JsSodiumAdvancedInterface = js("sodiumPointer.libsodium") + +fun setSodiumPointer(jsSodiumInterface: JsSodiumInterface) { + js("sodiumPointer = jsSodiumInterface") +} + +fun getSodiumLoaded() : Boolean = sodiumLoaded + +fun setSodiumLoaded(loaded: Boolean) { + js("sodiumLoaded = loaded") +} + +actual object Initializer { + private var isPlatformInitialized = false + + actual suspend fun initialize() { + JsSodiumLoader.load() + isPlatformInitialized = true + } + + actual fun initializeWithCallback(done: () -> Unit) { + JsSodiumLoader.loadWithCallback { + isPlatformInitialized = true + done() + } + } + + actual fun isInitialized(): Boolean { + return isPlatformInitialized + } + + +} \ No newline at end of file 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 new file mode 100644 index 0000000..1934eb9 --- /dev/null +++ b/multiplatform-crypto-libsodium-bindings/src/jsMain/kotlin/com/ionspin/kotlin/crypto/JsSodiumInterface.kt @@ -0,0 +1,60 @@ +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 randombytes_buf(numberOfBytes: Int): Uint8Array + + fun crypto_generichash(hashLength: Int, inputMessage: Uint8Array, key: Uint8Array,): Uint8Array + + fun crypto_hash_sha256(message: Uint8Array): Uint8Array + + fun crypto_hash_sha512(message: Uint8Array): Uint8Array + + //Updateable + + fun crypto_generichash_init(key : Uint8Array, hashLength: Int) : dynamic + + fun crypto_generichash_update(state: dynamic, inputMessage: Uint8Array) + + fun crypto_generichash_final(state: dynamic, hashLength: Int) : Uint8Array + + + fun crypto_hash_sha256_init() : dynamic + + fun crypto_hash_sha256_update(state: dynamic, message: Uint8Array) + + fun crypto_hash_sha256_final(state: dynamic): Uint8Array + + fun crypto_hash_sha512_init() : dynamic + + fun crypto_hash_sha512_update(state: dynamic, message: Uint8Array) + + fun crypto_hash_sha512_final(state: dynamic): Uint8Array + + //XChaCha20Poly1305 + fun crypto_aead_xchacha20poly1305_ietf_encrypt(message: Uint8Array, additionalData: Uint8Array, secretNonce: Uint8Array, nonce: Uint8Array, key: Uint8Array) : Uint8Array + fun crypto_aead_xchacha20poly1305_ietf_decrypt(secretNonce: Uint8Array, ciphertext: Uint8Array, additionalData: Uint8Array, nonce: Uint8Array, key: Uint8Array) : Uint8Array + + //XChaCha20Poly1305 + //encrypt + fun crypto_secretstream_xchacha20poly1305_init_push(header: Uint8Array) : dynamic + fun crypto_secretstream_xchacha20poly1305_push(state: dynamic, message: Uint8Array, additionalData: Uint8Array, tag: UByte) : Uint8Array + + //decrypt + fun crypto_secretstream_xchacha20poly1305_init_pull(header: Uint8Array, key: Uint8Array) : dynamic + fun crypto_secretstream_xchacha20poly1305_pull(state: dynamic, ciphertext: Uint8Array, additionalData: Uint8Array) : dynamic + + //util + fun memzero(array: Uint8Array) + + + +} diff --git a/multiplatform-crypto-libsodium-bindings/src/jsMain/kotlin/com/ionspin/kotlin/crypto/JsSodiumLoader.kt b/multiplatform-crypto-libsodium-bindings/src/jsMain/kotlin/com/ionspin/kotlin/crypto/JsSodiumLoader.kt new file mode 100644 index 0000000..b445487 --- /dev/null +++ b/multiplatform-crypto-libsodium-bindings/src/jsMain/kotlin/com/ionspin/kotlin/crypto/JsSodiumLoader.kt @@ -0,0 +1,56 @@ +package ext.libsodium.com.ionspin.kotlin.crypto + +import com.ionspin.kotlin.crypto.getSodiumLoaded +import com.ionspin.kotlin.crypto.setSodiumPointer +import com.ionspin.kotlin.crypto.sodiumLoaded +import ext.libsodium.* +import kotlin.coroutines.Continuation +import kotlin.coroutines.suspendCoroutine + +/** + * Created by Ugljesa Jovanovic + * ugljesa.jovanovic@ionspin.com + * on 27-May-2020 + */ +object JsSodiumLoader { + + class _EmitJsSodiumFunction { + init { + println(::crypto_generichash) + println(::crypto_hash_sha256) + println(::crypto_hash_sha512) + println(::crypto_hash_sha256_init) + } + + } + + fun storeSodium(promisedSodium: dynamic, continuation: Continuation) { + setSodiumPointer(promisedSodium) + sodiumLoaded = true + continuation.resumeWith(Result.success(Unit)) + } + + 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(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() + } + } +} diff --git a/multiplatform-crypto-libsodium-bindings/src/jsMain/kotlin/debug/test/DebugTest.kt b/multiplatform-crypto-libsodium-bindings/src/jsMain/kotlin/debug/test/DebugTest.kt new file mode 100644 index 0000000..9b624c8 --- /dev/null +++ b/multiplatform-crypto-libsodium-bindings/src/jsMain/kotlin/debug/test/DebugTest.kt @@ -0,0 +1,54 @@ +package debug.test + +import com.ionspin.kotlin.crypto.getSodium +import ext.libsodium.com.ionspin.kotlin.crypto.toUInt8Array +import kotlin.Any +import kotlin.Int +import kotlin.UByteArray + +actual typealias Sha256State = Any + +actual typealias Sha512State = Any + +actual typealias GenericHashState = Any + +actual class Crypto { + actual fun crypto_hash_sha256_init(state: Sha256State): Int { + println("Debug") + return getSodium().crypto_hash_sha256_init(state) + } + + actual fun crypto_hash_sha256_update(state: Sha256State, input: UByteArray) { + println("Debug") + getSodium().crypto_hash_sha256_update(state, input.toUInt8Array(), input.size.toLong()) + } + + actual fun crypto_hash_sha256_final(state: Sha256State, out: UByteArray) { + println("Debug") + getSodium().crypto_hash_sha256_final(state, out.toUInt8Array()) + } + + actual fun crypto_hash_sha512_init(state: Sha512State): Int { + println("Debug") + return getSodium().crypto_hash_sha512_init(state) + } + + actual fun crypto_hash_sha512_update(state: Sha512State, input: UByteArray) { + println("Debug") + getSodium().crypto_hash_sha512_update(state, input.toUInt8Array(), input.size.toLong()) + } + + actual fun crypto_hash_sha512_final(state: Sha512State, out: UByteArray) { + println("Debug") + getSodium().crypto_hash_sha512_final(state, out.toUInt8Array()) + } + + actual fun crypto_generichash_init( + state: GenericHashState, + key: UByteArray, + outlen: Int + ): Int { + println("Debug") + return getSodium().crypto_generichash_init(state, key.toUInt8Array(), key.size, outlen) + } +} diff --git a/multiplatform-crypto-libsodium-bindings/src/jvmMain/kotlin/com/ionspin/kotlin/crypto/Initializer.kt b/multiplatform-crypto-libsodium-bindings/src/jvmMain/kotlin/com/ionspin/kotlin/crypto/Initializer.kt new file mode 100644 index 0000000..2af22a3 --- /dev/null +++ b/multiplatform-crypto-libsodium-bindings/src/jvmMain/kotlin/com/ionspin/kotlin/crypto/Initializer.kt @@ -0,0 +1,27 @@ +package com.ionspin.kotlin.crypto + +import com.goterl.lazycode.lazysodium.SodiumJava + +/** + * Created by Ugljesa Jovanovic (jovanovic.ugljesa@gmail.com) on 02/Aug/2020 + */ +actual object Initializer { + private var isPlatformInitialized = false + + lateinit var sodium : SodiumJava + actual suspend fun initialize() { + sodium = SodiumJava() + isPlatformInitialized = true + } + + actual fun initializeWithCallback(done: () -> Unit) { + sodium = SodiumJava() + isPlatformInitialized = true + done() + } + + actual fun isInitialized(): Boolean { + return isPlatformInitialized + } + +} \ No newline at end of file diff --git a/multiplatform-crypto-libsodium-bindings/src/nativeMain/kotlin/com.ionspin.kotlin.crypto/Initializer.kt b/multiplatform-crypto-libsodium-bindings/src/nativeMain/kotlin/com.ionspin.kotlin.crypto/Initializer.kt new file mode 100644 index 0000000..944c199 --- /dev/null +++ b/multiplatform-crypto-libsodium-bindings/src/nativeMain/kotlin/com.ionspin.kotlin.crypto/Initializer.kt @@ -0,0 +1,30 @@ +@file:Suppress("VARIABLE_IN_SINGLETON_WITHOUT_THREAD_LOCAL") + +package com.ionspin.kotlin.crypto + +import libsodium.sodium_init +import kotlin.native.concurrent.AtomicInt + +actual object Initializer { + + private var isPlatformInitialized : AtomicInt = AtomicInt(0) + + actual suspend fun initialize() { + if (isPlatformInitialized.compareAndSet(0, 1)) { + sodium_init() + } + + + } + + actual fun initializeWithCallback(done: () -> Unit) { + if (isPlatformInitialized.compareAndSet(0, 1)) { + sodium_init() + } + done() + } + + actual fun isInitialized(): Boolean { + return isPlatformInitialized.value != 0 + } +}