From f5bd06dcaa69ab234da57028d63abab7b3b831fe Mon Sep 17 00:00:00 2001 From: MMGen Date: Sat, 16 Nov 2019 17:00:28 +0300 Subject: [PATCH] Revert commit 58951ac (syntax coloring) --- ...ng-Your-Keys-Without-the-MMGen-Software.md | 261 +++++++++--------- 1 file changed, 129 insertions(+), 132 deletions(-) diff --git a/Recovering-Your-Keys-Without-the-MMGen-Software.md b/Recovering-Your-Keys-Without-the-MMGen-Software.md index 16ab7b0..9db7cd9 100644 --- a/Recovering-Your-Keys-Without-the-MMGen-Software.md +++ b/Recovering-Your-Keys-Without-the-MMGen-Software.md @@ -79,9 +79,9 @@ 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 to hex and vice versa. Don’t forget to omit the checksum from the seed and remove the spaces: -```bash -$ echo 456d7f5f1c4bfe3bc916b87560ae6a3e | xxd -r -p > myseed.bin -``` + + $ echo 456d7f5f1c4bfe3bc916b87560ae6a3e | xxd -r -p > myseed.bin + #### Scramble the seed and save to binary (non-legacy and altcoin addresses and passwords) Other address types and passwords are generated by first “scrambling” the @@ -125,62 +125,62 @@ string, e.g. `alice@fubar.io`, all separated by colons: Once we’ve determined the correct string, we scramble our seed with it as follows using the `openssl` utility available by default on any Unix-based system: -```bash -# E.g. for LTC Segwit addresses: -$ scramble_str='ltc:segwit' -# E.g. for default-format passwords for Alice’s email account at fubar.io: -$ scramble_str='b58:20:alice@fubar.io' + # E.g. for LTC Segwit addresses: + $ scramble_str='ltc:segwit' + + # E.g. for default-format passwords for Alice’s email account at fubar.io: + $ scramble_str='b58:20:alice@fubar.io' + + $ echo -n "$scramble_str" | openssl dgst -r -sha256 -mac hmac -macopt hexkey:456d7f5f1c4bfe3bc916b87560ae6a3e | xxd -r -p > scrambled-round0.bin -$ echo -n "$scramble_str" | openssl dgst -r -sha256 -mac hmac -macopt hexkey:456d7f5f1c4bfe3bc916b87560ae6a3e | xxd -r -p > scrambled-round0.bin -``` Now add the ten rounds of sha256: -```bash -$ for i in 0 1 2 3 4 5 6 7 8 9; do - openssl dgst -sha256 -binary scrambled-round${i}.bin > scrambled-round$((i+1)).bin - done -$ mv scrambled-round10.bin myseed.bin -``` + + $ for i in 0 1 2 3 4 5 6 7 8 9; do + openssl dgst -sha256 -binary scrambled-round${i}.bin > scrambled-round$((i+1)).bin + done + $ mv scrambled-round10.bin myseed.bin + #### Generating the keys 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 obtain the chain’s first link, we make a single SHA-512 hash of the seed and save it in binary form: -```bash -$ sha512sum myseed.bin | xxd -r -p > link1.bin -``` + + $ sha512sum myseed.bin | xxd -r -p > link1.bin + A double SHA-256 hash of the first link gives us the key of our first address: -```bash -$ sha256sum link1.bin | xxd -r -p | sha256sum -05d7219524b983290138a60ada101370007f59a625c43a46f0f8d92950955e36 -# or, for the Segwit example: -b8e58ded53e9ba5a9f4e279a956c061a7da5487bde6a95f1ede0722d287881a0 + $ sha256sum link1.bin | xxd -r -p | sha256sum + 05d7219524b983290138a60ada101370007f59a625c43a46f0f8d92950955e36 + + # or, for the Segwit example: + b8e58ded53e9ba5a9f4e279a956c061a7da5487bde6a95f1ede0722d287881a0 + + # or, for the password example: + bd60b8ba034bbb40498667ee600bc0cc0b99eb19164e8d412a48f16da4e00d6b -# or, for the password example: -bd60b8ba034bbb40498667ee600bc0cc0b99eb19164e8d412a48f16da4e00d6b -``` #### Checking the result (optional, address example) With `mmgen-tool`, we can easily generate the WIF key and address from this hexadecimal key and see that it’s correct: -```bash -$ mmgen-tool hex2wif 05d7219524b983290138a60ada101370007f59a625c43a46f0f8d92950955e36 -5HrrmMdQbELyW7iCns5kvSbN9GCPTqEfG7iP1PZiYk49yDDivTi -$ mmgen-tool wif2addr 5HrrmMdQbELyW7iCns5kvSbN9GCPTqEfG7iP1PZiYk49yDDivTi -1JVi3qcNcjMM7cTR7y9ihKUG1yDLpKRJfL # matches FE3C6545:L:1 above -``` + $ mmgen-tool hex2wif 05d7219524b983290138a60ada101370007f59a625c43a46f0f8d92950955e36 + 5HrrmMdQbELyW7iCns5kvSbN9GCPTqEfG7iP1PZiYk49yDDivTi + + $ mmgen-tool wif2addr 5HrrmMdQbELyW7iCns5kvSbN9GCPTqEfG7iP1PZiYk49yDDivTi + 1JVi3qcNcjMM7cTR7y9ihKUG1yDLpKRJfL # matches FE3C6545:L:1 above + Or, for the Segwit example: -```bash -$ mmgen-tool hex2wif b8e58ded53e9ba5a9f4e279a956c061a7da5487bde6a95f1ede0722d287881a0 compressed=1 -L3R8Fn21PsY3PWgT8BMggFwXswA2EZntwEGFS5mfDJpSiLq29a9F -# for a compressed (`C`) address, leave out the `segwit=1` argument -$ mmgen-tool wif2addr L3R8Fn21PsY3PWgT8BMggFwXswA2EZntwEGFS5mfDJpSiLq29a9F segwit=1 -3LpkKqtGkcCukRrgEFWyCajSApioiEWeTw # matches FE3C6545:S:1 above -``` + $ mmgen-tool hex2wif b8e58ded53e9ba5a9f4e279a956c061a7da5487bde6a95f1ede0722d287881a0 compressed=1 + L3R8Fn21PsY3PWgT8BMggFwXswA2EZntwEGFS5mfDJpSiLq29a9F + + # for a compressed (`C`) address, leave out the `segwit=1` argument + $ 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 @@ -191,13 +191,13 @@ 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: -```bash -$ sha512sum link1.bin | xxd -r -p > link2.bin -$ sha256sum link2.bin | xxd -r -p | sha256sum -5db8fe3c8b52ccc98deab5afae780b6fbe56629e7ee1c6ed826fc2d6a81fb144 # uncompressed example -42f1b998f0f9b7b27b5d0b92ffa8c1c6b96d7202789c41b6e6a6a402e318a04d # Segwit example -9b59cec2e5d4f2a74f0d4eb2400efcf854f5a893bef0e9bf1ee83f72ca1118c3 # password example -``` + + $ sha512sum link1.bin | xxd -r -p > link2.bin + $ sha256sum link2.bin | xxd -r -p | sha256sum + 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. If we’re generating keys for Ethereum and Monero, our work is done: the raw @@ -208,50 +208,51 @@ hexadecimal keys are all we need. Otherwise, read on. If it’s passwords we’re generating, we must now convert our hex key to the desired password format, base58 in our case. For this we can use the homemade `hex2b58.py` [Base-conversion utility](#a_bcu) described below: -```bash -# bd60b8... is the double sha256 of our link1.bin from above -$ ./hex2b58.py bd60b8ba034bbb40498667ee600bc0cc0b99eb19164e8d412a48f16da4e00d6b -DkFbZk2fDKQ7C55ASHQjhwcCdTsCiRq4ZLMMD5WQVAvv -``` + + # bd60b8... is the double sha256 of our link1.bin from above + $ ./hex2b58.py bd60b8ba034bbb40498667ee600bc0cc0b99eb19164e8d412a48f16da4e00d6b + DkFbZk2fDKQ7C55ASHQjhwcCdTsCiRq4ZLMMD5WQVAvv + The password is just the last 20 characters of the output: -```bash -dTsCiRq4ZLMMD5WQVAvv -``` + + dTsCiRq4ZLMMD5WQVAvv + #### Hex to WIF by hand (address example) 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 `FE3C6545`: -```bash -05d7219524b983290138a60ada101370007f59a625c43a46f0f8d92950955e36 # uncompressed example -b8e58ded53e9ba5a9f4e279a956c061a7da5487bde6a95f1ede0722d287881a0 # Segwit example -``` + + 05d7219524b983290138a60ada101370007f59a625c43a46f0f8d92950955e36 (uncompressed example) + b8e58ded53e9ba5a9f4e279a956c061a7da5487bde6a95f1ede0722d287881a0 (Segwit example) + WIF format prepends hex `80` to the beginning of the key. If the key is associated with a compressed public key, it also appends `01`: -```bash -# uncompressed example: -8005d7219524b983290138a60ada101370007f59a625c43a46f0f8d92950955e36 -# Segwit example (Segwit uses compressed public keys): -80b8e58ded53e9ba5a9f4e279a956c061a7da5487bde6a95f1ede0722d287881a001 -``` + # uncompressed example: + 8005d7219524b983290138a60ada101370007f59a625c43a46f0f8d92950955e36 + + # Segwit example (Segwit uses compressed public keys): + 80b8e58ded53e9ba5a9f4e279a956c061a7da5487bde6a95f1ede0722d287881a001 + The Base58Check format invented by Satoshi for Bitcoin addresses and keys contains a checksum, which we now generate by taking the first four bytes (eight characters) of the double SHA256 of the above result: -```bash -# uncompressed example: -$ echo 8005d7219524b983290138a60ada101370007f59a625c43a46f0f8d92950955e36 | xxd -r -p | sha256sum | xxd -r -p | sha256sum | cut -c 1-8 -7b818629 -# Segwit example: -$ echo 80b8e58ded53e9ba5a9f4e279a956c061a7da5487bde6a95f1ede0722d287881a001 | xxd -r -p | sha256sum | xxd -r -p | sha256sum | cut -c 1-8 -89bba812 -``` + + # uncompressed example: + $ echo 8005d7219524b983290138a60ada101370007f59a625c43a46f0f8d92950955e36 | xxd -r -p | sha256sum | xxd -r -p | sha256sum | cut -c 1-8 + 7b818629 + + # Segwit example: + $ echo 80b8e58ded53e9ba5a9f4e279a956c061a7da5487bde6a95f1ede0722d287881a001 | xxd -r -p | sha256sum | xxd -r -p | sha256sum | cut -c 1-8 + 89bba812 + The checksum gets appended to the end, giving us the following final result: -```bash -8005d7219524b983290138a60ada101370007f59a625c43a46f0f8d92950955e367b818629 # uncompressed example -80b8e58ded53e9ba5a9f4e279a956c061a7da5487bde6a95f1ede0722d287881a00189bba812 # Segwit example -``` + + 8005d7219524b983290138a60ada101370007f59a625c43a46f0f8d92950955e367b818629 (uncompressed example) + 80b8e58ded53e9ba5a9f4e279a956c061a7da5487bde6a95f1ede0722d287881a00189bba812 (Segwit example) + The last step is to convert all this into Base 58. Satoshi created Base-58 encoding for convenient and error-free writing down and dictating of Bitcoin keys and addresses. He began with a Base-62 alphabet consisting of the ten @@ -270,22 +271,21 @@ so forth all the way up to ‘z’, representing decimal fifty-seven. Now all that remains is to convert our hexadecimal key to decimal and then Base 58 using this alphabet. This can be done in just four lines of code you can try out at the Python prompt: -```python -#!/usr/bin/env python3 -# uncompressed example: -b58a = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' -num = int('8005d7219524b983290138a60ada101370007f59a625c43a46f0f8d92950955e367b818629',16) -result = [b58a[num // 58**e % 58] for e in range(60)] -print(''.join(reversed(result)).lstrip('1')) -# ==> 5HrrmMdQbELyW7iCns5kvSbN9GCPTqEfG7iP1PZiYk49yDDivTi # matches key for FE3C6545:L:1 above + # uncompressed example: + $ python3 + >>> b58a = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' + >>> num = int('8005d7219524b983290138a60ada101370007f59a625c43a46f0f8d92950955e367b818629',16) + >>> result = [b58a[num // 58**e % 58] for e in range(60)] + >>> print(''.join(reversed(result)).lstrip('1')) + 5HrrmMdQbELyW7iCns5kvSbN9GCPTqEfG7iP1PZiYk49yDDivTi # matches key for FE3C6545:L:1 above + + # Segwit example has the following differences: + ... + >>> num = int('80b8e58ded53e9ba5a9f4e279a956c061a7da5487bde6a95f1ede0722d287881a00189bba812',16) + ... + L3R8Fn21PsY3PWgT8BMggFwXswA2EZntwEGFS5mfDJpSiLq29a9F # matches key for FE3C6545:S:1 above -# Segwit example has the following differences: -... -num = int('80b8e58ded53e9ba5a9f4e279a956c061a7da5487bde6a95f1ede0722d287881a00189bba812',16) -... -# ==> L3R8Fn21PsY3PWgT8BMggFwXswA2EZntwEGFS5mfDJpSiLq29a9F # matches key for FE3C6545:S:1 above -``` Explanation: the variable `b58a` holds the Base 58 alphabet; `num` holds the key in decimal, converted from hexidecimal by Python’s `int()` function; the third line does the base-58 conversion; and the last line formats the result by @@ -294,37 +294,35 @@ the leading zeroes (‘1’s). Programmers unfamiliar with Python might find the following base conversion code clearer: -```python -#!/usr/bin/env python3 -def numtob58(n): - result = [] - while n: - result = result + [b58a[n % 58]] # divide n by 58 and take the remainder - n = n // 58 - return result -result = numtob58(num) -``` + def numtob58(n): + result = [] + while n: + result = result + [b58a[n % 58]] # divide n by 58 and take the remainder + n = n // 58 + return result + + result = numtob58(num) + #### Base-conversion utility Adapting our code a bit and putting it in a file gives us have a handy conversion utility we can use for any key: -```python -#!/usr/bin/env python3 -import sys -b58a = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' -num = int(sys.argv[1],16) -result = [b58a[num // 58**e % 58] for e in range(60)] -print(''.join(reversed(result)).lstrip('1')) -``` -Save this in the file `hex2b58.py` and invoke it as follows: -```bash -$ ./hex2b58.py 8005d7219524b983290138a60ada101370007f59a625c43a46f0f8d92950955e367b818629 -5HrrmMdQbELyW7iCns5kvSbN9GCPTqEfG7iP1PZiYk49yDDivTi -$ ./hex2b58.py 80b8e58ded53e9ba5a9f4e279a956c061a7da5487bde6a95f1ede0722d287881a00189bba812 -L3R8Fn21PsY3PWgT8BMggFwXswA2EZntwEGFS5mfDJpSiLq29a9F -``` + $ cat hex2b58.py + #!/usr/bin/env python3 + import sys + b58a = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' + num = int(sys.argv[1],16) + result = [b58a[num // 58**e % 58] for e in range(60)] + print(''.join(reversed(result)).lstrip('1')) + + $ ./hex2b58.py 8005d7219524b983290138a60ada101370007f59a625c43a46f0f8d92950955e367b818629 + 5HrrmMdQbELyW7iCns5kvSbN9GCPTqEfG7iP1PZiYk49yDDivTi + + $ ./hex2b58.py 80b8e58ded53e9ba5a9f4e279a956c061a7da5487bde6a95f1ede0722d287881a00189bba812 + L3R8Fn21PsY3PWgT8BMggFwXswA2EZntwEGFS5mfDJpSiLq29a9F + #### Converting an MMGen mnemonic to hexadecimal format Our familiar base-10 system uses a series of ten symbols known as digits to @@ -403,25 +401,24 @@ sum the results, just as we did in our ‘1234’ example above: While we could theoretically do the math with a calculator, a few lines of Python will make our work much easier: -```python -#!/usr/bin/env python3 -sum = exp = 0 -for word in (200,59,1384,221,379,1493,1433,1348,1459,386,562,439): - sum += word * 1626 ** exp - exp += 1 -print(hex(sum)) -# ==> 0x456d7f5f1c4bfe3bc916b87560ae6a3e # the result in hexadecimal: matches our original hex seed above -``` + + $ python3 + >>> sum = exp = 0 + >>> for word in (200,59,1384,221,379,1493,1433,1348,1459,386,562,439): + >>> sum += word * 1626 ** exp + >>> exp += 1 + >>> print(hex(sum)) + 0x456d7f5f1c4bfe3bc916b87560ae6a3e # the result in hexadecimal: matches our original hex seed above + In case you’re wondering why 1626 was chosen as the base: 1626 is just large enough to allow a 128-bit seed to be represented by twelve words. This can also be demonstrated at the Python prompt: -```python -#!/usr/bin/env python3 ->>> 1626**12 >= 2**128 -True ->>> 1625**12 >= 2**128 -False -``` + + $ python3 + >>> 1626**12 >= 2**128 + True + >>> 1625**12 >= 2**128 + False [01]: https://github.com/casascius/Bitcoin-Address-Utility [02]: https://github.com/matja/bitcoin-tool