# Zero-knowledge proofs on mobile: Hekate's first benchmarks

> How long a zero-knowledge proof takes on a phone: 1.34 s for a post-quantum ML-DSA (Dilithium) signature, 1.02 s for a private payment, on an iPhone 14 Pro Max.

- URL: https://oumuamua.dev/blog/zk-on-mobile-benchmarks
- Published: 2026-10-12
- Updated: 2026-10-12
- Authors: [Andrei Kochergin](https://oumuamua.dev/blog/authors/andrei-kochergin)

Every number we had published came from a laptop. A prover built for client-side proving should be
judged on the client, and for most people the client is a phone. We took two iPhones off the desk,
one of them a 2019 model, and spent a day finding out what on-device proving costs there.

The short version. A post-quantum ML-DSA (Dilithium) signature, made inside the iPhone's Secure
Enclave, proves in zero knowledge in 1.34 s on an iPhone 14 Pro Max. An RSA-2048 signature proves
in 485 ms on that phone and in 832 ms on an iPhone 11 Pro Max. A private payment that spends four
notes and creates four proves in 1.02 s. Every proof verifies in 15 to 34 ms on the phone that
made it.

![Prove and verify times for RSA-2048 and ML-DSA-65 signature proofs on an iPhone 14 Pro Max and an iPhone 11 Pro Max](https://oumuamua.dev/assets/blog/zk-on-mobile-benchmarks/signatures.9e12a1b7d388019119c647cfba8090f9dd7d71d424bf2a846e29f9dc28a56519.png "Signature proofs on two iPhones: best of three, constant-time prover, zero knowledge on.")

## What the phone proves

[Hekate](https://oumuamua.dev/hekate) is our zero-knowledge prover: hash-based, with no trusted setup, working over
binary fields. The [mobile toolkit](https://oumuamua.dev/mobile) packs it into an iOS framework and an Android library, and an
app calls it with one `async` function in Swift or one `suspend` function in Kotlin.

The test app does what a real app would do with a hardware key. It makes a key on the phone, signs
a fresh 32-byte challenge with Apple's own library and hands the signature to Rust. Rust proves
the signature is valid without revealing it, and the app verifies the proof. The private key never
reaches our code. The ML-DSA key never leaves the Secure Enclave.

The two statements hide different things:

- [ML-DSA-65](https://oumuamua.dev/primitives/signatures/mldsa), the post-quantum signature of FIPS 204. The message
  and a hash of the public key are public. The public key and the signature stay hidden.
- [RSA-2048](https://oumuamua.dev/primitives/signatures/rsa) with PKCS#1 v1.5. The modulus is public. The message and
  the signature stay hidden.

One limit belongs next to every such claim. The proof shows that its maker holds a valid
signature. It does not show that the maker holds the private key: any signature the key ever
released will do. A verifier that wants a fresh one chooses the message, which the ML-DSA
statement allows because its message is public.

## The setup

Three commands build the Rust crate into frameworks and open the Xcode project. iOS hides its
Developer Mode switch until the phone has met Xcode. A free Apple account is enough to sign the
app. Apple gives each bundle identifier to a single team, and every other team has to change the
app's before Xcode will sign it.

## A hot phone that was no slower

The first report came from the iPhone 11 Pro Max while it was charging, with iOS reporting its
thermal state as "serious": 846 ms to prove the RSA signature. We expected the heat to have cost
something. We unplugged the phone, let it cool until the state read "nominal" and ran again:
832 ms. Under 2% apart, on one phone and one pair of runs.

The clean runs, each the best of three proofs, on battery, thermal state nominal, Low Power Mode
off, with the released constant-time prover 0.15.0 and hekate 0.37.0. The iPhone 14 Pro Max ran
iOS 26.7.1 and the iPhone 11 Pro Max iOS 18.6.2.

|            | ML-DSA-65, iPhone 14 Pro Max | RSA-2048, iPhone 14 Pro Max | RSA-2048, iPhone 11 Pro Max |
|:-----------|-----------------------------:|----------------------------:|----------------------------:|
| Witness    |                     190.4 ms |                     89.9 ms |                    150.0 ms |
| Prove      |                    1339.7 ms |                    484.6 ms |                    832.0 ms |
| Whole call |                    1546.4 ms |                    594.4 ms |                   1014.8 ms |
| Verify     |                      26.2 ms |                     20.7 ms |                     34.1 ms |
| Proof size |                    1,577 KiB |          1,808 to 1,814 KiB |          1,811 to 1,815 KiB |

The iPhone 11 Pro Max ran iOS 18, which has no ML-DSA signer, and its column stops at RSA.

For scale, the same constant-time build on an Apple M3 Max proves the RSA signature in about
240 ms and the ML-DSA signature in about 570 ms. The iPhone 14 Pro Max is 2.0 and 2.3 times
slower. The seven-year-old iPhone 11 Pro Max is 3.5 times slower on RSA.

The app reports the highest physical footprint since it was launched, across the three proofs:
156 MiB for ML-DSA and 252 MiB for RSA on the iPhone 14 Pro Max, 187 MiB for RSA on the
iPhone 11 Pro Max. The two phones run different iOS versions, which may count a footprint
differently, and we do not compare them.

## A private payment in one proof

Signatures are building blocks. We wanted a whole application on the phone, and we had one: the
shielded payment circuit of a private-payments chain we are building. One proof covers the whole
payment.

![A payment that spends four notes and creates four, with the five rules one zero-knowledge proof checks](https://oumuamua.dev/assets/blog/zk-on-mobile-benchmarks/one-proof.a4d3f325e991fa2079d9d483fb32a77877d9c5d2f2491c0facb32d5e94824a8b.png "One proof for the whole payment.")

Each spent note is shown to exist in a 256-level Merkle tree. Nullifiers stop a double spend.
Amounts balance per asset. A clearance rule can demand that the spender is on a whitelist or off a
blacklist. Audit and recovery capsules are encrypted with AES-256 inside the proof. The payments
we measured did not ask for clearance.

![Prove and verify times for three shielded payments on an iPhone 14 Pro Max, with peak memory, proof size and soundness](https://oumuamua.dev/assets/blog/zk-on-mobile-benchmarks/payment.cc2530e5b7c799764e6ecaa896eca3c37ca39872457be77595e341e77a63de3b.png "Three payments on an iPhone 14 Pro Max: zero knowledge on, constant-time prover.")

Four notes in and four out prove in 1.02 s. One note in and four out take 1.03 s, and a mint
1.11 s. Each verifies in 15 ms. The proof is about 1,050 KiB at 100 bits of soundness. One proof
peaks at 125 MiB: the app held 14 MiB before the first proof, 125 MiB after it, and the same after
the second and the third.

These three ran in one launch, mint first, with the phone on its charger, on a local SHA3 build of
the prover with Hekate 0.37.0 plus six unreleased commits. The released prover hashes with BLAKE3.
Treat these figures as a first measurement.

## What these numbers are

One day and two iPhones. No Android phone has run these proofs yet. The signature figures come
from the released, signed prover and are kept on the
[mobile benchmarks page](https://oumuamua.dev/mobile/benchmarks).

## Run it on your own phone

```bash
git clone https://github.com/oumuamua-labs/hekate-mobile
cd hekate-mobile/examples/prover
cargo xtask ios-build --unsigned
open ../ios/HekateExamples.xcodeproj
```

Pick your team in Xcode, select the phone and run. The app reports the same rows as the table
above, and the
[examples README](https://github.com/oumuamua-labs/hekate-mobile/blob/main/examples/README.md)
says what each one means.