diff --git a/.github/workflows/lint-pr.yml b/.github/workflows/lint-pr.yml new file mode 100644 index 00000000..e1d9d1b8 --- /dev/null +++ b/.github/workflows/lint-pr.yml @@ -0,0 +1,16 @@ +--- +name: 'Pull Request - Linter' + +env: + ATALA_GITHUB_TOKEN: ${{ secrets.ATALA_GITHUB_TOKEN }} + +on: [pull_request] + +jobs: + lint_pr: + name: Validate PR title + runs-on: ubuntu-latest + steps: + - uses: amannn/action-semantic-pull-request@v5 + env: + GITHUB_TOKEN: ${{ secrets.ATALA_GITHUB_TOKEN }} diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml new file mode 100644 index 00000000..01fca233 --- /dev/null +++ b/.github/workflows/pull-request.yml @@ -0,0 +1,91 @@ +--- +name: 'Pull Request - Compile Build' + +defaults: + run: + shell: bash + +concurrency: + group: ${{ github.head_ref }}${{ github.ref }} + cancel-in-progress: true + +env: + JAVA_VERSION: 11 + ATALA_GITHUB_ACTOR: ${{ secrets.ATALA_GITHUB_ACTOR }} + ATALA_GITHUB_TOKEN: ${{ secrets.ATALA_GITHUB_TOKEN }} + +on: [pull_request] + +jobs: + build: + runs-on: macos-latest + steps: + - name: "Checkout the repo" + uses: actions/checkout@v3 + with: + submodules: recursive + token: ${{ secrets.ATALA_GITHUB_TOKEN }} + fetch-depth: 0 + + - name: "Install Java ${{ env.JAVA_VERSION }}" + uses: actions/setup-java@v3 + with: + java-version: "${{ env.JAVA_VERSION }}" + distribution: zulu + + - name: "Gradle Build Action" + uses: gradle/gradle-build-action@v2 + + - name: "Test Kotlin code is properly formatted" + working-directory: ./anoncred-kmm + run: ./gradlew ktlintCheck + + - name: "Install Homebrew" + run: > + /bin/bash -c "$(curl -fsSL + https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + + - name: "Install autoconf, automake, libtool" + run: | + brew install autoconf automake libtool + + - name: "Install Mac ToolChain" + run: | + brew tap messense/macos-cross-toolchains + + - name: "Install Linux GNU for x86_64" + run: | + rm '/usr/local/bin/2to3' + rm '/usr/local/bin/python3' + rm '/usr/local/bin/python3.11' + rm '/usr/local/bin/2to3-3.11' + rm '/usr/local/bin/idle3' + rm '/usr/local/bin/idle3.11' + rm '/usr/local/bin/pydoc3' + rm '/usr/local/bin/pydoc3.11' + rm '/usr/local/bin/python3-config' + rm '/usr/local/bin/python3.11-config' + brew install --overwrite x86_64-unknown-linux-gnu + + - name: "Install Linux GNU for aarch64" + run: | + brew install --overwrite aarch64-unknown-linux-gnu + + - name: "Install Rust Targets" + run: | + rustup target add armv7-linux-androideabi + rustup target add i686-linux-android + rustup target add aarch64-linux-android + rustup target add x86_64-linux-android + rustup target add aarch64-apple-darwin + rustup target add x86_64-apple-darwin + rustup target add aarch64-unknown-linux-gnu + rustup target add x86_64-unknown-linux-gnu + + - name: "Install Rust Cargo NDK" + run: | + cargo install cargo-ndk + + - name: "Build Check All tests" + working-directory: ./anoncred-kmm + run: ./gradlew :anoncreds-kmp:allTests diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c2e71c1f..e32ee8d0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,3 +1,4 @@ +--- name: "Release Libraries" env: @@ -62,9 +63,77 @@ jobs: tag: ${{github.event.inputs.tag}} file: uniffi/output-frameworks/anoncreds-swift/libanoncreds.xcframework.zip asset_name: "libanoncreds.xcframework.zip" - + + build-kmp: + name: "Build KMP" + runs-on: macos-latest + steps: + - name: Checkout the repo + uses: actions/checkout@v3 + with: + submodules: recursive + token: ${{ secrets.ATALA_GITHUB_TOKEN }} + fetch-depth: 0 + + - name: "Install Java ${{ env.JAVA_VERSION }}" + uses: actions/setup-java@v3 + with: + java-version: "${{ env.JAVA_VERSION }}" + distribution: zulu + + - name: Install Homebrew + run: > + /bin/bash -c "$(curl -fsSL + https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + + - name: "Install autoconf, automake, libtool" + run: | + brew install autoconf automake libtool + + - name: "Install Mac ToolChain" + run: | + brew tap messense/macos-cross-toolchains + + - name: "Install Linux GNU for x86_64" + run: | + rm '/usr/local/bin/2to3' + rm '/usr/local/bin/python3' + rm '/usr/local/bin/python3.11' + rm '/usr/local/bin/2to3-3.11' + rm '/usr/local/bin/idle3' + rm '/usr/local/bin/idle3.11' + rm '/usr/local/bin/pydoc3' + rm '/usr/local/bin/pydoc3.11' + rm '/usr/local/bin/python3-config' + rm '/usr/local/bin/python3.11-config' + brew install --overwrite x86_64-unknown-linux-gnu + + - name: "Install Linux GNU for aarch64" + run: | + brew install --overwrite aarch64-unknown-linux-gnu + + - name: "Install Rust Targets" + run: | + rustup target add armv7-linux-androideabi + rustup target add i686-linux-android + rustup target add aarch64-linux-android + rustup target add x86_64-linux-android + rustup target add aarch64-apple-darwin + rustup target add x86_64-apple-darwin + rustup target add aarch64-unknown-linux-gnu + rustup target add x86_64-unknown-linux-gnu + + - name: "Install Rust Cargo NDK" + run: | + cargo install cargo-ndk + + - name: "Publish to GitHub Maven" + working-directory: ./anoncred-kmm + run: | + ./gradlew :anoncreds-kmp:publishAllPublicationsToGitHubPackagesRepository + build-release: - needs: build-swift-package + needs: [build-swift-package, build-kmp] name: Build Library strategy: @@ -153,4 +222,3 @@ jobs: tag: ${{github.event.inputs.tag}} file: library-${{ matrix.architecture }}.tar.gz asset_name: "library-${{ matrix.architecture }}-${{ github.sha }}.tar.gz" - diff --git a/.gitignore b/.gitignore index 9009006d..2246e24e 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,16 @@ Cargo.lock uniffi/targets uniffi/wrappers libanoncreds.xcframework.zip -.swiftpm \ No newline at end of file +.swiftpm + +.gradle +.idea +.DS_STORE +anoncred-wrapper-rust/build/generated +**/build/* +uniffi-kmm/Cargo.lock +uniffi-kmm/target +local.properties +anoncred-kmm/anoncred-wrapper-rust/src/* +**/.DS_Store +anoncred-kmm/anoncred-kmm/anoncred-wrapper-rust/src/* diff --git a/anoncred-kmm/.editorconfig b/anoncred-kmm/.editorconfig new file mode 100644 index 00000000..d0bbae6a --- /dev/null +++ b/anoncred-kmm/.editorconfig @@ -0,0 +1,10 @@ +root = true + +[*.{kt,kts}] +ktlint_code_style = ktlint_official +ktlint_standard_no_semi = disabled +ktlint_standard_trailing-comma-on-call-site = disabled +ktlint_standard_trailing-comma-on-declaration-site = disabled +ktlint_standard_function-signature = disabled +ktlint_standard_max-line-length = disabled +ktlint_standard_property-naming = disabled diff --git a/anoncred-kmm/.gitignore b/anoncred-kmm/.gitignore new file mode 100644 index 00000000..72301a27 --- /dev/null +++ b/anoncred-kmm/.gitignore @@ -0,0 +1,8 @@ +.gradle +.idea +.DS_STORE +anoncred-wrapper-rust/build/generated +**/build/* +uniffi-kmm/Cargo.lock +uniffi-kmm/target +local.properties \ No newline at end of file diff --git a/anoncred-kmm/.mega-linter.yml b/anoncred-kmm/.mega-linter.yml new file mode 100644 index 00000000..2ebf3bfc --- /dev/null +++ b/anoncred-kmm/.mega-linter.yml @@ -0,0 +1,28 @@ +--- +APPLY_FIXES: none +FILTER_REGEX_EXCLUDE: (karma.config.js|polyfill.js|timeout.js) +VALIDATE_ALL_CODEBASE: true +REPOSITORY_DEVSKIM_DISABLE_ERRORS: true +MARKDOWN_MARKDOWN_LINK_CHECK_FILTER_REGEX_EXCLUDE: (pull-request.yml|Deployment.yml|badge.svg) + +DISABLE: + - COPYPASTE + - SPELL + +DISABLE_LINTERS: + - MARKDOWN_MARKDOWN_LINK_CHECK + - C_CPPLINT + - CPP_CPPLINT + - BASH_SHELLCHECK + - BASH_EXEC + - REPOSITORY_TRIVY + - REPOSITORY_TRUFFLEHOG + - REPOSITORY_KICS + - REPOSITORY_CHECKOV + - RUST_CLIPPY + +DISABLE_ERRORS_LINTERS: + - REPOSITORY_TRUFFLEHOG + - REPOSITORY_TRIVY + - REPOSITORY_KICS + - RUST_CLIPPY diff --git a/anoncred-kmm/README.md b/anoncred-kmm/README.md new file mode 100644 index 00000000..f0d903f3 --- /dev/null +++ b/anoncred-kmm/README.md @@ -0,0 +1,53 @@ +# AnonCred-KMP + +[![Kotlin](https://img.shields.io/badge/kotlin-1.8.20-blue.svg?logo=kotlin)](http://kotlinlang.org) + +![apple-silicon](https://camo.githubusercontent.com/a92c841ffd377756a144d5723ff04ecec886953d40ac03baa738590514714921/687474703a2f2f696d672e736869656c64732e696f2f62616467652f737570706f72742d2535424170706c6553696c69636f6e2535442d3433424246462e7376673f7374796c653d666c6174) +![ios](https://camo.githubusercontent.com/1fec6f0d044c5e1d73656bfceed9a78fd4121b17e82a2705d2a47f6fd1f0e3e5/687474703a2f2f696d672e736869656c64732e696f2f62616467652f706c6174666f726d2d696f732d4344434443442e7376673f7374796c653d666c6174) +![jvm](https://camo.githubusercontent.com/700f5dcd442fd835875568c038ae5cd53518c80ae5a0cf12c7c5cf4743b5225b/687474703a2f2f696d672e736869656c64732e696f2f62616467652f706c6174666f726d2d6a766d2d4442343133442e7376673f7374796c653d666c6174) +![macos](https://camo.githubusercontent.com/1b8313498db244646b38a4480186ae2b25464e5e8d71a1920c52b2be5212b909/687474703a2f2f696d672e736869656c64732e696f2f62616467652f706c6174666f726d2d6d61636f732d3131313131312e7376673f7374796c653d666c6174) + +## Introduction + +This is a Kotlin MultiPlatform (KMP) wrapper of a rust library and reference implementation of the [Anoncreds V1.0 +specification](https://hyperledger.github.io/anoncreds-spec/). + + +The AnonCreds (Anonymous Credentials) specification is based on the open source verifiable credential implementation of AnonCreds that has been in use since 2017, initially as part of the Hyperledger Indy open source project and now in the Hyperledger AnonCreds project. The extensive use of AnonCreds around the world has made it a de facto standard for ZKP-based verifiable credentials, and this specification is the formalization of that implementation. + +## Library + +AnonCred-KMP exposes three main parts: [`issuer`](./anoncred-wrapper-rust/src/issuer/mod.rs), +[`prover`](./anoncred-wrapper-rust/src/prover/mod.rs) and +[`verifier`](./anoncred-wrapper-rust/src/verifier/mod.rs). + +The library provides wrapper for the following operations + +### Issuer + +- Create a [schema](https://hyperledger.github.io/anoncreds-spec/#schema-publisher-publish-schema-object) +- Create a [credential definition](https://hyperledger.github.io/anoncreds-spec/#issuer-create-and-publish-credential-definition-object) +- Create a [revocation registry definition](https://hyperledger.github.io/anoncreds-spec/#issuer-create-and-publish-revocation-registry-objects) +- Create a [revocation status list](https://hyperledger.github.io/anoncreds-spec/#publishing-the-initial-initial-revocation-status-list-object) +- Update a [revocation status list](https://hyperledger.github.io/anoncreds-spec/#publishing-the-initial-initial-revocation-status-list-object) +- Update a [revocation status list](https://hyperledger.github.io/anoncreds-spec/#publishing-the-initial-initial-revocation-status-list-object)'s timestamp +- Create a [credential offer](https://hyperledger.github.io/anoncreds-spec/#credential-offer) +- Create a [credential](https://hyperledger.github.io/anoncreds-spec/#issue-credential) + +### Prover / Holder + +- Create a [credential request](https://hyperledger.github.io/anoncreds-spec/#credential-request) +- Process an incoming [credential](https://hyperledger.github.io/anoncreds-spec/#receiving-a-credential) +- Create a [presentation](https://hyperledger.github.io/anoncreds-spec/#generate-presentation) +- Create, and update, a revocation state +- Create, and update, a revocation state with a witness + +### Verifier + +- [Verify a presentation](https://hyperledger.github.io/anoncreds-spec/#verify-presentation) +- generate a nonce + +## Requirement + +- Rust v1.72.0 +- KMP v1.8.20 diff --git a/anoncred-kmm/anoncred-wrapper-rust/.cargo/config.toml b/anoncred-kmm/anoncred-wrapper-rust/.cargo/config.toml new file mode 100644 index 00000000..d228cdcb --- /dev/null +++ b/anoncred-kmm/anoncred-wrapper-rust/.cargo/config.toml @@ -0,0 +1,12 @@ +[target.aarch64-unknown-linux-gnu] +linker = "aarch64-linux-gnu-gcc" +[target.x86_64-unknown-linux-gnu] +linker = "x86_64-linux-gnu-gcc" +[target.aarch64-linux-android] +linker = "aarch64-linux-android21-clang++" +[target.x86_64-linux-android] +linker = "x86_64-linux-android21-clang++" +[target.i686-linux-android] +linker = "i686-linux-android21-clang++" +[target.armv7-linux-androideabi] +linker = "armv7a-linux-androideabi21-clang++" diff --git a/anoncred-kmm/anoncred-wrapper-rust/.gitignore b/anoncred-kmm/anoncred-wrapper-rust/.gitignore new file mode 100644 index 00000000..33491a0e --- /dev/null +++ b/anoncred-kmm/anoncred-wrapper-rust/.gitignore @@ -0,0 +1,15 @@ +# Generated by Cargo +# will have compiled files and executables +.DS_Store +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb diff --git a/anoncred-kmm/anoncred-wrapper-rust/Cargo.toml b/anoncred-kmm/anoncred-wrapper-rust/Cargo.toml new file mode 100644 index 00000000..6d9647cd --- /dev/null +++ b/anoncred-kmm/anoncred-wrapper-rust/Cargo.toml @@ -0,0 +1,45 @@ +[package] +name = "anoncreds_wrapper" +version = "0.1.0" +authors = ['Ahmed Moussa '] +edition = "2021" +description = 'FFI wrapper for Anoncreds' + +[lib] +name = "anoncreds_wrapper" +path = "src/lib.rs" +crate-type = ['cdylib', 'staticlib'] + +[dependencies.anoncreds_core] +path = '../../' +package = "anoncreds" +features = ["vendored"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +uniffi = "0.23.0" +uniffi_macros = "0.23.0" +once_cell = "1.12" +thiserror = "1.0" +lazy_static = "1.3" +futures = { version = "0.3.17", features = ["thread-pool"] } +num_cpus = "1.8.0" +async-trait = '0.1' +serde = { version = "1.0", features = ["derive"] } +serde_json = '1.0' +ursa = { version = "0.3.7", default-features = false, features = ["cl_native", "serde"] } +swift-bridge = "0.1.51" +openssl = "0.10.45" +amcl = "0.2" + +[dev-dependencies.tokio] +version = '1.9' +features = ['rt', 'macros'] + +[build-dependencies] +uniffi = { version = "0.23.0", features=["build"] } +uniffi_bindgen = "0.23.0" +camino = "1.1.1" +swift-bridge-build = "0.1.51" +uniffi-kmm = { path = "../uniffi-kmm" } diff --git a/anoncred-kmm/anoncred-wrapper-rust/build.gradle.kts b/anoncred-kmm/anoncred-wrapper-rust/build.gradle.kts new file mode 100644 index 00000000..01d5bc94 --- /dev/null +++ b/anoncred-kmm/anoncred-wrapper-rust/build.gradle.kts @@ -0,0 +1,639 @@ +import org.gradle.internal.os.OperatingSystem +import java.io.ByteArrayOutputStream + +val os: OperatingSystem = OperatingSystem.current() + +@Throws(GradleException::class) +fun getNDKOSVariant(): String { + return if (os.isMacOsX) { + "darwin-x86_64" + } else if (os.isLinux) { + "linux-x86_64" + } else { + // It would be windows-x86_64 but we don't support Windows enviroment + throw GradleException("Unsported OS: ${os.name}") + } +} + +val minAndroidVersion: Int = 21 +val ANDROID_SDK = System.getenv("ANDROID_HOME") +val NDK = System.getenv("ANDROID_NDK_HOME") +val TOOLCHAIN = "$NDK/toolchains/llvm/prebuilt/${getNDKOSVariant()}" +val AR = "$TOOLCHAIN/bin/llvm-ar" +val CC = "$TOOLCHAIN/bin/aarch64-linux-android$minAndroidVersion-clang" +val CXX = "$TOOLCHAIN/bin/aarch64-linux-android$minAndroidVersion-clang++" +val LD = "$TOOLCHAIN/bin/ld" +val RANLIB = "$TOOLCHAIN/bin/llvm-ranlib" +val STRIP = "$TOOLCHAIN/bin/llvm-strip" + +/** + * Run CommandLine and return the output + * @param spec executions commands to run + * @return the result of these executions commands like they would show up in system terminal + */ +fun Project.execWithOutput(spec: ExecSpec.() -> Unit): String { + return ByteArrayOutputStream().use { outputStream -> + exec { + this.spec() + this.standardOutput = outputStream + } + outputStream.toString().trim() + } +} + +/* +fun Project.commandLineWithOutput(vararg commands: String) { + val pb = ProcessBuilder() +} +*/ + +// TODO: Replace commandLine with +// val pb = ProcessBuilder("brew", "--version") +// pb.start().waitFor() +// println(pb.output) + +/** + * Delete the generated `Target` folder that is being generated by Rust Cargo + */ +val rustClean by tasks.register("rustClean") { + group = "rust" + description = "Delete the generated files and folders that is being generated by this Gradle Script" + delete(projectDir.resolve("target")) + delete(rootDir.parentFile.resolve("target")) + delete(buildDir) +} + +tasks.matching { it.name == "clean" }.all { + dependsOn(rustClean) +} + +/** + * Building the original Rust anoncreds lib + * @note no longer needed. + */ +val buildAnoncredsLib by tasks.register("buildAnoncredsLib") { + group = "rust" + description = "Building the original Rust anoncreds lib" + onlyIf { + rootDir + .resolve("target") + .resolve("release") + .exists() + .not() + } + workingDir = rootDir.parentFile + inputs.files(rootDir.parentFile.resolve("Cargo.toml")) + outputs.files(fileTree(rootDir.parentFile.resolve("target"))) + commandLine("cargo", "build", "--release") +} + +// Compiling Tasks + +val buildAnonCredWrapperForMacOSArch64 by tasks.register("buildAnonCredWrapperForMacOSArch64") { + group = "rust-compiling" + description = "Build and compile AnonCred Wrapper for macOS aarch64" + inputs.files(fileTree(projectDir.resolve("src"))) + outputs.files(fileTree(projectDir.resolve("target").resolve("aarch64-apple-darwin"))) + commandLine("cargo", "build", "--release", "--target", "aarch64-apple-darwin", "--target-dir", "${projectDir.resolve("target")}") +} + +val buildAnonCredWrapperForMacOSX8664 by tasks.register("buildAnonCredWrapperForMacOSX86_64") { + group = "rust-compiling" + description = "Build and compile AnonCred Wrapper for macOS x86_64" + inputs.files(fileTree(projectDir.resolve("src"))) + outputs.files(fileTree(projectDir.resolve("target").resolve("x86_64-apple-darwin"))) + commandLine("cargo", "build", "--release", "--target", "x86_64-apple-darwin", "--target-dir", "${projectDir.resolve("target")}") +} + +val buildAnonCredWrapperForMacOSUniversal by tasks.register("buildAnonCredWrapperForMacOSUniversal") { + group = "rust-compiling" + description = "Build and compile AnonCred Wrapper for macOS" + dependsOn(buildAnonCredWrapperForMacOSArch64, buildAnonCredWrapperForMacOSX8664) +} + +val buildAnonCredWrapperForiOSArch64 by tasks.register("buildAnonCredWrapperForiOSArch64") { + group = "rust-compiling" + description = "Build and compile AnonCred Wrapper for iOS aarch64" + inputs.files(fileTree(projectDir.resolve("src"))) + outputs.files(fileTree(projectDir.resolve("target").resolve("aarch64-apple-ios"))) + commandLine("cargo", "build", "--release", "--target", "aarch64-apple-ios", "--target-dir", "${projectDir.resolve("target")}") +} + +val buildAnonCredWrapperForiOSArch64Sim by tasks.register("buildAnonCredWrapperForiOSArch64Sim") { + group = "rust-compiling" + description = "Build and compile AnonCred Wrapper for iOS aarch64 sim" + inputs.files(fileTree(projectDir.resolve("src"))) + outputs.files(fileTree(projectDir.resolve("target").resolve("aarch64-apple-ios-sim"))) + commandLine("cargo", "build", "--release", "--target", "aarch64-apple-ios-sim", "--target-dir", "${projectDir.resolve("target")}") +} + +val buildAnonCredWrapperForiOSX8664 by tasks.register("buildAnonCredWrapperForiOSX86_64") { + group = "rust-compiling" + description = "Build and compile AnonCred Wrapper for iOS x86_64" + inputs.files(fileTree(projectDir.resolve("src"))) + outputs.files(fileTree(projectDir.resolve("target").resolve("x86_64-apple-ios"))) + commandLine("cargo", "build", "--release", "--target", "x86_64-apple-ios", "--target-dir", "${projectDir.resolve("target")}") +} + +val buildAnonCredWrapperForiOSUniversal by tasks.register("buildAnonCredWrapperForiOSUniversal") { + group = "rust-compiling" + description = "Build and compile AnonCred Wrapper for iOS" + dependsOn(buildAnonCredWrapperForiOSArch64, buildAnonCredWrapperForiOSArch64Sim, buildAnonCredWrapperForiOSX8664) +} + +val buildAnonCredWrapperForLinuxX8664 by tasks.register("buildAnonCredWrapperForLinuxX86_64") { + group = "rust-compiling" + description = "Build and compile AnonCred Wrapper for Linux x86_64" + inputs.files(fileTree(projectDir.resolve("src"))) + outputs.files(fileTree(projectDir.resolve("target").resolve("x86_64-unknown-linux-gnu"))) + commandLine("cargo", "build", "--release", "--target", "x86_64-unknown-linux-gnu", "--target-dir", "${projectDir.resolve("target")}") +} + +val buildAnonCredWrapperForLinuxArch64 by tasks.register("buildAnonCredWrapperForLinuxArch64") { + group = "rust-compiling" + description = "Build and compile AnonCred Wrapper for Linux aarch64" + inputs.files(fileTree(projectDir.resolve("src"))) + outputs.files(fileTree(projectDir.resolve("target").resolve("aarch64-unknown-linux-gnu"))) + commandLine("cargo", "build", "--release", "--target", "aarch64-unknown-linux-gnu", "--target-dir", "${projectDir.resolve("target")}") +} + +val buildAnonCredWrapperForLinuxUniversal by tasks.register("buildAnonCredWrapperForLinuxUniversal") { + group = "rust-compiling" + description = "Build and compile AnonCred Wrapper for Linux" + dependsOn(buildAnonCredWrapperForLinuxArch64, buildAnonCredWrapperForLinuxX8664) +} + +val buildAnonCredWrapperForAndroidX8664 by tasks.register("buildAnonCredWrapperForAndroidX86_64") { + group = "rust-compiling" + description = "Build and compile AnonCred Wrapper for Android X86_64" + val localEnv = this.environment + localEnv += mapOf( + "PATH" to "${environment["PATH"]}:$TOOLCHAIN:$AR:$CC:$CXX:$LD:$RANLIB:$STRIP:$TOOLCHAIN/bin/", + "ANDROID_SDK" to ANDROID_SDK, + "NDK" to NDK, + "TOOLCHAIN" to TOOLCHAIN, + "AR" to AR, + "CC" to CC, + "CXX" to CXX, + "LD" to LD, + "RANLIB" to RANLIB, + "STRIP" to STRIP + ) + this.environment = localEnv + inputs.files(fileTree(projectDir.resolve("src"))) + outputs.files(fileTree(projectDir.resolve("target").resolve("x86_64-linux-android"))) + commandLine("cargo", "ndk", "build", "--release", "--target", "x86_64-linux-android", "--target-dir", "${projectDir.resolve("target")}") +} + +val buildAnonCredWrapperForAndroidArch64 by tasks.register("buildAnonCredWrapperForAndroidArch64") { + group = "rust-compiling" + description = "Build and compile AnonCred Wrapper for Android arch64" + val localEnv = this.environment + localEnv += mapOf( + "PATH" to "${environment["PATH"]}:$TOOLCHAIN:$AR:$CC:$CXX:$LD:$RANLIB:$STRIP:$TOOLCHAIN/bin/", + "ANDROID_SDK" to ANDROID_SDK, + "NDK" to NDK, + "TOOLCHAIN" to TOOLCHAIN, + "AR" to AR, + "CC" to CC, + "CXX" to CXX, + "LD" to LD, + "RANLIB" to RANLIB, + "STRIP" to STRIP + ) + this.environment = localEnv + println("Show all environment variables") + for (entry in this.environment.entries) { + println("${entry.key} : ${entry.value}") + } + inputs.files(fileTree(projectDir.resolve("src"))) + outputs.files(fileTree(projectDir.resolve("target").resolve("aarch64-linux-android"))) + commandLine("cargo", "ndk", "build", "--release", "--target", "aarch64-linux-android", "--target-dir", "${projectDir.resolve("target")}") +} + +val buildAnonCredWrapperForAndroidI686 by tasks.register("buildAnonCredWrapperForAndroidI686") { + group = "rust-compiling" + description = "Build and compile AnonCred Wrapper for Android I686" + val localEnv = this.environment + localEnv += mapOf( + "PATH" to "${environment["PATH"]}:$TOOLCHAIN:$AR:$CC:$CXX:$LD:$RANLIB:$STRIP:$TOOLCHAIN/bin/", + "ANDROID_SDK" to ANDROID_SDK, + "NDK" to NDK, + "TOOLCHAIN" to TOOLCHAIN, + "AR" to AR, + "CC" to CC, + "CXX" to CXX, + "LD" to LD, + "RANLIB" to RANLIB, + "STRIP" to STRIP + ) + this.environment = localEnv + inputs.files(fileTree(projectDir.resolve("src"))) + outputs.files(fileTree(projectDir.resolve("target").resolve("i686-linux-android"))) + commandLine("cargo", "ndk", "build", "--release", "--target", "i686-linux-android", "--target-dir", "${projectDir.resolve("target")}") +} + +val buildAnonCredWrapperForAndroidArmv7a by tasks.register("buildAnonCredWrapperForAndroidArmv7a") { + group = "rust-compiling" + description = "Build and compile AnonCred Wrapper for Android Armv7a" + val localEnv = this.environment + localEnv += mapOf( + "PATH" to "${environment["PATH"]}:$TOOLCHAIN:$AR:$CC:$CXX:$LD:$RANLIB:$STRIP:$TOOLCHAIN/bin/", + "ANDROID_SDK" to ANDROID_SDK, + "NDK" to NDK, + "TOOLCHAIN" to TOOLCHAIN, + "AR" to AR, + "CC" to CC, + "CXX" to CXX, + "LD" to LD, + "RANLIB" to RANLIB, + "STRIP" to STRIP + ) + this.environment = localEnv + inputs.files(fileTree(projectDir.resolve("src"))) + outputs.files(fileTree(projectDir.resolve("target").resolve("armv7-linux-androideabi"))) + commandLine("cargo", "ndk", "build", "--release", "--target", "armv7-linux-androideabi", "--target-dir", "${projectDir.resolve("target")}") +} + +val buildAnonCredWrapperForAndroidUniversal by tasks.register("buildAnonCredWrapperForAndroidUniversal") { + group = "rust-compiling" + description = "Build and compile AnonCred Wrapper for Android" + dependsOn(buildAnonCredWrapperForAndroidX8664, buildAnonCredWrapperForAndroidArch64, buildAnonCredWrapperForAndroidI686, buildAnonCredWrapperForAndroidArmv7a) +} + +val buildAnonCredWrapper by tasks.register("buildAnonCredWrapper") { + group = "rust-compiling" + description = "Build and compile AnonCred Wrapper" + dependsOn(buildAnonCredWrapperForMacOSUniversal, buildAnonCredWrapperForLinuxUniversal, buildAnonCredWrapperForAndroidUniversal) // buildAnonCredWrapperForiOSUniversal +} + +// Copy Bindings Tasks + +val copyGeneratedBinaryForMacOSX8664 by tasks.register("copyGeneratedBinaryForMacOSX86_64") { + group = "rust-compiling" + description = "Copy all generated macOS x86_64 binaries to generated resources folder" + duplicatesStrategy = DuplicatesStrategy.INCLUDE + include("*.so", "*.a", "*.d", "*.dylib") + from(projectDir.resolve("target").resolve("x86_64-apple-darwin").resolve("release")) + into(rootDir.resolve("anoncreds-kmp").resolve("build").resolve("generatedResources").resolve("jvm").resolve("main").resolve("darwin-x86-64")) + dependsOn(buildAnonCredWrapperForMacOSX8664) +} + +val copyGeneratedBinaryForMacOSArch64 by tasks.register("copyGeneratedBinaryForMacOSArch64") { + group = "rust-compiling" + description = "Copy all generated macOS aarch64 binaries to generated resources folder" + duplicatesStrategy = DuplicatesStrategy.INCLUDE + include("*.so", "*.a", "*.d", "*.dylib") + from(projectDir.resolve("target").resolve("aarch64-apple-darwin").resolve("release")) + into(rootDir.resolve("anoncreds-kmp").resolve("build").resolve("generatedResources").resolve("jvm").resolve("main").resolve("darwin-aarch64")) + dependsOn(buildAnonCredWrapperForMacOSArch64) +} + +val copyGeneratedBinaryForMacOS by tasks.register("copyGeneratedBinaryForMacOS") { + group = "rust-compiling" + description = "Copy all generated macOS binaries to generated resources folder" + dependsOn(copyGeneratedBinaryForMacOSX8664, copyGeneratedBinaryForMacOSArch64) +} + +val copyGeneratedBinaryForLinuxX8664 by tasks.register("copyGeneratedBinaryForLinuxX86_64") { + group = "rust-compiling" + description = "Copy all generated Linux x86_64 binaries to generated resources folder" + duplicatesStrategy = DuplicatesStrategy.INCLUDE + include("*.so", "*.a", "*.d", "*.dylib") + from(projectDir.resolve("target").resolve("x86_64-unknown-linux-gnu").resolve("release")) + into(rootDir.resolve("anoncreds-kmp").resolve("build").resolve("generatedResources").resolve("jvm").resolve("main").resolve("linux-x86-64")) + dependsOn(buildAnonCredWrapperForLinuxX8664) +} + +val copyGeneratedBinaryForLinuxArch64 by tasks.register("copyGeneratedBinaryForLinuxArch64") { + group = "rust-compiling" + description = "Copy all generated Linux aarch64 binaries to generated resources folder" + duplicatesStrategy = DuplicatesStrategy.INCLUDE + include("*.so", "*.a", "*.d", "*.dylib") + from(projectDir.resolve("target").resolve("aarch64-unknown-linux-gnu").resolve("release")) + into(rootDir.resolve("anoncreds-kmp").resolve("build").resolve("generatedResources").resolve("jvm").resolve("main").resolve("linux-aarch64")) + dependsOn(buildAnonCredWrapperForLinuxArch64) +} + +val copyGeneratedBinaryForLinux by tasks.register("copyGeneratedBinaryForLinux") { + group = "rust-compiling" + description = "Copy all generated Linux binaries to generated resources folder" + dependsOn(copyGeneratedBinaryForLinuxX8664, copyGeneratedBinaryForLinuxArch64) +} + +val copyGeneratedBinaryForiOS by tasks.register("copyGeneratedBinaryForiOS") { + group = "rust-compiling" + description = "Copy all generated iOS binaries to generated resources folder" + duplicatesStrategy = DuplicatesStrategy.INCLUDE + include("*.so", "*.a", "*.d", "*.dylib") + from(projectDir.resolve("target").resolve("ios-universal").resolve("release")) + into(rootDir.resolve("anoncreds-kmp").resolve("build").resolve("processedResources").resolve("binaries").resolve("ios")) + dependsOn(buildAnonCredWrapperForiOSUniversal) +} + +val copyGeneratedBinaryForAndroidX8664 by tasks.register("copyGeneratedBinaryForAndroidX86_64") { + group = "rust-compiling" + description = "Copy all generated Android X86_64 binaries to generated resources folder" + duplicatesStrategy = DuplicatesStrategy.INCLUDE + include("*.so", "*.a", "*.d", "*.dylib") + from(projectDir.resolve("target").resolve("x86_64-linux-android").resolve("release")) + into(rootDir.resolve("anoncreds-kmp").resolve("build").resolve("generatedResources").resolve("android").resolve("main").resolve("jniLibs").resolve("x86-64")) + dependsOn(buildAnonCredWrapperForAndroidX8664) +} + +val copyGeneratedBinaryForAndroidArch64 by tasks.register("copyGeneratedBinaryForAndroidArch64") { + group = "rust-compiling" + description = "Copy all generated Android aarch64 binaries to generated resources folder" + duplicatesStrategy = DuplicatesStrategy.INCLUDE + include("*.so", "*.a", "*.d", "*.dylib") + from(projectDir.resolve("target").resolve("aarch64-linux-android").resolve("release")) + into(rootDir.resolve("anoncreds-kmp").resolve("build").resolve("generatedResources").resolve("android").resolve("main").resolve("jniLibs").resolve("arm64-v8a")) + dependsOn(buildAnonCredWrapperForAndroidArch64) +} + +val copyGeneratedBinaryForAndroidI686 by tasks.register("copyGeneratedBinaryForAndroidI686") { + group = "rust-compiling" + description = "Copy all generated Android i686 binaries to generated resources folder" + duplicatesStrategy = DuplicatesStrategy.INCLUDE + include("*.so", "*.a", "*.d", "*.dylib") + from(projectDir.resolve("target").resolve("i686-linux-android").resolve("release")) + into(rootDir.resolve("anoncreds-kmp").resolve("build").resolve("generatedResources").resolve("android").resolve("main").resolve("jniLibs").resolve("x86")) + dependsOn(buildAnonCredWrapperForAndroidI686) +} + +val copyGeneratedBinaryForAndroidArmv7a by tasks.register("copyGeneratedBinaryForAndroidArmv7a") { + group = "rust-compiling" + description = "Copy all generated Android armv7a binaries to generated resources folder" + duplicatesStrategy = DuplicatesStrategy.INCLUDE + include("*.so", "*.a", "*.d", "*.dylib") + from(projectDir.resolve("target").resolve("armv7-linux-androideabi").resolve("release")) + into(rootDir.resolve("anoncreds-kmp").resolve("build").resolve("generatedResources").resolve("android").resolve("main").resolve("jniLibs").resolve("armeabi-v7a")) + dependsOn(buildAnonCredWrapperForAndroidArmv7a) +} + +val copyGeneratedBinaryForAndroid by tasks.register("copyGeneratedBinaryForAndroid") { + group = "rust-compiling" + description = "Copy all generated Android binaries to generated resources folder" + dependsOn(copyGeneratedBinaryForAndroidArch64, copyGeneratedBinaryForAndroidX8664, copyGeneratedBinaryForAndroidI686, copyGeneratedBinaryForAndroidArmv7a) +} + +val copyGeneratedBinariesToCorrectLocation by tasks.register("copyGeneratedBinariesToCorrectLocation") { + group = "rust-compiling" + description = "Copy all generated binaries to generated resources folder" + dependsOn(copyGeneratedBinaryForMacOS, copyGeneratedBinaryForLinux, copyGeneratedBinaryForAndroid) // copyGeneratedBinaryForiOS +} + +/** + * Copy generated bindings to the `anoncreds-kmm` module + */ +val copyBindings by tasks.register("copyBindings") { + group = "rust-compiling" + description = "Copy generated bindings to the `anoncreds-kmm` module" + from(buildDir.resolve("generated")) + into(rootDir.resolve("anoncreds-kmp").resolve("build").resolve("generated")) + dependsOn(copyGeneratedBinariesToCorrectLocation) +} + +/** + * Copy generated dylib to correct location + */ +val copyAnoncredsBinariesToProcessedResources by tasks.register("copyAnoncredsBinariesToProcessedResources") { + group = "rust-compiling" + description = "Copy generated AnonCreds binaries to generated resources folder" + include("*.so", "*.a", "*.d", "*.dylib") + from(rootDir.parentFile.resolve("target").resolve("release")) + into(rootDir.resolve("anoncreds-kmp").resolve("build").resolve("generatedResources").resolve("jvm").resolve("main")) + dependsOn(buildAnoncredsLib) +} + +/** + * Generate rust documentation + */ +val generateDocumentation by tasks.register("rustDoc") { + group = "documentation" + description = "Generate rust documentation" + commandLine("cargo", "doc") + dependsOn(buildAnonCredWrapper) +} + +// Verification Tasks + +/** + * Verify that all used rust targets are installed + */ +val verifyCrateTargets by tasks.register("verifyCrateTargets") { + group = "install" + description = "Verify that all used rust targets are installed" + val targets = if (os.isMacOsX) { + listOf( + "armv7-linux-androideabi", + "i686-linux-android", + "aarch64-linux-android", + "x86_64-linux-android", + "aarch64-apple-darwin", + "x86_64-apple-darwin", + "aarch64-unknown-linux-gnu", + "x86_64-unknown-linux-gnu" + ) + } else if (os.isLinux) { + listOf( + "armv7-linux-androideabi", + "i686-linux-android", + "aarch64-linux-android", + "x86_64-linux-android", + "aarch64-unknown-linux-gnu", + "x86_64-unknown-linux-gnu" + ) + } else { + throw GradleException("Unsupported OS ${os.name}") + } + + // Get all available targets + val availableTargetsString = execWithOutput { + commandLine("rustup", "target", "list") + } + var output = "" + for (target in targets) { + output += if (availableTargetsString.contains("$target (installed)").not()) { + // Install target if not installed + execWithOutput { + commandLine("rustup", "target", "add", target) + } + "Installing target: $target\n" + } else { + "Target $target already installed\n" + } + } + commandLine("echo", output) +} + +/** + * Verify that Rust is installed and if not, install it + */ +val verifyRustInstalled by tasks.register("verifyRustInstalled") { + group = "install" + description = "Verify that Rust is installed and if not, install it" + if (os.isLinux || os.isMacOsX) { + val output = execWithOutput { + commandLine("rustup", "--version") + } + if (output.contains("command not found")) { + // Install Rust + execWithOutput { + commandLine("curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh") + } + execWithOutput { + commandLine("echo", "Installed Rust") + } + } else { + execWithOutput { + commandLine("echo", "Rust is already installed") + } + } + } else { + throw GradleException("Unsupported OS ${os.name}") + } +} + +/** + * Verify that all needed tools for Rust are installed correctly + */ +val verifyRust by tasks.register("verifyRust") { + group = "install" + description = "Verify that all needed tools for Rust are installed correctly" + doFirst { + verifyRustInstalled + } + dependsOn(verifyRustInstalled, verifyCrateTargets) +} + +// Installation Tasks + +val installCargoNDK by tasks.register("installCargoNDK") { + group = "install" + description = "Install Cargo-NDK" + commandLine("cargo", "install", "cargo-ndk") +} + +val installHomeBrew by tasks.register("installHomeBrew") { + group = "install" + description = "Install HomeBrew" + onlyIf { + os.isMacOsX + } + val output = execWithOutput { + commandLine("brew", "--version") + } + if (output.contains("command not found").not()) { + execWithOutput { + commandLine("echo", "HomeBrew already installed") + } + } else { + execWithOutput { + commandLine("/bin/bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"") + } + } +} + +val updateLinuxApt by tasks.register("updateLinuxApt") { + group = "install" + description = "Update Linux Apt" + onlyIf { + os.isLinux + } + commandLine("apt-get", "update", "-y") +} + +val installMacToolChain by tasks.register("installMacToolChain") { + group = "install" + description = "Install Mac ToolChain" + onlyIf { + os.isMacOsX + } + commandLine("brew", "tap", "messense/macos-cross-toolchains") +} + +val installx8664LinuxGNU by tasks.register("installx86_64LinuxGNU") { + group = "install" + description = "Install Linux GNU for x86_64" + if (os.isMacOsX) { + commandLine("brew", "install", "x86_64-unknown-linux-gnu") + } else if (os.isLinux) { + commandLine("apt-get", "install", "-y", "gcc-x86-64-linux-gnu") + } else { + throw GradleException("${os.name} is not supported. Need to switch to macOS or Linux") + } +} + +val installAarch64LinuxGNU by tasks.register("installAarch64LinuxGNU") { + group = "install" + description = "Install Linux GNU for aarch64" + if (os.isMacOsX) { + commandLine("brew", "install", "aarch64-unknown-linux-gnu") + } else if (os.isLinux) { + commandLine("apt-get", "install", "-y", "gcc-4.8-aarch64-linux-gnu") + } else { + throw GradleException("${os.name} is not supported. Need to switch to macOS or Linux") + } +} + +/** + * Install all required compiler tools that is needed for this project + * - Homebrew for macOS + * - MacToolChains for macOS + * - Update APT for Linux + * - x86_64-linux-gnu for macOS & Linux + * - arch64-linux-gnu for macOS & Linux + */ +val requiredInstallation by tasks.register("RequiredInstallation") { + group = "install" + description = """ + Install all required compiler tools that is needed for this project + - Homebrew for macOS + - MacToolChains for macOS + - Update APT for Linux + - x86_64-linux-gnu for macOS & Linux + - arch64-linux-gnu for macOS & Linux + """.trimIndent() + dependsOn(installHomeBrew, updateLinuxApt, installMacToolChain, installx8664LinuxGNU, installAarch64LinuxGNU, installCargoNDK) +} + +val deleteRustSrcFiles by tasks.register("deleteRustSrcFiles") { + group = "rust" + delete( + fileTree(rootDir.resolve("anoncred-wrapper-rust").resolve("src")) + ) +} + +val moveRustSrcFiles by tasks.register("moveRustSrcFiles") { + group = "rust" + description = "Move rust src files from main rust folder to our sub module folder to generate all needed code" + duplicatesStrategy = DuplicatesStrategy.INCLUDE + from( + fileTree(rootDir.parentFile.resolve("uniffi").resolve("src")) + ) + into(rootDir.resolve("anoncred-wrapper-rust").resolve("src")) + dependsOn(deleteRustSrcFiles) +} + +/** + * The main build Rust lib task. It will do the following: + * - Build the lib + * - Generate the bindings + * - Move the generated bindings to the lib module to be used in Kotlin KMM + */ +val buildRust by tasks.register("buildRust") { + group = "rust" + description = """ + The main build Rust lib task. It will do the following: + - Move Rust src files + - Build the lib + - Generate the bindings + - Move the generated bindings to the lib module to be used in Kotlin KMM + """.trimIndent() + doFirst { + moveRustSrcFiles + } + mustRunAfter(moveRustSrcFiles) + dependsOn(moveRustSrcFiles, requiredInstallation, verifyRust, copyBindings, copyAnoncredsBinariesToProcessedResources) +} diff --git a/anoncred-kmm/anoncred-wrapper-rust/build.rs b/anoncred-kmm/anoncred-wrapper-rust/build.rs new file mode 100644 index 00000000..fe151f1e --- /dev/null +++ b/anoncred-kmm/anoncred-wrapper-rust/build.rs @@ -0,0 +1,14 @@ +use camino::Utf8Path; +use uniffi_kmm::KotlinBindingGenerator; + +// Script responsible for generating a scaffold using UniFFI +fn main() { + let out_dir = Utf8Path::new("build/generated"); + uniffi::generate_scaffolding("./src/anoncreds.udl").unwrap(); + uniffi_bindgen::generate_external_bindings( + KotlinBindingGenerator {}, + "./src/anoncreds.udl", + None::<&Utf8Path>, + Some(out_dir), + ).unwrap(); +} diff --git a/anoncred-kmm/anoncreds-kmp/build.gradle.kts b/anoncred-kmm/anoncreds-kmp/build.gradle.kts new file mode 100644 index 00000000..704e74c9 --- /dev/null +++ b/anoncred-kmm/anoncreds-kmp/build.gradle.kts @@ -0,0 +1,295 @@ +import org.gradle.internal.os.OperatingSystem +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeCompilation +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +val os: OperatingSystem = OperatingSystem.current() + +plugins { + kotlin("multiplatform") + id("com.android.library") + id("maven-publish") +} + +apply(plugin = "kotlinx-atomicfu") +version = "1.1.0" +group = "io.iohk.atala.prism.anoncredskmp" + +fun KotlinNativeCompilation.anoncredsCinterops(type: String) { + cinterops { + val anoncreds_wrapper by creating { + val crate = this.name + packageName("$crate.cinterop") + header( + buildDir + .resolve("generated") + .resolve("nativeInterop") + .resolve("cinterop") + .resolve("headers") + .resolve(crate) + .resolve("$crate.h") + ) + tasks.named(interopProcessingTaskName) { + dependsOn(":anoncred-wrapper-rust:buildRust") + } + when (type) { + "macosX64" -> { + extraOpts( + "-libraryPath", + rootDir + .resolve("anoncred-wrapper-rust") + .resolve("target") + .resolve("x86_64-apple-darwin") + .resolve("release") + .absolutePath + ) + } + "macosArm64" -> { + extraOpts( + "-libraryPath", + rootDir + .resolve("anoncred-wrapper-rust") + .resolve("target") + .resolve("aarch64-apple-darwin") + .resolve("release") + .absolutePath + ) + } +// "ios" -> { +// extraOpts( +// "-libraryPath", +// rootDir +// .resolve("anoncred-wrapper-rust") +// .resolve("target") +// .resolve("ios-universal") +// .resolve("release") +// .absolutePath +// ) +// } + else -> { + throw GradleException("Unsupported linking") + } + } + } + } +} + +kotlin { + jvm { + compilations.all { + kotlinOptions.jvmTarget = "11" + } + testRuns["test"].executionTask.configure { + useJUnitPlatform() + } + } + android { + publishAllLibraryVariants() + } + +// if (os.isMacOsX) { +// macosX64("native") { +// compilations.getByName("main") { +// this.anoncredsCinterops("macosX64") +// } +// } +// if (System.getProperty("os.arch") != "x86_64") { +// macosArm64 { +// compilations.getByName("main") { +// this.anoncredsCinterops("macosArm64") +// } +// } +// } +// } + +/* + val crateTargetLibDir = rootDir.resolve("anoncred-wrapper-rust").resolve("target").resolve("debug") + val hostOs = System.getProperty("os.name") + val isMingwX64 = hostOs.startsWith("Windows") + val nativeTarget = when { + hostOs == "Mac OS X" -> { + if (System.getProperty("os.arch") != "x86_64") { + macosArm64("native") + } else { + macosX64("native") + } + } + hostOs == "Linux" -> linuxArm64("native") + isMingwX64 -> mingwX64("native") + else -> throw GradleException("Host OS is not supported in Kotlin/Native.") + } + nativeTarget.apply { + compilations.getByName("main") { + println("nativeTarget-only-main") + cinterops { + val anoncreds_wrapper by creating { + val crate = this.name + packageName("$crate.cinterop") + header( + generatedDir.resolve("nativeInterop").resolve("cinterop").resolve("headers") + .resolve(crate).resolve("$crate.h") + ) + tasks.named(interopProcessingTaskName) { + dependsOn(":anoncred-wrapper-rust:buildRust") + } + extraOpts("-libraryPath", crateTargetLibDir.absolutePath) + } + } + } + } +*/ + + sourceSets { + val commonMain by getting { + val generatedDir = buildDir + .resolve("generated") + .resolve("commonMain") + .resolve("kotlin") + kotlin.srcDir(generatedDir) + dependencies { + implementation("com.squareup.okio:okio:3.4.0") + implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.1") + } + } + val commonTest by getting { + dependencies { + implementation(kotlin("test")) + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3") + } + } + val jvmMain by getting { + val generatedDir = buildDir + .resolve("generated") + .resolve("jvmMain") + .resolve("kotlin") + kotlin.srcDir(generatedDir) + val generatedResources = buildDir + .resolve("generatedResources") + .resolve("jvm") + .resolve("main") + resources.srcDir(generatedResources) + dependencies { + implementation("net.java.dev.jna:jna:5.13.0") + } + } + val jvmTest by getting + val androidMain by getting { + val generatedDir = buildDir + .resolve("generated") + .resolve("androidMain") + .resolve("kotlin") + kotlin.srcDir(generatedDir) + val generatedResources = buildDir + .resolve("generatedResources") + .resolve("android") + .resolve("main") + .resolve("jniLibs") + resources.srcDir(generatedResources) + dependencies { + implementation("net.java.dev.jna:jna:5.13.0@aar") + } + } + val androidUnitTest by getting { + dependencies { + implementation("junit:junit:4.13.2") + } + } +// if (os.isMacOsX) { +// val nativeMain by getting { // aka "macosX64" +// val generatedDir = buildDir +// .resolve("generated") +// .resolve("nativeMain") +// .resolve("kotlin") +// kotlin.srcDir(generatedDir) +// } +// val nativeTest by getting +// if (System.getProperty("os.arch") != "x86_64") { +// val macosArm64Main by getting { +// dependsOn(nativeMain) +// } +// } +// } + all { + languageSettings { + optIn("kotlin.RequiresOptIn") + optIn("kotlinx.cinterop.ExperimentalForeignApi") + } + } + } +} + +/** + * Delete the generated `Target` folder that is being generated by Rust Cargo + */ +val rustClean by tasks.register("rustClean") { + group = "rust" + delete(projectDir.resolve("target")) + dependsOn("clean") +} + +publishing { + repositories { + maven { + this.name = "GitHubPackages" + this.url = uri("https://maven.pkg.github.com/input-output-hk/anoncreds-rs/") + credentials { + this.username = System.getenv("ATALA_GITHUB_ACTOR") + this.password = System.getenv("ATALA_GITHUB_TOKEN") + } + } + } +} + +android { + ndkVersion = "26.0.10792818" + compileSdk = 33 + namespace = "anoncredskmp" + sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") + + sourceSets["main"].jniLibs { + setSrcDirs( + listOf( + buildDir + .resolve("generatedResources") + .resolve("android") + .resolve("main") + .resolve("jniLibs") + ) + ) + } + defaultConfig { + minSdk = 21 + targetSdk = 32 + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + /** + * Because Software Components will not be created automatically for Maven publishing from + * Android Gradle Plugin 8.0. To opt-in to the future behavior, set the Gradle property android. + * disableAutomaticComponentCreation=true in the `gradle.properties` file or use the new + * publishing DSL. + */ + publishing { + multipleVariants { + withSourcesJar() + withJavadocJar() + allVariants() + } + } +} + +afterEvaluate { + tasks.withType { + dependsOn(":anoncred-wrapper-rust:buildRust") + } + tasks.withType { + dependsOn(":anoncred-wrapper-rust:buildRust") + } + tasks.named("lintAnalyzeDebug") { + this.enabled = false + } + tasks.named("lintAnalyzeRelease") { + this.enabled = false + } +} diff --git a/anoncred-kmm/anoncreds-kmp/src/androidMain/AndroidManifest.xml b/anoncred-kmm/anoncreds-kmp/src/androidMain/AndroidManifest.xml new file mode 100644 index 00000000..9f3363d8 --- /dev/null +++ b/anoncred-kmm/anoncreds-kmp/src/androidMain/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/anoncred-kmm/anoncreds-kmp/src/androidUnitTest/kotlin/anoncred/wrapper/AndroidIgnore.kt b/anoncred-kmm/anoncreds-kmp/src/androidUnitTest/kotlin/anoncred/wrapper/AndroidIgnore.kt new file mode 100644 index 00000000..aab81b4c --- /dev/null +++ b/anoncred-kmm/anoncreds-kmp/src/androidUnitTest/kotlin/anoncred/wrapper/AndroidIgnore.kt @@ -0,0 +1,8 @@ +package anoncred.wrapper + +import org.junit.Ignore + +/** + * Ignore Unit tests on Android Platform + */ +actual typealias AndroidIgnore = Ignore diff --git a/anoncred-kmm/anoncreds-kmp/src/commonMain/kotlin/anoncred/wrapper/Platform.kt b/anoncred-kmm/anoncreds-kmp/src/commonMain/kotlin/anoncred/wrapper/Platform.kt new file mode 100644 index 00000000..faafda69 --- /dev/null +++ b/anoncred-kmm/anoncreds-kmp/src/commonMain/kotlin/anoncred/wrapper/Platform.kt @@ -0,0 +1,10 @@ +package anoncred.wrapper + +enum class Platform { + Linux, + Macos, + Windows, + Ios, + Android, + Js +} diff --git a/anoncred-kmm/anoncreds-kmp/src/commonTest/kotlin/anoncred/wrapper/AndroidIgnore.kt b/anoncred-kmm/anoncreds-kmp/src/commonTest/kotlin/anoncred/wrapper/AndroidIgnore.kt new file mode 100644 index 00000000..79632bdc --- /dev/null +++ b/anoncred-kmm/anoncreds-kmp/src/commonTest/kotlin/anoncred/wrapper/AndroidIgnore.kt @@ -0,0 +1,6 @@ +package anoncred.wrapper + +/** + * Ignore Unit tests on Android Platform + */ +expect annotation class AndroidIgnore() diff --git a/anoncred-kmm/anoncreds-kmp/src/commonTest/kotlin/anoncred/wrapper/IssuerTests.kt b/anoncred-kmm/anoncreds-kmp/src/commonTest/kotlin/anoncred/wrapper/IssuerTests.kt new file mode 100644 index 00000000..86a22d1e --- /dev/null +++ b/anoncred-kmm/anoncreds-kmp/src/commonTest/kotlin/anoncred/wrapper/IssuerTests.kt @@ -0,0 +1,226 @@ +package anoncred.wrapper + +import anoncreds_wrapper.AttributeValues +import anoncreds_wrapper.CredentialDefinitionConfig +import anoncreds_wrapper.Issuer +import anoncreds_wrapper.Nonce +import anoncreds_wrapper.Prover +import anoncreds_wrapper.RegistryType +import anoncreds_wrapper.Schema +import anoncreds_wrapper.SignatureType +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +@AndroidIgnore +class IssuerTests { + @Test + fun test_Issuer_createSchema() { + val expectedSchema = Schema("Moussa", "1.0", listOf("name", "age"), "sample:uri") + val scheme: Schema = Issuer().createSchema("Moussa", "1.0", "sample:uri", listOf("name", "age")) + assertEquals(expectedSchema, scheme, "scheme not equal") + assertEquals(expectedSchema.name, scheme.name, "name not correct") + assertEquals(expectedSchema.version, scheme.version, "version not correct") + assertEquals(expectedSchema.issuerId, scheme.issuerId, "issuerId not correct") + assertEquals(expectedSchema.attrNames.size, scheme.attrNames.size, "attrNames size is not correct") + expectedSchema.attrNames.forEach { + assertTrue(scheme.attrNames.contains(it)) + } + } + + @Test + fun test_Issuer_createCredentialDefinition() { + val issuer = Issuer() + val attributeNames = listOf("name", "age") + val schema: Schema = issuer.createSchema("Moussa", "1.0", "sample:uri", attributeNames) + val cred = issuer.createCredentialDefinition( + "did:web:xyz/resource/schema", + schema, + "did:web:xyz", + "default-tag", + SignatureType.CL, + CredentialDefinitionConfig(true) + ) + println(cred.credentialDefinition.getJson()) + println(cred.credentialDefinitionPrivate.getJson()) + println(cred.credentialKeyCorrectnessProof.getJson()) + assertTrue(true) + } + + @Test + fun test_Issuer_createRevocationRegistryDef() { + val issuer = Issuer() + val attributeNames = listOf("name", "age") + val schema = Schema("Moussa", "1.0", attributeNames, "sample:uri") + val cred = issuer.createCredentialDefinition( + "did:web:xyz/resource/schema", + schema, + "did:web:xyz", + "default-tag", + SignatureType.CL, + CredentialDefinitionConfig(true) + ) + val rev = issuer.createRevocationRegistryDef( + cred.credentialDefinition, + "did:web:xyz/resource/cred-def", + "did:web:xyz", + "default-tag", + RegistryType.CL_ACCUM, + 1000u + ) + println("regDef IssuerId: ${rev.regDef.getIssuerId()}") + println("regDef CredDefId: ${rev.regDef.getCredDefId()}") + println("regDefPrivate: ${rev.regDefPrivate.getJson()}") + assertTrue(true) + } + + @Test + fun test_Issuer_createRevocationStatusList() { + val issuer = Issuer() + val attributeNames = listOf("name", "age") + val schema = Schema("Moussa", "1.0", attributeNames, "sample:uri") + val cred = issuer.createCredentialDefinition( + "did:web:xyz/resource/schema", + schema, + "did:web:xyz", + "default-tag", + SignatureType.CL, + CredentialDefinitionConfig(true) + ) + val rev = issuer.createRevocationRegistryDef( + cred.credentialDefinition, + "did:web:xyz/resource/cred-def", + "did:web:xyz", + "default-tag", + RegistryType.CL_ACCUM, + 1000u + ) + val revStatusList = issuer.createRevocationStatusList( + "did:web:xyz/resource/rev-reg-def", + rev.regDef, + "did:web:xyz", + null, + true + ) + println(revStatusList.getJson()) + assertTrue(true) + } + + @Test + fun test_Issuer_updateRevocationStatusListTimestampOnly() { + val issuer = Issuer() + val attributeNames = listOf("name", "age") + val schema = Schema("Moussa", "1.0", attributeNames, "sample:uri") + val cred = issuer.createCredentialDefinition( + "did:web:xyz/resource/schema", + schema, + "did:web:xyz", + "default-tag", + SignatureType.CL, + CredentialDefinitionConfig(true) + ) + val rev = issuer.createRevocationRegistryDef( + cred.credentialDefinition, + "did:web:xyz/resource/cred-def", + "did:web:xyz", + "default-tag", + RegistryType.CL_ACCUM, + 1000u + ) + val revStatusList = issuer.createRevocationStatusList( + "did:web:xyz/resource/rev-reg-def", + rev.regDef, + "did:web:xyz", + null, + true + ) + val updatedRevStatusList = issuer.updateRevocationStatusListTimestampOnly(1000u, revStatusList) + println(updatedRevStatusList.getJson()) + assertTrue(true) + } + + @Test + fun test_Issuer_updateRevocationStatusList() { + val issuer = Issuer() + val attributeNames = listOf("name", "age") + val schema = Schema("Moussa", "1.0", attributeNames, "sample:uri") + val cred = issuer.createCredentialDefinition( + "did:web:xyz/resource/schema", + schema, + "did:web:xyz", + "default-tag", + SignatureType.CL, + CredentialDefinitionConfig(true) + ) + val rev = issuer.createRevocationRegistryDef( + cred.credentialDefinition, + "did:web:xyz/resource/cred-def", + "did:web:xyz", + "default-tag", + RegistryType.CL_ACCUM, + 1000u + ) + val revStatusList = issuer.createRevocationStatusList( + "did:web:xyz/resource/rev-reg-def", + rev.regDef, + "did:web:xyz", + null, + true + ) + val updatedRevStatusList = issuer.updateRevocationStatusList(null, listOf(1u), null, rev.regDef, revStatusList) + println(updatedRevStatusList.getJson()) + assertTrue(true) + Nonce().toString() + Nonce() + } + + @Test + fun test_Issuer_createCredentialOffer() { + val issuer = Issuer() + val attributeNames = listOf("name", "age") + val schema = Schema("Moussa", "1.0", attributeNames, "sample:uri") + val cred = issuer.createCredentialDefinition( + "did:web:xyz/resource/schema", + schema, + "did:web:xyz", + "default-tag", + SignatureType.CL, + CredentialDefinitionConfig(true) + ) + val credentialOffer = issuer.createCredentialOffer("did:web:xyz/resource/schema", "did:web:xyz/resource/cred-def", cred.credentialKeyCorrectnessProof) + println(credentialOffer.getJson()) + assertTrue(true) + } + + @Test + fun test_Issuer_createCredential() { + val issuer = Issuer() + val attributeNames = listOf("name", "age") + val schema = Schema("Moussa", "1.0", attributeNames, "sample:uri") + val cred = issuer.createCredentialDefinition( + "did:web:xyz/resource/schema", + schema, + "did:web:xyz", + "default-tag", + SignatureType.CL, + CredentialDefinitionConfig(true) + ) + val credentialOffer = issuer.createCredentialOffer("did:web:xyz/resource/schema", "did:web:xyz/resource/cred-def", cred.credentialKeyCorrectnessProof) + val Prover = Prover() + val linkSecret = Prover.createLinkSecret() + val credentialRequest = Prover.createCredentialRequest("entropy", null, cred.credentialDefinition, linkSecret, "my-secret-id", credentialOffer) + val credentialValues = listOf(AttributeValues("name", "Moussa")) + val credential = issuer.createCredential( + cred.credentialDefinition, + cred.credentialDefinitionPrivate, + credentialOffer, + credentialRequest.request, + credentialValues, + null, + null, + null + ) + println(credential.getJson()) + assertTrue(true) + } +} diff --git a/anoncred-kmm/anoncreds-kmp/src/commonTest/kotlin/anoncred/wrapper/ProverTests.kt b/anoncred-kmm/anoncreds-kmp/src/commonTest/kotlin/anoncred/wrapper/ProverTests.kt new file mode 100644 index 00000000..1220d352 --- /dev/null +++ b/anoncred-kmm/anoncreds-kmp/src/commonTest/kotlin/anoncred/wrapper/ProverTests.kt @@ -0,0 +1,54 @@ +package anoncred.wrapper + +import anoncreds_wrapper.CredentialDefinitionConfig +import anoncreds_wrapper.Issuer +import anoncreds_wrapper.Prover +import anoncreds_wrapper.Schema +import anoncreds_wrapper.SignatureType +import kotlin.test.Test +import kotlin.test.assertTrue + +@AndroidIgnore +class ProverTests { + @Test + fun test_Prover_createLinkSecret() { + val prover = Prover() + val linkSecret = prover.createLinkSecret() + println(linkSecret.getBigNumber()) + println(linkSecret.getValue()) + assertTrue(linkSecret.getBigNumber().length > 0) + } + + @Test + fun test_Prover_createCredentialRequest() { + val issuer = Issuer() + val attributeNames = listOf("name", "age") + val schema = Schema("Moussa", "1.0", attributeNames, "sample:uri") + val cred = issuer.createCredentialDefinition( + "did:web:xyz/resource/schema", + schema, + "did:web:xyz", + "default-tag", + SignatureType.CL, + CredentialDefinitionConfig(true) + ) + val credentialOffer = issuer.createCredentialOffer( + "did:web:xyz/resource/schema", + "did:web:xyz/resource/cred-def", + cred.credentialKeyCorrectnessProof + ) + + val prover = Prover() + val linkSecret = prover.createLinkSecret() + val credentialRequest = prover.createCredentialRequest( + "entropy", + null, + cred.credentialDefinition, + linkSecret, + "my-secret-id", + credentialOffer + ) + println(credentialRequest) + assertTrue(true) + } +} diff --git a/anoncred-kmm/anoncreds-kmp/src/jvmTest/kotlin/anoncred/wrapper/AndroidIgnore.kt b/anoncred-kmm/anoncreds-kmp/src/jvmTest/kotlin/anoncred/wrapper/AndroidIgnore.kt new file mode 100644 index 00000000..1382ef18 --- /dev/null +++ b/anoncred-kmm/anoncreds-kmp/src/jvmTest/kotlin/anoncred/wrapper/AndroidIgnore.kt @@ -0,0 +1,6 @@ +package anoncred.wrapper + +/** + * Ignore Unit tests on Android Platform + */ +actual annotation class AndroidIgnore diff --git a/anoncred-kmm/anoncreds-kmp/src/nativeInterop/cinterop/anoncreds.def b/anoncred-kmm/anoncreds-kmp/src/nativeInterop/cinterop/anoncreds.def new file mode 100644 index 00000000..c9936d7c --- /dev/null +++ b/anoncred-kmm/anoncreds-kmp/src/nativeInterop/cinterop/anoncreds.def @@ -0,0 +1 @@ +staticLibraries = libanoncreds.d \ No newline at end of file diff --git a/anoncred-kmm/anoncreds-kmp/src/nativeInterop/cinterop/anoncreds_wrapper.def b/anoncred-kmm/anoncreds-kmp/src/nativeInterop/cinterop/anoncreds_wrapper.def new file mode 100644 index 00000000..d3877794 --- /dev/null +++ b/anoncred-kmm/anoncreds-kmp/src/nativeInterop/cinterop/anoncreds_wrapper.def @@ -0,0 +1 @@ +staticLibraries = libanoncreds_wrapper.a \ No newline at end of file diff --git a/anoncred-kmm/build.gradle.kts b/anoncred-kmm/build.gradle.kts new file mode 100644 index 00000000..305c6e2c --- /dev/null +++ b/anoncred-kmm/build.gradle.kts @@ -0,0 +1,52 @@ +plugins { + id("org.jlleitschuh.gradle.ktlint") version "11.6.0" + kotlin("jvm") version "1.8.20" +} + +buildscript { + repositories { + gradlePluginPortal() + google() + mavenCentral() + } + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.20") + classpath("com.android.tools.build:gradle:7.2.2") + classpath("org.jetbrains.kotlinx:atomicfu-gradle-plugin:0.21.0") + } +} + +allprojects { + repositories { + mavenCentral() + gradlePluginPortal() + google() + maven { url = uri("https://jitpack.io") } + } +} + +subprojects { + apply(plugin = "org.jlleitschuh.gradle.ktlint") + ktlint { + verbose.set(true) + outputToConsole.set(true) + filter { + val generatedCodePath = rootDir + .resolve("anoncreds-kmp") + .resolve("build") + .resolve("generated") + + exclude( + "$generatedCodePath/*/*", + "$generatedCodePath/*", + "$generatedCodePath/**", + "$generatedCodePath/**/**" + ) + exclude("**/generated/**") + exclude { projectDir.toURI().relativize(it.file.toURI()).path.contains("/generated/") } + exclude { element -> element.file.path.contains("generated/") } + exclude { it.file.path.contains("$buildDir/generated/") } + exclude { it.file.path.contains(layout.buildDirectory.dir("generated").get().toString()) } + } + } +} diff --git a/anoncred-kmm/gradle.properties b/anoncred-kmm/gradle.properties new file mode 100644 index 00000000..4cfeee0f --- /dev/null +++ b/anoncred-kmm/gradle.properties @@ -0,0 +1,12 @@ +# Kotlin +kotlin.code.style=official + +# KMP +kotlin.mpp.enableCInteropCommonization=true +kotlin.native.cacheKind.macosArm64=none +kotlin.mpp.androidSourceSetLayoutVersion=2 +kotlinx.atomicfu.enableJvmIrTransformation=true +kotlinx.atomicfu.enableJsIrTransformation=true + +# Android +android.useAndroidX=true diff --git a/anoncred-kmm/gradle/wrapper/gradle-wrapper.jar b/anoncred-kmm/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..ccebba77 Binary files /dev/null and b/anoncred-kmm/gradle/wrapper/gradle-wrapper.jar differ diff --git a/anoncred-kmm/gradle/wrapper/gradle-wrapper.properties b/anoncred-kmm/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..f398c33c --- /dev/null +++ b/anoncred-kmm/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/anoncred-kmm/gradlew b/anoncred-kmm/gradlew new file mode 100755 index 00000000..79a61d42 --- /dev/null +++ b/anoncred-kmm/gradlew @@ -0,0 +1,244 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/anoncred-kmm/gradlew.bat b/anoncred-kmm/gradlew.bat new file mode 100644 index 00000000..6689b85b --- /dev/null +++ b/anoncred-kmm/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/anoncred-kmm/settings.gradle.kts b/anoncred-kmm/settings.gradle.kts new file mode 100644 index 00000000..59a37349 --- /dev/null +++ b/anoncred-kmm/settings.gradle.kts @@ -0,0 +1,5 @@ +rootProject.name = "anoncreds-kmm-main" +include(":uniffi-kmm") +include(":anoncred-wrapper-rust") +include(":anoncreds-kmp") +include(":testapp") diff --git a/anoncred-kmm/testapp/.gitignore b/anoncred-kmm/testapp/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/anoncred-kmm/testapp/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/anoncred-kmm/testapp/build.gradle.kts b/anoncred-kmm/testapp/build.gradle.kts new file mode 100644 index 00000000..412e0b39 --- /dev/null +++ b/anoncred-kmm/testapp/build.gradle.kts @@ -0,0 +1,58 @@ +plugins { + id("com.android.application") + id("org.jetbrains.kotlin.android") +} + +apply(plugin = "kotlinx-atomicfu") + +repositories { + mavenCentral() + gradlePluginPortal() + google() + maven { url = uri("https://jitpack.io") } +} + +android { + namespace = "com.example.testapp" + compileSdk = 33 + + defaultConfig { + applicationId = "com.example.testapp" + minSdk = 24 + targetSdk = 33 + versionCode = 1 + versionName = "1.0" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + implementation("androidx.core:core-ktx:1.9.0") + implementation("androidx.appcompat:appcompat:1.6.1") + implementation("com.google.android.material:material:1.8.0") + implementation("androidx.constraintlayout:constraintlayout:2.1.4") + + implementation(project(":anoncreds-kmp")) + + testImplementation("junit:junit:4.13.2") + androidTestImplementation("androidx.test.ext:junit:1.1.5") + androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") +} diff --git a/anoncred-kmm/testapp/proguard-rules.pro b/anoncred-kmm/testapp/proguard-rules.pro new file mode 100644 index 00000000..f1b42451 --- /dev/null +++ b/anoncred-kmm/testapp/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/anoncred-kmm/testapp/src/androidTest/java/com/example/testapp/ExampleInstrumentedTest.kt b/anoncred-kmm/testapp/src/androidTest/java/com/example/testapp/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..0078369a --- /dev/null +++ b/anoncred-kmm/testapp/src/androidTest/java/com/example/testapp/ExampleInstrumentedTest.kt @@ -0,0 +1,22 @@ +package com.example.testapp + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.example.testapp", appContext.packageName) + } +} diff --git a/anoncred-kmm/testapp/src/androidTest/java/com/example/testapp/IssuerTests.kt b/anoncred-kmm/testapp/src/androidTest/java/com/example/testapp/IssuerTests.kt new file mode 100644 index 00000000..474d1526 --- /dev/null +++ b/anoncred-kmm/testapp/src/androidTest/java/com/example/testapp/IssuerTests.kt @@ -0,0 +1,225 @@ +package com.example.testapp + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import anoncreds_wrapper.AttributeValues +import anoncreds_wrapper.CredentialDefinitionConfig +import anoncreds_wrapper.Issuer +import anoncreds_wrapper.Prover +import anoncreds_wrapper.RegistryType +import anoncreds_wrapper.Schema +import anoncreds_wrapper.SignatureType +import junit.framework.TestCase.assertEquals +import junit.framework.TestCase.assertTrue +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class IssuerTests { + @Test + fun test_Issuer_createSchema() { + val expectedSchema = Schema("Moussa", "1.0", listOf("name", "age"), "sample:uri") + val scheme: Schema = Issuer().createSchema("Moussa", "1.0", "sample:uri", listOf("name", "age")) + assertEquals("scheme not equal", expectedSchema, scheme) + assertEquals("name not correct", expectedSchema.name, scheme.name) + assertEquals("version not correct", expectedSchema.version, scheme.version) + assertEquals("issuerId not correct", expectedSchema.issuerId, scheme.issuerId) + assertEquals("attrNames size is not correct", expectedSchema.attrNames.size, scheme.attrNames.size) + expectedSchema.attrNames.forEach { + assertTrue(scheme.attrNames.contains(it)) + } + } + + @Test + fun test_Issuer_createCredentialDefinition() { + val issuer = Issuer() + val attributeNames = listOf("name", "age") + val schema: Schema = issuer.createSchema("Moussa", "1.0", "sample:uri", attributeNames) + val cred = issuer.createCredentialDefinition( + "did:web:xyz/resource/schema", + schema, + "did:web:xyz", + "default-tag", + SignatureType.CL, + CredentialDefinitionConfig(true) + ) + println(cred.credentialDefinition.getJson()) + println(cred.credentialDefinitionPrivate.getJson()) + println(cred.credentialKeyCorrectnessProof.getJson()) + assertTrue(true) + } + + @Test + fun test_Issuer_createRevocationRegistryDef() { + val issuer = Issuer() + val attributeNames = listOf("name", "age") + val schema = Schema("Moussa", "1.0", attributeNames, "sample:uri") + val cred = issuer.createCredentialDefinition( + "did:web:xyz/resource/schema", + schema, + "did:web:xyz", + "default-tag", + SignatureType.CL, + CredentialDefinitionConfig(true) + ) + val rev = issuer.createRevocationRegistryDef( + cred.credentialDefinition, + "did:web:xyz/resource/cred-def", + "did:web:xyz", + "default-tag", + RegistryType.CL_ACCUM, + 1000u + ) + println("regDef IssuerId: ${rev.regDef.getIssuerId()}") + println("regDef CredDefId: ${rev.regDef.getCredDefId()}") + println("regDefPrivate: ${rev.regDefPrivate.getJson()}") + assertTrue(true) + } + + @Test + fun test_Issuer_createRevocationStatusList() { + val issuer = Issuer() + val attributeNames = listOf("name", "age") + val schema = Schema("Moussa", "1.0", attributeNames, "sample:uri") + val cred = issuer.createCredentialDefinition( + "did:web:xyz/resource/schema", + schema, + "did:web:xyz", + "default-tag", + SignatureType.CL, + CredentialDefinitionConfig(true) + ) + val rev = issuer.createRevocationRegistryDef( + cred.credentialDefinition, + "did:web:xyz/resource/cred-def", + "did:web:xyz", + "default-tag", + RegistryType.CL_ACCUM, + 1000u + ) + val revStatusList = issuer.createRevocationStatusList( + "did:web:xyz/resource/rev-reg-def", + rev.regDef, + "did:web:xyz", + null, + true + ) + println(revStatusList.getJson()) + assertTrue(true) + } + + @Test + fun test_Issuer_updateRevocationStatusListTimestampOnly() { + val issuer = Issuer() + val attributeNames = listOf("name", "age") + val schema = Schema("Moussa", "1.0", attributeNames, "sample:uri") + val cred = issuer.createCredentialDefinition( + "did:web:xyz/resource/schema", + schema, + "did:web:xyz", + "default-tag", + SignatureType.CL, + CredentialDefinitionConfig(true) + ) + val rev = issuer.createRevocationRegistryDef( + cred.credentialDefinition, + "did:web:xyz/resource/cred-def", + "did:web:xyz", + "default-tag", + RegistryType.CL_ACCUM, + 1000u + ) + val revStatusList = issuer.createRevocationStatusList( + "did:web:xyz/resource/rev-reg-def", + rev.regDef, + "did:web:xyz", + null, + true + ) + val updatedRevStatusList = issuer.updateRevocationStatusListTimestampOnly(1000u, revStatusList) + println(updatedRevStatusList.getJson()) + assertTrue(true) + } + + @Test + fun test_Issuer_updateRevocationStatusList() { + val issuer = Issuer() + val attributeNames = listOf("name", "age") + val schema = Schema("Moussa", "1.0", attributeNames, "sample:uri") + val cred = issuer.createCredentialDefinition( + "did:web:xyz/resource/schema", + schema, + "did:web:xyz", + "default-tag", + SignatureType.CL, + CredentialDefinitionConfig(true) + ) + val rev = issuer.createRevocationRegistryDef( + cred.credentialDefinition, + "did:web:xyz/resource/cred-def", + "did:web:xyz", + "default-tag", + RegistryType.CL_ACCUM, + 1000u + ) + val revStatusList = issuer.createRevocationStatusList( + "did:web:xyz/resource/rev-reg-def", + rev.regDef, + "did:web:xyz", + null, + true + ) + val updatedRevStatusList = issuer.updateRevocationStatusList(null, listOf(1u), null, rev.regDef, revStatusList) + println(updatedRevStatusList.getJson()) + assertTrue(true) + } + + @Test + fun test_Issuer_createCredentialOffer() { + val issuer = Issuer() + val attributeNames = listOf("name", "age") + val schema = Schema("Moussa", "1.0", attributeNames, "sample:uri") + val cred = issuer.createCredentialDefinition( + "did:web:xyz/resource/schema", + schema, + "did:web:xyz", + "default-tag", + SignatureType.CL, + CredentialDefinitionConfig(true) + ) + val credentialOffer = issuer.createCredentialOffer("did:web:xyz/resource/schema", "did:web:xyz/resource/cred-def", cred.credentialKeyCorrectnessProof) + println(credentialOffer.getJson()) + assertTrue(true) + } + + @Test + fun test_Issuer_createCredential() { + val issuer = Issuer() + val attributeNames = listOf("name", "age") + val schema = Schema("Moussa", "1.0", attributeNames, "sample:uri") + val cred = issuer.createCredentialDefinition( + "did:web:xyz/resource/schema", + schema, + "did:web:xyz", + "default-tag", + SignatureType.CL, + CredentialDefinitionConfig(true) + ) + val credentialOffer = issuer.createCredentialOffer("did:web:xyz/resource/schema", "did:web:xyz/resource/cred-def", cred.credentialKeyCorrectnessProof) + val prover = Prover() + val linkSecret = prover.createLinkSecret() + val credentialRequest = prover.createCredentialRequest("entropy", null, cred.credentialDefinition, linkSecret, "my-secret-id", credentialOffer) + val credentialValues = listOf(AttributeValues("name", "Moussa")) + val credential = issuer.createCredential( + cred.credentialDefinition, + cred.credentialDefinitionPrivate, + credentialOffer, + credentialRequest.request, + credentialValues, + null, + null, + null + ) + println(credential.getJson()) + assertTrue(true) + } +} diff --git a/anoncred-kmm/testapp/src/androidTest/java/com/example/testapp/ProverTests.kt b/anoncred-kmm/testapp/src/androidTest/java/com/example/testapp/ProverTests.kt new file mode 100644 index 00000000..48bd8110 --- /dev/null +++ b/anoncred-kmm/testapp/src/androidTest/java/com/example/testapp/ProverTests.kt @@ -0,0 +1,56 @@ +package com.example.testapp + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import anoncreds_wrapper.CredentialDefinitionConfig +import anoncreds_wrapper.Issuer +import anoncreds_wrapper.Prover +import anoncreds_wrapper.Schema +import anoncreds_wrapper.SignatureType +import junit.framework.TestCase.assertTrue +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class ProverTests { + @Test + fun test_Prover_createLinkSecret() { + val prover = Prover() + val linkSecret = prover.createLinkSecret() + println(linkSecret.getBigNumber()) + println(linkSecret.getValue()) + assertTrue(linkSecret.getBigNumber().length > 0) + } + + @Test + fun test_Prover_createCredentialRequest() { + val issuer = Issuer() + val attributeNames = listOf("name", "age") + val schema = Schema("Moussa", "1.0", attributeNames, "sample:uri") + val cred = issuer.createCredentialDefinition( + "did:web:xyz/resource/schema", + schema, + "did:web:xyz", + "default-tag", + SignatureType.CL, + CredentialDefinitionConfig(true) + ) + val credentialOffer = issuer.createCredentialOffer( + "did:web:xyz/resource/schema", + "did:web:xyz/resource/cred-def", + cred.credentialKeyCorrectnessProof + ) + + val prover = Prover() + val linkSecret = prover.createLinkSecret() + val credentialRequest = prover.createCredentialRequest( + "entropy", + null, + cred.credentialDefinition, + linkSecret, + "my-secret-id", + credentialOffer + ) + println(credentialRequest) + assertTrue(true) + } +} diff --git a/anoncred-kmm/testapp/src/main/AndroidManifest.xml b/anoncred-kmm/testapp/src/main/AndroidManifest.xml new file mode 100644 index 00000000..ceec6af8 --- /dev/null +++ b/anoncred-kmm/testapp/src/main/AndroidManifest.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/anoncred-kmm/testapp/src/main/java/com/example/testapp/MainActivity.kt b/anoncred-kmm/testapp/src/main/java/com/example/testapp/MainActivity.kt new file mode 100644 index 00000000..b4b78b9c --- /dev/null +++ b/anoncred-kmm/testapp/src/main/java/com/example/testapp/MainActivity.kt @@ -0,0 +1,19 @@ +package com.example.testapp + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import anoncreds_wrapper.Prover + +class MainActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + try { + val prover = Prover() + val linkSecret = prover.createLinkSecret() + println(linkSecret.getValue()) + } catch (ex: Throwable) { + throw ex + } + } +} diff --git a/anoncred-kmm/testapp/src/main/res/drawable/ic_launcher_background.xml b/anoncred-kmm/testapp/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 00000000..07d5da9c --- /dev/null +++ b/anoncred-kmm/testapp/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/anoncred-kmm/testapp/src/main/res/drawable/ic_launcher_foreground.xml b/anoncred-kmm/testapp/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 00000000..2b068d11 --- /dev/null +++ b/anoncred-kmm/testapp/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/anoncred-kmm/testapp/src/main/res/layout/activity_main.xml b/anoncred-kmm/testapp/src/main/res/layout/activity_main.xml new file mode 100644 index 00000000..17eab17b --- /dev/null +++ b/anoncred-kmm/testapp/src/main/res/layout/activity_main.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/anoncred-kmm/testapp/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/anoncred-kmm/testapp/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 00000000..6f3b755b --- /dev/null +++ b/anoncred-kmm/testapp/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/anoncred-kmm/testapp/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/anoncred-kmm/testapp/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 00000000..6f3b755b --- /dev/null +++ b/anoncred-kmm/testapp/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/anoncred-kmm/testapp/src/main/res/mipmap-hdpi/ic_launcher.webp b/anoncred-kmm/testapp/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 00000000..c209e78e Binary files /dev/null and b/anoncred-kmm/testapp/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/anoncred-kmm/testapp/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/anoncred-kmm/testapp/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 00000000..b2dfe3d1 Binary files /dev/null and b/anoncred-kmm/testapp/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/anoncred-kmm/testapp/src/main/res/mipmap-mdpi/ic_launcher.webp b/anoncred-kmm/testapp/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 00000000..4f0f1d64 Binary files /dev/null and b/anoncred-kmm/testapp/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/anoncred-kmm/testapp/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/anoncred-kmm/testapp/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 00000000..62b611da Binary files /dev/null and b/anoncred-kmm/testapp/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/anoncred-kmm/testapp/src/main/res/mipmap-xhdpi/ic_launcher.webp b/anoncred-kmm/testapp/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 00000000..948a3070 Binary files /dev/null and b/anoncred-kmm/testapp/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/anoncred-kmm/testapp/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/anoncred-kmm/testapp/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 00000000..1b9a6956 Binary files /dev/null and b/anoncred-kmm/testapp/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/anoncred-kmm/testapp/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/anoncred-kmm/testapp/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 00000000..28d4b77f Binary files /dev/null and b/anoncred-kmm/testapp/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/anoncred-kmm/testapp/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/anoncred-kmm/testapp/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 00000000..9287f508 Binary files /dev/null and b/anoncred-kmm/testapp/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/anoncred-kmm/testapp/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/anoncred-kmm/testapp/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 00000000..aa7d6427 Binary files /dev/null and b/anoncred-kmm/testapp/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/anoncred-kmm/testapp/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/anoncred-kmm/testapp/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 00000000..9126ae37 Binary files /dev/null and b/anoncred-kmm/testapp/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/anoncred-kmm/testapp/src/main/res/values-night/themes.xml b/anoncred-kmm/testapp/src/main/res/values-night/themes.xml new file mode 100644 index 00000000..f474bfe3 --- /dev/null +++ b/anoncred-kmm/testapp/src/main/res/values-night/themes.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/anoncred-kmm/testapp/src/main/res/values/colors.xml b/anoncred-kmm/testapp/src/main/res/values/colors.xml new file mode 100644 index 00000000..c8524cd9 --- /dev/null +++ b/anoncred-kmm/testapp/src/main/res/values/colors.xml @@ -0,0 +1,5 @@ + + + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/anoncred-kmm/testapp/src/main/res/values/strings.xml b/anoncred-kmm/testapp/src/main/res/values/strings.xml new file mode 100644 index 00000000..ede419b6 --- /dev/null +++ b/anoncred-kmm/testapp/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + TestApp + \ No newline at end of file diff --git a/anoncred-kmm/testapp/src/main/res/values/themes.xml b/anoncred-kmm/testapp/src/main/res/values/themes.xml new file mode 100644 index 00000000..2248cb4b --- /dev/null +++ b/anoncred-kmm/testapp/src/main/res/values/themes.xml @@ -0,0 +1,9 @@ + + + + +