Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: refactor the Kotlin wrapper and hide the generated uniffi api [WPB-14473] #777

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/publish-android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ jobs:
- name: Publish package
run: |
cd crypto-ffi/bindings
./gradlew uniffi-android:publishAllPublicationsToMavenCentral --no-configuration-cache
./gradlew android:publishAllPublicationsToMavenCentral --no-configuration-cache
env:
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_PASSWORD }}
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/publish-jvm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ jobs:
- name: Publish package
run: |
cd crypto-ffi/bindings
./gradlew :uniffi-jvm:publishAllPublicationsToMavenCentral --no-configuration-cache
./gradlew :jvm:publishAllPublicationsToMavenCentral --no-configuration-cache
env:
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_PASSWORD }}
Expand Down
6 changes: 2 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,8 @@ DerivedData/
.exrc

# Kotlin
crypto-ffi/bindings/jvm/src/main/kotlin/com/wire/crypto/CoreCrypto.kt
crypto-ffi/bindings/jvm/src/main/kotlin/uniffi/core_crypto/core_crypto.kt
crypto-ffi/bindings/android/src/main/kotlin/com/wire/crypto/CoreCrypto.kt
crypto-ffi/bindings/android/src/main/kotlin/uniffi/core_crypto/core_crypto.kt
crypto-ffi/bindings/uniffi-jvm/src
crypto-ffi/bindings/uniffi-android/src

# Test databases leftovers
*.edb
Expand Down
12 changes: 4 additions & 8 deletions crypto-ffi/Makefile.toml
Original file line number Diff line number Diff line change
Expand Up @@ -152,19 +152,17 @@ args = [
"generate",
"--config", "uniffi-android.toml",
"--language", "kotlin",
"--out-dir", "./bindings/android/src/main/kotlin/",
"--out-dir", "./bindings/uniffi-android/src/main/kotlin/",
"--library", "../target/release/libcore_crypto_ffi.${LIBRARY_EXTENSION}"
]

[tasks.ffi-kotlin-android]
dependencies = ["compile-ffi-kotlin-android"]
script = '''
mv ./bindings/android/src/main/kotlin/com/wire/crypto/core_crypto_ffi.kt ./bindings/android/src/main/kotlin/com/wire/crypto/CoreCrypto.kt

perl -i \
-pe 's/\bCryptoException\b/CryptoError/g;' \
-pe 's/\bE2eIdentityException\b/E2eIdentityError/g;' \
./bindings/android/src/main/kotlin/uniffi/core_crypto/core_crypto.kt
./bindings/uniffi-android/src/main/kotlin/uniffi/core_crypto/core_crypto.kt
'''

[tasks.compile-ffi-kotlin-jvm]
Expand All @@ -177,19 +175,17 @@ args = [
"--bin", "uniffi-bindgen",
"generate",
"--language", "kotlin",
"--out-dir", "./bindings/jvm/src/main/kotlin/",
"--out-dir", "./bindings/uniffi-jvm/src/main/kotlin/",
"--library", "../target/release/libcore_crypto_ffi.${LIBRARY_EXTENSION}"
]

[tasks.ffi-kotlin-jvm]
dependencies = ["compile-ffi-kotlin-jvm"]
script = '''
mv ./bindings/jvm/src/main/kotlin/com/wire/crypto/core_crypto_ffi.kt ./bindings/jvm/src/main/kotlin/com/wire/crypto/CoreCrypto.kt

perl -i \
-pe 's/\bCryptoException\b/CryptoError/g;' \
-pe 's/\bE2eIdentityException\b/E2eIdentityError/g;' \
./bindings/jvm/src/main/kotlin/uniffi/core_crypto/core_crypto.kt
./bindings/uniffi-jvm/src/main/kotlin/uniffi/core_crypto/core_crypto.kt
'''

[tasks.ffi]
Expand Down
37 changes: 1 addition & 36 deletions crypto-ffi/bindings/android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,14 @@ val copyBindings by tasks.register<Copy>("copyBindings") {
group = "uniffi"
from(kotlinSources)
include("**/*")
exclude("**/CoreCrypto.kt", "**/core_crypto.kt")
into(generatedDir)
}

dependencies {
implementation(project(":uniffi-android"))
implementation(platform(kotlin("bom")))
implementation(platform(libs.coroutines.bom))
implementation(kotlin("stdlib-jdk7"))
implementation("${libs.jna.get()}@aar")
implementation(libs.appCompat)
implementation(libs.ktx.core)
implementation(libs.coroutines.core)
Expand Down Expand Up @@ -59,31 +58,6 @@ android {
}
}

val processedResourcesDir = buildDir.resolve("processedResources")

fun registerCopyJvmBinaryTask(target: String, jniTarget: String, include: String = "*.so"): TaskProvider<Copy> =
tasks.register<Copy>("copy-${target}") {
group = "uniffi"
from(projectDir.resolve("../../../target/${target}/release"))
include(include)
into(processedResourcesDir.resolve(jniTarget))
}

val copyBinariesTasks = listOf(
registerCopyJvmBinaryTask("aarch64-linux-android", "arm64-v8a"),
registerCopyJvmBinaryTask("armv7-linux-androideabi", "armeabi-v7a"),
registerCopyJvmBinaryTask("x86_64-linux-android", "x86_64")
)

project.afterEvaluate {
tasks.getByName("mergeReleaseJniLibFolders") { dependsOn(copyBinariesTasks) }
tasks.getByName("mergeDebugJniLibFolders") { dependsOn(copyBinariesTasks) }
}

tasks.withType<ProcessResources> {
dependsOn(copyBinariesTasks)
}

tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
dependsOn(copyBindings)
}
Expand All @@ -92,11 +66,6 @@ tasks.withType<Jar> {
dependsOn(copyBindings)
}

tasks.withType<Test> {
enabled = false // FIXME: find a way to do this at some point
dependsOn(copyBinariesTasks)
}

kotlin.sourceSets.getByName("main").apply {
kotlin.srcDir(generatedDir.resolve("main"))
}
Expand All @@ -105,10 +74,6 @@ kotlin.sourceSets.getByName("androidTest").apply {
kotlin.srcDir(generatedDir.resolve("test"))
}

android.sourceSets.getByName("main").apply {
jniLibs.srcDir(processedResourcesDir)
}

// Allows skipping signing jars published to 'MavenLocal' repository
tasks.withType<Sign>().configureEach {
if (System.getenv("CI") == null) { // i.e. not in Github Action runner
Expand Down
4 changes: 2 additions & 2 deletions crypto-ffi/bindings/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
kotlin = "1.9.0"
app-compat = "1.6.1"
coroutines = "1.7.3"
jna = "5.13.0"
jna = "5.14.0"
ktx-core = "1.10.1"
slf4j = "2.0.7"
assertj = "3.24.2"
espresso = "3.5.1"
android-junit = "1.1.5"
android-logback = "2.0.0"
android-tools = "8.0.0"
android-tools = "8.1.1"
sdk-compile = "34"
sdk-min = "26"
gradle = "8.2.1"
Expand Down
30 changes: 1 addition & 29 deletions crypto-ffi/bindings/jvm/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.api.tasks.testing.logging.TestLogEvent

plugins {
kotlin("jvm")
id("java-library")
Expand All @@ -13,38 +10,13 @@ java {
}

dependencies {
implementation(platform(kotlin("bom")))
implementation(platform(libs.coroutines.bom))
implementation(kotlin("stdlib-jdk7"))
implementation(libs.jna)
implementation(libs.coroutines.core)
implementation(project(":uniffi-jvm"))
testImplementation(kotlin("test"))
testImplementation(libs.coroutines.test)
testImplementation(libs.assertj.core)
}

val processedResourcesDir = buildDir.resolve("processedResources")

fun registerCopyJvmBinaryTask(target: String, jniTarget: String, include: String = "*.so"): TaskProvider<Copy> =
tasks.register<Copy>("copy-${target}") {
group = "uniffi"
from(projectDir.resolve("../../../target/${target}/release"))
include(include)
into(processedResourcesDir.resolve(jniTarget))
}

val copyBinariesTasks = listOf(
registerCopyJvmBinaryTask("x86_64-unknown-linux-gnu", "linux-x86-64"),
registerCopyJvmBinaryTask("aarch64-apple-darwin", "darwin-aarch64", "*.dylib"),
registerCopyJvmBinaryTask("x86_64-apple-darwin", "darwin-x86-64", "*.dylib"),
)

tasks.withType<ProcessResources> { dependsOn(copyBinariesTasks) }

tasks.withType<Test> { dependsOn(copyBinariesTasks) }

sourceSets { main { resources { srcDir(processedResourcesDir) } } }

// Allows skipping signing jars published to 'MavenLocal' repository
project.afterEvaluate {
tasks.named("signMavenPublication").configure {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package com.wire.crypto.client

import com.wire.crypto.*
import java.util.*
import kotlin.reflect.typeOf

typealias EnrollmentHandle = ByteArray

/**
* Initializes the logging inside Core Crypto. Not required to be called and by default there will be no logging.
*
* @param logger a callback to implement the platform specific logging. It will receive the string with the log text from Core Crypto
**/
fun setLogger(logger: CoreCryptoLogger) {
com.wire.crypto.setLoggerOnly(logger)
}

/**
* Set maximum log level of logs which are forwarded to the [CoreCryptoLogger].
*
* @param level the max level that should be logged, by default it will be WARN
*/
fun setMaxLogLevel(level: CoreCryptoLogLevel) {
com.wire.crypto.setMaxLogLevel(level)
}

class CoreCrypto(val cc: com.wire.crypto.CoreCrypto) {

companion object {
internal const val DEFAULT_NB_KEY_PACKAGE: UInt = 100U

suspend operator fun invoke(
keystore: String,
databaseKey: String
): CoreCrypto {
val cc = coreCryptoDeferredInit(keystore, databaseKey)
cc.setCallbacks(Callbacks())
return CoreCrypto(cc)
}
}

internal fun lower() = cc

/**
* Starts a transaction in Core Crypto. If the callback succeeds, it will be committed, otherwise, every operation
* performed with the context will be discarded.
*
* @param block the function to be executed within the transaction context. A [CoreCryptoContext] will be given as parameter to this function
*
* @return the return of the function passed as parameter
*/
@Suppress("unchecked_cast")
suspend fun <R> transaction(block: suspend (context: CoreCryptoContext) -> R): R {
var result: R? = null
var error: Throwable? = null
try {
this.cc.transaction(object : CoreCryptoCommand {
override suspend fun execute(context: com.wire.crypto.CoreCryptoContext) {
try {
result = block(CoreCryptoContext(context))
} catch (e: Throwable) {
// We want to catch the error before it gets wrapped by core crypto.
error = e
// This is to tell core crypto that there was an error inside the transaction.
throw e
}
}
})
// Catch the wrapped error, which we don't need, because we caught the original error above.
} catch (_: Throwable) { }
if (error != null) {
throw error as Throwable
}

// Since we know that transaction will either run or throw it's safe to do unchecked cast here
return result as R
}

suspend fun proteusInit() {
cc.proteusInit()
}

/**
* Dumps the PKI environment as PEM
*
* @return a struct with different fields representing the PKI environment as PEM strings
*/
suspend fun e2eiDumpPKIEnv(): E2eiDumpedPkiEnv? {
return cc.e2eiDumpPkiEnv()
}

/**
* Returns whether the E2EI PKI environment is setup (i.e. Root CA, Intermediates, CRLs)
*/
suspend fun e2eiIsPKIEnvSetup(): Boolean {
return cc.e2eiIsPkiEnvSetup()
}

/**
* Closes this [CoreCryptoCentral] instance and deallocates all loaded resources.
*
* **CAUTION**: This {@link CoreCrypto} instance won't be usable after a call to this method, but there's no way to express this requirement in Kotlin, so you'll get errors instead!
*/
fun close() {
cc.close()
}
}

private class Callbacks : CoreCryptoCallbacks {

override suspend fun authorize(conversationId: ByteArray, clientId: ByteArray): Boolean = true

override suspend fun userAuthorize(
conversationId: ByteArray,
externalClientId: ByteArray,
existingClients: List<ByteArray>
): Boolean = true

override suspend fun clientIsExistingGroupUser(
conversationId: ByteArray,
clientId: ByteArray,
existingClients: List<ByteArray>,
parentConversationClients: List<ByteArray>?
): Boolean = true
}
Loading
Loading