Generate Ethereum, Bitcoin, etc. account seed and backup Mnemonics in SLIP-39 format (Trezor & Ledger compatible), with details in printable PDF format. Optionally, also print encrypted JSON and BIP-38 paper wallets.
#+title: SLIP-39 Wallet "Seed" Generation & Backup #+author: Perry Kundert #+email: perry@kundert.ca #+date: 2021-12-20 10:55:00 #+draft: false #+EXPORTFILENAME: README.pdf #+STARTUP: org-startup-with-inline-images inlineimages #+STARTUP: org-latex-tables-centered nil #+OPTIONS: ^:nil # Disable sub/superscripting with bare ; {...} still works #+OPTIONS: toc:nil
#+PROPERTY: header-args :exports both :results output
#+LATEX_HEADER: \usepackage[margin=1.333in]{geometry}
#+RESULTS:
#+BEGIN_ABSTRACT Creating Ethereum, Bitcoin and other accounts is complex and fraught with potential for loss of funds.
A 12- or 24-word BIP-39 seed recovery Mmnemonic Phrase helps, but a single lapse in security dooms the account (and all derived accounts, in fact). If someone finds your recovery phrase (or you lose it), the accounts derived from that seed are /gone/.
The SLIP-39 standard allows you to split the seed between 1, 2, or more groups of several mnemonic recovery phrases. This is better, but creating such accounts is difficult; presently, only the Trezor supports these directly, and they can only be created "manually". Writing down 5 or more sets of 20 words is difficult, error-prone and time consuming. #+END_ABSTRACT
#+TOC: headlines 2
- Hardware Wallet "Seed" Configuration
The [[https://github.com/pjkundert/python-slip39.git][python-slip39]] project (and the [[https://slip39.com/app][SLIP-39 macOS/win32 App]]) exists to assist in the safe creation, backup and documentation of [[https://wolovim.medium.com/ethereum-201-hd-wallets-11d0c93c87][Hierarchical Deterministic (HD) Wallet]] seeds and derived accounts, with various SLIP-39 sharing parameters. It generates the new random wallet seed, and generates the expected standard Ethereum account(s) (at [[https://medium.com/myetherwallet/hd-wallets-and-derivation-paths-explained-865a643c7bf2][derivation path]] m/44'/60'/0'/0/0 by default) and Bitcoin accounts (at Bech32 derivation path m/84'/0'/0'/0/0 by default), with wallet address and QR code (compatible with Trezor and Ledger derivations). It produces the required SLIP-39 phrases, and outputs a single PDF containing all the required printable cards to document the seed (and the specified derived accounts).
On an secure (ideally air-gapped) computer, new seeds can /safely/ be generated (*without trusting this program*) and the PDF saved to a USB drive for printing (or directly printed without the file being saved to disk.). Presently, =slip39= can output example ETH, BTC, LTC, DOGE, BSC, and XRP addresses derived from the seed, to /illustrate/ what accounts are associated with the backed-up seed. Recovery of the seed to a [[https://trezor.go2cloud.org/SHdv][Trezor Safe 3]] is simple, by entering the mnemonics right on the device.
We also support the backup of existing insecure and unreliable 12- or 24-word BIP-39 Mnemonic Phrases as SLIP-39 Mnemonic cards, for existing BIP-39 hardware wallets like the [[https://shop.ledger.com/pages/ledger-nano-x?r=2cd1cb6ae51f][Ledger Nano]], etc.! Use your existing BIP-39 Phrase as your Seed Source, select "Using BIP-39" (and enter your BIP-39 passphrase), and generate a set of SLIP-39 Mnemonic cards. Later, use the SLIP-39 App and set your Seed Source to Recover from your SLIP-39 Mnemonic cards, click "Using BIP-39" to get your BIP-39 Mnemonic back, and use it (and your passphrase) to recover your accounts to your Ledger (or other) hardware wallet.
Output of BIP-38 or JSON encrypted Paper Wallets is also supported, for import into standard software cryptocurrency wallets.
#+CAPTION: SLIP-39 App GUI #+ATTR_LATEX: :width 6in :options angle=0 [[./images/slip39.png]]
** TL;DR Backup and Recover your BIP-39 Mnemonic
Here's a full round-trip demonstration of: - Creating new (or "Backing Up" existing) Seed Entropy as a BIP-39 Mnemonic - Recovering the Seed Entropy from SLIP-39 (via [[https://iancoleman.io/slip39/]]) - Recovering the original BIP-39 (via [[https://iancoleman.io/bip39/]])
First, we generate SLIP-39 Cards representing a BIP-39 Mnemonic seed. Remember, your BIP-39 Mnemonic simply encodes your 128- or 256-bit Seed Entropy. So, we're not backing up your Mnemonic phrase -- we're backing up the raw seed data that is encoded into your BIP-39 Mnemonic. #+LATEX: {\scriptsize #+BEGIN_SRC bash :export both :results output drawer
python3 -m pip install slip39; slip39 -q --using-bip39 # to generate one from scratch, or
slip39 --secret "seven replace great luggage fox rent general tower guess inside smile sing" #+END_SRC#+RESULTS: :results: SLIP39-2025-11-22+14.18.44-ETH-0x6E6268F14B922cb924C7683A415B30C2bf967000.pdf :end:
#+LATEX: }
#+CAPTION: SLIP-39 Backup #+ATTR_LATEX: :float wrap :width 2in :placement {r} [[./images/SLIP39-backup-BIP39.png]]
If you look at the generated SLIP39 PDF, you'll see that the cover page contains the original BIP-39 Mnemonic phrase (for confirmation), and generates a number of SLIP-39 Mnemonic cards. These cards encode the original Seed Entropy, and are what you use to recover the BIP-39 Mnemonic whenever you need it.
I recommend that you /tear off and destroy/ the BIP-39 Mnemonic from the cover sheet, once you've confirmed you can recover it anytime you want, and you've set up your hardware wallet, and confirmed that it contains the same cryptocurrency addresses displayed in the PDF.
#+CAPTION: BIP-39 Entropy #+ATTR_LATEX: :float wrap :width 3in :placement {l} [[./images/SLIP39-recover-BIP39-entropy.png]]
Practice this full round-trip several times with a bad BIP-39 Mnemonic like "zoo zoo ... wrong". This is the only way to become comfortable with your ability to recover your original seed data, and (hence) your BIP-39 Mnemonic.
Later, when you need to recover your BIP-39 Seed Entropy and Mnemonic, use this SLIP-39 App or [[https://iancoleman.io/slip39/]] and enter some of your SLIP-39 Mnemonic Cards. These may need to be collected from friends and family.
#+CAPTION: BIP-39 Mnemonic #+ATTR_LATEX: :float wrap :width 2in :placement {r} [[./images/SLIP39-recover-BIP39-mnemonic.png]]
In this case, we're using the First and Second cards, intended for you to secure, separately from each other; for example, in two safes or other secure locations like locked filing cabinets, at 2 locations known to you and your partner(s):
Finally, convert the recovered Seed Entropy back to your BIP-39 Mnemonic. This requires 2 steps if you use [[https://iancoleman.io/bip39/]]
In this step, we're simply converting the recovered Seed Entropy back into its BIP-39 Mnemonic. You need to select the "[X] show entropy details" checkbox in order to enter the raw Seed Entropy we've recovered in the last step:
Alternatively, you can use the SLIP-39 App or the =slip39-recovery= command-line tool, and do it all in one step. This illustrates recovering your BIP-39 Mnemonic from the SLIP-39 Cards generated in the first step:
#+LATEX: {\scriptsize #+BEGIN_SRC bash :exports both :results output drawer python3 -m slip39.recovery --using-bip39 \ -m "pitch negative acrobat romp desert usual negative darkness friar artist estimate aluminum beard crowd email season guard hybrid kidney cards" \ -m "pitch negative beard romp diagnose timely ruler emission acrobat adult stilt dress typical blue inmate lilac pajamas trend duration endless" #+END_SRC
#+RESULTS: :results: seven replace great luggage fox rent general tower guess inside smile sing :end:
#+LATEX: }
- Security with Availability
Losing this seed means that all of the HD Wallet accounts are permanently lost. It must be /both/ backed up securely, /and/ be readily accessible.
Therefore, we must:
- Ensure that nobody untrustworthy can recover the seed, but - Store the seed in many places, probably with several (some perhaps untrustworthy) people.
How can we address these conflicting requirements?
** Shamir's Secret Sharing System (SSSS)
[[https://github.com/satoshilabs/slips/blob/master/slip-0039.md][Satoshi Lab's (Trezor) SLIP-39]] uses SSSS to distribute the ability to recover the key to 1 or more "groups". Collecting the mnemonics from the required number of groups allows recovery of the seed.
For BIP-39, the number of groups is always 1, and the number of mnemonics required for that group is always 1. This selection is both insecure (easy to accidentally disclose) and unreliable (easy to accidentally lose), but since most hardware wallets only accept BIP-39 phrases, we also provide a way to /backup your BIP-39 phrase/ using SLIP-39!
For SLIP-39, you specify a "group_threshold" of /how many/ of your groups must be successfully collected, to recover the seed; this seed is (conceptually) split between 1 or more groups (though not in reality -- each group's data /alone/ gives away /no information/ about the seed).
For example, you might have First, Second, Fam and Frens groups, and decide that any 2 groups can be combined to recover the seed. Each group has members with varying levels of trust and persistence, so have different number of Members, and differing numbers Required to recover that group's data:
#+LATEX: {\scriptsize | Group | Required | | Members | Description | |--------+----------+---+---------+---------------------------------------| | |
The account owner might store their First and Second group data in their home and office safes. These are 1/1 groups (1 required, and only 1 member, so each of these are 1-card groups.)
If the Seed needs to be recovered, collecting the First and Second cards from the home and office safe is sufficient to recover the Seed, and re-generate all of the HD Wallet accounts.
Only 2 Fam group member's cards must be collected to recover the Fam group's data. So, if the HD Wallet owner loses their home (and the one and only First group card) in a fire, they could get the one Second group card from the office safe, and also 2 cards from Fam group members, and recover the Seed and all of their wallets.
If catastrophe strikes and the wallet owner dies, and the heirs don't have access to either the First (at home) or Second (at the office) cards, they can collect 2 Fam cards and 3 Frens cards (at the funeral, for example), completing the Fam and Frens groups' data, and recover the Seed, and all derived HD Wallet accounts.
Since Frens are less likely to persist long term, we'll produce more (6) of these cards. Depending on how trustworthy the group is, adjust the Fren group's Required number higher (less trustworthy, more likely to know each-other, need to collect more to recover the group), or lower (more trustworthy, less likely to collude, need less to recover).
- SLIP-39 Account Creation, Recovery and Generation
** Creating New SLIP-39 Recoverable Seeds
This is what the first page of the output SLIP-39 mnemonic cards PDF looks like:
#+CAPTION: SLIP-39 Cards PDF (from =--secret ffff...=) #+ATTR_LATEX: :width 5in :options angle=0 [[./images/slip39-cards.png]]
Run the following to obtain a PDF file containing business cards with the default SLIP-39 groups for a new account Seed named "Personal" (usable with any hardware wallet with SLIP-39 support, such as the Trezor Safe) ; insert a USB drive to collect the output, and run:
#+LATEX: {\scriptsize #+BEGIN_EXAMPLE $ python3 -m pip install slip39 # Install slip39 in Python3 $ cd /Volumes/USBDRIVE/ # Change current directory to USB $ python3 -m slip39 Personal # Or just run "slip39 Personal" 2022-11-22 05:35:21 slip39.layout ETH m/44'/60'/0'/0/0 : 0x0F04cab1855CE275bd098c918075373EB3944Ba3 2022-11-22 05:35:21 slip39.layout BTC m/84'/0'/0'/0/0 : bc1qszvts5vyxy265er6ngk3ew4utx5sll2ck2m7m2 2022-11-22 05:35:22 slip39.layout Writing SLIP39-encoded wallet for 'Personal' to:\ Personal-2022-11-22+05.35.22-ETH-0x0F04cab1855CE275bd098c918075373EB3944Ba3.pdf #+END_EXAMPLE #+LATEX: }
The resultant PDF will be output into the designated file. This PDF file contains business card sized SLIP-39 Mnemonic cards, and will print on a single page of 8-1/2"x11" paper or card stock, and the cards can be cut out (=--card index=, =credit=, =half= (page), =third= and =quarter= are also available, as well as 4x6 =photo= and custom ="(
* BIP-39 Mnemonic Phrase Backup using SLIP-39
To obtain the Seed in BIP-39 format, with its original "entropy" backed up using SLIP-39 (supporting any BIP-39 hardware wallet, and recoverable from the Mnemonic cards using SLIP-39), use the =--using-bip39= option:
#+LATEX: {\scriptsize #+BEGIN_EXAMPLE $ slip39 --using-bip39 Personal-BIP-39 2022-11-22 05:47:13 slip39.layout ETH m/44'/60'/0'/0/0 : 0x927232296120343A89DeAb15F108a420087a2Ef3 2022-11-22 05:47:13 slip39.layout BTC m/84'/0'/0'/0/0 : bc1qgs6xg5kvrrxp4579y22a4tf0d8me4dslwxjr9x 2022-11-22 05:47:15 slip39.layout Writing SLIP39 backup for BIP-39-encoded wallet for 'Personal-BIP-39' to:\ Personal-BIP-39-2022-11-22+05.47.15-ETH-0x927232296120343A89DeAb15F108a420087a2Ef3.pdf #+END_EXAMPLE #+LATEX: }
This is the best approach, if you want a new Seed and need to support a BIP-39-only Hardware Wallet. (If you already have a BIP-39 Mnemonic Phrase, see [[Pipelining Backup of a BIP-39 Mnemonic Phrase]])
* Paper Wallets for Software Wallet Support
The Trezor hardware wallet natively supports the input of SLIP-39 Mnemonics. However, most software wallets do not (yet) support SLIP-39. So, how do we load the Crypto wallets produced from our Seed into software wallets such as the Metamask plugin or the Brave browser, for example?
The =slip39.gui= (and the macOS/win32 SLIP-39.App) support output of standard BIP-38 encrypted wallets for Bitcoin-like cryptocurrencies such as BTC, LTC and DOGE. It also outputs encrypted Ethereum JSON wallets for ETH. Here is how to produce them (from a test secret Seed; exclude =--secret ffff...= for yours!):
#+LATEX: {\scriptsize #+BEGIN_SRC bash :exports both slip39 -c ETH -c BTC -c DOGE -c LTC --secret ffffffffffffffffffffffffffffffff \ --no-card --wallet password --wallet-hint 'bad:pass...' 2>&1 #+END_SRC
#+RESULTS: : 2025-11-22 14:55:22 slip39 It is recommended to not use '-s|--secret
#+LATEX: }
And what they look like: #+CAPTION: Paper Wallets (from =--secret ffff...=) #+ATTR_LATEX: :width 5in :options angle=0 [[./images/slip39-wallets.png]]
To recover your real SLIP-39 Seed Entropy and print wallets, use the SLIP-39 App's "Recover" Controls, or to do so on the command-line, use =slip39-recover=:
#+LATEX: {\scriptsize #+BEGIN_SRC bash :exports both slip39-recovery -v \ --mnemonic "material leaf acrobat romp charity capital omit skunk change firm eclipse crush fancy best tracks flip grownup plastic chew peanut" \ --mnemonic "material leaf beard romp disaster duke flame uncover group slice guest blue gums duckling total suitable trust guitar payment platform" \ 2>&1 #+END_SRC
#+RESULTS: : 2025-11-22 14:55:53 slip39.recovery Recovered 128-bit Encrypted SLIP-39 Seed Entropy using 2 groups comprising 2 mnemonics : 2025-11-22 14:55:53 slip39.recovery Seed decoded from SLIP-39 Mnemonics w/ no passphrase : 2025-11-22 14:55:53 slip39.recovery Recovered SLIP-39 secret; To re-generate SLIP-39 wallet, send it to: python3 -m slip39 --secret - : ffffffffffffffffffffffffffffffff
#+LATEX: }
You can run this as a command-line pipeline. Here, we use some SLIP-39 Mnemonics that encode the =ffff...= Seed Entropy; note that the wallets match those output above: #+LATEX: {\scriptsize #+BEGIN_SRC bash :exports both slip39-recovery \ --mnemonic "material leaf acrobat romp charity capital omit skunk change firm eclipse crush fancy best tracks flip grownup plastic chew peanut" \ --mnemonic "material leaf beard romp disaster duke flame uncover group slice guest blue gums duckling total suitable trust guitar payment platform" \ | slip39 -c ETH -c BTC -c DOGE -c LTC --secret - \ --no-card --wallet password --wallet-hint 'bad:pass...' \ 2>&1 #+END_SRC
#+RESULTS: : 2025-11-22 14:56:05 slip39 It is recommended to not use '-w|--wallet
#+LATEX: }
* Supported Cryptocurrencies
While the SLIP-39 Seed is not cryptocurrency-specific (any wallet for any cryptocurrency can be derived from it), each type of cryptocurrency has its own standard derivation path (eg. =m/44'/3'/0'/0/0= for DOGE), and its own address representation (eg. Bech32 at =m/84'/0'/0'/0/0= for BTC eg. =bc1qcupw7k8enymvvsa7w35j5hq4ergtvus3zk8a8s=).
When you import your SLIP-39 Seed into a Trezor, you gain access to all derived HD cryptocurrency wallets supported directly by that hardware wallet, and indirectly, to any coin and/or blockchain network supported by any wallet software (eg. Metamask). | Crypto | Semantic | Path | Address | Support | |--------+----------+-------------------+---------+---------| | ETH | Legacy | m/44'/ 60'/0'/0/0 | 0x... | | | BTC | Legacy | m/44'/ 0'/0'/0/0 | 1... | | | | SegWit | m/49'/ 0'/0'/0/0 | 3... | | | | Bech32 | m/84'/ 0'/0'/0/0 | bc1... | | | LTC | Legacy | m/44'/ 2'/0'/0/0 | L... | | | | SegWit | m/49'/ 2'/0'/0/0 | M... | | | | Bech32 | m/84'/ 2'/0'/0/0 | ltc1... | | | DOGE | Legacy | m/44'/ 3'/0'/0/0 | D... | | | BNB | Legacy | m/44'/714'/0'/0/0 | 0x... | Beta | | XRP | Legacy | m/44'/144'/0'/0/0 | rUP... | Beta |
** ETH, BTC, LTC, DOGE
These coins are natively supported both directly by the Trezor hardware wallet, and by most software wallets and "web3" platforms that interact with the Trezor, or can import the BIP-38 or Ethereum JSON Paper Wallets produced by =python-slip39=.
** Binance Smart Chain (BNB): binance.com
The Binance Smart Chain uses standard Ethereum addresses, but at a different derivation path.
** Ripple (XRP)
Support for Ripple addresses is presently tested, but still considered Beta quality.
** Other HDWallet support cryptocurrencies
Any other cryptocurrency supported by HDWallet is available (so long as it supports BIP44, and are also considered Beta quality.
** The macOS/win32 =SLIP-39.app= GUI App
If you prefer a graphical user-interface, try the macOS/win32 SLIP-39.App. You can run it directly if you install Python 3.9+ from [[https://python.org/downloads][python.org/downloads]] or using homebrew =brew install python-tk@3.10=. Then, start the GUI in a variety of ways:
#+LATEX: {\scriptsize #+BEGIN_EXAMPLE slip39-gui python3 -m slip39.gui #+END_EXAMPLE #+LATEX: }
Alternatively, download and install the macOS/win32 GUI App .zip, .pkg or .dmg installer from [[https://github.com/pjkundert/python-slip39/releases/latest][github.com/pjkundert/python-slip-39/releases]].
** The Python =slip39= CLI
From the command line, you can create SLIP-39 Seed Mnemonic card PDFs.
* =slip39= Synopsis
The full command-line argument synopsis for =slip39= is:
#+LATEX: {\scriptsize #+BEGIN_SRC bash :exports both :results raw slip39 --help 2>&1 | sed 's/^/: /' # (just for output formatting) #+END_SRC
#+RESULTS: : usage: slip39 [-h] [-v] [-q] [-o OUTPUT] [-t THRESHOLD] [-g GROUP] [-f FORMAT] : [-c CRYPTOCURRENCY] [-p PATH] [-j JSON] [-w WALLET] : [--wallet-hint WALLETHINT] [--wallet-format WALLETFORMAT] : [-s SECRET] [-e ENTROPY] [--show] [--no-show] [--bits BITS] : [--using-bip39] [--anonymous] [--passphrase PASSPHRASE] : [-C CARD] [--no-card] [--paper PAPER] [--cover] [--no-cover] : [--identifier IDENTIFIER] [--extendable] [--no-extendable] : [--text] [--watermark WATERMARK] [--double-sided] : [--no-double-sided] [--single-sided] : [names ...] : : Create and output SLIP-39 encoded Seeds and Paper Wallets to a PDF file. : : positional arguments: : names Account names to produce; if --secret Entropy is : supplied, only one is allowed. : : options: : -h, --help show this help message and exit : -v, --verbose Display logging information. : -q, --quiet Reduce logging output. : -o OUTPUT, --output OUTPUT : Output PDF to file or '-' (stdout: use -q!); : formatting w/ name, date, time, crypto, path, address : allowed : -t THRESHOLD, --threshold THRESHOLD : Number of groups required for recovery (default: half : of groups, rounded up) : -g GROUP, --group GROUP : A group name[[
#+LATEX: }
** Recovery & Re-Creation
Later, if you need to recover the wallet seed, keep entering SLIP-39 mnemonics into =slip39-recovery= until the secret is recovered (invalid/duplicate mnemonics will be ignored):
#+LATEX: {\scriptsize #+BEGIN_EXAMPLE $ python3 -m slip39.recovery # (or just "slip39-recovery") Enter 1st SLIP-39 mnemonic: ab c Enter 2nd SLIP-39 mnemonic: veteran guilt acrobat romp burden campus purple webcam uncover ... Enter 3rd SLIP-39 mnemonic: veteran guilt acrobat romp burden campus purple webcam uncover ... Enter 4th SLIP-39 mnemonic: veteran guilt beard romp dragon island merit burden aluminum worthy ... 2021-12-25 11:03:33 slip39.recovery Recovered SLIP-39 secret; Use: python3 -m slip39 --secret ... 383597fd63547e7c9525575decd413f7 #+END_EXAMPLE #+LATEX: }
Finally, re-create the wallet seed, perhaps including an encrypted JSON Paper Wallet for import of some accounts into a software wallet (use =--json password= to output encrypted Ethereum JSON wallet files):
#+LATEX: {\scriptsize #+BEGIN_SRC bash :exports both slip39 --secret 383597fd63547e7c9525575decd413f7 --wallet password --wallet-hint bad:pass... 2>&1 #+END_SRC
#+RESULTS: : 2025-11-22 14:56:40 slip39 It is recommended to not use '-s|--secret
#+LATEX: }
* =slip39.recovery= Synopsis
#+LATEX: {\scriptsize #+BEGIN_SRC bash :exports both :results raw python3 -m slip39.recovery --help 2>&1 | sed 's/^/: /' # (just for output formatting) #+END_SRC
#+RESULTS: : usage: main.py [-h] [-v] [-q] [-m MNEMONIC] [-e] [--no-entropy] [-b] [-u] : [--binary] [--language LANGUAGE] [-p PASSPHRASE] : : Recover and output secret Seed from SLIP-39 or BIP-39 Mnemonics : : options: : -h, --help show this help message and exit : -v, --verbose Display logging information. : -q, --quiet Reduce logging output. : -m MNEMONIC, --mnemonic MNEMONIC : Supply another SLIP-39 (or a BIP-39) mnemonic phrase : -e, --entropy Return the BIP-39 Mnemonic Seed Entropy instead of the : generated Seed (default: True if --using-bip39 w/o : passphrase) : --no-entropy Return the BIP-39 Mnemonic generated Seed : -b, --bip39 Recover Entropy and generate 512-bit secret Seed from : BIP-39 Mnemonic + passphrase : -u, --using-bip39 Recover Entropy from SLIP-39, generate 512-bit secret : Seed using BIP-39 Mnemonic + passphrase : --binary Output seed in binary instead of hex : --language LANGUAGE BIP-39 Mnemonic language (default: english) : -p PASSPHRASE, --passphrase PASSPHRASE : Decrypt the SLIP-39 or BIP-39 master secret w/ this : passphrase, '-' reads it from stdin (default: None/'') : : If you obtain a threshold number of SLIP-39 mnemonics, you can recover the original : secret Seed Entropy, and then re-generate one or more wallets from it. : : Enter the mnemonics when prompted and/or via the command line with -m |--mnemonic "...". : : The secret Seed Entropy can then be used to generate a new SLIP-39 encoded wallet: : : python3 -m slip39 --secret = "ab04...7f" : : SLIP-39 Mnemonics may be encrypted with a passphrase; this is not Ledger-compatible, so it rarely : recommended! Typically, on a Trezor, you recover using your SLIP-39 Mnemonics, and then use the : "Hidden wallet" feature (passwords entered on the device) to produce alternative sets of accounts. : : BIP-39 Mnemonics can be backed up as SLIP-39 Mnemonics, in two ways: : : 1) The actual BIP-39 standard 512-bit Seed can be generated by supplying --passphrase, but only at : the cost of 59-word SLIP-39 mnemonics. This is because the output 512-bit BIP-39 Seed must be : stored in SLIP-39 -- not the input 128-, 160-, 192-, 224-, or 256-bit entropy used to create the : original BIP-39 mnemonic phrase. : : 2) The original BIP-39 12- or 24-word, 128- to 256-bit Seed Entropy can be recovered by supplying : --entropy. This modifies the BIP-39 recovery to return the original BIP-39 Mnemonic Entropy, before : decryption and seed generation. It has no effect for SLIP-39 recovery. #+LATEX: }
* Pipelining =slip39.recovery | slip39 --secret -=
The tools can be used in a pipeline to avoid printing the secret. Here we generate some mnemonics, sorting them in reverse order so we need more than just the first couple to recover. Observe the Ethereum wallet address generated.
Then, we recover the master secret seed in hex with =slip39-recovery=, and finally send it to =slip39 --secret -= to re-generate the same wallet as we originally created.
#+LATEX: {\scriptsize #+BEGIN_SRC bash :exports both ( python3 -m slip39 --text --no-card \ | ( sort -r ; echo "...later, after recovering SLIP-39 mnemonics..." 1>&2 ) \ | python3 -m slip39.recovery \ | python3 -m slip39 --secret - --no-card \ ) 2>&1 #+END_SRC
#+RESULTS: : 2025-11-22 14:57:14 slip39 Generated 128-bit SLIP-39 Mnemonics w/ identifier 6037 requiring 2 of 4 (extendable) groups to recover : 2025-11-22 14:57:14 slip39.layout ETH m/44'/60'/0'/0/0 : 0x4240cF80a18790C161783c2686c64F53CCd7a02E : 2025-11-22 14:57:14 slip39.layout BTC m/84'/0'/0'/0/0 : bc1qsll7nfaa2xcmuyykfsz0w4lrq6p3au6aq2tqns : ...later, after recovering SLIP-39 mnemonics... : 2025-11-22 14:57:14 slip39 Generated 128-bit SLIP-39 Mnemonics w/ identifier 13225 requiring 2 of 4 (extendable) groups to recover : 2025-11-22 14:57:14 slip39.layout ETH m/44'/60'/0'/0/0 : 0x4240cF80a18790C161783c2686c64F53CCd7a02E : 2025-11-22 14:57:14 slip39.layout BTC m/84'/0'/0'/0/0 : bc1qsll7nfaa2xcmuyykfsz0w4lrq6p3au6aq2tqns
#+LATEX: }
* Pipelining Backup of a BIP-39 Mnemonic Phrase
A primary use case for =python-slip39= will be to backup an existing BIP-39 Mnemonic Phrase to SLIP-39 cards, so here it is. Suppose you have some (arbitrary) way to recover (or generate) some Entropy; for example, by recovering the original seed entropy used to generate a BIP-39 Mhemonic:
#+LATEX: {\scriptsize #+BEGIN_SRC bash :exports both ( python3 -m slip39.recovery --bip39 --entropy \ --mnemonic "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong" \ | python3 -m slip39 --using-bip39 --secret - \ ) 2>&1 #+END_SRC
#+RESULTS: : 2025-11-22 14:57:23 slip39 Assuming BIP-39 seed entropy: Ensure you recover and use via a BIP-39 Mnemonic : 2025-11-22 14:57:23 slip39 Generated 128-bit SLIP-39 Mnemonics w/ identifier 8676 requiring 2 of 4 (extendable) groups to recover : 2025-11-22 14:57:23 slip39.layout ETH m/44'/60'/0'/0/0 : 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E : 2025-11-22 14:57:23 slip39.layout BTC m/84'/0'/0'/0/0 : bc1qk0a9hr7wjfxeenz9nwenw9flhq0tmsf6vsgnn2 : 2025-11-22 14:57:25 slip39.layout Writing SLIP39 backup for BIP-39-encoded wallet for 'SLIP39' to: SLIP39-2025-11-22+14.57.25-ETH-0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E.pdf : SLIP39-2025-11-22+14.57.25-ETH-0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E.pdf
#+LATEX: }
Better yet, if you already have a BIP-39 Mnemonic, you can just use that directly (we'll use a bit of "wrapping" around the filename output, so the first page shows up here):
#+LATEX: {\scriptsize #+BEGIN_SRC bash :exports both :results output raw echo -n "[[./$( \ python3 -m slip39 --secret "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong" --output ./images/SLIP39-Example.pdf \ )]]"
#+END_SRC
#+RESULTS: [[././images/SLIP39-Example.pdf]]
Note the presence of the BIP-39 recovery phrase on the cover sheet; this is recovered by round-tripping the original BIP-39 seed entropy, through SLIP-39, and re-encoding back to BIP-39.
** Generation of Addresses
For systems that require a stream of groups of wallet Addresses (eg. for preparing invoices for clients, with a choice of cryptocurrency payment options), =slip-generator= can produce a stream of groups of addresses.
* =slip39-generator= Synopsis #+LATEX: {\scriptsize #+BEGIN_SRC bash :exports both :results raw slip39-generator --help --version | sed 's/^/: /' # (just for output formatting) #+END_SRC
#+RESULTS: : usage: slip39-generator [-h] [-v] [-q] [-s SECRET] [-f FORMAT] [--xpub] : [--no-xpub] [-c CRYPTOCURRENCY] [--path PATH] : [-d DEVICE] [--baudrate BAUDRATE] [-e ENCRYPT] : [--decrypt ENCRYPT] [--enumerated] [--no-enumerate] : [--receive] [--corrupt CORRUPT] : : Generate public wallet address(es) from a secret seed : : options: : -h, --help show this help message and exit : -v, --verbose Display logging information. : -q, --quiet Reduce logging output. : -s SECRET, --secret SECRET : Use the supplied 128-, 256- or 512-bit hex value as : the secret seed; '-' (default) reads it from stdin : (eg. output from slip39.recover) : -f FORMAT, --format FORMAT : Specify crypto address formats: legacy, segwit, : bech32; default: BNB:legacy, BTC:bech32, DOGE:legacy, : ETH:legacy, LTC:bech32, XRP:legacy : --xpub Output xpub... instead of cryptocurrency wallet : address (and trim non-hardened default path segments) : --no-xpub Inhibit output of xpub (compatible w/ pre-v10.0.0) : -c CRYPTOCURRENCY, --cryptocurrency CRYPTOCURRENCY : A crypto name and optional derivation path (default: : "ETH:{Account.path_default('ETH')}"), optionally w/ : ranges, eg: ETH:../0/- : --path PATH Modify all derivation paths by replacing the final : segment(s) w/ the supplied range(s), eg. '.../1/-' : means .../1/[0,...) : -d DEVICE, --device DEVICE : Use this serial device to transmit (or --receive) : records : --baudrate BAUDRATE Set the baud rate of the serial device (default: : 115200) : -e ENCRYPT, --encrypt ENCRYPT : Secure the channel from errors and/or prying eyes with : ChaCha20Poly1305 encryption w/ this password; '-' : reads from stdin : --decrypt ENCRYPT : --enumerated Include an enumeration in each record output (required : for --encrypt) : --no-enumerate Disable enumeration of output records : --receive Receive a stream of slip.generator output : --corrupt CORRUPT Corrupt a percentage of output symbols : : Once you have a secret seed (eg. from slip39.recovery), you can generate a sequence : of HD wallet addresses from it. Emits rows in the form: : :
#+LATEX: }
* Producing Addresses :PROPERTIES: :ID: D38209C2-DFD1-4C46-BCB4-BEF5B1BDC433 :END:
Addresses can be produced in plaintext or encrypted, and output to stdout or to a serial port.
#+LATEX: {\scriptsize #+BEGIN_SRC bash :exports both echo ffffffffffffffffffffffffffffffff | slip39-generator --secret - --path '../-3' 2>&1 #+END_SRC
#+RESULTS: : 0: [["ETH", "m/44'/60'/0'/0/0", "0x824b174803e688dE39aF5B3D7Cd39bE6515A19a1"], ["BTC", "m/84'/0'/0'/0/0", "bc1q9yscq3l2yfxlvnlk3cszpqefparrv7tk24u6pl"]] : 1: [["ETH", "m/44'/60'/0'/0/1", "0x8D342083549C635C0494d3c77567860ee7456963"], ["BTC", "m/84'/0'/0'/0/1", "bc1qnec684yvuhfrmy3q856gydllsc54p2tx9w955c"]] : 2: [["ETH", "m/44'/60'/0'/0/2", "0x52787E24965E1aBd691df77827A3CfA90f0166AA"], ["BTC", "m/84'/0'/0'/0/2", "bc1q2snj0zcg23dvjpw7m9lxtu0ap0hfl5tlddq07j"]] : 3: [["ETH", "m/44'/60'/0'/0/3", "0xc2442382Ae70c77d6B6840EC6637dB2422E1D44e"], ["BTC", "m/84'/0'/0'/0/3", "bc1qxwekjd46aa5n0s3dtsynvtsjwsne7c5f5w5dsd"]]
#+LATEX: }
To produce accounts from a BIP-39 or SLIP-39 seed, recover it using slip39-recovery.
Here's an example of recovering a test BIP-39 seed; note that it yields the well-known ETH =0xfc20...1B5E= and BTC =bc1qk0...gnn2= accounts associated with this test Mnemonic:
#+LATEX: {\scriptsize #+BEGIN_SRC bash :exports both ( python3 -m slip39.recovery --bip39 --mnemonic 'zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong' \ | python3 -m slip39.generator --secret - --path '../-3' --format 'BTC:segwit' --crypto 'DOGE' ) 2>&1 #+END_SRC
#+RESULTS: : 0: [["DOGE", "m/44'/3'/0'/0/0", "DTMaJd8wqye1fymnjxZ5Cc5QkN1w4pMgXT"], ["BTC", "m/49'/0'/0'/0/0", "3CfyLSjYFFV6MUAMh3auTK9kfpPscPCHth"]] : 1: [["DOGE", "m/44'/3'/0'/0/1", "DGkL2LD5FfccAaKtx8G7TST5iZwrNkecTY"], ["BTC", "m/49'/0'/0'/0/1", "31nD3MEioUDchu7bVaHUCdCa4vxxsqDYwu"]] : 2: [["DOGE", "m/44'/3'/0'/0/2", "DQa3SpFZH3fFpEFAJHTXZjam4hWiv9muJX"], ["BTC", "m/49'/0'/0'/0/2", "32pqj8rgW1BdXK2Cygwn2JVYPnVRknfTE4"]] : 3: [["DOGE", "m/44'/3'/0'/0/3", "DTW5tqLwspMY3NpW3RrgMfjWs5gnpXtfwe"], ["BTC", "m/49'/0'/0'/0/3", "3CimS2PfrNykKtJe1uxM4QtaDopaFHdVN1"]]
#+LATEX: }
We can encrypt the output, to secure the sequence (and due to integrated MACs, ensures no errors occur over an insecure channel like a serial cable):
#+LATEX: {\scriptsize #+BEGIN_SRC bash :exports both ( slip39-recovery --bip39 --mnemonic 'zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong' \ | slip39-generator --secret - --path '../-3' --encrypt 'password' ) 2>&1 \ | sed -E 's/^(.{100})(.{1,})$/\1.../' # (shorten output) #+END_SRC
#+RESULTS: : : : nonce: 6adebead989ad33697f6b016b94ea579f468c05224fd036dab5f1029 : 0: 5d4c3a90b621b89e8833b763b004d459c2cb6cc09c9d0884a1a4e4e144e750597609d8f2950203524ecea344879e6... : 1: e5b4bece96d4616688db6127fdf86cbe5dcfab0ddf9e2ac6f461ecf65480e19299408c619c8adf817f93bf14a5216... : 2: 7272a33c726446d1f504a4b20dfb0695323ac43469b7c1836a87649e7d31b458548f09f5e0bab4793d136ab665f5d... : 3: 60a261a1122c9b9c44e6d1ec9b36c582a4d1d499e062b0c3ba64ee4cfa44dd2d2af8eae6a24f879e5de239579d8b5...
#+LATEX: }
On the receiving computer, we can decrypt and recover the stream of accounts from the wallet seed; any rows with errors are ignored: #+LATEX: {\scriptsize #+BEGIN_SRC bash :exports both ( slip39-recovery --bip39 --mnemonic 'zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong' \ | slip39-generator --secret - --path '../-3' --encrypt 'password' \ | slip39-generator --receive --decrypt 'password' ) 2>&1 #+END_SRC
#+RESULTS: : 0: [["ETH", "m/44'/60'/0'/0/0", "0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E"], ["BTC", "m/84'/0'/0'/0/0", "bc1qk0a9hr7wjfxeenz9nwenw9flhq0tmsf6vsgnn2"]] : 1: [["ETH", "m/44'/60'/0'/0/1", "0xd1a7451beB6FE0326b4B78e3909310880B781d66"], ["BTC", "m/84'/0'/0'/0/1", "bc1qkd33yck74lg0kaq4tdcmu3hk4yruhjayxpe9ug"]] : 2: [["ETH", "m/44'/60'/0'/0/2", "0x578270B5E5B53336baC354756b763b309eCA90Ef"], ["BTC", "m/84'/0'/0'/0/2", "bc1qvr7e5aytd0hpmtaz2d443k364hprvqpm3lxr8w"]] : 3: [["ETH", "m/44'/60'/0'/0/3", "0x909f59835A5a120EafE1c60742485b7ff0e305da"], ["BTC", "m/84'/0'/0'/0/3", "bc1q6t9vhestkcfgw4nutnm8y2z49n30uhc0kyjl0d"]]
#+LATEX: }
* X Public Keys :PROPERTIES: :ID: AB360B75-8710-456E-B98A-10F838A42A92 :END:
If you prefer, you can output "xpub..." format public keys, instead of account addresses. By default, this will elide the non-hardened portion of the default addresses -- use the "xpub..." keys to produce the remaining non-hardened portion of the HD wallet paths locally.
For example, assume you must produce a sequence of accounts for each client client of your company to deposit into. Your highly secure serial-connected "key enclave" system (which must know your HD wallet seed) emits a sequence of xpubkeys for each new client over a serial cable, to your accounting system:
#+LATEX: {\scriptsize #+BEGIN_SRC bash :exports both ( echo 'zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong' \ | python3 -m slip39.generator --secret - --xpub --path "../-2'" --encrypt 'password' \ | python3 -m slip39.generator -v --receive --decrypt 'password' ) 2>&1 #+END_SRC
#+RESULTS: : 2025-11-22 14:59:27 slip39.generator Decrypting accountgroups with nonce: a5bfaea303c5faf3b79306f6 : 0: [["ETH", "m/44'/60'/0'", "xpub6C2y6te3rtGg9SspDDFbjGEgn7yxc5ZzzkBk62yz3GRKvuqdaMDS7NUbesTJ44FprxAE7hvm5ZQjDMbYWehdJQsyBCP3mL87nnB4cB47HGS"], ["BTC", "m/84'/0'/0'", "zpub6rD5AGSXPTDMSnpmczjENMT3NvVF7q5MySww6uxitUsBYgkZLeBywrcwUWhW5YkeY2aS7xc45APPgfA6s6wWfG2gnfABq6TDz9zqeMu2JCY"]] : 1: [["ETH", "m/44'/60'/1'", "xpub6C2y6te3rtGgCPb4Gi89Qin7Da2dvnnHSuR9rLQV6bWQKiyfKyjtVzr2n9mKmTEHzr4rzK78LmdSXLSzvpZqVs4ussUU8NyXpt9nWWbKG3C"], ["BTC", "m/84'/0'/1'", "zpub6rD5AGSXPTDMUaSe3aGDqWk4uMTwcrFwytkKuDGmi3ofUkJ4dQxXHZwiXWbHHrELJAor8xGs61F8sbKS2JdQkLZRnu5PGktmr6F32nEBUBb"]] : 2: [["ETH", "m/44'/60'/2'", "xpub6C2y6te3rtGgENnaK62SyPawqKvbde17wc2ndMGFWi2yAkk3piwEY9QK8egtE9ye9uoqiqs5WV3MTNCCP2qjUNDb8cmSg4ZsVnwQnkziXVh"], ["BTC", "m/84'/0'/2'", "zpub6rD5AGSXPTDMYx2sQPuZgceniniRXDK5tELiREjxfSGJENNxuQD3u2yfpRqnNE1JeH14Pa7MVGrofDJtyXw252ws9HgRcd82X2M4KzkUfpZ"]]
#+LATEX: }
As required (throttled by hardward the serial cable RTS/CTS signals) your accounting system receives these "xpub..." addresses:
#+LATEX: {\scriptsize #+BEGIN_SRC bash :exports both ( echo 'zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong' \ | python3 -m slip39.generator --secret - --xpub --path "../-2'" --encrypt 'password' \ | python3 -m slip39.generator -v --receive --decrypt 'password' \ | while IFS=':' read num json; do \ echo "--- $(( num ))"; \ echo "$json" | jq -c '.[]'; \ done \ ) 2>&1 #+END_SRC
#+RESULTS: #+begin_example 2025-11-22 14:59:54 slip39.generator Decrypting accountgroups with nonce: 6b30ea6d946ca3753874fd67 --- 0 ["ETH","m/44'/60'/0'","xpub6C2y6te3rtGg9SspDDFbjGEgn7yxc5ZzzkBk62yz3GRKvuqdaMDS7NUbesTJ44FprxAE7hvm5ZQjDMbYWehdJQsyBCP3mL87nnB4cB47HGS"] ["BTC","m/84'/0'/0'","zpub6rD5AGSXPTDMSnpmczjENMT3NvVF7q5MySww6uxitUsBYgkZLeBywrcwUWhW5YkeY2aS7xc45APPgfA6s6wWfG2gnfABq6TDz9zqeMu2JCY"] --- 1 ["ETH","m/44'/60'/1'","xpub6C2y6te3rtGgCPb4Gi89Qin7Da2dvnnHSuR9rLQV6bWQKiyfKyjtVzr2n9mKmTEHzr4rzK78LmdSXLSzvpZqVs4ussUU8NyXpt9nWWbKG3C"] ["BTC","m/84'/0'/1'","zpub6rD5AGSXPTDMUaSe3aGDqWk4uMTwcrFwytkKuDGmi3ofUkJ4dQxXHZwiXWbHHrELJAor8xGs61F8sbKS2JdQkLZRnu5PGktmr6F32nEBUBb"] --- 2 ["ETH","m/44'/60'/2'","xpub6C2y6te3rtGgENnaK62SyPawqKvbde17wc2ndMGFWi2yAkk3piwEY9QK8egtE9ye9uoqiqs5WV3MTNCCP2qjUNDb8cmSg4ZsVnwQnkziXVh"] ["BTC","m/84'/0'/2'","zpub6rD5AGSXPTDMYx2sQPuZgceniniRXDK5tELiREjxfSGJENNxuQD3u2yfpRqnNE1JeH14Pa7MVGrofDJtyXw252ws9HgRcd82X2M4KzkUfpZ"] #+end_example
#+LATEX: }
Then, it generates each client's sequence of addresses locally: you are creating HD wallet accounts from each "xpub..." key, and adding the remaining non-hardened HD wallet path segments:
#+LATEX: {\scriptsize #+BEGIN_SRC bash :exports both ( echo 'zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong' \ | python3 -m slip39.generator --secret - --xpub --path "../-2'" --encrypt 'password' \ | python3 -m slip39.generator -v --receive --decrypt 'password' \ | while IFS=':' read num json; do \ echo "--- $(( num ))"; \ echo "$json" | jq -cr '.[]|"--crypto " + .[0] + " --secret " + .[2]' | while read command; do \ python3 -m slip39.cli -v --no-json addresses $command --paths m/0/-2; \ done; \ done \ ) 2>&1 #+END_SRC
#+RESULTS: #+begin_example 2025-11-22 14:59:58 slip39.generator Decrypting accountgroups with nonce: 41c206de454d4a87f0b9a224 --- 0 ETH m/0/0 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E ETH m/0/1 0xd1a7451beB6FE0326b4B78e3909310880B781d66 ETH m/0/2 0x578270B5E5B53336baC354756b763b309eCA90Ef BTC m/0/0 bc1qk0a9hr7wjfxeenz9nwenw9flhq0tmsf6vsgnn2 BTC m/0/1 bc1qkd33yck74lg0kaq4tdcmu3hk4yruhjayxpe9ug BTC m/0/2 bc1qvr7e5aytd0hpmtaz2d443k364hprvqpm3lxr8w --- 1 ETH m/0/0 0x9176A747BA67C1d7F80AaDC930180b4183AfB5c4 ETH m/0/1 0xa1409B655aC3e09eF261de00BAa4e85bD2820AA4 ETH m/0/2 0xae22C13Ef5891Ed835C24Ed5090542DFa748c21F BTC m/0/0 bc1q8pqnqs573vx3qdp0xp6qdqzvnvy8px24rxh9lp BTC m/0/1 bc1qwtc58u4mmnxa29u8j07e6lmqpnrs38vefy3y24 BTC m/0/2 bc1qg9s8qzm0lcetfv6umhlm3evtca5zsqv7elqd5s --- 2 ETH m/0/0 0x32A8b066c5dbD37147766491A32A612d313fda25 ETH m/0/1 0xff8b88b975f9C296531C1E93d5e4f28757b4571A ETH m/0/2 0xc95Bdf50CA542E1B689f5C06e2D8bAd0625Dfa23 BTC m/0/0 bc1q09zpchmkcnny90ghkg76gd69dvaf57qwcsrhes BTC m/0/1 bc1qjytdyw6zramwt4nvvpte93hfry2d4xhhqn0xg4 BTC m/0/2 bc1qcummre0pxv5xj4gvyut0t84vfwjd6eu7r387v4 #+end_example
#+LATEX: }
You'll notice that, after this elaborate exercise of generating xpubkeys, encrypted transmission and recovery, generating accounts from the xpubkeys, and producing multiples addresses using the remainder of the original HD wallet paths: the output addresses are identical to those generated directly from the BIP-39 Mnemonic Phrase:
#+LATEX: {\scriptsize #+BEGIN_SRC bash :exports both secret='zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong' for crypto in BTC ETH; do python3 -m slip39.cli -v --no-json addresses --secret "$secret" --crypto $crypto --paths "../-2" done #+END_SRC
#+RESULTS: : BTC m/84'/0'/0'/0/0 bc1qk0a9hr7wjfxeenz9nwenw9flhq0tmsf6vsgnn2 : BTC m/84'/0'/0'/0/1 bc1qkd33yck74lg0kaq4tdcmu3hk4yruhjayxpe9ug : BTC m/84'/0'/0'/0/2 bc1qvr7e5aytd0hpmtaz2d443k364hprvqpm3lxr8w : ETH m/44'/60'/0'/0/0 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E : ETH m/44'/60'/0'/0/1 0xd1a7451beB6FE0326b4B78e3909310880B781d66 : ETH m/44'/60'/0'/0/2 0x578270B5E5B53336baC354756b763b309eCA90Ef
#+LATEX: }
* Serial Port Connected Secure Seed Enclave
What if you or your company wants to accept Crypto payments, and needs to generate a sequence of wallets unique to each client? You can use an xpubkey and then generate a sequence of unique addresses from that, which doesn't disclose any of your private key material:
#+LATEX: {\scriptsize #+BEGIN_SRC bash :exports both ( python3 -m slip39.generator -q --secret 'zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong' \ --xpub --path "../-2'" --crypto BTC ) 2>&1 #+END_SRC
#+RESULTS: : 0: [["BTC", "m/84'/0'/0'", "zpub6rD5AGSXPTDMSnpmczjENMT3NvVF7q5MySww6uxitUsBYgkZLeBywrcwUWhW5YkeY2aS7xc45APPgfA6s6wWfG2gnfABq6TDz9zqeMu2JCY"]] : 1: [["BTC", "m/84'/0'/1'", "zpub6rD5AGSXPTDMUaSe3aGDqWk4uMTwcrFwytkKuDGmi3ofUkJ4dQxXHZwiXWbHHrELJAor8xGs61F8sbKS2JdQkLZRnu5PGktmr6F32nEBUBb"]] : 2: [["BTC", "m/84'/0'/2'", "zpub6rD5AGSXPTDMYx2sQPuZgceniniRXDK5tELiREjxfSGJENNxuQD3u2yfpRqnNE1JeH14Pa7MVGrofDJtyXw252ws9HgRcd82X2M4KzkUfpZ"]]
#+LATEX: }
Since you have to generate such an xpubkey from a "hardened" path, such as with =slip39.generate --xpub ...=, you still need to run that tool chain on some secure "air gapped" computer. So, how do you do that safely, knowing that you need to input your SLIP-39 or BIP-39 Mnemonics on that computer? Especially, if you want to do this under any kind of automation, and deliver the output xpubkey to your insecure business computer systems?
One solution is to have the computer hosting your Seed or Mnemonic private key material only connected to your business computer systems with a guaranteed safe mechanism. Definitely not with any kind of general purpose network system!
The solution: The RS-232 Serial Port
With USB to [[https://amzn.to/3DXSYol][DB-9 female]] to [[https://amzn.to/3toukby][DB-9 male]] serial adapters, any small computer with USB ports (such as the [[https://amzn.to/3A6Gwlb][Raspberry Pi 400]]) can be connected serially and serve as your "secure" computer, storing your Seed Mnemonic.
Remember to disable all other wired and wireless networking!
The RS-232 port on the "secure" computer can be protected from all incoming data transmissions, make an exploit effectively impossible, while still allowing outgoing data (the generated xpubkeys).
A DB-9 [[https://amzn.to/3EnLEEd][serial breakout]] board or custom serial adapter be easily constructed that disconnects pin 3 (TXD) on the "business" side from pin 2 (RXD) on the "secure" side, eliminating any chance of data being sent to the "secure" side. The only electronic connection that transmits data to the "secure" side is the hardware flow control pin 7 (RTS) to pin 8 (CTS). An exploit using this single-bit approach vector is ... unlikely. :)
** The =slip39= module API Provide SLIP-39 Mnemonic set creation from a 128-bit master secret, and recovery of the secret from a subset of the provided Mnemonic set. * =slip39.create=
Creates a set of SLIP-39 groups and their mnemonics.
#+LATEX: {\scriptsize | Key | Description | |--------------------+----------------------------------------------------------------------------| | name | Who/what the account is for | | group_threshold | How many groups' data is required to recover the account(s) | | groups | Each group's description, as {"
Outputs a =slip39.Details= namedtuple containing: #+LATEX: {\scriptsize | Key | Description | |-----------------+----------------------------------------------------| | name | (same) | | group_threshold | (same) | | groups | Like groups, w/
This is immediately usable to pass to =slip39.output=.
#+LATEX: {\scriptsize #+BEGIN_SRC python :session py :results value import codecs import random from tabulate import tabulate
#
NOTE:
#We turn off randomness here during SLIP-39 generation to get deterministic phrases;
during normal operation, secure entropy is used during mnemonic generation, yielding
random phrases, even when the same seed is used multiple times.
# import shamir_mnemonic shamirmnemonic.shamir.RANDOMBYTES = lambda n: b'\00' * nimport slip39
cryptopaths = [("ETH","../-2"), ("BTC","../-2")] master_secret = b'\xFF' * 16 master_secret = 'zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong' passphrase = b"" create_details = slip39.create( "Test", 2, { "Mine": (1,1), "Fam": (2,3) }, mastersecret=mastersecret, passphrase=passphrase, cryptopaths=cryptopaths, extendable=False, identifier=0, )
[ [ "Card", "Mnemonics 1 ", "Mnemonics 2", "Mnemonics 3" ], None, ] + [ f"{gname}({gof}
README truncated. [View on GitHub