Giter Site home page Giter Site logo

pinkdiamond1 / python-slip39 Goto Github PK

View Code? Open in Web Editor NEW

This project forked from pjkundert/python-slip39

0.0 1.0 0.0 23.51 MB

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.

Home Page: https://slip39.com

License: Other

Shell 0.60% Python 93.10% Makefile 6.30%

python-slip39's Introduction

Ethereum SLIP-39 Account Generation

Security with Availability

For both BIP-39 and SLIP-39, a 128- or 256-bit random “seed” is the source of an unlimited sequence of Ethereum and Bitcoin HD (Heirarchical Deterministic) derived Wallet accounts. Anyone who can obtain this seed gains control of all Ethereum, Bitcoin (and other) accounts derived from it, so it must be securely stored.

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)

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:

GroupRequiredMembersDescription
<r><l>
First1/1Stored at home
Second1/1Stored in office safe
Fam2/4Distributed to family members
Frens3/6Distributed to friends and associates

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

Generating a new SLIP-39 encoded Seed is easy, with results available as PDF and text. Any number of derived HD wallet account addresses can be generated from this Seed, and the Seed (and all derived HD wallets, for all cryptocurrencies) can be recovered by collecting the desired groups of recover card phrases. The default recovery groups are as described above.

Creating New SLIP-39 Recoverable Seeds

This is what the first page of the output SLIP-39 mnemonic cards PDF looks like:

./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”; insert a USB drive to collect the output, and run:

$ 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"
2021-12-25 11:10:38 slip39           ETH m/44'/60'/0'/0/0    : 0xb44A2011A99596671d5952CdC22816089f142FB3
2021-12-25 11:10:38 slip39           Wrote SLIP-39-encoded wallet for 'Personal' to:\
  Personal-2021-12-22+15.45.36-0xb44A2011A99596671d5952CdC22816089f142FB3.pdf

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 =”(<h>,<w>),<margin>”=).

To get the data printed on the terminal as in this example (so you could write it down on cards instead), add a -v (to see it logged in a tabular format), or --text to have it printed to stdout in full lines (ie. for pipelining to other programs).

Paper Wallets

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!):

slip39 -c ETH -c BTC -c DOGE -c LTC --secret ffffffffffffffffffffffffffffffff \
    --no-card --wallet password --wallet-hint 'bad:pass...' 2>&1
2022-10-05 15:32:42 slip39           It is recommended to not use '-s|--secret <hex>'; specify '-' to read from input
2022-10-05 15:32:42 slip39           It is recommended to not use '-w|--wallet <password>'; specify '-' to read from input
2022-10-05 15:32:42 slip39.layout    ETH    m/44'/60'/0'/0/0    : 0x824b174803e688dE39aF5B3D7Cd39bE6515A19a1
2022-10-05 15:32:42 slip39.layout    BTC    m/84'/0'/0'/0/0     : bc1q9yscq3l2yfxlvnlk3cszpqefparrv7tk24u6pl
2022-10-05 15:32:42 slip39.layout    DOGE   m/44'/3'/0'/0/0     : DN8PNN3dipSJpLmyxtGe4EJH38EhqF8Sfy
2022-10-05 15:32:42 slip39.layout    LTC    m/84'/2'/0'/0/0     : ltc1qe5m2mst9kjcqtfpapaanaty40qe8xtusmq4ake
2022-10-05 15:32:47 slip39.layout    Writing SLIP39-encoded wallet for 'SLIP39' to: SLIP39-2022-10-05+15.32.44-ETH-0x824b174803e688dE39aF5B3D7Cd39bE6515A19a1.pdf

And what they look like:

./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:

   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
2022-10-05 15:33:13 slip39.recovery  Recovered 128-bit SLIP-39 Seed Entropy with 2 (all) of 2 supplied mnemonics; Seed decoded from SLIP-39 Mnemonics w/ passphrase
2022-10-05 15:33:13 slip39.recovery  Recovered BIP-39 secret; To re-generate SLIP-39 wallet, send it to: python3 -m slip39 --secret -
ffffffffffffffffffffffffffffffff

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:

   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
2022-10-05 15:33:38 slip39           It is recommended to not use '-w|--wallet <password>'; specify '-' to read from input
2022-10-05 15:33:38 slip39.layout    ETH    m/44'/60'/0'/0/0    : 0x824b174803e688dE39aF5B3D7Cd39bE6515A19a1
2022-10-05 15:33:38 slip39.layout    BTC    m/84'/0'/0'/0/0     : bc1q9yscq3l2yfxlvnlk3cszpqefparrv7tk24u6pl
2022-10-05 15:33:38 slip39.layout    DOGE   m/44'/3'/0'/0/0     : DN8PNN3dipSJpLmyxtGe4EJH38EhqF8Sfy
2022-10-05 15:33:38 slip39.layout    LTC    m/84'/2'/0'/0/0     : ltc1qe5m2mst9kjcqtfpapaanaty40qe8xtusmq4ake
2022-10-05 15:33:42 slip39.layout    Writing SLIP39-encoded wallet for 'SLIP39' to: SLIP39-2022-10-05+15.33.39-ETH-0x824b174803e688dE39aF5B3D7Cd39bE6515A19a1.pdf

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).

CryptoSemanticPathAddressSupport
ETHLegacym/44’/60’/0’/0/00x…
BNBLegacym/44’/60’/0’/0/00x…Beta
CROBech32m/44’/60’/0’/0/0crc1…Beta
BTCLegacym/44’/ 0’/0’/0/01…
SegWitm/44’/ 0’/0’/0/03…
Bech32m/84’/ 0’/0’/0/0bc1…
LTCLegacym/44’/ 2’/0’/0/0L…
SegWitm/44’/ 2’/0’/0/0M…
Bech32m/84’/ 2’/0’/0/0ltc1…
DOGELegacym/44’/ 3’/0’/0/0D…

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.

BNB on the Binance Smart Chain (BSC): binance.com

The Binance Smart Chain uses standard Ethereum addresses; support for the BSC is added directly to the wallet software; here are the instructions for adding BSC support for the Trezor hardware wallet, using the Metamask software wallet. In python-slip39, BNB is simply an alias for ETH, since the wallet addresses and Ethereum JSON Paper Wallets are identical.

CRO on Cronos: crypto.com

The Cronos chain (formerly known as the Crypto.org chain). It is the native chain of the crypto.com CRO coin.

Cronos also uses Ethereum addresses on the m/44'/60'/0'/0/0 derivation path, but represents them as Bech32 addresses with a “crc” prefix, eg. crc19a6r74dvfxjyvjzf3pg9y3y5rhk6rds2c9265n. As with BNB, the wallet must support the Cronos blockchain; instructions exist for adding CRO support for the Trezor hardware wallet, using the Metamask software wallet.

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 python.org/downloads or using homebrew brew install [email protected]. Then, start the GUI in a variety of ways:

slip39-gui
python3 -m slip39.gui

Alternatively, download and install the macOS/win32 GUI App .zip, .pkg or .dmg installer from 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:

slip39 --help 2>&1                | sed 's/^/: /' # (just for output formatting)
: usage: slip39 [-h] [-v] [-q] [-o OUTPUT] [-t THRESHOLD] [-g GROUP] [-f FORMAT]
:               [-c CRYPTOCURRENCY] [-p PATH] [-j JSON] [-w WALLET]
:               [--wallet-hint WALLET_HINT] [--wallet-format WALLET_FORMAT]
:               [-s SECRET] [--bits BITS] [--using-bip39]
:               [--passphrase PASSPHRASE] [-C CARD] [--no-card] [--paper PAPER]
:               [--cover] [--no-cover] [--text]
:               [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); 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[[<require>/]<size>] (default: <size> = 1,
:                         <require> = half of <size>, rounded up, eg.
:                         'Frens(3/5)' ).
:   -f FORMAT, --format FORMAT
:                         Specify crypto address formats: legacy, segwit,
:                         bech32; default: ETH:legacy, BTC:bech32, LTC:bech32,
:                         DOGE:legacy, CRO:bech32, BNB:legacy, XRP:legacy
:   -c CRYPTOCURRENCY, --cryptocurrency CRYPTOCURRENCY
:                         A crypto name and optional derivation path (eg.
:                         '../<range>/<range>'); defaults: ETH:m/44'/60'/0'/0/0,
:                         BTC:m/84'/0'/0'/0/0, LTC:m/84'/2'/0'/0/0,
:                         DOGE:m/44'/3'/0'/0/0, CRO:m/44'/60'/0'/0/0,
:                         BNB:m/44'/60'/0'/0/0, XRP:m/44'/144'/0'/0/0
:   -p PATH, --path PATH  Modify all derivation paths by replacing the final
:                         segment(s) w/ the supplied range(s), eg. '.../1/-'
:                         means .../1/[0,...)
:   -j JSON, --json JSON  Save an encrypted JSON wallet for each Ethereum
:                         address w/ this password, '-' reads it from stdin
:                         (default: None)
:   -w WALLET, --wallet WALLET
:                         Produce paper wallets in output PDF; each wallet
:                         private key is encrypted this password
:   --wallet-hint WALLET_HINT
:                         Paper wallets password hint
:   --wallet-format WALLET_FORMAT
:                         Paper wallet size; half, third, quarter or
:                         '(<h>,<w>),<margin>' (default: quarter)
:   -s SECRET, --secret SECRET
:                         Use the supplied 128-, 256- or 512-bit hex value as
:                         the secret seed; '-' reads it from stdin (eg. output
:                         from slip39.recover)
:   --bits BITS           Ensure that the seed is of the specified bit length;
:                         128, 256, 512 supported.
:   --using-bip39         Generate Seed from secret Entropy using BIP-39
:                         generation algorithm (encode as BIP-39 Mnemonics,
:                         encrypted using --passphrase)
:   --passphrase PASSPHRASE
:                         Encrypt the master secret w/ this passphrase, '-'
:                         reads it from stdin (default: None/'')
:   -C CARD, --card CARD  Card size; business, credit, index, half, third,
:                         quarter, photo or '(<h>,<w>),<margin>' (default:
:                         business)
:   --no-card             Disable PDF SLIP-39 mnemonic card output
:   --paper PAPER         Paper size (default: Letter)
:   --cover               Produce PDF SLIP-39 cover page
:   --no-cover            Disable PDF SLIP-39 cover page
:   --text                Enable textual SLIP-39 mnemonic output to stdout

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):

$ 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

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):

slip39 --secret 383597fd63547e7c9525575decd413f7 --wallet password --wallet-hint bad:pass... 2>&1
2022-10-05 16:36:32 slip39           It is recommended to not use '-s|--secret <hex>'; specify '-' to read from input
2022-10-05 16:36:32 slip39           It is recommended to not use '-w|--wallet <password>'; specify '-' to read from input
2022-10-05 16:36:32 slip39.layout    ETH    m/44'/60'/0'/0/0    : 0xb44A2011A99596671d5952CdC22816089f142FB3
2022-10-05 16:36:32 slip39.layout    BTC    m/84'/0'/0'/0/0     : bc1qcupw7k8enymvvsa7w35j5hq4ergtvus3zk8a8s
2022-10-05 16:36:35 slip39.layout    Writing SLIP39-encoded wallet for 'SLIP39' to: SLIP39-2022-10-05+16.36.33-ETH-0xb44A2011A99596671d5952CdC22816089f142FB3.pdf

slip39.recovery Synopsis

slip39-recovery --help 2>&1                | sed 's/^/: /' # (just for output formatting)
: usage: slip39-recovery [-h] [-v] [-q] [-m MNEMONIC] [-e] [-b] [-u]
:                        [-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: False)
:   -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
:   -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 "Model T", 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.

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.

( python3 -m slip39 --text --no-card \
    | ( sort -r  ; echo "...later..." 1>&2 ) \
    | python3 -m slip39.recovery \
    | python3 -m slip39 --secret - --no-card \
 ) 2>&1
2022-10-05 16:36:16 slip39.layout    ETH    m/44'/60'/0'/0/0    : 0x252B6C41bCE5f580054fb54b6de539433C58B86D
2022-10-05 16:36:16 slip39.layout    BTC    m/84'/0'/0'/0/0     : bc1qpksxy2n5ct67du5wjytgntuw9dl99uwews2wg5
...later...
2022-10-05 16:36:16 slip39.layout    ETH    m/44'/60'/0'/0/0    : 0x252B6C41bCE5f580054fb54b6de539433C58B86D
2022-10-05 16:36:16 slip39.layout    BTC    m/84'/0'/0'/0/0     : bc1qpksxy2n5ct67du5wjytgntuw9dl99uwews2wg5

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

slip39-generator --help --version         | sed 's/^/: /' # (just for output formatting)
: usage: slip39-generator [-h] [-v] [-q] [-s SECRET] [-f FORMAT]
:                         [-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: ETH:legacy, BTC:bech32, LTC:bech32,
:                         DOGE:legacy, CRO:bech32, BNB:legacy, XRP:legacy
:   -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:
: 
:     <enumeration> [<address group(s)>]
: 
: If the output is to be transmitted by an insecure channel (eg. a serial port), which may insert
: errors or allow leakage, it is recommended that the records be encrypted with a cryptographic
: function that includes a message authentication code.  We use ChaCha20Poly1305 with a password and a
: random nonce generated at program start time.  This nonce is incremented for each record output.
: 
: Since the receiver requires the nonce to decrypt, and we do not want to separately transmit the
: nonce and supply it to the receiver, the first record emitted when --encrypt is specified is the
: random nonce, encrypted with the password, itself with a known nonce of all 0 bytes.  The plaintext
: data is random, while the nonce is not, but since this construction is only used once, it should be
: satisfactory.  This first nonce record is transmitted with an enumeration prefix of "nonce".

Producing Addresses

Addresses can be produced in plaintext or encrypted, and output to stdout or to a serial port.

echo ffffffffffffffffffffffffffffffff | slip39-generator --secret - --path '../-3' 2>&1
    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"]]

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:

( slip39-recovery --bip39 --mnemonic 'zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong' \
    | slip39-generator --secret - --path '../-3' ) 2>&1
    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"]]

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):

( 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
nonce: 530f5d2784b872e16c1f25912891866ab380b4eb767f15312f0a60ae
    0: 3a33abe5b4ed567e238b059576059067795fa7b4760ac80d29a42796c213296dcbde47c75826f2adc0c8821da6e438d9ef7271f5285239e6c5208f409001dfe4888169282a3f4a13953e7a9dab74e724e2447ed7413e92836b5f0ff100eaa5bb80dd933ce76dae58324957c427b6228fdbe46c06fdc114394954a1843e7358d651f30ffcf5aa3bf66db776d3ae7eb265bcb5a8fc3aeb4fed73da27c4d94a7ad6fe893de526
    1: 8fe5a59696c32d3e096930a905fc7474c1987a9d5582ddbd1b82ebe62118a36d81a8e210f50b0345c3fc2c83b84628208ea45e46397133476c04d1ffca48b246c2547f2ea912fd12cc9b334215b745675d2fd65bd90d9e7805195d7a66f493305d2c3ca364c9dfdd73097f1898cd193720a9410f66efed79ea9fb5537cc9aafda3ae211fc5e9f30a1de24df012ad846ab3b17a6e0dd4d07d4c6fecf2593cf7290f35a5875d
    2: 534fa3352e576be04f749f57a56101d7fc723cdc1cf9a205ea3127a088625957b1dee1495f9622223a7c13e1535f1e1f20da4cdd79d3704fe27d796aec4b07f1a0a8088bd4422271d7d3b851eaa410d6bae5d25fe22d20fb29477a2e83a6eb15145efa019a4f74e729932f469428e3c7864faa797448374aa8a916547f9b3021e9074ef8611470999dba241f66969632a0d8a57641f3fa7e675ab57c3b05892069c8aa1ad0
    3: d0488b4808f1028f57792c2dd147eb347bada39175f7be38d9fa0ef770ffedd2cea664f91a8fd94b761e0d726e33d9983aaab2c25906b0c0092f0e71505a9cae086841d8c5b95de65f9dd890f5d6eac2c59c913159ce9c26f76d5a17390958589a38d756679de00d3f547c9b4c4234845273fafa5af8560456e1871304895d9991d0a54ffc9e9fd190ef2a688530d380ee725daebaa948e4072237dfdde965f098ba8eac5f

On the receiving computer, we can decrypt and recover the stream of accounts from the wallet seed; any rows with errors are ignored:

( 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
    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"]]

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.

KeyDescription
nameWho/what the account is for
group_thresholdHow many groups’ data is required to recover the account(s)
groupsEach group’s description, as {“<group>”:(<required>, <members>), …}
master_secret128-bit secret (default: from secrets.token_bytes)
passphraseAn optional additional passphrase required to recover secret (default: “”)
using_bip39Produce wallet Seed from master_secret Entropy using BIP-39 generation
iteration_exponentFor encrypted secret, exponentially increase PBKDF2 rounds (default: 1)
cryptopathsA number of crypto names, and their derivation paths ]
strengthDesired master_secret strength, in bits (default: 128)

Outputs a slip39.Details namedtuple containing:

KeyDescription
name(same)
group_threshold(same)
groupsLike groups, w/ <members> = [“<mnemonics>”, …]
accountsResultant list of groups of accounts
using_bip39Seed produced from entropy using BIP-39 generation

This is immediately usable to pass to slip39.output.

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
shamir_mnemonic.shamir.RANDOM_BYTES = lambda n: b'\00' * n

import slip39

cryptopaths         = [("ETH","m/44'/60'/0'/0/-2"), ("BTC","m/44'/0'/0'/0/-2")]
master_secret       = b'\xFF' * 16
passphrase          = b""
create_details      = slip39.create(
    "Test", 2, { "Mine": (1,1), "Fam": (2,3) },
    master_secret=master_secret, passphrase=passphrase, cryptopaths=cryptopaths )

print( tabulate( [
    [
        f"{g_name}({g_of}/{len(g_mnems)}) #{g_n+1}:" if l_n == 0 else ""
    ] + words
    for g_name,(g_of,g_mnems) in create_details.groups.items()
    for g_n,mnem in enumerate( g_mnems )
    for l_n,(line,words) in enumerate(slip39.organize_mnemonic(
            mnem, label=f"{g_name}({g_of}/{len(g_mnems)}) #{g_n+1}:" ))
  ], tablefmt='orgtbl' ))
Mine(1/1) #1:1 academic8 safari15 standard
2 acid9 drug16 angry
3 acrobat10 browser17 similar
4 easy11 trash18 aspect
5 change12 fridge19 smug
6 injury13 busy20 violence
7 painting14 finger
Fam(2/3) #1:1 academic8 prevent15 dwarf
2 acid9 mouse16 dream
3 beard10 daughter17 flavor
4 echo11 ancient18 oral
5 crystal12 fortune19 chest
6 machine13 ruin20 marathon
7 bolt14 warmth
Fam(2/3) #2:1 academic8 prune15 briefing
2 acid9 pickup16 often
3 beard10 device17 escape
4 email11 device18 sprinkle
5 dive12 peanut19 segment
6 warn13 enemy20 devote
7 ranked14 graduate
Fam(2/3) #3:1 academic8 dining15 intimate
2 acid9 invasion16 satoshi
3 beard10 bumpy17 hobo
4 entrance11 identify18 ounce
5 alarm12 anxiety19 both
6 health13 august20 award
7 discuss14 sunlight

Add the resultant HD Wallet addresses:

print( tabulate( [
    [ account.path, account.address ]
    for group in create_details.accounts
    for account in group
], tablefmt='orgtbl' ))
m/44’/60’/0’/0/00x824b174803e688dE39aF5B3D7Cd39bE6515A19a1
m/44’/0’/0’/0/0bc1qm5ua96hx30snwrwsfnv97q96h53l86ded7wmjl
m/44’/60’/0’/0/10x8D342083549C635C0494d3c77567860ee7456963
m/44’/0’/0’/0/1bc1qwz6v9z49z8mk5ughj7r78hjsp45jsxgzh29lnh
m/44’/60’/0’/0/20x52787E24965E1aBd691df77827A3CfA90f0166AA
m/44’/0’/0’/0/2bc1q690m430qu29auyefarwfrvfumncunvyw6v53n9

slip39.produce_pdf

KeyDescription
name(same as slip39.create)
group_threshold(same as slip39.create)
groupsLike groups, w/ <members> = [“<mnemonics>”, …]
accountsResultant { “path”: Account, …}
using_bip39Generate Seed from Entropy via BIP-39 generation algorithm
card_format‘index’, ‘(<h>,<w>),<margin>’, …
paper_format‘Letter’, …
orientationForce an orientation (default: portrait, landscape)
cover_textProduce a cover page w/ the text (and BIP-39 Phrase if using_bip39)

Layout and produce a PDF containing all the SLIP-39 details on cards for the crypto accounts, on the paper_format provided. Returns the paper (orientation,format) used, the FPDF, and passes through the supplied cryptocurrency accounts derived.

(paper_format,orientation),pdf,accounts = slip39.produce_pdf( *create_details )
pdf_binary = pdf.output()
print( tabulate( [
    [ "Orientation:",	orientation ],
    [ "Paper:",		paper_format ],
    [ "PDF Pages:",		pdf.pages_count ],
    [ "PDF Size:",		len( pdf_binary )],
], tablefmt='orgtbl' ))
Orientation:landscape
Paper:Letter
PDF Pages:1
PDF Size:13137

slip39.write_pdfs

KeyDescription
namesA sequence of Seed names, or a dict of { name: <details> } (from slip39.create)
master_secretA Seed secret (only appropriate if exactly one name supplied)
passphraseA SLIP-39 passphrase (not Trezor compatible; use “hidden wallet” phrase on device instead)
using_bip39Generate Seed from Entropy via BIP-39 generation algorithm
groupA dict of {“<group>”:(<required>, <members>), …}
group_thresholdHow many groups are required to recover the Seed
cryptocurrencyA sequence of [ “<crypto>”, “<crypto>:<derivation>”, … ] w/ optional ranges
editDerivation range(s) for each cryptocurrency, eg. “../0-4/-9” is 9 accounts first 5 change addresses
card_formatCard size (eg. “credit”); False specifies no SLIP-39 cards (ie. only BIP-39 or JSON paper wallets)
paper_formatPaper size (eg. “letter”)
filenameA filename; may contain “…{name}…” formatting, for name, date, time, crypto path and address
filepathA file path, if PDF output to file is desired; empty implies current dir.
printerA printer name (or True for default), if output to printer is desired
json_pwdIf password supplied, encrypted Ethereum JSON wallet files will be saved, and produced into PDF
textIf True, outputs SLIP-39 phrases to stdout
wallet_pwdIf password supplied, produces encrypted BIP-38 or JSON Paper Wallets to PDF (preferred vs. json_pwd)
wallet_pwd_hintAn optional passphrase hint, printed on paper wallet
wallet_formatPaper wallet size, (eg. “third”); the default is 1/3 letter size
wallet_paperOther paper format (default: Letter)
cover_pageA bool indicating whether to produce a cover page (default: True)

For each of the names provided, produces a separate PDF containing all the SLIP-39 details and optionally encrypted BIP-38 paper wallets and Ethereum JSON wallets for the specified cryptocurrency accounts derived from the seed, and writes the PDF and JSON wallets to the specified file name(s).

slip39.write_pdfs( ... )

slip39.recover

Takes a number of SLIP-39 mnemonics, and if sufficient group_threshold groups’ mnemonics are present (and the options passphrase is supplied), the master_secret is recovered. This can be used with slip39.accounts to directly obtain any Account data.

Note that the SLIP-39 passphrase is not checked; entering a different passphrase for the same set of mnemonics will recover a different wallet! This is by design; it allows the holder of the SLIP-39 mnemonic phrases to recover a “decoy” wallet by supplying a specific passphrase, while protecting the “primary” wallet.

Therefore, it is essential to remember any non-default (non-empty) passphrase used, separately and securely. Take great care in deciding if you wish to use a passphrase with your SLIP-39 wallet!

KeyDescription
mnemonics[“<mnemonics>”, …]
passphraseOptional passphrase to decrypt secret Seed Entropy
using_bip39Use BIP-39 Seed generation from recover Entropy
# Recover with the wrong password (on purpose, as a decoy wallet w/ a small amount)
recoverydecoy       = slip39.recover(
    create_details.groups['Mine'][1][:] + create_details.groups['Fam'][1][:2],
    passphrase=b"wrong!"
)
recoverydecoy_hex   = codecs.encode( recoverydecoy, 'hex_codec' ).decode( 'ascii' )

# But, recovering w/ correct passphrase yields our original Seed Entropy
recoveryvalid       = slip39.recover(
    create_details.groups['Mine'][1][:] + create_details.groups['Fam'][1][:2],
    passphrase=passphrase
)
recoveryvalid_hex   = codecs.encode( recoveryvalid, 'hex_codec' ).decode( 'ascii' )

print( tabulate( [
  [ f"{len(recoverydecoy)*8}-bit secret (decoy):", f"{recoverydecoy_hex}" ],
  [ f"{len(recoveryvalid)*8}-bit secret recovered:", f"{recoveryvalid_hex}" ]
], tablefmt='orgtbl' ))
128-bit secret (decoy):2e522cea2b566840495c220cf79c756e
128-bit secret recovered:ffffffffffffffffffffffffffffffff

slip39.recover_bip39

Generate the 512-bit Seed from a BIP-39 Mnemonic + passphrase. Or, return the original 128- to 256-bit Seed Entropy, if as_entropy is specified.

KeyDescription
mnemonic“<mnemonic>”
passphraseOptional passphrase to decrypt secret Seed Entropy
as_entropyReturn the BIP-39 Seed Entropy, not the generated Seed

slip39.produce_bip39

Produce a BIP-39 Mnemonic from the supplied 128- to 256-bit Seed Entropy.

KeyDescription
entropyThe bytes of Seed Entropy
strengthOr, the number of bits of Entropy to produce (Default: 128)
languageDefault is “english”

Conversion from BIP-39 to SLIP-39

If we already have a BIP-39 wallet, it would certainly be nice to be able to create nice, safe SLIP-39 mnemonics for it, and discard the unsafe BIP-39 mnemonics we have lying around, just waiting to be accidentally discovered and the account compromised!

Fortunately, we can do this! It takes a bit of practice to become comfortable with the process, but once you do – you can confidently discard your original insecure and unreliable BIP-39 Mnemonic backups.

BIP-39 vs. SLIP-39 Incompatibility

Unfortunately, it is not possible to cleanly convert a BIP-39 generated wallet Seed into a SLIP-39 wallet. Both BIP-39 and SLIP-39 preserve the original 128- to 256-bit Seed Entropy (random) bits, but these bits are used very differently – and incompatibly – to generate the resultant wallet Seed.

In native SLIP-39, the original, recovered Seed Entropy (128- or 256-bits) is used directly by the BIP-44 wallet derivation. In BIP-39, the Seed entropy is not directly used at all! It is only indirectly used; the BIP-39 Seed Phrase (which contains the exact, original entropy) is used, as normalized text, as input to a hashing function, along with some other fixed text, to produce a 512-bit Seed, which is then fed into the BIP-44 wallet derivation process.

The least desirable method is to preserve the 512-bit output of the BIP-39 mnemonic phrase as a set of 512-bit (59-word) SLIP-39 Mnemonics. But first, lets review how BIP-39 works.

BIP-39 Entropy to Mnemonic

BIP-39 uses a single set of 12, 15, 18, 21 or 24 BIP-39 words to carefully preserve a specific 128 to 256 bits of initial Seed Entropy. Here’s a 128-bit (12-word) example using some fixed “entropy” 0xFFFF..FFFF. You’ll note that, from the BIP-39 Mnemonic, we can either recover the original 128-bit Seed Entropy, or we can generate the resultant 512-bit Seed w/ the correct passphrase:

from mnemonic import Mnemonic
bip39_english       = Mnemonic("english")
entropy             = b'\xFF' * 16
entropy_hex		= codecs.encode( entropy, 'hex_codec' ).decode( 'ascii' )
entropy_mnemonic    = bip39_english.to_mnemonic( entropy )

recovered		= slip39.recover_bip39( entropy_mnemonic, as_entropy=True )
recovered_hex	= codecs.encode( recovered, 'hex_codec' ).decode( 'ascii' )

recovered_seed	= slip39.recover_bip39( entropy_mnemonic, passphrase=passphrase )
recovered_seed_hex	= codecs.encode( recovered_seed, 'hex_codec' ).decode( 'ascii' )

print( tabulate( [
    [ "Original Entropy", entropy_hex ],
    [ "BIP-39 Mnemonic", entropy_mnemonic ],
    [ "Recovered Entropy", recovered_hex ],
    [ "Recovered Seed", f"{recovered_seed_hex:.50}..." ],
], tablefmt='orgtbl'))
Original Entropyffffffffffffffffffffffffffffffff
BIP-39 Mnemoniczoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong
Recovered Entropyffffffffffffffffffffffffffffffff
Recovered Seedb6a6d8921942dd9806607ebc2750416b289adea669198769f2…

Each word is one of a corpus of 2048 words; therefore, each word encodes 11 bits (2048 = 2**11) of entropy. So, we provided 128 bits, but 12*11 = 132. So where does the extra 4 bits of data come from?

It comes from the first few bits of a SHA256 hash of the entropy, which is added to the end of the supplied 128 bits, to reach the required 132 bits: 132 / 11 = 12 words.

This last 4 bits (up to 8 bits, for a 256-bit 24-word BIP-39) is checked, when validating the BIP-39 mnemonic. Therefore, making up a random BIP-39 mnemonic will succeed only 1 / 16 times on average, due to an incorrect checksum 4-bit (16 = 2**4) . Lets check:

def random_words( n, count=100 ):
    for _ in range( count ):
        yield ' '.join( random.choice( bip39_english.wordlist ) for _ in range( n ))

successes           = sum(
    bip39_english.check( m )
    for i,m in enumerate( random_words( 12, 10000 ))) / 100

print( tabulate( [
  [ "Valid random 12-word mnemonics:", f"{successes}%" ],
  [ "Or, about: ", f"1 / {100/successes:.3}" ],
], tablefmt='orgtbl' ))
Valid random 12-word mnemonics:6.26%
Or, about:1 / 16.0

Sure enough, about 1/16 random 12-word phrases are valid BIP-39 mnemonics. OK, we’ve got the contents of the BIP-39 phrase dialed in. How is it used to generate accounts?

BIP-39 Mnemonic to Seed

Unfortunately, BIP-39 does not use the carefully preserved 128-bit entropy to generate the wallet! Nope, it is stretched to a 512-bit seed using PBKDF2 HMAC SHA512. The normalized text (not the Entropy bytes) of the 12-word mnemonic is then used (with a salt of “mnemonic” plus an optional passphrase, “” by default), to obtain the 512-bit seed:

seed                = bip39_english.to_seed( entropy_mnemonic )
seed_hex            = codecs.encode( seed, 'hex_codec' ).decode( 'ascii' )
print( tabulate( [
 [ f"{len(seed)*8}-bit seed:", f"{seed_hex:.50}..." ]
], tablefmt='orgtbl' ))
512-bit seed:b6a6d8921942dd9806607ebc2750416b289adea669198769f2…

BIP-39 Seed to Address

Finally, this 512-bit seed is used to derive HD wallet(s). The HD Wallet key derivation process consumes whatever seed entropy is provided (512 bits in the case of BIP-39), and uses HMAC SHA512 with a prefix of b”Bitcoin seed” to stretch the supplied seed entropy to 64 bytes (512 bits). Then, the HD Wallet path segments are iterated through, permuting the first 32 bytes of this material as the key with the second 32 bytes of material as the chain node, until finally the 32-byte (256-bit) Ethereum account private key is produced. We then use this private key to compute the rest of the Ethereum account details, such as its public address.

path                = "m/44'/60'/0'/0/0"
bip39_eth_hd        = slip39.account( seed, 'ETH', path )
print( tabulate( [
 [ f"{len(bip39_eth_hd.key)*4}-bit derived key path:", f"{path}" ],
 [ "Produces private key: ", f"{bip39_eth_hd.key}" ],
 [ "Yields Ethereum address:", f"{bip39_eth_hd.address}" ],
], tablefmt='orgtbl' ))
256-bit derived key path:m/44’/60’/0’/0/0
Produces private key:7af65ba4dd53f23495dcb04995e96f47c243217fc279f10795871b725cd009ae
Yields Ethereum address:0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E

Thus, we see that while the 12-word BIP-39 mnemonic careful preserves the original 128-bit entropy, this data is not directly used to derive the wallet private key and address. Also, since an irreversible hash is used to derive the Seed from the Mnemonic, we can’t reverse the process on the seed to arrive back at the BIP-39 mnemonic phrase.

SLIP-39 Entropy to Mnemonic

Just like BIP-39 carefully preserves the original 128-bit Seed Entropy bytes in a single 12-word mnemonic phrase, SLIP-39 preserves the original 128- or 256-bit Seed Entropy in a set of 20- or 33-word Mnemonic phrases.

name,thrs,grps,acct,ub39 = slip39.create(
    "Test", 2, { "Mine": (1,1), "Fam": (2,3) }, entropy )
print( tabulate( [
    [ f"{g_name}({g_of}/{len(g_mnems)}) #{g_n+1}:" if l_n == 0 else "" ] + words
    for g_name,(g_of,g_mnems) in grps.items()
    for g_n,mnem in enumerate( g_mnems )
    for l_n,(line,words) in enumerate(slip39.organize_mnemonic(
            mnem, rows=7, cols=3, label=f"{g_name}({g_of}/{len(g_mnems)}) #{g_n+1}:" ))
], tablefmt='orgtbl' ))
Mine(1/1) #1:1 academic8 safari15 standard
2 acid9 drug16 angry
3 acrobat10 browser17 similar
4 easy11 trash18 aspect
5 change12 fridge19 smug
6 injury13 busy20 violence
7 painting14 finger
Fam(2/3) #1:1 academic8 prevent15 dwarf
2 acid9 mouse16 dream
3 beard10 daughter17 flavor
4 echo11 ancient18 oral
5 crystal12 fortune19 chest
6 machine13 ruin20 marathon
7 bolt14 warmth
Fam(2/3) #2:1 academic8 prune15 briefing
2 acid9 pickup16 often
3 beard10 device17 escape
4 email11 device18 sprinkle
5 dive12 peanut19 segment
6 warn13 enemy20 devote
7 ranked14 graduate
Fam(2/3) #3:1 academic8 dining15 intimate
2 acid9 invasion16 satoshi
3 beard10 bumpy17 hobo
4 entrance11 identify18 ounce
5 alarm12 anxiety19 both
6 health13 august20 award
7 discuss14 sunlight

Since there is some randomness used in the SLIP-39 mnemonics generation process, we would get a different set of words each time for the fixed “entropy” 0xFFFF..FF used in this example (if we hadn’t manually disabled entropy for shamir_mnemonic, above), but we will always derive the same Ethereum account 0x824b..19a1 at the specified HD Wallet derivation path.

print( tabulate( [
    [ account.crypto, account.path, account.address ]
    for group in create_details.accounts
    for account in group
], tablefmt='orgtbl', headers=[ "Crypto", "HD Wallet Path:", "Ethereum Address:" ] ))
CryptoHD Wallet Path:Ethereum Address:
ETHm/44’/60’/0’/0/00x824b174803e688dE39aF5B3D7Cd39bE6515A19a1
BTCm/44’/0’/0’/0/0bc1qm5ua96hx30snwrwsfnv97q96h53l86ded7wmjl
ETHm/44’/60’/0’/0/10x8D342083549C635C0494d3c77567860ee7456963
BTCm/44’/0’/0’/0/1bc1qwz6v9z49z8mk5ughj7r78hjsp45jsxgzh29lnh
ETHm/44’/60’/0’/0/20x52787E24965E1aBd691df77827A3CfA90f0166AA
BTCm/44’/0’/0’/0/2bc1q690m430qu29auyefarwfrvfumncunvyw6v53n9

SLIP-39 Mnemonic to Seed

Lets prove that we can actually recover the original Seed Entropy from the SLIP-39 recovery Mnemonics; in this case, we’ve specified a SLIP-39 group_threshold of 2 groups, so we’ll use 1 Mnemonic from Mine, and 2 from the Fam group:

_,mnem_mine         = grps['Mine']
_,mnem_fam          = grps['Fam']
recseed             = slip39.recover( mnem_mine + mnem_fam[:2] )
recseed_hex         = codecs.encode( recseed, 'hex_codec' ).decode( 'ascii' )
print( tabulate( [
    [ f"{len(recseed)*8}-bit Seed:", f"{recseed_hex}" ]
], tablefmt='orgtbl' ))
128-bit Seed:ffffffffffffffffffffffffffffffff

SLIP-39 Seed to Address

And we’ll use the same style of code as for the BIP-39 example above, to derive the Ethereum address directly from this recovered 128-bit seed:

slip39_eth_hd       = slip39.account( recseed, 'ETH', path )
print( tabulate( [
    [ f"{len(slip39_eth_hd.key)*4}-bit derived key path:", f"{path}" ],
    [ "Produces private key: ", f"{slip39_eth_hd.key}" ],
    [ "Yields Ethereum address:", f"{slip39_eth_hd.address}" ],
], tablefmt='orgtbl' ))
256-bit derived key path:m/44’/60’/0’/0/0
Produces private key:6a2ec39aab88ec0937b79c8af6aaf2fd3c909e9a56c3ddd32ab5354a06a21a2b
Yields Ethereum address:0x824b174803e688dE39aF5B3D7Cd39bE6515A19a1

And we see that we obtain the same Ethereum address 0x824b..1a2b as we originally got from slip39.create above. However, this is not the same Ethereum wallet address obtained from BIP-39 with exactly the same 0xFFFF...FF Seed Entropy, which was 0xfc20..1B5E!

This is due to the fact that BIP-39 does not use the recovered Seed Entropy to produce the seed like SLIP-39 does, but applies additional one-way hashing of the Mnemonic to produce a 512-bit Seed.

BIP-39 vs SLIP-39 Key Derivation Summary

At no time in BIP-39 account derivation is the original 128-bit Seed Entropy used (directly) in the derivation of the wallet key. This differs from SLIP-39, which directly uses the 128-bit Seed Entropy recovered from the SLIP-39 Shamir’s Secret Sharing System recovery process to generate each HD Wallet account’s private key.

Furthermore, there is no point in the BIP-39 Seed Entropy to account generation where we could introduce a known 128-bit seed and produce a known Ethereum wallet from it, other than as the very beginning.

Therefore, our BIP-39 Backup via SLIP-39 strategy must focus on backing up the original 128- to 256-bit Seed Entropy.

BIP-39 Backup via SLIP-39

Here are the two available methods for backing up insecure and unreliable BIP-39 Mnemonic phrases, using SLIP-39.

The first “Emergency Recovery” method allows you to recover your BIP-39 generated wallets without the passphrase, but does not support recovery using hardware wallets; you must output “Paper Wallets” and use them to recover the Cryptocurrency funds.

The second “Best Recovery: Using BIP-39” allows us to recover the accounts to any standard BIP-39 hardware wallet! However, the SLIP-39 Mnemonics are not compatible with standard SLIP-39 wallets like the Trezor “Model T” – you have to use the recovered BIP-39 Mnemonic phrase to recover the hardware wallet.

Emergency Recovery: Using Recovered Paper Wallets

There is one approach which can preserve an original BIP-39 generated wallet addresses, using SLIP-39 mnemonics.

It is clumsy, as it preserves the BIP-39 output 512-bit stretched seed, and the resultant 59-word SLIP-39 mnemonics cannot be used (at present) with the Trezor hardware wallet. They can, however, be used to recover the HD wallet private keys without access to the original BIP-39 Mnemonic phrase or passphrase – you could generate and distribute a set of more secure SLIP-39 Mnemonic phrases, instead of trying to secure the original BIP-39 mnemonic + passphrase – without abandoning your existing BIP-39 wallets.

We’ll use slip39.recovery --bip39 ... to recover the 512-bit stretched seed from BIP-39:

( python3 -m slip39.recovery --bip39 -v \
    --mnemonic "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong" 
) 2>&1
2022-06-10 06:28:20 slip39.recovery  Recovered 512-bit BIP-39 secret from english mnemonic
2022-06-10 06:28:20 slip39.recovery  Recovered BIP-39 secret; To re-generate SLIP-39 wallet, send it to: python3 -m slip39 --secret -
b6a6d8921942dd9806607ebc2750416b289adea669198769f2e15ed926c3aa92bf88ece232317b4ea463e84b0fcd3b53577812ee449ccc448eb45e6f544e25b6

Then we can generate a 59-word SLIP-39 mnemonic set from the 512-bit secret:

( python3 -m slip39.recovery --bip39 \
    --mnemonic "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong" \
  | python3 -m slip39 --secret - --no-card -v
) 2>&1 | tail -20
2022-10-05 16:34:32 slip39                7 rescue    19 exchange  31 solution  43 increase  55 slap      
2022-10-05 16:34:32 slip39                8 jump      20 lunar     32 check     44 rapids    56 lungs     
2022-10-05 16:34:32 slip39                9 news      21 fawn      33 income    45 density   57 nervous   
2022-10-05 16:34:32 slip39               10 detailed  22 plastic   34 image     46 sack      58 bracelet  
2022-10-05 16:34:32 slip39               11 spit      23 lying     35 criminal  47 maximum   59 loyalty   
2022-10-05 16:34:32 slip39               12 pajamas   24 morning   36 belong    48 radar     
2022-10-05 16:34:32 slip39           6th  1 large     13 method    25 license   37 snapshot  49 medical   
2022-10-05 16:34:32 slip39                2 training  14 pile      26 license   38 chubby    50 provide   
2022-10-05 16:34:32 slip39                3 decision  15 envy      27 leaves    39 hairy     51 tolerate  
2022-10-05 16:34:32 slip39                4 spider    16 dictate   28 gesture   40 literary  52 climate   
2022-10-05 16:34:32 slip39                5 academic  17 roster    29 lawsuit   41 revenue   53 adapt     
2022-10-05 16:34:32 slip39                6 texture   18 verify    30 chew      42 furl      54 counter   
2022-10-05 16:34:32 slip39                7 total     19 endless   31 glasses   43 race      55 surprise  
2022-10-05 16:34:32 slip39                8 judicial  20 jacket    32 soul      44 making    56 husband   
2022-10-05 16:34:32 slip39                9 racism    21 elegant   33 jewelry   45 mason     57 elite     
2022-10-05 16:34:32 slip39               10 mansion   22 building  34 tenant    46 ruin      58 insect    
2022-10-05 16:34:32 slip39               11 hearing   23 museum    35 manual    47 switch    59 cylinder  
2022-10-05 16:34:32 slip39               12 sharp     24 ticket    36 realize   48 ounce     
2022-10-05 16:34:32 slip39.layout    ETH    m/44'/60'/0'/0/0    : 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E
2022-10-05 16:34:32 slip39.layout    BTC    m/84'/0'/0'/0/0     : bc1qk0a9hr7wjfxeenz9nwenw9flhq0tmsf6vsgnn2

This 0xfc20..1B5E address is the same Ethereum address as is recovered on a Trezor using this BIP-39 mnemonic phrase. Thus, we can generate “Paper Wallets” for the desired Cryptocurrency accounts, and recover the funds.

So, this does the job:

  • Uses our original BIP-39 Mnemonic
  • Does not require remembering the BIP-39 passphrase
  • Preserves all of the original wallets

But:

  • The 59-word SLIP-39 Mnemonics cannot (yet) be imported into the Trezor “Model T”
  • The original BIP-39 Mnemonic phrase cannot be recovered, for any hardware wallet
  • Must use the SLIP-39 App to generate “Paper Wallets”, to recover the funds

So, this is a good “emergency backup” solution; you or your heirs would be able to recover the funds with a very high level of security and reliability.

Best Recovery: Using Recovered BIP-39 Mnemonic Phrase

The best solution is to use SLIP-39 to back up the original BIP-39 Seed Entropy (not the generated Seed), and then later recover that Seed Entropy and re-generate the BIP-39 Mnemonic phrase. You will continue to need to remember and use your original BIP-39 passphrase:

sequenceDiagram
Participant BIP as BIP-39 Mnemonic
Participant SLIP as SLIP-39 Mnemonics
Participant BIP_REC as BIP-39 Recovered
Participant HW as Hardware Wallet

BIP-->>SLIP: slip39 --bip39 --mnemonic "..."
SLIP-->>BIP_REC: slip39-recovery --mnemonic "..."
BIP_REC-->>HW: Recover w/ BIP-39 Mnemonic + passphrase

First, observe that we can recover the 128-bit Seed Entropy from the BIP-39 Mnemonic phrase (not the 512-bit generated Seed):

( python3 -m slip39.recovery --bip39 --entropy -v \
    --mnemonic "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong" 
) 2>&1
2022-10-05 16:34:49 slip39.recovery  Recovered 128-bit BIP-39 secret from english mnemonic
2022-10-05 16:34:49 slip39.recovery  Recovered BIP-39 secret; To re-generate SLIP-39 wallet, send it to: python3 -m slip39 --secret -
ffffffffffffffffffffffffffffffff

Now we generate SLIP-39 Mnemonics to recover the 128-bit Seed Entropy. Note that these are 20-word Mnemonics. However, these are NOT the wallets we expected! These are the well-known native SLIP-39 wallets from the 0xFFFF...FF Seed Entropy; not the well-known native BIP-39 wallets from that Seed Entropy, which generate the Ethereum wallet address 0xfc20..1B5E! Why not?

( python3 -m slip39.recovery --bip39 --entropy \
    --mnemonic "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong" \
  | python3 -m slip39 --secret - --no-card -v
) 2>&1 | tail -20
2022-10-05 16:34:52 slip39                4 skin      11 very      18 vitamins  
2022-10-05 16:34:52 slip39                5 brave     12 treat     19 leaves    
2022-10-05 16:34:52 slip39                6 rapids    13 fortune   20 valuable  
2022-10-05 16:34:52 slip39                7 therapy   14 script    
2022-10-05 16:34:52 slip39           5th  1 length     8 explain   15 reward    
2022-10-05 16:34:52 slip39                2 merit      9 usual     16 news      
2022-10-05 16:34:52 slip39                3 decision  10 starting  17 receiver  
2022-10-05 16:34:52 slip39                4 snake     11 deliver   18 scramble  
2022-10-05 16:34:52 slip39                5 briefing  12 grocery   19 wits      
2022-10-05 16:34:52 slip39                6 downtown  13 dragon    20 solution  
2022-10-05 16:34:52 slip39                7 daisy     14 hamster   
2022-10-05 16:34:52 slip39           6th  1 length     8 involve   15 vexed     
2022-10-05 16:34:52 slip39                2 merit      9 payment   16 argue     
2022-10-05 16:34:52 slip39                3 decision  10 dismiss   17 terminal  
2022-10-05 16:34:52 slip39                4 spider    11 shadow    18 clinic    
2022-10-05 16:34:52 slip39                5 activity  12 general   19 stilt     
2022-10-05 16:34:52 slip39                6 finance   13 nuclear   20 ecology   
2022-10-05 16:34:52 slip39                7 oven      14 rhyme     
2022-10-05 16:34:52 slip39.layout    ETH    m/44'/60'/0'/0/0    : 0x824b174803e688dE39aF5B3D7Cd39bE6515A19a1
2022-10-05 16:34:52 slip39.layout    BTC    m/84'/0'/0'/0/0     : bc1q9yscq3l2yfxlvnlk3cszpqefparrv7tk24u6pl

Because we must tell slip39 to that we’re using the BIP-39 Mnemonic and Seed generation process to derived the wallet addresses from the Seed Entropy (not the SLIP-39 standard). So, we add the -using-bip39 option:

( python3 -m slip39.recovery --bip39 --entropy \
    --mnemonic "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong" \
  | python3 -m slip39 --secret - --no-card -v --using-bip39
) 2>&1 | tail -20
2022-10-05 16:34:55 slip39                4 skin      11 biology   18 chest     
2022-10-05 16:34:55 slip39                5 album     12 subject   19 dough     
2022-10-05 16:34:55 slip39                6 merit     13 capacity  20 frequent  
2022-10-05 16:34:55 slip39                7 warn      14 canyon    
2022-10-05 16:34:55 slip39           5th  1 funding    8 axle      15 reward    
2022-10-05 16:34:55 slip39                2 upstairs   9 domestic  16 oral      
2022-10-05 16:34:55 slip39                3 decision  10 dynamic   17 canyon    
2022-10-05 16:34:55 slip39                4 snake     11 plastic   18 drug      
2022-10-05 16:34:55 slip39                5 deadline  12 total     19 primary   
2022-10-05 16:34:55 slip39                6 bracelet  13 mother    20 closet    
2022-10-05 16:34:55 slip39                7 evidence  14 brother   
2022-10-05 16:34:55 slip39           6th  1 funding    8 satisfy   15 black     
2022-10-05 16:34:55 slip39                2 upstairs   9 velvet    16 crush     
2022-10-05 16:34:55 slip39                3 decision  10 lend      17 lawsuit   
2022-10-05 16:34:55 slip39                4 spider    11 radar     18 zero      
2022-10-05 16:34:55 slip39                5 bucket    12 license   19 hunting   
2022-10-05 16:34:55 slip39                6 class     13 infant    20 voice     
2022-10-05 16:34:55 slip39                7 stay      14 year      
2022-10-05 16:34:55 slip39.layout    ETH    m/44'/60'/0'/0/0    : 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E
2022-10-05 16:34:55 slip39.layout    BTC    m/84'/0'/0'/0/0     : bc1qk0a9hr7wjfxeenz9nwenw9flhq0tmsf6vsgnn2

And, there we have it – we’ve recovered exactly the same Ethereum and Bitcoin wallets as would a native BIP-39 hardware wallet like a Ledger Nano.

Using SLIP-39 App “Backup” Controls

In the SLIP-39 App, the default Controls presented are to “Backup” a BIP-39 recovery phrase.

In “Seed Source”, enter your existing BIP-39 recovery phrase. In “Seed Secret”, make sure “Using BIP-39” is selected, and enter your BIP-39 passphrase. This allows us to display the proper wallet addresses – we do not store your password, or save it as part of the SLIP-39 cards! You will need to remember and use your passphrase whenever you use your BIP-39 phrase to initialize a hardware wallet.

Check that the Recovery needs … Mnemonic Card Groups are correct for your application, and hit Save!

Later, use the “Recover” Controls to get your BIP-39 recovery phrase back, from your SLIP-39 cards, whenever you need it.

Practice this a few times (using the “zoo zoo … wrong” 12-word or “zoo zoo … vote” 24-word phrase) until you’re confident. Then, back up your real BIP-39 recovery phrase.

Once you’re convinced you can securely and reliably recover your BIP-39 phrase any time you need it, we recommend that you destroy your original BIP-39 recovery phrase backup(s). They are dangerous and unreliable, and only serve to make your Cryptocurrency accounts less secure!

Building & Installing

The python-slip39 project is tested under both homebrew:

$ brew install [email protected]

and using the official python.org/downloads installer.

Either of these methods will get you a python3 executable running version 3.9+, usable for running the slip39 module, and the slip39.gui GUI.

The slip39 Module

To build the wheel and install slip39 manually:

$ git clone [email protected]:pjkundert/python-slip39.git
$ make -C python-slip39 install

To install from Pypi, including the optional requirements to run the PySimpleGUI/tkinter GUI, support serial I/O, and to support creating encrypted BIP-38 and Ethereum JSON Paper Wallets:

$ python3 -m pip install slip39[gui,paper,serial]

The slip39 GUI

To install from Pypi, including the optional requirements to run the PySimpleGUI/tkinter GUI:

$ python3 -m pip install slip39[gui]

Then, there are several ways to run the GUI:

$ python3 -m slip39.gui     # Execute the python slip39.gui module main method
$ slip39-gui                # Run the main function provided by the slip39.gui module

The macOS/win32 SLIP-39.app GUI

You can build the native macOS and win32 SLIP-39.app App.

This requires the official python.org/downloads installer; the homebrew [email protected] will not work for building the native app using either PyInstaller. (The py2app approach doesn’t work in either version of Python).

$ git clone [email protected]:pjkundert/python-slip39.git
$ make -C python-slip39 app

The Windows 10 SLIP-39 GUI

Install Python from https://python.org/downloads, and the Microsoft C++ Build Tools via the Visual Studio Installer (required for installing some slip39 package dependencies).

To run the GUI, just install slip39 package from Pypi using pip, including the gui and wallet options. Building the Windows SLIP-39 executable GUI application requires the dev option.

PS C:\Users\IEUser> pip install slip39[gui,wallet,dev]

To work with the python-slip39 Git repo on Github, you’ll also need to install Git from git-scm.com. Once installed, run “Git bash”, and

$ ssh-keygen.exe -t ed25519

to create an id_ed25519.pub SSH identity, and import it into your Git Settings SSH keys. Then,

$ mkdir src
$ cd src
$ git clone [email protected]:pjkundert/python-slip39.git

Code Signing

The MMC (Microsoft Management Console) is used to store your code-signing certificates. See stackoverflow.com for how to enable its Certificate management.

Licensing

Each installation of the SLIP-39 App requires an Ed25519 “Agent” identity, and cryptographically signed license(s) to activate various python-slip39 features. No license is required to use basic features; advanced features require a license.

Create an Ed25519 “Agent” Key

The Ed25519 signing “Agent” identity is loaded at start-up, and (if necessary) is created automatically on first execution. This is similar to the ssh-keygen -t ed25519 procedure.

Each separate installation must have a ~/.crypto-licensing/python-slip39.crypto-keypair. This contains the licensing “Agent” credentials: a passphrase-encrypted Ed25519 private key, and a self-signed public key. This shows that we actually had access to the private key and used it to create a signature for the claimed public key and the supplied encrypted private key – proving that the public key is valid, and associated with the encrypted private key.

Validating an Advanced Feature License

When an advanced feature is used, all available python-slip39.crypto-license files are loaded. They are examined, and if a license is found that is:

  • Assigned to this Agent and Machine-ID
  • Contains the required license authorizations

then the functionality is allowed to proceed.

If no license is found, instructions on how to obtain a license for this Agent on this Machine-ID will be displayed.

If you’ve already obtained a “master” license on your primary machine’s SLIP-39 installation, you can use it to issue a sub-license to this installation (eg. for your air-gapped cryptocurrency management machine).

Otherwise, a URL is displayed at which the required “master” license can be issued.

Get a sub-license From Your “master” License

Typically, you’ll be using python-slip39’s advanced features on an air-gapped computer. You do not want to visit websites from this computer. So, you obtain a sub-license from your primary computer’s python-slip39 installation, and place it on your secure air-gapped computer (eg. using a USB stick).

Take note of the secondary machine’s Agent ID (pubkey) and Machine ID. On your primary computer (with the “master” license), run:

python3 -m slip39.sublicense <agent-pubkey> <machine-id>

Take the output, and place it in the file ~/.crypto-licensing/python-slip39.crypto-license on your air-gapped computer.

Obtaining an Advanced Feature “master” License

On your primary computer, open the provided URL in a browser. The URL contains the details of the advanced feature desired.

This URL’s web page will request an Ed25519 “Agent” public key to issue your “master” license to. This should be your primary user account’s Ed25519 “Agent” public key – this master “Agent” will be issuing sub-licenses to any of your other SLIP-39 installations. You will be redirected to a URL that is unique to the advanced feature plus your Agent ID.

An invoice will be generated with unique Bitcoin, Ethereum and perhaps other cryptocurrency addresses. Pay the required amount of cryptocurrency to one of the provided wallet addresses. Within a few seconds, the cryptocurrency transfer will be confirmed.

Once the payment for the advanced feature is confirmed, the URL including your agent ID will always allow you to re-download the license. It is only usable by your Agent ID to issue sub-licenses to your python-slip39 installations on your machines.

Dependencies

Internally, python-slip39 project uses Trezor’s python-shamir-mnemonic to encode the seed data to SLIP-39 phrases, python-hdwallet to convert seeds to ETH, BTC, LTC and DOGE wallets, and the Ethereum project’s eth-account to produce encrypted JSON wallets for specified Ethereum accounts.

The python-shamir-mnemonic API

To use it directly, obtain , and install it, or run python3 -m pip install shamir-mnemonic.

$ shamir create custom --group-threshold 2 --group 1 1 --group 1 1 --group 2 5 --group 3 6
Using master secret: 87e39270d1d1976e9ade9cc15a084c62
Group 1 of 4 - 1 of 1 shares required:
merit aluminum acrobat romp capacity leader gray dining thank rhyme escape genre havoc furl breathe class pitch location render beard
Group 2 of 4 - 1 of 1 shares required:
merit aluminum beard romp briefing email member flavor disaster exercise cinema subject perfect facility genius bike include says ugly package
Group 3 of 4 - 2 of 5 shares required:
merit aluminum ceramic roster already cinema knit cultural agency intimate result ivory makeup lobe jerky theory garlic ending symbolic endorse
merit aluminum ceramic scared beam findings expand broken smear cleanup enlarge coding says destroy agency emperor hairy device rhythm reunion
merit aluminum ceramic shadow cover smith idle vintage mixture source dish squeeze stay wireless likely privacy impulse toxic mountain medal
merit aluminum ceramic sister duke relate elite ruler focus leader skin machine mild envelope wrote amazing justice morning vocal injury
merit aluminum ceramic smug buyer taxi amazing marathon treat clinic rainbow destroy unusual keyboard thumb story literary weapon away move
Group 4 of 4 - 3 of 6 shares required:
merit aluminum decision round bishop wrote belong anatomy spew hour index fishing lecture disease cage thank fantasy extra often nail
merit aluminum decision scatter carpet spine ruin location forward priest cage security careful emerald screw adult jerky flame blanket plot
merit aluminum decision shaft arcade infant argue elevator imply obesity oral venture afraid slice raisin born nervous universe usual racism
merit aluminum decision skin already fused tactics skunk work floral very gesture organize puny hunting voice python trial lawsuit machine
merit aluminum decision snake cage premium aide wealthy viral chemical pharmacy smoking inform work cubic ancestor clay genius forward exotic
merit aluminum decision spider boundary lunar staff inside junior tendency sharp editor trouble legal visual tricycle auction grin spit index

python-slip39's People

Contributors

pjkundert avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.