Terminology
The key words "MUST", "MUST NOT", "SHOULD", and "SHOULD NOT" in this document are to be interpreted as described in BCP 14 1 when, and only when, they appear in all capitals.
The terms "Mainnet" and "Testnet" in this document are to be interpreted as defined in the Zcash protocol specification 2.
The terms below are to be interpreted as follows:
- Group
- A set of participants holding a FROST threshold key for Orchard-protocol spend authorization 3, any of whom can sign.
- Member
- One participant of a group, identified by its FROST identifier.
- Signing share
- The member's secret share of the group's Spend authorizing key, as output by key generation.
- Group verifying key,
- The FROST group public key, used as the Spend validating key of the group's Orchard full viewing key. Its encoding has a bit of , as § 4.2.3 of the protocol specification requires 4.
- The 32-byte value the members agree on during key generation, from which the rest of the group's key components are derived 5.
- Derivation
- How and are obtained from and . This ZIP knows two, defined in [Derivations].
- Member backup string
- The string defined in this ZIP.
Abstract
This ZIP defines a backup string for one member of a FROST group that spends shielded ZEC with Orchard-protocol keys (in the Orchard or Ironwood pool). The string holds the member's identifier, the threshold, the group size, the member's signing share and the group's . Together with the group's unified full viewing key, it is enough to rebuild the member in any wallet that implements this ZIP, without the other members being online. Restoring checks the string against the viewing key, which both detects a string from another group and tells the wallet which key derivation the group uses.
Version 1 of the payload is byte-for-byte the zkfrost string that Zkool
already ships, so existing backups stay valid.
The string is more sensitive than the signing share it carries: it also carries , from which a holder can compute the group's quantum spending key 6. It never appears on chain.
Motivation
Several wallets now offer FROST groups for shielded ZEC: Zkool, Zafe, Konclave, and the Zcash Foundation's tools. Each one backs up its members in its own format, so a member can only restore their seat in the app that created it. In July 2026, Zkool's author described the situation: "There is no standard for it. I choose a way that makes it restorable on Zkool only. One day, someone will write a ZIP." 7
Since then, Zkool has moved to a minimal backup string (zkfrost, Zkool
6.35.0) that holds only what a member needs. This ZIP takes that string as its
first version, so that:
- a user can move a seat from one wallet to another, or restore it in a different app after losing a device;
- a group can mix implementations, which avoids having every signer come from one vendor;
- funds stay reachable if one app is abandoned.
ZIP 2005 adds a reason. To recover notes with the Recovery Protocol, "it is necessary to retain, or be able to rederive, , and also to know whether was used to derive the and addresses" 4. A backup format is where that knowledge has to live.
Privacy Implications
The string never leaves the member's custody in normal use and is not published on chain, so this ZIP changes nothing for on-chain privacy.
Combined with the group's unified full viewing key, a string gives no viewing capability beyond what that viewing key already gives. Without the viewing key, yields but not the incoming viewing key, which also depends on . The string does reveal , and the member's identifier.
A wallet that shows the string as a QR code exposes it to cameras and onlookers, as with any secret displayed on screen.
Requirements
- A member can be rebuilt from its string and the group's unified full viewing key, with no other member online.
- A wallet detects a string that belongs to another group before using it.
- The restore tells which derivation the group uses.
- The format does not depend on how was agreed.
- Backups made with Zkool's
zkfroststring stay valid. - The format can gain new versions without breaking existing strings.
Non-requirements
- Key generation, including how the members agree on 8.
- Signing, and the transport between members.
- Encryption of the string at rest (see [Open issues]).
- A birthday height. zips#1401 proposes to carry it in the unified full viewing key 9, which the restore already needs.
- Splitting between members.
- FROST groups using Sapling keys.
Specification
Derivations
Let , , , , and be as defined in § 4.2.3 of the protocol specification as updated by ZIP 2005 4.
ZIP 2005 derivation
: , and where . This is the branch that ZIP 2005 requires for FROST 5. Notes received by such a group are quantum recoverable.
Legacy derivation
: and , with the group verifying key as . Zkool created groups this way before supporting ZIP 2005. Such a group follows neither branch of § 4.2.3
Encoding
A member backup string is the Bech32m 10 encoding of a payload, with
the human-readable part zkfrost. As in ZIP 316 11, the length limit
of Bech32m is not applied; a version 1 string is 123 characters long.
Encoders MUST produce lowercase strings. Decoders MUST accept a string that is entirely lowercase or entirely uppercase, and MUST reject a string in mixed case, as Bech32m requires. A wallet can still show the string in uppercase, for instance in a QR code (see [Rationale]), by converting it after encoding.
The human-readable part is the same on Mainnet and Testnet, and the payload names no network: the network is given by the unified full viewing key used to restore. A string paired with another group's viewing key, on either network, fails the check in [Restore].
Payload, version 1
| Offset | Size | Field |
|---|---|---|
| 0 | 1 | format version, |
| 1 | 1 | member identifier |
| 2 | 1 | threshold |
| 3 | 1 | group size |
| 4 | 32 | signing share |
| 36 | 32 |
The member's FROST identifier is the Pallas scalar with integer value . The group size is the number of members.
The signing share is the little-endian encoding of a Pallas scalar, as in the
FROST(Pallas, BLAKE2b-512) ciphersuite 3. It MUST be the share that
matches the in the group's viewing key. Key generation can output
a group key with either bit; when it is , the group key and
every share are negated. The reddsa crate does this at the end of its key
generation, and frost-client does it after the DKG. An implementation that
skips this step would export a wrong share for about half of all groups.
Decoding
A decoder MUST reject the string if any of the following holds:
- it is not valid Bech32m, including a bad checksum, a Bech32 checksum, or mixed case;
- the bits left over when converting its data part to bytes are more than 4 or not all zero, as ZIP 173 requires 12. A version 1 string leaves 1 bit, so each payload has only one lowercase string;
- its human-readable part, compared case-insensitively, is not
zkfrost; - the format version is not ;
- the payload is not exactly 68 bytes long;
- , , , , or . FROST needs at least two signers.
Checking the version before the length lets a decoder report a string of a later version as unsupported rather than as corrupt.
Restore
The inputs are a member backup string and the group's unified full viewing key. The viewing key MUST contain an Orchard component (typecode 11), which holds , and . Ironwood uses the same Orchard-protocol keys.
- Decode the string as above.
- Reject the string if is not a valid Orchard spending key, that is if , or if the signing share is not the canonical encoding of a Pallas scalar. (A group's is not derived from ; the first check only keeps decoders in agreement with the validity rule for Orchard spending keys.)
- Take from the viewing key. Compute and under each derivation of [Derivations].
- If the ZIP 2005 derivation gives the viewing key's and , the group follows the ZIP 2005 derivation. Otherwise, if the legacy derivation does, the group follows the legacy derivation. Otherwise the wallet MUST reject the string: it belongs to another group.
- Rebuild the member's key material from the identifier, the signing share, the threshold and as the group verifying key. If the wallet knows the group's verifying shares, it SHOULD check that the member's verifying share matches.
A wallet SHOULD tell the user which derivation was found, and SHOULD warn when it is the legacy one, whose notes are not quantum recoverable.
The check in step 4 proves that the viewing key was derived from the string's , not that its is the group's (see [Security Considerations]). Before it shows an address to receive funds, a wallet SHOULD check the viewing key against a source the member trusts, such as the export made with its own string (see [Export]), an address of the group that the member recorded before and that the viewing key gives, or the same viewing key obtained from at least two other members. A member that lost its device may have lost its export too, and then has to ask the others for the key, which is the case the attack needs: if one of the members it asks is honest, a forged key shows up as a mismatch.
After the restore, the wallet MUST retain when the group follows the ZIP 2005 derivation. ZIP 2005 makes , which is derived from , essential for recovering the group's notes once the current Orchard protocol is disabled 5, and the signing share and viewing key alone do not give it. A wallet SHOULD also retain for a legacy group, since without it the member cannot export its string again.
A restored member holds no signing nonces. It MUST generate fresh ones for its next signing session.
The check in step 4 proves , not the signing share. A wrong share, such as one stored with the wrong sign, passes the restore and is caught at the member's first signing: the verifying shares of the signers, which travel with their commitments, interpolate to only if every one of them is right. The signing transport is out of scope for this ZIP, but a signing protocol used with these strings SHOULD carry each signer's verifying share, so that a member restored from a string is checked when it first signs.
Export
A wallet that exports a member backup string SHOULD export the group's unified full viewing key with it, or tell the user that the restore needs it. ZIP 2005 asks FROST members to retain the group verifying key 4; in version 1 it is only held by the viewing key.
A wallet MUST NOT put signing nonces, or any other secret beyond the fields above, in a version 1 string.
Storage and transmission
Wallets MUST protect every member backup string at least as securely and reliably as , as ZIP 2005 requires of 5, and not merely like one signing share.
Wallets SHOULD NOT store or transmit a string encrypted only under public-key encryption based on discrete logarithms or on factoring, such as Orchard note encryption, X25519, or RSA and elliptic-curve OpenPGP keys.
Rationale
Bech32m. It gives a checksum that catches copy errors and a string users can paste or show as a QR code. An uppercase string fits the QR alphanumeric mode, which makes the code smaller. Its checksum guarantees the detection of any error affecting at most 4 characters only for strings of at most 90 characters 12 10. A version 1 string is 123 characters long, so it keeps the 30-bit checksum without that guarantee, as unified addresses and viewing keys do 11.
No network in the string. A member's share is the same on every network the group lives on. The viewing key carries the network.
is not stored. The restore needs the viewing key anyway to scan the chain, and checking and against it catches a string from another group. The cost is that the string does not authenticate (see [Security Considerations]).
is stored whole. ZIP 2005 makes it necessary "to retain, or be able to rederive, " 4, and every member already holds it, along with 5.
The derivation is found, not recorded. Version 1 is Zkool's existing payload, which has no field for it. With two derivations and the viewing key at hand, trying both is cheap and unambiguous: both matching would need a collision in the hash functions behind . A later version can record the derivation explicitly (see [Open issues]).
One-byte identifier. It covers groups whose identifiers are the integers 1 to , as in Zkool and in frost-client's trusted-dealer key generation. frost-client's DKG and Zafe derive 32-byte identifiers, which need another version (see [Open issues]).
Version 1 is Zkool's payload. Backups users already made keep working, and the first implementation exists today.
Security Considerations
A string holds one signing share. Fewer than strings cannot spend, as long as discrete logarithms on the Pallas curve stay hard.
Every string also holds , which yields . ZIP 2005 notes that "a quantum adversary may be able to steal the funds with only access to , which is held by every participant" 5. Against such an adversary the threshold may give no protection: one leaked string, from any member, may be enough to steal the funds. ZIP 2005 requires each participant to "treat as a secret held at least as securely and reliably as " 5, which is why [Storage and transmission] asks the same for every string.
For the same reason, a string that is stored or sent encrypted only under public-key encryption based on discrete logarithms or on factoring is exposed to an adversary who records the ciphertext now and gains a quantum computer later. A passphrase with a memory-hard KDF and an AEAD (see [Open issues]) does not have this weakness.
The restore does not prove the signing share (see [Restore]). A wrong share, for instance one exported with the wrong sign (see [Payload, version 1]), gives signature shares that fail verification. It stops that member from signing until it is restored from a correct string, but it cannot sign anything or leak the other shares.
The restore does not authenticate either. Every member holds , so a dishonest member can build a viewing key around another , for instance one whose secret it holds alone, with the and that either derivation gives for it. A string restored with that key passes every check in [Restore], and the wallet then shows addresses that only that member can spend from. The wallet would not see the group's notes, and the group's signatures would not verify under that , but funds received at those addresses before anyone notices are lost to the group. Checking the viewing key against a trusted source, as [Restore] asks, prevents this; so would carrying in the string (see [Open issues]).
A member restored from a string has the same authority as before: the string adds no signer to the group. It carries no nonces, so a restore cannot bring back a nonce that was already used or published.
Deployment
This ZIP changes no consensus rule. Wallets can adopt it at any time.
Reference implementation
- Zkool:
rust/src/frost/backup.rsin hhanh00/zkool2, released in Zkool 6.35.0 13. - frost-zec:
src/backup.rsin OpenPGP-Wallet/frost-zec, by one of this ZIP's owners, written from this ZIP's rules apart from Zkool's code, withbackup exportandbackup restorecommands 14.
Test vectors
The test vectors are generated by the Python code proposed in
zcash/zcash-test-vectors#136 15, in three files:
frost_member_backup, frost_member_backup_decoding and
frost_member_backup_restore.
They cover a 2-of-3 ZIP 2005 group, a 2-of-3 legacy group together with the
ZIP 2005 viewing key built from the same and (whose
strings restore under either key, with a different derivation), and a 3-of-5
ZIP 2005 group; an uppercase string; seventeen strings that decoders must
reject, two of them for the padding rule and one with a threshold of 1; and
three cases that decode but that the restore must reject: a valid string
paired with another group's viewing key, a string whose signing share is not
canonical, and a viewing key with no Orchard component.
Every valid string decodes to the same fields and matches the stated
derivation with Zkool's code, and every -subset of members restored from
their strings produces a FROST signature that verifies under .
Zkool 6.35.0 rejects every invalid string except three. It does not check
the padding bits, and decodes the two padding strings to the same fields as
the valid string they come from; it also accepts a threshold of 1. Zkool
6.36.0 rejects all three: it has the padding fix, hhanh00/zkool2#1468
16, and its decoder now refuses a threshold below 2.
A third decoder, by the other owner of this ZIP, with the derivations written
directly from BIP 350, ZIP 316 and ZIP 2005 and no Zcash library, gives the
expected result for every string, both viewing keys of each group, and every
rejected case. With frost-client's version of reddsa, every -subset also
rebuilds and signs.
One member of the 2-of-3 ZIP 2005 group (byte strings in hex):
sk 24cf13c724758886048e05834d5d0a644e9c8e155d7e0bac3f25586b6caa11c3
ak a0dffa5f21db1b2c6cc72d9b6af9d777884c2c7d3e06ad479732bec46cee110d
nk e32868e0d0770d63ad17b8a807c8133bfb957c81fb973f1c43531c7cc15e5831
qsk 7193049fb014e408fd139cfcc47c58d6c20451473bab44ccb9fffc983ecf8ada
qk eec37a6202cd2f8b1bea8279bf87ef0dcb2bf73bbec28f077c4c5e1a743dd79f
rivk d0971e49314b6ad96d67af2766afc7db08fe6bf01f3ab0d84376016aad6ef419
t, n 2, 3
id 1
share 2caaa5adfcad8ac0bb8c50dcdbbc1e954825be403f97f44bc1d6c543e15d4f3b
string zkfrost1qyqsyqev42j6ml9d3tqthrzsmndmc854fqjmuspljl6yhswkc4p7zh208vjv7y78y36c3psy3czcxn2apfjya8ywz4whuzav8uj4s6mv4gguxm4ktp7
ufvk uview1mvknnjr2s9cuc7z5kjsd3twesswde37tpfw2z4v5a8jmsnwdwsg8sc2hdq302ud7j57d7dvmmt6mtc6c3fk7zkdyx5mrhq8fvyw8xp0dlgzl8kkwawt9j7m9z5adwpuskfscg5fyrpc0sca8lydtxfw7hfaaydtg88ayqj6y89a9jllhupn3hzgp7vq6d
Open issues
- Identifier encoding. frost-client's DKG derives identifiers as
Identifier::derive(session_id || pubkey), and Zafe from the vault id and the member's identity key, so groups created by either use full 32-byte identifiers (frost-client's trusted dealer uses 1 to ). For their groups to fit, a version 2 needs a type byte followed by a short or a full identifier. - Identifiers after a refresh. Refreshing shares to remove a member keeps the other members' identifiers 17, so a group can end up with identifiers 1, 2 and 4 and . Version 1 rejects member 4 (), so it covers only groups whose identifiers are 1 to , which is what Zkool creates. A version 2 could drop that check: the restore can match the identifier against the group's verifying shares when it knows them.
- Explicit derivation. A version 2 could record the derivation, so a restore does not depend on trying each one, and a third derivation could be added without ambiguity.
- Groups with no . zcash-sign's
generate, which Konclave uses, derives and from a random and then discards it, so version 1 cannot hold its groups. A version 2 could record that and come from the viewing key, leave out, and carry the viewing key itself (next item). Carrying only , as first suggested in zcash/zips#1413, would let the restore catch a viewing key from another group, but not a different viewing key for the same group. Such groups are not quantum recoverable under ZIP 2005. - The viewing key in the string, so that a restore needs only the string. For a group with no , the viewing key is the only source of and . A version 2 would carry the raw 96-byte Orchard full viewing key rather than its unified encoding, so that the string stays network-agnostic. With the viewing key in the string, nothing outside it is left to mismatch. For a group with , is enough, since and follow from and . Either way, would then come from the member's own backup rather than from a viewing key obtained elsewhere (see [Security Considerations]). A string for a group with no would be about 230 characters long, or 280 with a 32-byte identifier (see [Rationale] on Bech32m).
- Birthday height in version 2. A raw Orchard full viewing key does not carry the birthday height that zips#1401 would add to the unified encoding 9, so a string that restores on its own would need a birthday field of its own, as Konclave noted in zcash/zips#1413. A height only means something on one network, so the field would either name the network or apply to the network the wallet restores on.
- Passphrase profile. An optional version encrypting the payload with a memory-hard KDF and an AEAD, as Zafe does for its backups.
- Human-readable part.
zkfrostis kept for compatibility with existing backups. Should a later version use a more neutral one?
References
Footnotes
-
Information on BCP 14 — "RFC 2119: Key words for use in RFCs to Indicate Requirement Levels" and "RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words" ↩
-
Zcash Protocol Specification, Version 2026.7.0 [NU6.2] or later. Section 3.12: Mainnet and Testnet ↩
-
is not derived from , and does not commit to a . Notes received by such a group are therefore not quantum recoverable. ↩ ↩2 ↩3 ↩4 ↩5
-
ZIP 2005: Ironwood Quantum Recoverability — Usage with FROST ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7
-
ZIP 312: FROST for Spend Authorization Multisignatures — Key Generation ↩
-
zcash/zips#1401: Assign a Metadata Typecode for viewing key birthday height ↩ ↩2
-
zcash/zcash-test-vectors#136: Add test vectors for FROST member backup strings ↩
-
hhanh00/zkool2#1468: Bech32 decoders reject non-canonical padding ↩