Air-gapped cold wallet for Zcash: turn a spare phone into an offline signer.
Keys stay on the offline device. Transactions go in and signatures come out as QR codes. Part of the Zafu ecosystem.
- Pair: export a viewing key from Zigner as a QR and scan it into Zafu or Zashi. The hot wallet can see balances and build transactions, but cannot spend.
- Sign: the hot wallet shows an unsigned transaction as a QR. Zigner scans it, shows recipient, amount and fee, and after you approve, shows the signature as a QR.
- Broadcast: the hot wallet scans the signature back and sends the transaction.
The device should never be online after setup. Airplane mode with WiFi, Bluetooth and NFC off is the minimum. A Pixel 8 or newer running GrapheneOS is recommended. See Security and Privacy.
- Orchard shielded spends and transparent inputs, signed from a PCZT (Keystone-compatible animated UR QR codes).
- Keys: ZIP-32
m/32'/133'/account', with UFVK export (ZIP-316) to Zafu or Zashi. - The review screen decodes recipients, amounts and fee from the PCZT itself. Spends that don't match notes this device has verified get a warning. Read the screen before you approve: what you approve is what gets signed.
- FROST threshold multisig for Orchard via
frost-spend: key generation and signing run over QR codes, with no server in the cryptographic path. Round 2 signs only what you approved in round 1, and nonces are never reused.
- One seed, separate keys: each network derives its own keys on its own path
(Zcash
m/32'/133'/…, Penumbram/44'/6532'/…). A key for one network never signs for another. - Penumbra requests carry a chain ID, which is checked on every signature.
- The camera is the only input. A network's codes are parsed only when the device holds a key for that network: a Zcash-only phone never runs the Penumbra parser. Other networks' codes are always refused. (Multi-frame codes still pass through a shared frame decoder before they are sorted.)
Signs Penumbra transactions from Zafu or Prax. Exports a full viewing key, and a receive address derived from that same key.
Requires Rust and
cargo install uniffi_bindgen --version 0.22.0. All cryptography and parsing
live in the Rust core under rust/. android/ and ios/ hold the native apps.
- Android:
rustup target add aarch64-linux-android armv7-linux-androideabi x86_64-linux-android, then build in Android Studio with NDK24.0.8215888. - iOS:
ios/is kept but no longer maintained, untested and not built in CI. It lacks the camera filtering, air-gap checks and recent fixes the Android app has, and FROST signing is off. Do not use it to hold funds. - Tests:
cd rust && cargo test --locked
Pushing a v* tag builds, signs and publishes the APK with SHA256SUMS and
SHA256SUMS.sig. Releases are also published to
our F-Droid repo.
echo 'release@rotko.net ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILOpUCz+cYuqfvZVBVBTXvUHXD8HZTTyesPqhgOvwQWl' > allowed_signers
ssh-keygen -Y verify -f allowed_signers -I release@rotko.net -n file -s SHA256SUMS.sig < SHA256SUMS
sha256sum -c SHA256SUMS