Noir & zkEmail On Mobile: Native Proofs And Verifications With Mopro
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.
- Swift/iOS:
zkemail-swift-package - Kotlin/Android:
zkemail-kotlin-package
For cross platform development, we supports
- React-Native module:
zkemail-react-native-package - Flutter plugin:
zkemail_flutter_package
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
- Rust Crate – we expose
proveZkemail&verifyZkemailaround Barretenberg backend. - UniFFI consumes a
.udlinterface for bindings. - Build Tools –
mopro buildcreates a static.afor iOS and a dynamic.sofor Android; CocoaPods, SwiftPM, Gradle, Pub and npm publish the artifacts. - 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 .
| Operation | iOS (ms) | Android (ms) |
|---|---|---|
| Proof Generation | 1,309 | 3,826 |
| Verification | 962 | 2,857 |
![]() iOS | ![]() 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!

