whyoleg / cryptography-kotlin

Kotlin Multiplatform cryptography / crypto library
https://whyoleg.github.io/cryptography-kotlin/
Apache License 2.0
337 stars 19 forks source link

fix(CryptographyProvider): `No providers registered. Please provide a dependency or register provider explicitly` #43

Closed jvondermarck closed 2 months ago

jvondermarck commented 2 months ago

In my Kotlin Compose Multiplatform app I declare the Provider this way to be able to encrypt and decrypt text with AES.CBC in my class :

object Cryptography {

    private val cryptography = CryptographyProvider.Default.get(AES.CBC)

    private val secureRandom = CryptographyRandom.Default

   @OptIn(DelicateCryptographyApi::class)
    suspend fun encryptAesCbcPkcs5Padding(plainText: ByteArray, key: ByteArray, iv: ByteArray): ByteArray {
        if (key.size != 32) {
            throw IllegalArgumentException("Key size has to be 32 bytes, but is ${key.size}")
        }

        if (iv.size != 16) {
            throw IllegalArgumentException("IV size has to be 16 bytes, but is ${iv.size}")
        }

        //val aes = cryptography.get(AES.CBC)
        val aesKey = cryptography.keyDecoder().decodeFrom(AES.Key.Format.RAW, key)
        val cipher = aesKey.cipher(padding = true) // // PKCS5Padding is implied here
        return cipher.encryptBlocking(iv, plainText)
    }

    ...
 }

And when I run my program I have this error :

Caused by: java.lang.IllegalStateException: No providers registered. Please provide a dependency or register provider explicitly
 at dev.whyoleg.cryptography.CryptographyProvider$Companion$Default$2.invoke(CryptographyProvider.kt:33)
 at dev.whyoleg.cryptography.CryptographyProvider$Companion$Default$2.invoke(CryptographyProvider.kt:31)
 at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
 at dev.whyoleg.cryptography.CryptographyProvider$Companion.getDefault(CryptographyProvider.kt:31)
 at de.siedle.poc.kmp.core.encryption.Cryptography.<clinit>(Cryptography.kt:12)
 ... 13 more

And when I try to use the method registerProvider I have an error because the Registry object contains an error annotation : "API of everything what is implemented in providers is experimental for now and subject to change (if possible in backward-compatible way)"

Find below your current code:

package dev.whyoleg.cryptography

@RequiresOptIn(
    message = "API of everything what is implemented in providers is experimental for now and subject to change (if possible in backward-compatible way)",
    level = RequiresOptIn.Level.ERROR
)
public annotation class CryptographyProviderApi

@SubclassOptInRequired(CryptographyProviderApi::class)
public abstract class CryptographyProvider {
    public abstract val name: String

    public abstract fun <A : CryptographyAlgorithm> getOrNull(identifier: CryptographyAlgorithmId<A>): A?
    public open fun <A : CryptographyAlgorithm> get(identifier: CryptographyAlgorithmId<A>): A =
        getOrNull(identifier) ?: throw CryptographyAlgorithmNotFoundException(identifier)

    @CryptographyProviderApi
    public object Registry {
        private val providers = initProviders().toMutableList()
        public val registeredProviders: Sequence<CryptographyProvider>
            get() = providers.toList().asSequence().map(Lazy<CryptographyProvider>::value)

        public fun registerProvider(provider: CryptographyProvider) {
            providers.add(lazyOf(provider))
        }

        public fun registerProvider(provider: Lazy<CryptographyProvider>) {
            providers.add(provider)
        }
    }

    public companion object {
        public val Default: CryptographyProvider by lazy {
            @OptIn(CryptographyProviderApi::class)
            checkNotNull(Registry.registeredProviders.firstOrNull()) {
                "No providers registered. Please provide a dependency or register provider explicitly"
            }
        }
    }
}

// used only on JVM for ServiceLoader
internal expect fun initProviders(): List<Lazy<CryptographyProvider>>

My Kotlin version is 2.0.10. And I use dev.whyoleg.cryptography:cryptography-core:0.3.1 declared in the commonMain.dependencies

whyoleg commented 2 months ago

Hey! Could you please check if you have added provider dependencies. F.e for iOS + Android case it will be something like this:

kotlin {
    sourceSets {
        commonMain.dependencies {
            implementation(project.dependencies.platform("dev.whyoleg.cryptography:cryptography-bom:0.3.1"))
            implementation("dev.whyoleg.cryptography:cryptography-core")
        }
        androidMain.dependencies {
            implementation("dev.whyoleg.cryptography:cryptography-provider-jdk")
        }
        iosMain.dependencies {
            implementation("dev.whyoleg.cryptography:cryptography-provider-openssl3-prebuilt")
            // or `apple` provider
            // implementation("dev.whyoleg.cryptography:cryptography-provider-apple")
        }
    }
}
jvondermarck commented 2 months ago

Wow thank you SO SO much @whyoleg !! It works perfectly now !

So I just added these two libraries iOS and Android in my build.gradle.kts and it immediately worked, there is no need for people who might ask, to use expect / actual in both platforms to set up the CryptographyProvider :)

Thank you for your quick answer ! Have a good day.