Interoperable FROST co-signing for shielded ZEC: open specs and a reference implementation

GitHub Issue: https://github.com/ZcashCommunityGrants/zcashcommunitygrants/issues/490
Requested: $16,500, two milestones, each paid only once accepted, no startup payment
Applicant: Khadim, solo developer of OpenPGP Wallet (open source, MIT)

Why this, and why now

FROST 2-of-3 with a second device the user owns, such as their phone, removes the weak spot of every software wallet: the moment the full spending key sits in RAM on one machine, and a confirmation screen drawn by that same machine.

This week, three wallets turned out to be building it for shielded ZEC: Zkool already ships FROST accounts, NoirWallet published its Secure Vault design, and I published mine the day before (design thread linked below). The architectures converge almost step by step, but each wallet is defining its own coordinator-to-signer messages and its own share backups. Without a common spec:

  • a co-signer from one wallet can’t co-sign for another, so every signer of an account tends to come from one publisher, and one compromised release pipeline or store account can reach the 2-of-3 threshold on its own;
  • a FROST backup only restores in the app that made it. Hanh put it plainly earlier: there is no standard for FROST account backups yet;
  • every team works out the same subtle points alone (randomizers derived from the signers’ commitments, the confidential channel ZIP 312 requires, the ZIP 374 signer checks).

What the grant funds

An open, versioned specification with test vectors, a standalone Rust crate that implements it, and a working reference implementation, tested on testnet and mainnet:

  1. Milestone 1, $9,000, 6 weeks. A FROST 2-of-3 account in the OpenPGP Wallet desktop app (frost-core DKG, re-randomized signing over PCZT with the Ironwood bundle), a test co-signer speaking the QR protocol, reproducible 2-of-3 Ironwood spends on testnet and mainnet, and spec draft v0.1 posted here for review.
  2. Milestone 2, $7,500, 5 weeks. The standalone crate on crates.io; the share backup format with a profile that needs no recovery phrase and a mnemonic profile; the recovery flow (sweep to a fresh FROST account); spec v1.0 after review; and an audit scope for the independent audit.

Testing happens on both testnet and mainnet, with small amounts. A public mainnet release for all users waits for the Ironwood viewing-key standard (zcash/zips#895, ZIP 2005) and an independent audit. An Android co-signer built on the same crate would be a follow-up request. Nothing in this grant pays for the other networks my wallet supports.

Where things stand

The wallet is live (17 releases since September, shielded ZEC on librustzcash, Ironwood since NU6.3). FROST is not implemented yet: I’m building the desktop testnet proof now and will post the transaction ID here before the committee votes. Three beta testers from my circle will try the builds, and I’ll post their results here. Anyone here who wants to reproduce the spend is very welcome too.

Links

Questions, objections and reviews are all welcome, especially from other wallet teams: @noir_wallet @hanh @conradoplg

Hi there,

I hope you find this proposal and its rejection reason useful.

1 Like

Thanks a lot @aryaethn, that’s kind of you. Your thread and the committee’s feedback on it shaped this application more than anything else: it’s why it builds on a wallet that already runs, why it’s much smaller, and why it’s paid only on delivery.

You also said you’d like to help turn the backup question into a ZIP, which is the heart of this proposal. If you’re ever up for reviewing the spec draft or working on that part together, I’d be really glad to.

Oh, I’m happy knowing that. :star_struck:

If this proposal gets accepted, showing me ZC’s shift of opinion about FROST multisig, I’ll be happy collaborating on the ZIP spec, and communicating with conrado about its development plans.

Wish the bests :crossed_fingers:t2:

2 Likes

Thank you, that means a lot. If it goes through, I’d love to work on the ZIP with you, and having you in the loop with Conrado would make a real difference. Thanks again for taking the time.

1 Like

Quick update: the first proof is done, on mainnet.

Three FROST 2-of-3 spends of shielded ZEC from the same group, one per pair of signers:

In the last two, the second device checked the transaction on its own before signing. The keys follow ZIP 2005, so the funds keep the quantum recovery path. I used mainnet because testnet moved to NU7 and the tools don’t support it yet. Thanks to aryaethn: my signing path follows his Ironwood work in zcash-sign, and I test it against his testnet transaction.

Following @hanh point, the spec will have a small common core plus room for each app’s own data.

Code: GitHub - OpenPGP-Wallet/frost-zec: Prototype: FROST 2-of-3 for shielded ZEC (Ironwood), ZIP 2005 keys, and a co-signer that verifies the PCZT · GitHub

To make the scope clearer, here’s how I see the FROST projects for shielded ZEC, and how this work would connect them. Please correct anything I got wrong or missed.

Project What it is How signers talk Backup
Zkool wallet with FROST, signs Orchard and Ironwood shielded memos passphrase-encrypted export (age), restores in Zkool only
Zafe mobile shared vault (Android), mainnet beta encrypted relay passphrase-encrypted file (share, sk, vault descriptor), to cloud or file
Konclave group treasury in the browser, mainnet beta relay and a hosted helper passphrase-encrypted export (share, viewing key, metadata)
Quorum org treasury tool, hackathon build on testnet frostd none yet: share passphrase-encrypted on the device, lost-share repair planned
NoirWallet Secure Vault design QR or Bluetooth one recovery phrase per share
OpenPGP Wallet (me) prototype, mainnet spends QR (files in the prototype) 3rd share as an encrypted offline file, no phrase (design)

They all build on the Zcash Foundation’s FROST crates (re-randomized FROST, ZIP 312), and most of them sign PCZTs. What differs is the envelope, the transport, the key derivation and the backup, so today a share made in one app can’t be used or restored in another.

The plan, with no new crypto:

  1. Keys: derive them per ZIP 2005 (Zafe’s test vectors and mine already match), and follow the ZIP 312 key generation once zips#895 lands. I left a review note there: [ZIP 312] Specify key generation, change randomizer handling by conradoplg · Pull Request #895 · zcash/zips · GitHub
  2. Messages: a shared group descriptor and a shared envelope (Zafe’s looks like a good starting point), carried over any transport (memos, a relay, frostd), with QR as the one every app can do.
  3. Backup: a common format with room for each app’s own data. @aryaethn said in August he’d like to help turn this into a ZIP starting from @hanh format. I’d do the drafting and the implementation, with their input.
  4. Proof: cross-implementation signing tests with whoever is up for it. Zafe’s stack is the closest to mine.
  5. A small reference crate for the descriptor, the envelope and the backup, so adopting them takes a few lines rather than a rewrite. It’s not a wallet SDK: each app keeps its own UX and transport.

This doesn’t change the application’s scope, milestones or budget: the QR protocol becomes the first transport profile of the shared envelope, and the backup format and the crate are the same deliverables.

If you build FROST for Zcash and want to join, or think this is the wrong approach, please say so.

1 Like

Small update on the backup side.

I wrote a draft of a common member backup format for FROST groups on shielded ZEC:

It starts from Zkool’s zkfrost string: version 1 is exactly its 68-byte payload, so existing Zkool backups stay valid. It keeps only what’s needed to rebuild one member (id, t, n, signing share, sk; ak comes from the group’s UFVK), and it doesn’t depend on how sk was agreed. A passphrase and a Shamir split of sk are optional profiles behind the version byte. Open points are listed at the end (identifier encoding, test vectors…).

@aryaethn and I agreed on it as a starting point for discussion, not a finished spec. Comments welcome.

On the co-signer side: it now refuses dummy spending keys (ZIP 374), checks witnesses against the anchor, and flags v6 pre-authorization.

Scope and budget are unchanged.

1 Like

I came at this from the other direction — I wrote the ZecHub brief on FROST and viewing keys for Zcash/Dash interop — and derivation was exactly the thing I couldn’t settle from public sources. ZIP 312 was still Draft and nothing pinned down how a group’s keys should be derived, which is what zips#895 now addresses.

One question on the group descriptor. Does it record the derivation profile itself, or is ZIP 2005 assumed? If it carries an identifier rather than an assumption, the same envelope survives a second derivation scheme later — which matters if anyone revisits the cross-chain case, since derivation is the part that diverges there.

1 Like

Separately, and within what I can actually contribute: once the backup format settles, I’d be glad to document it on the wiki alongside the existing FROST page. I could draft that from docs/backup-format.md for you to check.

1 Like

Thanks @benjoshyo derivation is exactly the part I keep going back and forth on.

Right now the backup is the narrow case: version 1 is Zkool’s zkfrost payload byte-for-byte, so ZIP 2005 use_qsk is baked into that version rather than recorded as its own field. The format stays agnostic about how sk was agreed (mailbox addresses in Zkool, COCKTAIL elsewhere), but you’re right that how the viewing key / qsk is derived from sk is a separate thing, and a consumer needs to know it to rederive.

So I’ll take the suggestion, the group descriptor should carry an explicit derivation-profile id instead of assuming ZIP 2005. Same envelope stays valid if a second scheme shows up later, which is exactly where the cross-chain case diverges like you said. I’ll add it to the open points.

Could you link your ZecHub brief? I’d like to read how you framed the viewing-key / derivation part.

That would be really welcome. A ZecHub page drafted from docs/backup-format.md would be a great home for it, next to your existing FROST page. Happy to review the draft and keep it in sync as the spec moves.