|
@@ -2,10 +2,13 @@
|
|
|
|
|
|
* <a href='#a_i'>Introduction</a>
|
|
|
* <a href='#a_rs'>Obtaining the binary seed</a>
|
|
|
- * <a href='#a_ss'>Convert the seed to binary (legacy addresses)</a>
|
|
|
- * <a href='#a_cs'>Scramble the seed and save to binary (compressed addresses, Segwit addresses and passwords)</a>
|
|
|
+ * <a href='#a_ss'>Convert the seed to binary (legacy uncompressed addresses)</a>
|
|
|
+ * <a href='#a_cs'>Scramble the seed and save to binary (non-legacy and altcoin addresses and passwords)</a>
|
|
|
* <a href='#a_gk'>Generating the keys</a>
|
|
|
+ * <a href='#a_cr'>Checking the result (optional, address example)</a>
|
|
|
+ * <a href='#a_hpw'>Converting the hex value to a password (password example)</a>
|
|
|
* <a href='#a_hw'>Hex to WIF by hand</a>
|
|
|
+ * <a href='#a_bcu'>Base-conversion utility</a>
|
|
|
* <a href='#a_mh'>Converting an MMGen mnemonic to hexadecimal format</a>
|
|
|
|
|
|
#### <a name='a_i'>Introduction</a>
|
|
@@ -48,8 +51,9 @@ case too:
|
|
|
3 37wM8hwt69qwH7hZHAMn6RVdc8vMuM1CwJ
|
|
|
}
|
|
|
|
|
|
-Keys for MMGen's compressed ('C') addresses are generated in a similar way as
|
|
|
-Segwit ones, as you'll see below, so we won't consider that case separately.
|
|
|
+Keys for compressed ('C'), Bech32 (‘B’) and altcoin addresses, as well as
|
|
|
+passwords, are generated in a way analogous to Segwit keys, so for them you’ll
|
|
|
+proceed as with the Segwit case.
|
|
|
|
|
|
Here's the seed itself in mmhex format, which you've stored in some safe place (on
|
|
|
paper in a safe-deposit box, for example):
|
|
@@ -66,7 +70,7 @@ into Bitcoin Core or some other wallet.
|
|
|
We'll solve this task using standard command-line utilities available on any
|
|
|
Linux or other Unix-like system.
|
|
|
|
|
|
-> #### <a name='a_ss'>Convert the seed to binary (legacy addresses)</a>
|
|
|
+> #### <a name='a_ss'>Convert the seed to binary (legacy uncompressed addresses)</a>
|
|
|
|
|
|
> For the legacy addresses, we begin by converting the seed to binary form and
|
|
|
> storing it in a file. For that we use 'xxd', a handy tool for converting binary
|
|
@@ -75,7 +79,7 @@ Linux or other Unix-like system.
|
|
|
|
|
|
$ echo 456d7f5f1c4bfe3bc916b87560ae6a3e | xxd -r -p > myseed.bin
|
|
|
|
|
|
-> #### <a name='a_cs'>Scramble the seed and save to binary (compressed addresses, Segwit addresses and passwords)</a>
|
|
|
+> #### <a name='a_cs'>Scramble the seed and save to binary (non-legacy and altcoin addresses and passwords)</a>
|
|
|
|
|
|
> Other address types and passwords are generated by first “scrambling” the
|
|
|
> seed with a unique identifier, or “scramble string”, using the HMAC-SHA256
|
|
@@ -84,39 +88,46 @@ Linux or other Unix-like system.
|
|
|
|
|
|
> Our first task then is to find out the correct scramble string for our coin
|
|
|
> and address type (or password). For BTC and BTC fork coins, the string will
|
|
|
-> be simply the address type: 'compressed' or 'segwit'. For Bitcoin-derived
|
|
|
+> be simply the address type: `compressed` or `segwit`. For Bitcoin-derived
|
|
|
> altcoins, the string is the coin symbol and address type separated by a colon,
|
|
|
-> e.g. 'ltc:legacy'. The strings for non-Bitcoin-derived altcoins are irregular
|
|
|
+> e.g. `ltc:legacy`. The strings for non-Bitcoin-derived altcoins are irregular
|
|
|
> and are listed in the table below. For passwords, the string is the password
|
|
|
-> format, e.g. 'b58'; the password length, e.g. '20'; and the password ID
|
|
|
-> string, e.g. 'alice@fubar.io', all separated by colons:
|
|
|
-
|
|
|
-> | Coin+Addrtype / Passwd type+length+ID | | | Scramble String |
|
|
|
-> |:----------------------------------------|-|-|:-------------------------|
|
|
|
-> | BTC compressed | | | `compressed` |
|
|
|
-> | BTC Segwit | | | `segwit` |
|
|
|
-> | LTC legacy | | | `ltc:legacy` |
|
|
|
-> | LTC compressed | | | `ltc:compressed` |
|
|
|
-> | LTC Segwit | | | `ltc:segwit` |
|
|
|
-> | DASH legacy | | | `dash:legacy` |
|
|
|
-> | DASH compressed | | | `dash:compressed` |
|
|
|
-> | ETH | | | `eth` |
|
|
|
-> | ETC | | | `etc` |
|
|
|
-> | XMR | | | `xmr:monero` |
|
|
|
-> | ZEC-T | | | `zec:legacy` |
|
|
|
-> | ZEC-Z | | | `zec:zcash_z` |
|
|
|
-> | Base58 passwords for Alice's email acct.| | | `b58:20:alice@fubar.io` |
|
|
|
-> | Same as above, half-length passwords | | | `b58:10:alice@fubar.io` |
|
|
|
-> | Same as above, Base32 passwords | | | `b32:24:alice@fubar.io` |
|
|
|
-> | Hex seed for Alice's PGP key | | | `hex:64:alice@gnupg` |
|
|
|
+> format, e.g. `b58`; the password length, e.g. `20`; and the password ID
|
|
|
+> string, e.g. `alice@fubar.io`, all separated by colons:
|
|
|
+
|
|
|
+> | Coin + Address type | Scramble String |
|
|
|
+> |:-----------------------------------------|:-------------------------|
|
|
|
+> | BTC/BCH compressed | `compressed` |
|
|
|
+> | BTC Segwit-P2SH | `segwit` |
|
|
|
+> | BTC native Segwit (Bech32) | `bech32` |
|
|
|
+> | LTC legacy | `ltc:legacy` |
|
|
|
+> | LTC compressed | `ltc:compressed` |
|
|
|
+> | LTC Segwit | `ltc:segwit` |
|
|
|
+> | DASH legacy | `dash:legacy` |
|
|
|
+> | DASH compressed | `dash:compressed` |
|
|
|
+> | ETH | `eth` |
|
|
|
+> | ETC | `etc` |
|
|
|
+> | XMR | `xmr:monero` |
|
|
|
+> | ZEC-T | `zec:legacy` |
|
|
|
+> | ZEC-Z | `zec:zcash_z` |
|
|
|
+
|
|
|
+> | Password type | Scramble String |
|
|
|
+> |:-----------------------------------------|:-------------------------|
|
|
|
+> | Base58 passwords for Alice's email acct. | `b58:20:alice@fubar.io` |
|
|
|
+> | Same as above, half-length passwords | `b58:10:alice@fubar.io` |
|
|
|
+> | Same as above, default Base32 passwords | `b32:24:alice@fubar.io` |
|
|
|
+> | Hex seed for Alice's PGP key | `hex:64:alice@gnupg` |
|
|
|
|
|
|
> Once we've determined the correct string, we scramble our seed with it as
|
|
|
-> follows using the `openssl` utility, which is included by default on all
|
|
|
-> Unix-based systems:
|
|
|
+> follows using the `openssl` utility available by default on any Unix-based
|
|
|
+> system:
|
|
|
|
|
|
# E.g. for LTC Segwit addresses:
|
|
|
$ echo -n 'ltc:segwit' | openssl dgst -r -sha256 -mac hmac -macopt hexkey:456d7f5f1c4bfe3bc916b87560ae6a3e | xxd -r -p > scrambled-round0.bin
|
|
|
|
|
|
+ # E.g. for default-format passwords for Alice's email account:
|
|
|
+ $ echo -n 'b58:20:alice@fubar.io' | openssl dgst -r -sha256 -mac hmac -macopt hexkey:456d7f5f1c4bfe3bc916b87560ae6a3e | xxd -r -p > scrambled-round0.bin
|
|
|
+
|
|
|
> Now add the ten rounds of sha256:
|
|
|
|
|
|
$ for i in 0 1 2 3 4 5 6 7 8 9; do
|
|
@@ -124,7 +135,7 @@ Linux or other Unix-like system.
|
|
|
done
|
|
|
$ mv scrambled-round10.bin myseed.bin
|
|
|
|
|
|
-#### <a name='a_gk'>Generating the keys</a>
|
|
|
+#### <a name='a_gk'>Generating the keys</a>
|
|
|
|
|
|
The MMGen key-generating algorithm uses a chain of SHA-512 hashes with double
|
|
|
SHA-256 branches to generate the keys from which each address is derived. To
|
|
@@ -136,14 +147,18 @@ save it in binary form:
|
|
|
A double SHA-256 hash of the first link gives us the key of our first address:
|
|
|
|
|
|
$ sha256sum link1.bin | xxd -r -p | sha256sum
|
|
|
- 05d7219524b983290138a60ada101370007f59a625c43a46f0f8d92950955e36 -
|
|
|
+ 05d7219524b983290138a60ada101370007f59a625c43a46f0f8d92950955e36
|
|
|
+
|
|
|
+ # or, for the Segwit example:
|
|
|
+ b8e58ded53e9ba5a9f4e279a956c061a7da5487bde6a95f1ede0722d287881a0
|
|
|
|
|
|
-Or, in the Segwit case:
|
|
|
+ # or, for the password example:
|
|
|
+ bd60b8ba034bbb40498667ee600bc0cc0b99eb19164e8d412a48f16da4e00d6b
|
|
|
|
|
|
- b8e58ded53e9ba5a9f4e279a956c061a7da5487bde6a95f1ede0722d287881a0 -
|
|
|
+> #### <a name='a_cr'>Checking the result (optional, address example)</a>
|
|
|
|
|
|
-With 'mmgen-tool', we can easily generate the WIF key and address from this
|
|
|
-hexadecimal key and see that it's correct:
|
|
|
+> With 'mmgen-tool', we can easily generate the WIF key and address from this
|
|
|
+> hexadecimal key and see that it's correct:
|
|
|
|
|
|
$ mmgen-tool hex2wif 05d7219524b983290138a60ada101370007f59a625c43a46f0f8d92950955e36
|
|
|
5HrrmMdQbELyW7iCns5kvSbN9GCPTqEfG7iP1PZiYk49yDDivTi
|
|
@@ -151,7 +166,7 @@ hexadecimal key and see that it's correct:
|
|
|
$ mmgen-tool wif2addr 5HrrmMdQbELyW7iCns5kvSbN9GCPTqEfG7iP1PZiYk49yDDivTi
|
|
|
1JVi3qcNcjMM7cTR7y9ihKUG1yDLpKRJfL # matches FE3C6545:L:1 above
|
|
|
|
|
|
-Or, in the Segwit case:
|
|
|
+> Or, for the Segwit example:
|
|
|
|
|
|
$ mmgen-tool hex2wif b8e58ded53e9ba5a9f4e279a956c061a7da5487bde6a95f1ede0722d287881a0 compressed=1
|
|
|
L3R8Fn21PsY3PWgT8BMggFwXswA2EZntwEGFS5mfDJpSiLq29a9F
|
|
@@ -160,24 +175,41 @@ Or, in the Segwit case:
|
|
|
$ mmgen-tool wif2addr L3R8Fn21PsY3PWgT8BMggFwXswA2EZntwEGFS5mfDJpSiLq29a9F segwit=1
|
|
|
3LpkKqtGkcCukRrgEFWyCajSApioiEWeTw # matches FE3C6545:S:1 above
|
|
|
|
|
|
-But since we're trying to do this without the MMGen software, we need to find
|
|
|
-some other way to do the hex-to-WIF conversion. We could use one of many
|
|
|
-key-manipulation tools available on the Internet, such as [this one][01], or
|
|
|
-[this one][02]. Or we can do it ourselves: that will be covered in the next
|
|
|
-section.
|
|
|
+> But since we're trying to do this without the MMGen software, we need to find
|
|
|
+> some other way to do the hex-to-WIF conversion. We could use one of many
|
|
|
+> key-manipulation tools available on the Internet, such as [this one][01], or
|
|
|
+> [this one][02]. Or we can do it ourselves: that will be covered in the next
|
|
|
+> section.
|
|
|
|
|
|
-Meanwhile, let's finish generating hex keys for the rest of our addresses. To
|
|
|
-get the next key, we generate the next link in the chain from the first link and
|
|
|
-take its double SHA-256 hash, just as we did for the first one:
|
|
|
+Meanwhile, let's finish generating hex keys for the rest of our addresses (or
|
|
|
+passwords). To get the next key, we generate the next link in the chain from
|
|
|
+the first link and take its double SHA-256 hash, just as we did for the first
|
|
|
+one:
|
|
|
|
|
|
$ sha512sum link1.bin | xxd -r -p > link2.bin
|
|
|
$ sha256sum link2.bin | xxd -r -p | sha256sum
|
|
|
- 5db8fe3c8b52ccc98deab5afae780b6fbe56629e7ee1c6ed826fc2d6a81fb144 - (uncompressed example)
|
|
|
- 42f1b998f0f9b7b27b5d0b92ffa8c1c6b96d7202789c41b6e6a6a402e318a04d - (Segwit example)
|
|
|
+ 5db8fe3c8b52ccc98deab5afae780b6fbe56629e7ee1c6ed826fc2d6a81fb144 (uncompressed example)
|
|
|
+ 42f1b998f0f9b7b27b5d0b92ffa8c1c6b96d7202789c41b6e6a6a402e318a04d (Segwit example)
|
|
|
+ 9b59cec2e5d4f2a74f0d4eb2400efcf854f5a893bef0e9bf1ee83f72ca1118c3 (password example)
|
|
|
|
|
|
And so on and so forth, until we've generated all the keys we need: three, in our case.
|
|
|
|
|
|
-#### <a name='a_hw'>Hex to WIF by hand</a>
|
|
|
+> #### <a name='a_hpw'>Converting the hex value to a password (password example)</a>
|
|
|
+
|
|
|
+> If it's passwords we're generating, we must now convert our hex key to the
|
|
|
+> desired password format. This example uses the default base-58, 20-character
|
|
|
+> format and the key we generated above using the ID string `alice@fubar.io`.
|
|
|
+> We can convert the key to base 58 using our homemade `hex2b58.py` utility (see
|
|
|
+> <a href='#a_bcu'>Base-conversion utility</a> below):
|
|
|
+
|
|
|
+ $ ./hex2b58.py bd60b8ba034bbb40498667ee600bc0cc0b99eb19164e8d412a48f16da4e00d6b
|
|
|
+ DkFbZk2fDKQ7C55ASHQjhwcCdTsCiRq4ZLMMD5WQVAvv
|
|
|
+
|
|
|
+> The password is just the last 20 characters of the output:
|
|
|
+
|
|
|
+ dTsCiRq4ZLMMD5WQVAvv
|
|
|
+
|
|
|
+#### <a name='a_hw'>Hex to WIF by hand</a>
|
|
|
|
|
|
Since we've chosen to convert our hex keys to WIF format manually, we have a bit
|
|
|
of work ahead of us. Let's begin with our just-generated key #1 from seed
|
|
@@ -264,8 +296,10 @@ clearer:
|
|
|
|
|
|
result = numtob58(num)
|
|
|
|
|
|
-Adapting our code a bit and putting it in a file gives us have a handy
|
|
|
-conversion utility we can use for any key:
|
|
|
+> #### <a name='a_bcu'>Base-conversion utility</a>
|
|
|
+
|
|
|
+> Adapting our code a bit and putting it in a file gives us have a handy
|
|
|
+> conversion utility we can use for any key:
|
|
|
|
|
|
$ cat hex2b58.py
|
|
|
#!/usr/bin/env python
|
|
@@ -281,7 +315,7 @@ conversion utility we can use for any key:
|
|
|
$ hex2b58.py 80b8e58ded53e9ba5a9f4e279a956c061a7da5487bde6a95f1ede0722d287881a00189bba812
|
|
|
L3R8Fn21PsY3PWgT8BMggFwXswA2EZntwEGFS5mfDJpSiLq29a9F
|
|
|
|
|
|
-#### <a name='a_mh'>Converting an MMGen mnemonic to hexadecimal format</a>
|
|
|
+#### <a name='a_mh'>Converting an MMGen mnemonic to hexadecimal format</a>
|
|
|
|
|
|
Our familiar base-10 system uses a series of ten symbols known as digits to
|
|
|
represent numbers from zero to nine:
|