whyoleg/cryptography-kotlin

Usage of OpenSSL only in KMM library

NinjaRat84 opened this issue · 6 comments

I'm trying to create a KMM library which uses cryptography-kotlin for crypto operations by isung openssl for both Android and iOS.

Using a custom [cryptography-kotlin] during development which is published to mavenLocal with version 0.6.0.

libs.versions.toml

cryptography = "0.6.0"

cryptography-core = { group = "dev.whyoleg.cryptography", name = "cryptography-core", version.ref = "cryptography" }

cryptography-provider-openssl3-prebuilt = { group = "dev.whyoleg.cryptography", name = "cryptography-provider-openssl3-prebuilt", version.ref = "cryptography" }

My build.gradle.kts

plugins {
    // this is necessary to avoid the plugins to be loaded multiple times
    // in each subproject's classloader
    alias(libs.plugins.androidApplication) apply false
    alias(libs.plugins.androidLibrary) apply false
    alias(libs.plugins.composeMultiplatform) apply false
    alias(libs.plugins.composeCompiler) apply false
    alias(libs.plugins.kotlinMultiplatform) apply false
}

And build.gradle.kts for :shared

import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.dsl.JvmTarget

plugins {
    alias(libs.plugins.kotlinMultiplatform)
    alias(libs.plugins.androidLibrary)
}

kotlin {

    iosArm64()
    iosX64()
    iosSimulatorArm64()

    androidNativeX64()
    androidNativeX86()
    androidNativeArm64()
    androidNativeArm32()

    androidTarget {
        @OptIn(ExperimentalKotlinGradlePluginApi::class)
        compilerOptions {
            jvmTarget.set(JvmTarget.JVM_11)
        }
    }

    sourceSets {
        commonMain.dependencies {
            implementation(libs.cryptography.core)
            implementation(libs.cryptography.provider.openssl3.prebuilt)
        }
        commonTest.dependencies {
            implementation(kotlin("test"))
            implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.9.0")
            implementation(libs.cryptography.provider.openssl3.prebuilt)
        }
        androidMain.dependencies {
            implementation(libs.cryptography.provider.openssl3.prebuilt)
        }
        iosMain.dependencies {
            implementation(libs.cryptography.provider.openssl3.prebuilt)
        }
        nativeMain.dependencies {
            implementation(libs.cryptography.provider.openssl3.prebuilt)
        }
    }
}

android {
    namespace = "com.middlecrypto.shared"
    compileSdk = libs.versions.android.compileSdk.get().toInt()
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_11
        targetCompatibility = JavaVersion.VERSION_11
    }
    defaultConfig {
        minSdk = libs.versions.android.minSdk.get().toInt()
    }
}

But when I try to trigger a test for the crypto operations to verify everyting is working, I get this error which I cannot seem to get rid off:

Execution failed for task ':shared:compileDebugKotlinAndroid'.
> Error while evaluating property 'friendPathsSet$kotlin_gradle_plugin_common' of task ':shared:compileDebugKotlinAndroid'.
   > Could not resolve all files for configuration ':shared:debugCompileClasspath'.
      > Could not resolve dev.whyoleg.cryptography:cryptography-provider-openssl3-prebuilt:0.6.0.
        Required by:
            project :shared
            project :shared > dev.whyoleg.cryptography:cryptography-bom:0.6.0
         > No matching variant of dev.whyoleg.cryptography:cryptography-provider-openssl3-prebuilt:0.6.0 was found. The consumer was configured to find a library for use during compile-time, preferably optimized for Android, as well as attribute 'com.android.build.api.attributes.AgpVersionAttr' with value '8.5.0', attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'debug', attribute 'org.jetbrains.kotlin.platform.type' with value 'androidJvm' but:
             - Variant 'androidNativeArm32ApiElements-published' declares a library, preferably optimized for non-jvm:
                 - Incompatible because this component declares a component for use during 'kotlin-api', as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'native' and the consumer needed a component for use during compile-time, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'androidJvm'
                 - Other compatible attributes:
                     - Doesn't say anything about com.android.build.api.attributes.AgpVersionAttr (required '8.5.0')
                     - Doesn't say anything about com.android.build.api.attributes.BuildTypeAttr (required 'debug')
             - Variant 'androidNativeArm32SourcesElements-published' declares a component, preferably optimized for non-jvm:
                 - Incompatible because this component declares documentation for use during 'kotlin-runtime', as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'native' and the consumer needed a library for use during compile-time, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'androidJvm'
                 - Other compatible attributes:
                     - Doesn't say anything about com.android.build.api.attributes.AgpVersionAttr (required '8.5.0')
                     - Doesn't say anything about com.android.build.api.attributes.BuildTypeAttr (required 'debug')
             - Variant 'androidNativeArm64ApiElements-published' declares a library, preferably optimized for non-jvm:
                 - Incompatible because this component declares a component for use during 'kotlin-api', as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'native' and the consumer needed a component for use during compile-time, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'androidJvm'
                 - Other compatible attributes:
                     - Doesn't say anything about com.android.build.api.attributes.AgpVersionAttr (required '8.5.0')
                     - Doesn't say anything about com.android.build.api.attributes.BuildTypeAttr (required 'debug')

Any suggestions how I can make sure to use OpenSSL only for all variants?

Hey!
OpenSSL provider is currently supported only for native targets.
Do you have any problems when using JDK provider on Android?

Since I've verified generating a CMAC diversified key by using OpenSSL3, it would be rather nice to use this for Android / iOS / etc. in order to make sure they are correctly generated despite platform. :)

How is it possible to make use for i.e. Android and iOS is using OpenSSL?
Thus having same test vectors to verify data.

in order to make sure they are correctly generated despite platform. :)

Thus having same test vectors to verify data.

Yeah, I get the idea. And specifically for this, the library has three kinds of tests (not all present for each algorithm yet):

  • default tests - checks some specific properties of algorithms like output size or
  • testvectors tests - validates over test vectors from NIST or any other well-known values
  • compatibility tests - validates that with the same input parameters, we receive the same result and that the result of one provider will work with another provider.

So even adding compatibility tests will most likely catch all issues as it will do cross-provider testing, adding three kind of tests will ensure that both JDK and OpenSSL implementations are correct and compatible.

Coming back to openssl for JVM - it's not in my short-todo-list, and most likely it will require more involvement into https://github.com/whyoleg/ffi-kotlin from my side.

Sure thing, but how can I define that the Android should use BouncyCastle?

I've tried by doing:
val provider = CryptographyProvider.JDK(BouncyCastleProvider())

But CryptographyProvider.JDK cannot be found. :/
Any tips, since I would like JDK for Android and OpenSSL3 for iOS.

Sorry for the delay. Have you called those in jvmMain (or androidMain) source sets? It works fine at my side.

0.5.0 introduces optimal provider which simplifies provider dependency manangement. Hope this helps :)