Skip to main content

Noir & zkEmail On Mobile: Native Proofs And Verifications With Mopro

· 4 min read

Noir with Barretenberg backend gives us a Rust-native prover that is quick enough to run on-device. zkEmail converts an ordinary email (header + body) into a zero-knowledge proof (ZKP) that says “I own this mailbox and have this email – without revealing anything else.”

Mopro wires those two worlds into first-class Swift, Kotlin, Flutter and React-Native packages generated by UniFFI, so your mobile app can create a proof offline, verify it locally, and ship only the compact blob to the server or a on-chain verifier.

Currently, we explored mobile platforms as below. Please check these Github repos for more details.

For cross platform development, we supports

Notice that, due to the limitation of Noir, we currently support architecture of aarch64-apple-ios and aarch64-linux-android. For iOS developers, since the iPhone simulator does not work, please use "My Mac (designed for iPad)" as target if you want to run the app on your laptop. For Android developers, the good news is the app work well on the emulator. Now, Let's get started!

Integration Flow End-To-End

Under The Hood: UniFFI In Four Steps

  1. Rust Crate – we expose proveZkemail & verifyZkemail around Barretenberg backend.
  2. UniFFI consumes a .udl interface for bindings.
  3. Build Toolsmopro build creates a static .a for iOS and a dynamic .so for Android; CocoaPods, SwiftPM, Gradle, Pub and npm publish the artifacts.
  4. Your App – calls the generated functions exactly like platform code; no manual JNI or Objective-C shims required.

Example Usage (Swift)

import ZKEmailSwift
let inputs = ["header_storage": ["..."]]
let proof = try proveZkemail(srsPath: "/path/to/srs", inputs: inputs)
let isValid = try verifyZkemail(srsPath: "/path/to/srs", proof: proof)

Functions proveZkemail and verifyZkemail come straight from zkemail-swift-package.

Example Usage (Kotlin)

import uniffi.mopro.*
val inputs = mapOf("header_storage" to listOf("..."))
val proof = proveZkemail(srsPath, inputs)
val isValid = verifyZkemail(srsPath, proof)

These top-level functions are generated by UniFFI and exposed in zkemail-kotlin-package.

Current Benchmarks

All numbers were captured on an Apple M3 8-core device and were built in release mode. The circuit is the header-only proof from zkemail.nr_header_demo; proof size is a flat 28 KB across platforms .

OperationiOS (ms)Android (ms)
Proof Generation1,3093,826
Verification9622,857

iOS zkEmail App Example
iOS
Android zkEmail App Example
Android

Flutter App for iOS & Android zkEmail Example

Under The Hood: Managing Static Library Size On iOS

Why Static Linking?

UniFFI exports Swift bindings as a static archive (libmoproiosbindings.a).
Static linking guarantees that every Rust symbol is available at link-time and avoids Objective-C trampolines, simplifying Xcode integration. The trade-off is that all Rust dependencies—including Barretenberg, rayon, and big-integer math—end up inside the archive.

Baseline Size

A full build produces an archive of ≈ 153 MB.
While acceptable for some package management platforms, the raw size slows down:

  • CocoaPods or SwiftPM downloads
  • CI cache restores
  • Repository clones by contributors on slower networks

Compression Strategy

To keep developer workflows responsive we compress the entire MoproBindings.xcframework and reduce the size to ≈ 41 MB.

A short script_phase in the podspec (or buildPhase in SwiftPM) unpacks the file during pod install / swift package resolve, so Xcode still links against the original static library. The extra CPU time is negligible compared with the network savings.

Comparison With Android

Android ships a dynamic .so (≈ 20 MB). ART loads symbols lazily, allowing smaller artifacts without additional steps. Because iOS prohibits third-party Rust dylibs inside App Store builds, static linking plus compression remains the most practical approach for now.

What's Next

The general support for Noir proving system is on the way. Follow us on Telegram and X for first-hand updates. Happy proving!