mirror of
https://github.com/SeedSigner/seedsigner.git
synced 2026-10-05 15:08:25 +00:00
refactor script into tools/
This commit is contained in:
@@ -212,15 +212,19 @@ _(for more advanced/python-savvy users)_
|
||||
|
||||
Run the exact same SeedSigner mnemonic generation code from the command line to quickly test and externally verify the results.
|
||||
|
||||
Install the `embit` dependency:
|
||||
```
|
||||
Create a python virtualenv (out of the scope of this doc) and install dependencies:
|
||||
```bash
|
||||
pip3 install embit
|
||||
|
||||
# Install the main project code to make it importable
|
||||
pip3 install -e .
|
||||
```
|
||||
|
||||
|
||||
Then run the utility script with `-h` to view the usage instructions:
|
||||
```
|
||||
cd src/seedsigner/helpers
|
||||
python3 mnemonic_generation.py -h
|
||||
```bash
|
||||
cd tools
|
||||
python3 mnemonic.py -h
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import hashlib
|
||||
import random
|
||||
import unicodedata
|
||||
|
||||
from embit import bip39
|
||||
from embit.bip39 import mnemonic_to_bytes, mnemonic_from_bytes
|
||||
from embit.wordlists.bip39 import WORDLIST as WORDLIST__ENGLISH
|
||||
|
||||
"""
|
||||
@@ -152,160 +150,3 @@ def generate_mnemonic_from_image(image, wordlist_language_code: str = WORDLIST_L
|
||||
return bip39.mnemonic_from_bytes(hash.digest(), wordlist=_get_wordlist(wordlist_language_code)).split()
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import argparse
|
||||
|
||||
usage = f"""
|
||||
Verify SeedSigner's dice rolls and coin flip entropy-to-mnemonic conversion via this tool.
|
||||
|
||||
Compare its results against iancoleman.io/bip39 and bitcoiner.guide/seed
|
||||
|
||||
Usage:
|
||||
# {DICE__NUM_ROLLS__12WORD} dice rolls / 12-word mnemonic
|
||||
python3 mnemonic_generation.py dice 5624433434...
|
||||
|
||||
# {DICE__NUM_ROLLS__24WORD} dice rolls / 24-word mnemonic
|
||||
python3 mnemonic_generation.py dice 6151463561...
|
||||
|
||||
# {DICE__NUM_ROLLS__12WORD} dice rolls, entered as 0-5 / 12-word mnemonic
|
||||
python3 mnemonic_generation.py --zero-indexed-dice dice 5135535514...
|
||||
|
||||
# 128 coin flips / 12-word mnemonic
|
||||
python3 mnemonic_generation.py coins 1111100111...
|
||||
|
||||
# 256 coin flips / 24-word mnemonic
|
||||
python mnemonic_generation.py coins 0010111010...
|
||||
|
||||
# GENERATE {DICE__NUM_ROLLS__12WORD} random dice rolls / 12-word mnemonic
|
||||
python3 mnemonic_generation.py dice rand12
|
||||
|
||||
# GENERATE {DICE__NUM_ROLLS__24WORD} random dice rolls / 24-word mnemonic
|
||||
python3 mnemonic_generation.py dice rand24
|
||||
|
||||
# GENERATE {DICE__NUM_ROLLS__24WORD} random dice rolls, entered as 0-5 / 24-word mnemonic
|
||||
python3 mnemonic_generation.py --zero-indexed-dice dice rand24
|
||||
|
||||
# GENERATE 128 random coin flips / 12-word mnemonic
|
||||
python3 mnemonic_generation.py coins rand12
|
||||
|
||||
# GENERATE 256 random coin flips / 24-word mnemonic
|
||||
python3 mnemonic_generation.py coins rand24
|
||||
"""
|
||||
RAND_12 = "rand12"
|
||||
RAND_24 = "rand24"
|
||||
parser = argparse.ArgumentParser(description=f'SeedSigner entropy-to-mnemonic tool\n\n{usage}', formatter_class=argparse.RawTextHelpFormatter)
|
||||
|
||||
# Required positional arguments
|
||||
parser.add_argument('method', type=str, choices=['dice', 'coins', 'final_word'], help="Input entropy method")
|
||||
parser.add_argument('entropy', type=str, help=f"""Entropy data. Enter "{ RAND_12 }" or "{ RAND_24 }" to create a random (not-secure) example seed.""")
|
||||
|
||||
# Optional arguments
|
||||
parser.add_argument('-z', '--zero-indexed-dice',
|
||||
action="store_true",
|
||||
default=False,
|
||||
dest="zero_indexed_dice",
|
||||
help="Enables dice entry as [0-5] instead of default [1-6]")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
method = args.method
|
||||
entropy = args.entropy
|
||||
zero_indexed_dice = args.zero_indexed_dice
|
||||
|
||||
is_rand_seed = 'rand' in entropy
|
||||
if is_rand_seed:
|
||||
# Generate random data as our entropy
|
||||
if entropy not in [RAND_12, RAND_24]:
|
||||
print(f"""Invalid random entropy value: Must be either "{RAND_12}" or "{RAND_24}".""")
|
||||
exit(1)
|
||||
mnemonic_length = 12 if entropy == RAND_12 else 24
|
||||
|
||||
if method == 'dice':
|
||||
if zero_indexed_dice:
|
||||
entropy = ''.join([str(random.randint(0, 5)) for i in range(DICE__NUM_ROLLS__12WORD if mnemonic_length == 12 else DICE__NUM_ROLLS__24WORD)])
|
||||
else:
|
||||
entropy = ''.join([str(random.randint(1, 6)) for i in range(DICE__NUM_ROLLS__12WORD if mnemonic_length == 12 else DICE__NUM_ROLLS__24WORD)])
|
||||
|
||||
elif method == 'coins':
|
||||
entropy = ''.join([str(random.randint(0, 1)) for i in range(128 if mnemonic_length == 12 else 256)])
|
||||
|
||||
elif method == 'final_word':
|
||||
random_dice_rolls = ''.join([str(random.randint(0, 1)) for i in range(128 if mnemonic_length == 12 else 256)])
|
||||
entropy = " ".join(generate_mnemonic_from_coin_flips(random_dice_rolls)[:-1])
|
||||
print(len(entropy.split()), entropy)
|
||||
|
||||
if method == 'dice':
|
||||
if not zero_indexed_dice and ('0' in entropy or '6' not in entropy):
|
||||
print("Dice entry must be 1-6 unless --zero-indexed-dice is specified")
|
||||
exit(1)
|
||||
if len(entropy) not in [DICE__NUM_ROLLS__12WORD, DICE__NUM_ROLLS__24WORD]:
|
||||
print(f"Dice entropy must be {DICE__NUM_ROLLS__12WORD} or {DICE__NUM_ROLLS__24WORD} rolls")
|
||||
exit(1)
|
||||
mnemonic = generate_mnemonic_from_dice(entropy)
|
||||
|
||||
elif method == 'coins':
|
||||
if len(entropy) not in [128, 256]:
|
||||
print("Coin flip entropy must be 128 or 256 flips")
|
||||
exit(1)
|
||||
mnemonic = generate_mnemonic_from_coin_flips(entropy)
|
||||
|
||||
elif method == 'final_word':
|
||||
num_input_words = len(entropy.split())
|
||||
if num_input_words not in [11, 12, 23, 24]:
|
||||
print(f"Final word entropy must be 11, 12, 23, or 24 words ({num_input_words} provided)")
|
||||
exit(1)
|
||||
|
||||
if num_input_words in [11, 23]:
|
||||
# We need to fill the last bits of entropy
|
||||
if num_input_words == 11:
|
||||
# 7 final bits of entropy in the 12th word (7 + 4-bit checksum)
|
||||
num_final_entropy_bits = 7
|
||||
else:
|
||||
# 3 final bits of entropy in the 24th word (3 + 8-bit checksum)
|
||||
num_final_entropy_bits = 3
|
||||
|
||||
final_entropy_method = None
|
||||
while final_entropy_method not in ['1', '2', '3']:
|
||||
final_entropy_method = input(f"""
|
||||
How would you like to fill the final {num_final_entropy_bits} bits of entropy?
|
||||
|
||||
1.) {num_final_entropy_bits} coin flips
|
||||
2.) Select an additional word from the wordlist
|
||||
3.) Fill with zeros
|
||||
|
||||
Type 1, 2, or 3: """)
|
||||
|
||||
if final_entropy_method == '1':
|
||||
coin_flips = input(f""" Enter {num_final_entropy_bits} coin flips as 0 or 1 (e.g. { "".join(str(random.randint(0,1)) for f in range(0, num_final_entropy_bits)) }): """)
|
||||
if len(coin_flips) != num_final_entropy_bits:
|
||||
print(f"Invalid number of coin flips: needed {num_final_entropy_bits}, got {len(coin_flips)}")
|
||||
final_word = get_partial_final_word(coin_flips)
|
||||
entropy += f" {final_word}"
|
||||
|
||||
elif final_entropy_method == '2':
|
||||
final_word = input(f""" Enter the final word: """)
|
||||
if final_word not in WORDLIST__ENGLISH:
|
||||
print(f"Invalid word: {final_word}")
|
||||
exit(1)
|
||||
entropy += f" {final_word}"
|
||||
|
||||
elif final_entropy_method == '3':
|
||||
# Nothing to do; just pass the 11 or 23 words straight to calculate_checksum
|
||||
pass
|
||||
|
||||
mnemonic = calculate_checksum(entropy)
|
||||
|
||||
print("\n")
|
||||
if is_rand_seed:
|
||||
print("\t***** This is a demo seed. Do not use it to store funds!!! *****")
|
||||
|
||||
print(f"""\t{" ".join(mnemonic)}\n""")
|
||||
|
||||
if is_rand_seed:
|
||||
print(f"\tEntropy: {entropy}\n")
|
||||
|
||||
if method == "dice":
|
||||
print(f"""\tVerify at iancoleman.io/bip39 or bitcoiner.guide/seed using "Base 10" or "Hex" mode.\n""")
|
||||
elif method == "coins":
|
||||
print(f"""\tVerify at iancoleman.io/bip39 or bitcoiner.guide/seed using "Binary" mode.\n""")
|
||||
@@ -0,0 +1,169 @@
|
||||
import argparse
|
||||
import random
|
||||
from seedsigner.helpers import mnemonic_generation
|
||||
from embit.wordlists.bip39 import WORDLIST as WORDLIST__ENGLISH
|
||||
|
||||
"""
|
||||
see: docs/dice_verification.md (the "Command Line Tool" section) for full instructions.
|
||||
|
||||
tldr:
|
||||
pip3 install embit
|
||||
pip3 install -e .
|
||||
cd tools
|
||||
python3 mnemonic.py -h
|
||||
"""
|
||||
|
||||
|
||||
usage = f"""
|
||||
Verify SeedSigner's dice rolls and coin flip entropy-to-mnemonic conversion via this tool.
|
||||
|
||||
Compare its results against iancoleman.io/bip39 and bitcoiner.guide/seed
|
||||
|
||||
Usage:
|
||||
# {mnemonic_generation.DICE__NUM_ROLLS__12WORD} dice rolls / 12-word mnemonic
|
||||
python3 mnemonic_generation.py dice 5624433434...
|
||||
|
||||
# {mnemonic_generation.DICE__NUM_ROLLS__24WORD} dice rolls / 24-word mnemonic
|
||||
python3 mnemonic_generation.py dice 6151463561...
|
||||
|
||||
# {mnemonic_generation.DICE__NUM_ROLLS__12WORD} dice rolls, entered as 0-5 / 12-word mnemonic
|
||||
python3 mnemonic_generation.py --zero-indexed-dice dice 5135535514...
|
||||
|
||||
# 128 coin flips / 12-word mnemonic
|
||||
python3 mnemonic_generation.py coins 1111100111...
|
||||
|
||||
# 256 coin flips / 24-word mnemonic
|
||||
python mnemonic_generation.py coins 0010111010...
|
||||
|
||||
# GENERATE {mnemonic_generation.DICE__NUM_ROLLS__12WORD} random dice rolls / 12-word mnemonic
|
||||
python3 mnemonic_generation.py dice rand12
|
||||
|
||||
# GENERATE {mnemonic_generation.DICE__NUM_ROLLS__24WORD} random dice rolls / 24-word mnemonic
|
||||
python3 mnemonic_generation.py dice rand24
|
||||
|
||||
# GENERATE {mnemonic_generation.DICE__NUM_ROLLS__24WORD} random dice rolls, entered as 0-5 / 24-word mnemonic
|
||||
python3 mnemonic_generation.py --zero-indexed-dice dice rand24
|
||||
|
||||
# GENERATE 128 random coin flips / 12-word mnemonic
|
||||
python3 mnemonic_generation.py coins rand12
|
||||
|
||||
# GENERATE 256 random coin flips / 24-word mnemonic
|
||||
python3 mnemonic_generation.py coins rand24
|
||||
"""
|
||||
RAND_12 = "rand12"
|
||||
RAND_24 = "rand24"
|
||||
parser = argparse.ArgumentParser(description=f'SeedSigner entropy-to-mnemonic tool\n\n{usage}', formatter_class=argparse.RawTextHelpFormatter)
|
||||
|
||||
# Required positional arguments
|
||||
parser.add_argument('method', type=str, choices=['dice', 'coins', 'final_word'], help="Input entropy method")
|
||||
parser.add_argument('entropy', type=str, help=f"""Entropy data. Enter "{ RAND_12 }" or "{ RAND_24 }" to create a random (not-secure) example seed.""")
|
||||
|
||||
# Optional arguments
|
||||
parser.add_argument('-z', '--zero-indexed-dice',
|
||||
action="store_true",
|
||||
default=False,
|
||||
dest="zero_indexed_dice",
|
||||
help="Enables dice entry as [0-5] instead of default [1-6]")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
method = args.method
|
||||
entropy = args.entropy
|
||||
zero_indexed_dice = args.zero_indexed_dice
|
||||
|
||||
is_rand_seed = 'rand' in entropy
|
||||
if is_rand_seed:
|
||||
# Generate random data as our entropy
|
||||
if entropy not in [RAND_12, RAND_24]:
|
||||
print(f"""Invalid random entropy value: Must be either "{RAND_12}" or "{RAND_24}".""")
|
||||
exit(1)
|
||||
mnemonic_length = 12 if entropy == RAND_12 else 24
|
||||
|
||||
if method == 'dice':
|
||||
if zero_indexed_dice:
|
||||
entropy = ''.join([str(random.randint(0, 5)) for i in range(mnemonic_generation.DICE__NUM_ROLLS__12WORD if mnemonic_length == 12 else mnemonic_generation.DICE__NUM_ROLLS__24WORD)])
|
||||
else:
|
||||
entropy = ''.join([str(random.randint(1, 6)) for i in range(mnemonic_generation.DICE__NUM_ROLLS__12WORD if mnemonic_length == 12 else mnemonic_generation.DICE__NUM_ROLLS__24WORD)])
|
||||
|
||||
elif method == 'coins':
|
||||
entropy = ''.join([str(random.randint(0, 1)) for i in range(128 if mnemonic_length == 12 else 256)])
|
||||
|
||||
elif method == 'final_word':
|
||||
random_dice_rolls = ''.join([str(random.randint(0, 1)) for i in range(128 if mnemonic_length == 12 else 256)])
|
||||
entropy = " ".join(mnemonic_generation.generate_mnemonic_from_coin_flips(random_dice_rolls)[:-1])
|
||||
print(len(entropy.split()), entropy)
|
||||
|
||||
if method == 'dice':
|
||||
if not zero_indexed_dice and ('0' in entropy or '6' not in entropy):
|
||||
print("Dice entry must be 1-6 unless --zero-indexed-dice is specified")
|
||||
exit(1)
|
||||
if len(entropy) not in [mnemonic_generation.DICE__NUM_ROLLS__12WORD, mnemonic_generation.DICE__NUM_ROLLS__24WORD]:
|
||||
print(f"Dice entropy must be {mnemonic_generation.DICE__NUM_ROLLS__12WORD} or {mnemonic_generation.DICE__NUM_ROLLS__24WORD} rolls")
|
||||
exit(1)
|
||||
mnemonic = mnemonic_generation.generate_mnemonic_from_dice(entropy)
|
||||
|
||||
elif method == 'coins':
|
||||
if len(entropy) not in [128, 256]:
|
||||
print("Coin flip entropy must be 128 or 256 flips")
|
||||
exit(1)
|
||||
mnemonic = mnemonic_generation.generate_mnemonic_from_coin_flips(entropy)
|
||||
|
||||
elif method == 'final_word':
|
||||
num_input_words = len(entropy.split())
|
||||
if num_input_words not in [11, 12, 23, 24]:
|
||||
print(f"Final word entropy must be 11, 12, 23, or 24 words ({num_input_words} provided)")
|
||||
exit(1)
|
||||
|
||||
if num_input_words in [11, 23]:
|
||||
# We need to fill the last bits of entropy
|
||||
if num_input_words == 11:
|
||||
# 7 final bits of entropy in the 12th word (7 + 4-bit checksum)
|
||||
num_final_entropy_bits = 7
|
||||
else:
|
||||
# 3 final bits of entropy in the 24th word (3 + 8-bit checksum)
|
||||
num_final_entropy_bits = 3
|
||||
|
||||
final_entropy_method = None
|
||||
while final_entropy_method not in ['1', '2', '3']:
|
||||
final_entropy_method = input(f"""
|
||||
How would you like to fill the final {num_final_entropy_bits} bits of entropy?
|
||||
|
||||
1.) {num_final_entropy_bits} coin flips
|
||||
2.) Select an additional word from the wordlist
|
||||
3.) Fill with zeros
|
||||
|
||||
Type 1, 2, or 3: """)
|
||||
|
||||
if final_entropy_method == '1':
|
||||
coin_flips = input(f""" Enter {num_final_entropy_bits} coin flips as 0 or 1 (e.g. { "".join(str(random.randint(0,1)) for f in range(0, num_final_entropy_bits)) }): """)
|
||||
if len(coin_flips) != num_final_entropy_bits:
|
||||
print(f"Invalid number of coin flips: needed {num_final_entropy_bits}, got {len(coin_flips)}")
|
||||
final_word = mnemonic_generation.get_partial_final_word(coin_flips)
|
||||
entropy += f" {final_word}"
|
||||
|
||||
elif final_entropy_method == '2':
|
||||
final_word = input(f""" Enter the final word: """)
|
||||
if final_word not in WORDLIST__ENGLISH:
|
||||
print(f"Invalid word: {final_word}")
|
||||
exit(1)
|
||||
entropy += f" {final_word}"
|
||||
|
||||
elif final_entropy_method == '3':
|
||||
# Nothing to do; just pass the 11 or 23 words straight to calculate_checksum
|
||||
pass
|
||||
|
||||
mnemonic = mnemonic_generation.calculate_checksum(entropy)
|
||||
|
||||
print("\n")
|
||||
if is_rand_seed:
|
||||
print("\t***** This is a demo seed. Do not use it to store funds!!! *****")
|
||||
|
||||
print(f"""\t{" ".join(mnemonic)}\n""")
|
||||
|
||||
if is_rand_seed:
|
||||
print(f"\tEntropy: {entropy}\n")
|
||||
|
||||
if method == "dice":
|
||||
print(f"""\tVerify at iancoleman.io/bip39 or bitcoiner.guide/seed using "Base 10" or "Hex" mode.\n""")
|
||||
elif method == "coins":
|
||||
print(f"""\tVerify at iancoleman.io/bip39 or bitcoiner.guide/seed using "Binary" mode.\n""")
|
||||
Reference in New Issue
Block a user