Skip to content

feat: BIP39 passphrase (25th word) support for keystore seed derivation #1737

Description

@Thorian1te

Summary

Add optional BIP39 passphrase support to seed derivation across xchainjs keystore clients so consumers (notably asgardex-desktop) can offer a “25th word” for single-sig wallets.

Same recovery words + different passphrase → different keys/addresses. Empty passphrase must preserve today’s addresses (non-breaking).

Why

  • High-value software single-sig hardening without hardware
  • Duress / plausible deniability (decoy wallet vs passphrase wallet)
  • Passphrase is not part of the written mnemonic backup
  • App cannot do this alone: every client currently derives via getSeed(phrase) / fromMnemonic(phrase) with an implicit empty passphrase

Design (lib)

  1. @xchainjs/xchain-crypto: getSeed(phrase, bip39Passphrase = '')mnemonicToSeedSync(phrase, bip39Passphrase)
  2. @xchainjs/xchain-client: optional bip39Passphrase on XChainClientParams; store on BaseXChainClient; optional 3rd arg on setPhrase; clear on purgeClient
  3. Keystore clients: thread this.bip39Passphrase into all seed paths:
    • UTXO: btc, ltc, doge, bch, dash, zcash → getSeed(phrase, this.bip39Passphrase)
    • Cosmos family: thor, maya, cosmos, kuji → getSeed + cosmjs bip39Password
    • EVM: Mnemonic.fromPhrase(phrase, bip39Passphrase) in KeystoreSigner
    • solana, sui, radix, tron
    • cardano: from_bip39_entropy(entropy, passphraseBytes) (2nd arg is CIP-3 password; currently '')
    • ripple: fromMnemonic ignores passphrase in xrpl lib → use getSeed + fromSeed
  4. Skip xchain-monero (WIP / out of scope per monorepo rules) unless explicitly requested
  5. Changesets: minor (optional params only)

Non-goals (this issue)

  • Storing passphrase inside keystore JSON
  • Asgardex UI (follow-up app PR after publish)
  • Ledger / Vultisig passphrase models
  • Defaulting all users onto a passphrase

Security notes

  • Empty string === current BIP39 behavior
  • Passphrase should never be persisted by clients (app holds it only while unlocked)
  • Wrong passphrase cannot be cryptographically detected at unlock (valid alternate wallet)

Full plan

Living doc in this repo:

docs/BIP39_PASSPHRASE_PLAN.md

(If the file is only on a branch until merged, see that branch’s docs/BIP39_PASSPHRASE_PLAN.md.)

Includes: Asgardex Phase 2 UX, storage flag usesBip39Passphrase, QA matrix, risks.

Acceptance criteria

  • getSeed(phrase) unchanged vs today when passphrase omitted/empty
  • getSeed(phrase, p) differs by passphrase; covered by unit test
  • All keystore-capable packages above pass passphrase through consistently
  • Cardano + XRP special paths handled
  • Changesets + build/tests green on touched packages

Follow-up

  • asgardex-desktop: create/import/unlock UX, in-memory passphrase, usesBip39Passphrase wallet metadata, version bumps

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions