Protocol
Your account

The account signature

The single wallet signature every Umbra key is derived from, why signing again recovers the whole account, and the ways it can go wrong.

Umbra's clients, not the protocol

Deriving every key from one wallet signature is how Umbra's applications and SDK do it, so that a wallet alone can recover an account. Nothing on chain or in a proof checks it, and another client could generate keys at random or derive them some other way.

Your whole Umbra account comes from one signature: your main wallet signs a fixed Umbra message, and every key, the platform wallet and the encryption keypair are derived from the 64 bytes it returns. The same wallet signing the same message gives the same account on any device, on the same chain and deployment, which is the recovery mechanism and the main hazard at once.

Account signature
your main wallet signs one fixed, versioned message
  • Account context
    HKDF-SHA512, salt umbra/nimbus-solana/v0
    • Wallet seed
      labelled HKDF to a 24-word phrase under the platform-wallet domain, then its BIP-39 seed
      • Platform wallet
        SLIP-0010 at m/44'/501'/0'/0'
      • Identity master secret
        labelled HKDF, bound to the chain, the deployment and the platform wallet
        • Spending, nullifier, outgoing viewing and proof-authorisation keys
        • Eight slot blinders
        • Sharing polynomials
          for the registration split
        • ML-KEM-768 keypair
          FIPS 203 key generation from a seed
      • Journal key
        encrypts the local note store
Every branch is a deterministic function of the signature. Nothing is drawn at random.

The message

The message begins "Umbra Privacy: Signature v0" and asks the user to review the access it enables. It warns that the signature derives spending and viewing keys, explains local encrypted caching and the limits of clearing cached material, and authorizes operations permitted by the Terms, including restricted automation and applicable fees without a separate wallet confirmation. Signing the message itself does not submit a transaction. The notice includes non-custodial and third-party application risks, acceptance of the Terms, acknowledgement of the Privacy Policy, and their update procedures.

The exact notice uses UTF-8, LF line endings, no byte-order mark, and no leading or trailing whitespace. The SDK normalizes CRLF or CR to LF and applies trim() before hashing and encoding. Internal line breaks and spaces are preserved. Its 2,525 content bytes hash to:

1cfd6a3d865fd2da81c9f9c5675db58a89cd129e85151196cdd46beab890c24b

Before signing or restoring a signature, the SDK checks this SHA-256 pin and refuses a mismatch with message-altered. The wallet signs only the 2,525 readable notice bytes, with no binary preamble. This keeps ordinary message-signing prompts readable. The SHA-256 above is also the hash of the exact bytes signed.

The wallet adapter must sign these UTF-8 bytes verbatim. The SDK verifies the returned Ed25519 signature over the notice and wallet public key before HKDF. Signatures over modified or binary-framed messages, and signatures from another wallet, are rejected. Both creation and signature restoration require sha256, keypair and hkdf dependencies; creation additionally requires the connected wallet. Use HASH_TO_CURVE.sha256 from /primitives and KEYPAIR from /keypair for the standard bindings.

There is no nonce or timestamp, so signing again reproduces the same account. Account signing uses ordinary signMessage; wallets that cannot sign these exact bytes are unsupported for this flow. Hardware-wallet compatibility is not guaranteed. The platform-keypair SDK still supports Anza off-chain v1 for other operations, but account derivation does not use it or select signing formats by wallet.

From signature to keys

The SDK accepts only a 64-byte Ed25519 signature and refuses anything else, because a signature scheme that is not deterministic by specification would derive a different account next time.

  1. Account context. The signature is not uniformly random (half of it is a curve point), so it is first run through HKDF-SHA512: extract with the salt umbra/nimbus-solana/v0, then expand to 64 bytes. The salt is also the scheme's version marker.
  2. Wallet seed. The context becomes 32 bytes of entropy through HKDF-SHA512 in RFC 9180's labelled form, under a domain that names the chain (such as solana:mainnet), the deployment (such as mainnet or devnet-test), the application com.umbraprivacy.nimbus-solana, the purpose platform-wallet and a generation. Thirty-two bytes of entropy are exactly a 24-word BIP-39 phrase, and that phrase's BIP-39 seed, PBKDF2-HMAC-SHA512 at 2048 rounds, is the root of everything below. The SDK derives the seed once per session and holds it in memory.
  3. Platform wallet. The seed goes through a SLIP-0010 ed25519 derivation along Solana's path m/44'/501'/0'/0'. See the platform wallet.
  4. Identity master secret. The seed goes through HKDF-SHA512, again in labelled form, under a context that names the chain, the deployment, a generation and the platform wallet's public key, to a 64-byte master secret. Every identity key and blinder is an HKDF expansion of the master secret under its own label, such as key/spending, reduced to a non-zero BN254 field element. The ML-KEM-768 keypair comes from a 64-byte expansion fed to FIPS 203 key generation, and the two polynomials that split your outgoing viewing key at registration come from expansions under labels of their own. See your keys.
  5. Journal key. The key that encrypts your device's note store is drawn from the seed under its own purpose, note-journal.

Because each step is keyed by its own label, holding one derived value does not reveal any other. Because the chain, the deployment and the platform wallet all enter the derivation, the same signature gives an unrelated account on every chain and deployment.

Launch derivation versions

The account derivation namespace starts at v0 for the first launch. Every downstream key-derivation protocol already uses v0; platform-wallet and identity generations are both zero. The signature notice, extraction salt and expansion label must remain frozen after release. Changing a legal page does not require editing the signed notice.

Derivation layerLaunch namespace or generation
Readable signature noticeUmbra Privacy: Signature v0
Account contextumbra/nimbus-solana/v0
Platform-wallet seed and journal keyumbra-platform-keypair-solana-v0, generation 0
Identity master secret and its keysumbra-nimbus-identity-v0, generation 0
Registration sharing polynomialsumbra-nimbus-solana-sharing-v0
Key Words encodingumbra-key-words-v0

Chain, deployment, application, purpose, wallet account and identity wallet bindings continue separating derived values. Transaction versions, memo and note formats, guardian service domains, browser-session encryption namespaces and storage schema versions describe other protocols. They retain their existing versions; renaming them would require coordinated changes in their consumers.

Recovery is signing again

Nothing needs to be backed up beyond access to your main wallet. On a new device, connect the same wallet to the same deployment, sign the same message and the device re-derives everything, then rescans the note archive for your notes. See recovery.

The platform wallet's derivation passes through a standard 24-word phrase, but the Solana SDK does not show it, and offers no separate recovery phrase for the account. The main wallet is the backup.

Hazards

  • Wallets that alter the message are refused. The SDK verifies the signature against the exact ASCII notice and signing wallet before deriving key material. An adapter that alters the notice or adds a binary envelope receives signature-invalid.
  • A different key is a different account. Importing your wallet's seed into software that derives a different key, or signing with another wallet, gives another account.
  • A different chain or deployment is a different account. Both enter the derivation, so an application pointed at another deployment derives an unrelated account from the same signature, with no notes in it.
  • Different signing bytes or derivation versions produce different accounts. The launch "Signature v0" notice uses the salt umbra/nimbus-solana/v0. This is a frozen production scheme, not a disposable development version. Earlier development flows used different text, framing, or salts (including v2 and v3). Their accounts require the original signing bytes and original derivation inputs for recovery. Moving to the launch flow requires moving funds from any earlier account; clearing cached signatures does not migrate it.
  • A leak is permanent. Keys cannot be rotated in place. If the signature leaks, the only remedy is to move your funds to an account derived from a different wallet, and the old account's history stays readable to whoever holds it.

Last updated

On this page