def test_iteration_exponent(): mnemonics = shamir.generate_mnemonics(1, [(3, 5)], MS, b"TREZOR", 1)[0] assert MS == shamir.combine_mnemonics(mnemonics[1:4], b"TREZOR") assert MS != shamir.combine_mnemonics(mnemonics[1:4]) mnemonics = shamir.generate_mnemonics(1, [(3, 5)], MS, b"TREZOR", 2)[0] assert MS == shamir.combine_mnemonics(mnemonics[1:4], b"TREZOR") assert MS != shamir.combine_mnemonics(mnemonics[1:4])
def split_mnemonic(m, n): phrase = input('Enter BIP39 phrase:\n') is_valid = check_bip39_phrase(phrase) if not is_valid: print('The BIP39 phrase is invalid.', file=sys.stderr) sys.exit(2) seed = phrase_to_seed(phrase) print_seed(seed) print('\nGenerating shares...') mnemonics = shamir_mnemonic.generate_mnemonics(1, [(m, n)], seed)[0] combinations = [combo for combo in itertools.combinations(mnemonics, m)] for combo in combinations: secret = shamir_mnemonic.combine_mnemonics(combo) if secret != seed: print( 'Error: a combination of shares failed to verify. Try again.', file=sys.stderr) sys.exit(2) return mnemonics
def test_all_groups_exist(): for threshold in (1, 2, 5): mnemonics = shamir.generate_mnemonics(threshold, [(3, 5), (1, 1), (2, 3), (2, 5), (3, 5)], MS) assert len(mnemonics) == 5 assert len(sum(mnemonics, [])) == 19
def test_group_sharing(): group_threshold = 2 group_sizes = (5, 3, 5, 1) member_thresholds = (3, 2, 2, 1) mnemonics = shamir.generate_mnemonics( group_threshold, list(zip(member_thresholds, group_sizes)), MS) # Test all valid combinations of mnemonics. for groups in combinations(zip(mnemonics, member_thresholds), group_threshold): for group1_subset in combinations(groups[0][0], groups[0][1]): for group2_subset in combinations(groups[1][0], groups[1][1]): mnemonic_subset = list(group1_subset + group2_subset) shuffle(mnemonic_subset) assert MS == shamir.combine_mnemonics(mnemonic_subset) # Minimal sets of mnemonics. assert MS == shamir.combine_mnemonics( [mnemonics[2][0], mnemonics[2][2], mnemonics[3][0]]) assert MS == shamir.combine_mnemonics( [mnemonics[2][3], mnemonics[3][0], mnemonics[2][4]]) # One complete group and one incomplete group out of two groups required. with pytest.raises(MnemonicError): shamir.combine_mnemonics(mnemonics[0][2:] + [mnemonics[1][0]]) # One group of two required. with pytest.raises(MnemonicError): shamir.combine_mnemonics(mnemonics[0][1:4])
def test_recover_ems(): mnemonics = shamir.generate_mnemonics(1, [(3, 5)], MS, b"TREZOR")[0] groups = shamir.decode_mnemonics(mnemonics[:3]) encrypted_master_secret = shamir.recover_ems(groups) recovered = encrypted_master_secret.decrypt(b"TREZOR") assert recovered == MS
def test_recover_ems(): mnemonics = shamir.generate_mnemonics(1, [(3, 5)], MS, b"TREZOR")[0] identifier, exponent, encrypted_master_secret = shamir.recover_ems( mnemonics[:3]) recovered = shamir.decrypt(encrypted_master_secret, b"TREZOR", exponent, identifier) assert recovered == MS
def test_group_sharing_threshold_1(): group_threshold = 1 group_sizes = (5, 3, 5, 1) member_thresholds = (3, 2, 2, 1) mnemonics = shamir.generate_mnemonics( group_threshold, list(zip(member_thresholds, group_sizes)), MS) # Test all valid combinations of mnemonics. for group, threshold in zip(mnemonics, member_thresholds): for group_subset in combinations(group, threshold): mnemonic_subset = list(group_subset) shuffle(mnemonic_subset) assert MS == shamir.combine_mnemonics(mnemonic_subset)
def test_invalid_sharing(): # Short master secret. with pytest.raises(ValueError): shamir.generate_mnemonics(1, [(2, 3)], MS[:14]) # Odd length master secret. with pytest.raises(ValueError): shamir.generate_mnemonics(1, [(2, 3)], MS + b"X") # Group threshold exceeds number of groups. with pytest.raises(ValueError): shamir.generate_mnemonics(3, [(3, 5), (2, 5)], MS) # Invalid group threshold. with pytest.raises(ValueError): shamir.generate_mnemonics(0, [(3, 5), (2, 5)], MS) # Member threshold exceeds number of members. with pytest.raises(ValueError): shamir.generate_mnemonics(2, [(3, 2), (2, 5)], MS) # Invalid member threshold. with pytest.raises(ValueError): shamir.generate_mnemonics(2, [(0, 2), (2, 5)], MS) # Group with multiple members and threshold 1. with pytest.raises(ValueError): shamir.generate_mnemonics(2, [(3, 5), (1, 3), (2, 5)], MS)
def test_passphrase(): mnemonics = shamir.generate_mnemonics(1, [(3, 5)], MS, b"TREZOR")[0] assert MS == shamir.combine_mnemonics(mnemonics[1:4], b"TREZOR") assert MS != shamir.combine_mnemonics(mnemonics[1:4])
def test_basic_sharing_fixed(): mnemonics = shamir.generate_mnemonics(1, [(3, 5)], MS)[0] assert MS == shamir.combine_mnemonics(mnemonics[:3]) assert MS == shamir.combine_mnemonics(mnemonics[1:4]) with pytest.raises(MnemonicError): shamir.combine_mnemonics(mnemonics[1:3])
def test_basic_sharing_random(): secret = secrets.token_bytes(16) mnemonics = shamir.generate_mnemonics(1, [(3, 5)], secret)[0] assert shamir.combine_mnemonics(mnemonics[:3]) == shamir.combine_mnemonics( mnemonics[2:])
output.i += 1 output.data.append(("{}. {}".format(output.i, description), mnemonics, secret.hex())) output.i = 0 output.data = [] shamir.RANDOM_BYTES = random_bytes if __name__ == "__main__": random.seed(1337) for n in [16, 32]: description = "Valid mnemonic without sharing ({} bits)" secret = random_bytes(n) groups = shamir.generate_mnemonics(1, [(1, 1)], secret, b"TREZOR") output(description.format(8 * n), groups[0], secret) description = "Mnemonic with invalid checksum ({} bits)" indices = list(shamir.mnemonic_to_indices(groups[0][0])) indices[-1] ^= 1 mnemonic = shamir.mnemonic_from_indices(indices) output(description.format(8 * n), [mnemonic], b"") description = "Mnemonic with invalid padding ({} bits)" overflowing_bits = (8 * n) % shamir.RADIX_BITS if overflowing_bits: indices = list(shamir.mnemonic_to_indices(groups[0][0])) indices[4] += 1 << overflowing_bits indices = tuple(indices[: -shamir.CHECKSUM_LENGTH_WORDS]) mnemonic = shamir.mnemonic_from_indices(
from pycoin.symbols.btc import network as btc from pycoin.networks.bitcoinish import create_bitcoinish_network as btcnet from shamir_mnemonic import combine_mnemonics, generate_mnemonics # Nonsense Passphrase and Master-Seed PP = "Pa55w0rd" MSEC = "0660cc198330660cc198330660cc1983" # SLIP-14 entryopy for fun ACCT44 = '44H/0H/0H' ACCT49 = '49H/0H/0H' ACCT84 = '84H/0H/0H' HW49 = dict(bip32_pub_prefix_hex="049d7cb2", bip32_prv_prefix_hex="049d7878") HW84 = dict(bip32_pub_prefix_hex="04b24746", bip32_prv_prefix_hex="04b2430c") # Can be done MS -> shamir-mnemonic or shamir-mnemonic -> MS SSS = generate_mnemonics(1, [(1,1)], bytes.fromhex(MSEC), PP.encode(), 0) #SSS = [["armed husband academic academic document aquatic wisdom " + # "pleasure lilac response axle parking shaft crazy cargo " + # "dish diet dramatic together unfold"]] seed = combine_mnemonics(SSS[-1],PP.encode()) print(seed.hex()) print(SSS[-1][-1], "\n") # BIP44 derivations and xpub/xprv prefix acct44 = btc.keys.bip32_seed(seed).subkey_for_path(ACCT44) key44 = acct44.subkey_for_path('0/0') addr44 = key44.address() xprv44 = acct44.hwif(as_private=True) xpub44 = acct44.hwif(as_private=False) print(xprv44)
addr = btc.address.for_p2s(script) if path[0:2] == "84": addr = btc.address.for_p2pkh_wit(hash) xpub = acct.hwif(as_private=False) xprv = acct.hwif(as_private=True) print(f"\n{xprv}\n{xpub}\n{addr}") def main(seed, sss, passphrase): print("Passphrase:", f'"{passphrase}"') print("Master Seed:", seed.hex()) print(sss[-1][-1]) print_keys(seed, ACCT44, None) print_keys(seed, ACCT49, HW49) print_keys(seed, ACCT84, HW84) if __name__ == "__main__": if len(argv) < 3: print(f"Usage:\t{argv[0]}", "<passphrase> <master_seed | shamir-mnemonic>") print('\tHint: Use "" as your passphrase if you have none') else: passphrase = argv[1] args = " ".join(argv[2:]).replace("0x","").strip() if all(c in hexdigits for c in args): sss = generate_mnemonics(1, [(1,1)], bytes.fromhex(args), passphrase.encode(), 0) else: sss = [[args]] seed = combine_mnemonics(sss[-1],passphrase.encode()) main(seed, sss, passphrase)
def create_shares(secret): #secret = secrets.token_bytes(16) #secret = secret.encode('utf-8') shares = shamir.generate_mnemonics(1, [(3, 6)], secret)[0] return shares