· 3 min read Posted by Samuel Hill

Multiplatform Encryption with SQLDelight and SQLCipher

Last year we received a PR to SQLiter (The Kotlin/Native driver that Touchlab maintains for SQLDelight) which laid the groundwork for handling encryption of our SQLite databases. Recently, Kevin Schildhorn and I have been working on taking the next step of introducing an encryption library in our Kotlin/Native build, specifically, SQLCipher.

The first wrinkle we ran in to was that vanilla SQLite3 seemed to be getting linked automatically which made ensuring SQLCipher gets used tricky. We eventually tracked that down to a default setting in SQLDelight appropriately named linkSqlite. This defaults to ‘true’ and adds sqlite3 to our linker flags. When building a dynamic framework this is what was linking it to the default sqlite3 which isn’t what we wanted.

sqldelight {
    database("kampstarterdb") {
        packageName = "co.touchlab.kampstarter.db"
        linkSqlite = false
    }
}

This can be disabled by setting the flag to false. We ultimately elected to compile a static framework making this moot, but the process raised yet another question.

While using the copy of sqlite3 included on iOS we noticed that our key pragma was apparently causing our database to be encrypted without us setting it up. This sent us down a bit of a rabbit hole.

Rabbit Hole

We wanted to find out what on the iOS platform is using the key pragma. If it’s something secure and documented we could potentially forgo SQLCipher in most cases. Unfortunately there was no such documentation and in fact we couldn’t find it mentioned at all in the conversations around iOS database encryption. We eventually got some insight with the help of Stack Overflow users. It appears that the sqlite3 version used by iOS does contain SQLite Encryption Extensions. Since SEE requires a license it’s not clear if Apple has licensed it in a way that makes it available for use to developers but without any mention of it anywhere, it’s probably best to steer clear. The bigger issue is that you may, like we did, think you are using SQLCipher (or other encryption library) when in reality you are getting undocumented behavior from the iOS copy.

Anyways…

We got back to implementing SQLCipher in our Kotlin Multiplatform project with most of our question marks ironed out. If you keep an eye on our sample projects (such as KaMPKit), you may notice we like using the CocoaPods plugin to handle integrating our shared code into our iOS app. It may have limitations at times but it goes a long way towards streamlining build setup. Touchlab maintains a fork which adds some extra configurability. It shouldn’t be needed for this, but my snippet below will be using it.

The cleanest way we found to add SQLCipher for now is as a pod dependency in a static framework.

cocoapodsext {
    pod("SQLCipher", "~> 4.0")
    framework {
        isStatic = true
    }
}

This will handle all the CInterop for you and fulfill SQLiter’s requirements. We tested this in a sample project and confirmed it was being encrypted by SQLCipher (and not our mystery iOS encryption) by decrypting it with the SQLCipher CLI.

Other configurations (such as with a dynamic framework) should be possible but will take some more figuring out around CInterop and linking.

Overall, it’s good to add another tool to our Kotlin Multiplatform toolbox. At Touchlab we are always working to expand our expertise in KMP and provide more options for our clients. To learn more about what we do and how we help your team share code with KMP check out our website.