Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.DS_Store
._*
/.build
/Packages
/*.xcodeproj
Expand All @@ -8,4 +9,4 @@ DerivedData/
.netrc
target/
out/
.kotlin
.kotlin
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ And add the following dependency to your target:

Take a look at the tests in `swift/Tests` directory for examples on how to use the wrappers.


### Kotlin

The Kotlin wrappers are distributed as a Maven package hosted by GitHub Packages.
Expand Down
23 changes: 23 additions & 0 deletions anoncreds/build-swift-framework.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,33 @@ X86_64_APPLE_DARWIN_PATH="./target/x86_64-apple-darwin/release"

targets=("aarch64-apple-ios" "aarch64-apple-ios-sim" "x86_64-apple-ios" "aarch64-apple-darwin" "x86_64-apple-darwin")

# Keep Rust, clang, and dependencies such as openssl-sys on the same minimum
# Apple OS versions. Without these exports/link args, newer Xcode SDKs can build
# objects for iOS 26.0 while cargo links the final iOS library as iOS 10.0,
# which fails with symbols such as ___chkstk_darwin during aarch64 iOS linking.
export IPHONEOS_DEPLOYMENT_TARGET="$MIN_IOS_VERSION"
export MACOSX_DEPLOYMENT_TARGET="12.0"

# Build for all targets
for target in "${targets[@]}"; do
echo "Building for $target..."
rustup target add $target

# Pass the matching deployment target flag for each Apple target explicitly to
# the Rust linker invocation. Simulator, device, and macOS use different clang
# flags, so one generic iOS flag is not enough for the full XCFramework build.
case "$target" in
aarch64-apple-ios)
export RUSTFLAGS="-C link-arg=-miphoneos-version-min=$MIN_IOS_VERSION"
;;
aarch64-apple-ios-sim|x86_64-apple-ios)
export RUSTFLAGS="-C link-arg=-mios-simulator-version-min=$MIN_IOS_VERSION"
;;
aarch64-apple-darwin|x86_64-apple-darwin)
export RUSTFLAGS="-C link-arg=-mmacosx-version-min=$MACOSX_DEPLOYMENT_TARGET"
;;
esac

cargo build --release --target $target
done

Expand Down
4 changes: 3 additions & 1 deletion anoncreds/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
mod uffi;
use uffi::credential_conversions::CredentialConversions;
use uffi::issuer::Issuer;
use uffi::prover::Prover;
use uffi::types::{
Credential, CredentialDefinition, CredentialDefinitionPrivate, CredentialKeyCorrectnessProof,
CredentialOffer, CredentialRequest, CredentialRequestMetadata, CredentialRevocationState,
Presentation, PresentationRequest, RevocationRegistryDefinition,
RevocationRegistryDefinitionPrivate, RevocationRegistryDelta, RevocationStatusList, Schema,
W3CCredential,
};
use uffi::verifier::Verifier;
use uffi::credential_conversions::CredentialConversions;
use uffi::w3c::W3cProcess;

uniffi::include_scaffolding!("anoncreds_uniffi");
4 changes: 2 additions & 2 deletions anoncreds/src/uffi/credential_conversions.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use std::sync::Arc;

use super::{error::ErrorCode, types::Credential};
use anoncreds::data_types::issuer_id::IssuerId;
use anoncreds::data_types::w3c::VerifiableCredentialSpecVersion;
use anoncreds::w3c::credential_conversion::credential_from_w3c;
use anoncreds::{
data_types::w3c::credential::W3CCredential, w3c::credential_conversion::credential_to_w3c,
};
use std::convert::TryFrom;
use super::{error::ErrorCode, types::Credential};

pub struct CredentialConversions {}

Expand Down Expand Up @@ -38,7 +38,7 @@ impl CredentialConversions {
let issuer_id = IssuerId::new(&issuer_id_string).expect("Error initializing issuer_id");
let version = match version_string {
Some(ref v) => Some(VerifiableCredentialSpecVersion::try_from(v.as_str())?),
None => None
None => None,
};

let w3c_credential = credential_to_w3c(&credential.0, &issuer_id, version)?;
Expand Down
5 changes: 3 additions & 2 deletions anoncreds/src/uffi/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
pub mod credential_conversions;
pub mod error;
pub mod issuer;
pub mod prover;
pub mod types;
pub mod verifier;
pub mod credential_conversions;
pub mod w3c;

#[uniffi::export]
pub fn set_default_logger() -> Result<(), error::ErrorCode> {
Expand All @@ -20,4 +21,4 @@ pub fn create_link_secret() -> Result<String, error::ErrorCode> {
.try_into()
.map_err(|err| anoncreds::Error::from(err))?;
Ok(dec_secret)
}
}
50 changes: 49 additions & 1 deletion anoncreds/src/uffi/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use super::error::ErrorCode;
use anoncreds::data_types::{
cred_def::CredentialDefinition as RustCredentialDefinition,
rev_status_list::RevocationStatusList as RustRevocationStatusList,
schema::Schema as RustSchema,
schema::Schema as RustSchema, w3c::credential::W3CCredential as RustW3cCredential,
};
use anoncreds::types::{
Credential as RustCredential, CredentialDefinitionPrivate as RustCredentialDefinitionPrivate,
Expand Down Expand Up @@ -223,6 +223,8 @@ impl Credential {
}
}

pub struct W3CCredential(pub RustW3cCredential);

#[derive(uniffi::Record)]
pub struct RequestedCredential {
pub cred: Arc<Credential>,
Expand All @@ -232,6 +234,52 @@ pub struct RequestedCredential {
pub requested_predicates: Vec<String>,
}

#[uniffi::export]
impl W3CCredential {
#[uniffi::constructor]
pub fn new(json: String) -> Result<Arc<Self>, ErrorCode> {
Ok(Arc::new(Self(serde_json::from_str::<RustW3cCredential>(
&json,
)?)))
}

pub fn to_json(&self) -> String {
serde_json::to_string(&self.0).unwrap()
}

pub fn context(&self) -> String {
serde_json::to_string(&self.0.context).unwrap()
}

pub fn id(&self) -> Option<String> {
self.0.id.as_ref().map(|uri| uri.0.clone())
}

pub fn r#type(&self) -> Vec<String> {
self.0.type_.0.iter().cloned().collect()
}

pub fn issuer(&self) -> String {
serde_json::to_string(&self.0.issuer).unwrap()
}

pub fn credential_subject(&self) -> String {
serde_json::to_string(&self.0.credential_subject).unwrap()
}

pub fn issuance_date(&self) -> String {
serde_json::to_string(&self.0.issuance_date).unwrap()
}

pub fn proof(&self) -> String {
serde_json::to_string(&self.0.proof).unwrap()
}

pub fn valid_from(&self) -> String {
serde_json::to_string(&self.0.valid_from).unwrap()
}
}

macro_rules! define_serializable_struct {
($struct_name:ident, $rust_struct_name:ident) => {
pub struct $struct_name(pub $rust_struct_name);
Expand Down
43 changes: 43 additions & 0 deletions anoncreds/src/uffi/w3c.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use super::error::ErrorCode;
use super::types::{
CredentialDefinition, CredentialRequestMetadata, RevocationRegistryDefinition, W3CCredential,
};

use anoncreds::types::LinkSecret;
use anoncreds::Error;
use std::sync::Arc;

pub struct W3cProcess {}

impl W3cProcess {
pub fn new() -> Self {
Self {}
}
}

#[uniffi::export]
impl W3cProcess {
pub fn process_credential(
&self,
cred: Arc<W3CCredential>,
cred_req_metadata: Arc<CredentialRequestMetadata>,
link_secret: String,
cred_def: Arc<CredentialDefinition>,
rev_reg_def: Option<Arc<RevocationRegistryDefinition>>,
) -> Result<Arc<W3CCredential>, ErrorCode> {
let link_secret =
LinkSecret::try_from(link_secret.as_str()).map_err(|err| Error::from(err))?;
let rev_reg_def = rev_reg_def.as_ref().map(|def| &def.0);
let mut new_cred = cred.0.clone();

anoncreds::w3c::prover::process_credential(
&mut new_cred,
&cred_req_metadata.0,
&link_secret,
&cred_def.0,
rev_reg_def,
)?;

Ok(Arc::new(W3CCredential(new_cred)))
}
}
4 changes: 4 additions & 0 deletions anoncreds/uniffi/anoncreds_uniffi.udl
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,15 @@ interface RevocationRegistryDefinitionPrivate {};
interface RevocationStatusList {};
interface RevocationRegistryDelta {};
interface Schema {};
interface W3CCredential {};
interface Verifier {
constructor();
};
interface CredentialConversions {
constructor();
};
interface W3cProcess {
constructor();
};

namespace anoncreds_uniffi {};
23 changes: 23 additions & 0 deletions indy-vdr/build-swift-framework.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,33 @@ X86_64_APPLE_DARWIN_PATH="./target/x86_64-apple-darwin/release"

targets=("aarch64-apple-ios" "aarch64-apple-ios-sim" "x86_64-apple-ios" "aarch64-apple-darwin" "x86_64-apple-darwin")

# Keep Rust, clang, and native dependencies such as libzmq-sys on the same
# minimum Apple OS versions. Without these exports/link args, newer Xcode SDKs
# can build objects for iOS 26.0 while cargo links the final iOS library as iOS
# 10.0, which fails with symbols such as ___chkstk_darwin during arm64 linking.
export IPHONEOS_DEPLOYMENT_TARGET="$MIN_IOS_VERSION"
export MACOSX_DEPLOYMENT_TARGET="12.0"

# Build for all targets
for target in "${targets[@]}"; do
echo "Building for $target..."
rustup target add $target

# Pass the matching deployment target flag for each Apple target explicitly to
# the Rust linker invocation. Simulator, device, and macOS use different clang
# flags, so one generic iOS flag is not enough for the full XCFramework build.
case "$target" in
aarch64-apple-ios)
export RUSTFLAGS="-C link-arg=-miphoneos-version-min=$MIN_IOS_VERSION"
;;
aarch64-apple-ios-sim|x86_64-apple-ios)
export RUSTFLAGS="-C link-arg=-mios-simulator-version-min=$MIN_IOS_VERSION"
;;
aarch64-apple-darwin|x86_64-apple-darwin)
export RUSTFLAGS="-C link-arg=-mmacosx-version-min=$MACOSX_DEPLOYMENT_TARGET"
;;
esac

cargo build --release --target $target
done

Expand Down
46 changes: 30 additions & 16 deletions kotlin/anoncreds/build.gradle.kts

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't these changes break the 16kb page requirement for Google play?

@cbbathaglini cbbathaglini Jul 7, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TheTreek

Thank you for the review! That's a valid concern. At the time of your comment (commit 4dba536), the anoncreds build.gradle.kts did not have the RUSTFLAGS for 16KB page size — you were right to call that out.

In the latest commits (f877234 and 10638f9/77acd20), we've addressed this:

  • Added the 16KB page size flags to anoncreds's cargo.builds.android block — the same RUSTFLAGS that askar and indy-vdr already had.
  • Updated NDK to 28.2.13676358 — matching askar and indy-vdr.
  • The remaining changes are just aligning the project structure (unifying duplicate builds blocks, macos block for local dev, version catalog for dependencies) and don't affect Android.

The final result is that anoncreds now has the same Android build configuration as askar and indy-vdr, including the 16KB page size compatibility. No regression was introduced.

Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import gobley.gradle.cargo.dsl.android
import gobley.gradle.cargo.dsl.appleMobile
import gobley.gradle.cargo.dsl.jvm
import gobley.gradle.cargo.dsl.linux
import gobley.gradle.cargo.dsl.macos
import gobley.gradle.rust.targets.RustPosixTarget
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetTree
Expand All @@ -27,36 +28,48 @@ cargo {
jvmVariant = Variant.Release
nativeVariant = Variant.Release

// Use cross when building Linux
val home = System.getProperty("user.home")
val crossFile = File("$home/.cargo/bin/cross")

builds {
linux {
val crossFile = File("$home/.cargo/bin/cross")
variants {
buildTaskProvider.configure {
cargo = crossFile
}
}
}

android{
variants{
buildTaskProvider.configure {
additionalEnvironment.put("RUSTFLAGS", "-C link-args=-Wl,-z,max-page-size=16384")
}
macos {
release.buildTaskProvider.configure{
additionalEnvironment.put("CC", "clang")
additionalEnvironment.put("CXX", "clang++")
additionalEnvironment.put("CFLAGS", "-Wno-error=deprecated")
additionalEnvironment.put("CXXFLAGS", "-Wno-error=deprecated")
}
}

appleMobile {
release.buildTaskProvider.configure {
additionalEnvironment.put("IPHONEOS_DEPLOYMENT_TARGET", "10")
additionalEnvironment.put("IPHONEOS_DEPLOYMENT_TARGET", "10.0")
additionalEnvironment.put("CC", "clang")
additionalEnvironment.put("CXX", "clang++")
}
}

android {
dynamicLibraries.addAll("c++_shared")
variants{
buildTaskProvider.configure {
// Required by Google Play for native libraries on 16 KB page-size devices.
additionalEnvironment.put("RUSTFLAGS", "-C link-args=-Wl,-z,max-page-size=16384")
}
}
}

jvm {
embedRustLibrary = true
if (GobleyHost.Platform.MacOS.isCurrent) {
// Don't build for linux or windows on MacOS (mainly for github actions purposes)
val exclude = listOf(
RustPosixTarget.MinGWX64,
RustPosixTarget.LinuxArm64,
Expand Down Expand Up @@ -101,15 +114,16 @@ if (secretPropsFile.exists()) {

fun getExtraString(name: String) = ext[name]?.toString()


publishing {
repositories {
maven {
name = "github"
setUrl("https://maven.pkg.github.com/LF-Decentralized-Trust-labs/aries-uniffi-wrappers")
credentials {
username = getExtraString("githubUsername")
password = getExtraString("githubToken")
}
credentials {
username = getExtraString("githubUsername") ?: ""
password = getExtraString("githubToken") ?: ""
}
}
}

Expand Down Expand Up @@ -142,7 +156,6 @@ publishing {
}
}


kotlin {
jvmToolchain(17)
applyDefaultHierarchyTemplate()
Expand Down Expand Up @@ -187,7 +200,7 @@ kotlin {
val commonTest by getting {
dependencies {
implementation(kotlin("test"))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2")
implementation(libs.kotlinx.coroutines.core)
}
}

Expand All @@ -212,6 +225,7 @@ kotlin {


android {
sourceSets["androidTest"].manifest.srcFile("src/androidTest/AndroidManifest.xml")
namespace = "anoncreds_uniffi"
compileSdk = 35
ndkVersion = "28.2.13676358"
Expand All @@ -232,4 +246,4 @@ android {
androidTestImplementation("androidx.test:runner:1.5.0")
androidTestUtil("androidx.test:orchestrator:1.4.2")
}
}
}
Loading