Is AES 128 Crypto (Cipher) logic is there in Kotlin Multi Platform (KMM)?

Viewed 762

I found AES encryption logic in Kotlin by using JavaX libraries. Since it's specific to java (Android) so it's not executing for iOS.

import javax.crypto.Cipher
import javax.crypto.SecretKey
import javax.crypto.spec.SecretKeySpec

object Crypto {

    fun calculateHash(data: ByteArray, key: ByteArray): ByteArray {
        val cipher: Cipher
        var encrypted = ByteArray(16)

        try {
            val secretKeyEcb: SecretKey = SecretKeySpec(key, "AES")
            cipher = Cipher.getInstance("AES")
            cipher.init(Cipher.ENCRYPT_MODE, secretKeyEcb)
            encrypted = cipher.doFinal(data, 0, 16)
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return encrypted.copyOf(8)
    }
}

Is there any way to achieve the above code in iOS or in KMM ?

1 Answers

The Kotlin multiplatform is a new technology, and it lacks many libraries.

You will not be able to run java code on iOS, so using Cipher in the common code will not work.

When writing an application you will often encounter a similar problem, and the solution is always the same: create an interface class and implement it for each of the platforms.

commomMain/Crypto.kt

expect object Crypto {
    fun calculateHash(data: ByteArray, key: ByteArray): ByteArray
}

On android part you can use Cipher easily:

androidMain/Crypto.kt

actual object Crypto {
    fun calculateHash(data: ByteArray, key: ByteArray): ByteArray {
        val cipher: Cipher
        var encrypted = ByteArray(16)

        try {
            val secretKeyEcb: SecretKey = SecretKeySpec(key, "AES")
            cipher = Cipher.getInstance("AES")
            cipher.init(Cipher.ENCRYPT_MODE, secretKeyEcb)
            encrypted = cipher.doFinal(data, 0, 16)
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return encrypted.copyOf(8)
    }
}

And to implement the iosCommon part, you need to look for an iOS solution to your problem. I advise you to look for an Objective C solution, because kotlin generates its files based on the headers of that language, so such a solution will be easier to implement than a Swift solution.

The first one I came across was this answer and I started working with it.

You can try searching on github to see if someone has already implemented it. I try key classes from iOS and kotlin filtering, usually the number of results is minimal, if you are lucky you will find what you need.

In your case, I was lucky enough to find this code. That's the only search result for CCCrypt + kotlin language=). I combined it with obj-c answer. This doesn't looks exactly like your Cipher code, you also taking only first 8 bytes for some reason. But you should get the idea:

actual object Crypto {
    @Throws(Throwable::class)
    fun calculateHash(data: ByteArray, key: ByteArray): ByteArray {
        if (!listOf(
                kCCKeySizeAES128,
                kCCKeySizeAES192,
                kCCKeySizeAES256,
            ).contains(key.count().toUInt())
        ) {
            throw IllegalStateException("Invalid key length ${key.count()}")
        }
        val ivLength = kCCBlockSizeAES128
        val output = ByteArray(
            size = ivLength.toInt() * 2 + data.size
        ) { 0.toByte() }
        val outputSize = ULongArray(1) { 0u }
        key.usePinned { keyPinned ->
            data.usePinned { inputPinned ->
                output.usePinned { outputPinned ->
                    outputSize.usePinned { outputSizePinned ->
                        val rcbStatus = SecRandomCopyBytes(
                            kSecRandomDefault,
                            ivLength.toULong(),
                            outputPinned.addressOf(0)
                        )
                        if (rcbStatus != kCCSuccess) {
                            throw IllegalStateException("calculateHash rcbStatus $rcbStatus")
                        }
                        val ccStatus = CCCrypt(
                            op = kCCEncrypt,
                            alg = kCCAlgorithmAES,
                            options = kCCOptionPKCS7Padding,
                            key = keyPinned.addressOf(0),
                            keyLength = key.size.toULong(),
                            iv = outputPinned.addressOf(0),
                            dataIn = inputPinned.addressOf(0),
                            dataInLength = data.size.toULong(),
                            dataOut = outputPinned.addressOf(ivLength.toInt()),
                            dataOutAvailable = output.size.toULong() - ivLength,
                            dataOutMoved = outputSizePinned.addressOf(0),
                        )
                        if (ccStatus != kCCSuccess) {
                            throw IllegalStateException("calculateHash ccStatus $ccStatus")
                        }
                    }
                }
            }
        }
        return output.copyOf((outputSize.first() + ivLength).toInt())
    }
}
Related