def test_die_rolls_to_bitstring_insufficient_entropy(self): """Try to get bitstring but with not enough die rolls""" with self.assertRaises(entropy.InsufficientEntropyError): entropy.die_rolls_to_bitstring(dice_vals=[1], bitstring_len=4) with self.assertRaises(entropy.InsufficientEntropyError): entropy.die_rolls_to_bitstring(dice_vals=[1, 2], bitstring_len=6)
def test_die_rolls_to_bitstring_entropy_uniformity(): """Demonstrate uniformity of bits for all possible die rolls""" failures = [] with progressbar.ProgressBar(max_value=TEST2_MAX_BITS * TEST2_MAX_ROLL_NUM) as prog_bar: for bitstring_len in range(2, TEST2_MAX_BITS + 1, 2): for rolls_num in range(1, TEST2_MAX_ROLL_NUM + 1): roll_sequences = _get_dice_rolls_of_len(rolls_num) results_0 = [0] * bitstring_len results_1 = [0] * bitstring_len for roll_sequence in roll_sequences: #count up the results for bistrings generated from each roll combo _dprint("Testing bit len {0} roll sequence {1}".format( bitstring_len, roll_sequence)) try: bits = entropy.die_rolls_to_bitstring( roll_sequence, bitstring_len) _dprint( "bitlen = {0} sequence = {1} bits = {2}".format( bitstring_len, roll_sequence, bits)) for index, bit in enumerate(bits): assert bit in ("0", "1") if bit == "0": results_0[index] += 1 else: results_1[index] += 1 except entropy.InsufficientEntropyError: _dprint( ("Not enough dice rolls ({0}) for {1} bits of " "entropy. Omiting from frequency count.").format( rolls_num, bitstring_len)) #all bits in results arrays should be equally distributed frequency = results_0[0] for index, val in enumerate(results_0): if frequency != val: msg = (("Failure for {0} bits and {1} rolls: Bit " "results not uniform: {2} != {3}").format( bitstring_len, rolls_num, results_0, results_1)) print msg failures.append(msg) for index, val in enumerate(results_1): if frequency != val: msg = (( "Failure for {0} bits and {1} rolls: Bit results " "not uniform: {2} != {3}").format( bitstring_len, rolls_num, results_0, results_1)) print msg failures.append(msg) prog_bar.update(bitstring_len * rolls_num) if len(failures) == 0: print "Test #2: Passed. No failures." else: print "Test #2: Failed:" for failure in failures: print failure
def test_die_rolls_to_bitstring_longer(self): """Test function with some longer strings, binary output manually computed""" self.assertEqual( entropy.die_rolls_to_bitstring(dice_vals=[6] * 32, bitstring_len=64), "0" * 64) self.assertEqual( entropy.die_rolls_to_bitstring(dice_vals=[3] * 32, bitstring_len=64), "1" * 64) self.assertEqual( entropy.die_rolls_to_bitstring(dice_vals=[1, 2, 3, 6] * 16, bitstring_len=128), "00111001" * 16) self.assertEqual( entropy.die_rolls_to_bitstring(dice_vals=[6] * 63 + [2], bitstring_len=128), "1" + "0" * 127) self.assertEqual( entropy.die_rolls_to_bitstring(dice_vals=[1] + [6] * 63, bitstring_len=128), "0" * 127 + "1")
def test_die_rolls_to_bitstring_entropy_uniformity(self): """Demonstrate uniformity of bits for all possible die rolls""" #Note: This is repeated for bigger ranges in check_dice_entropy.py for bitstring_len in [2, 4, 6]: for rolls_num in [1, 2, 3, 4, 5]: roll_sequences = _get_dice_rolls_of_len(rolls_num) results_0 = [0] * bitstring_len results_1 = [0] * bitstring_len for roll_sequence in roll_sequences: #count up the results for bistrings generated from each roll combo _dprint("Testing bit len {0} roll sequence {1}".format( bitstring_len, roll_sequence)) try: bits = entropy.die_rolls_to_bitstring( roll_sequence, bitstring_len) _dprint( "bitlen = {0} sequence = {1} bits = {2}".format( bitstring_len, roll_sequence, bits)) for index, bit in enumerate(bits): assert bit in ("0", "1") if bit == "0": results_0[index] += 1 else: results_1[index] += 1 except entropy.InsufficientEntropyError: _dprint( ("Not enough dice rolls ({0}) for {1} bits of " "entropy. Omiting from frequency count.").format( rolls_num, bitstring_len)) #all bits in results arrays should be equally distributed frequency = results_0[0] for index, val in enumerate(results_0): self.assertEqual( frequency, val, ("Failure for {0} bits and {1} rolls: Bit results not " "uniform: {2} != {3}".format(bitstring_len, rolls_num, results_0, results_1))) for index, val in enumerate(results_1): self.assertEqual( frequency, val, ("Failure for {0} bits and {1} rolls: Bit results not " "uniform: {2} != {3}".format(bitstring_len, rolls_num, results_0, results_1)))
def rand_wrapper(n_bits): """Wrapper for randint and die_rolls_to_bitstring""" #doubled rolls to account for the average number of rolls dropped as 4 or 5 n_rolls = int(math.ceil(entropy.die_rolls_per_bits(n_bits) * 2)) enough_rolls = False rolls = [] rand = random.SystemRandom() for _ in repeat(None, n_rolls): rolls.append(rand.randint(1, 6)) while not enough_rolls: try: bitstring = entropy.die_rolls_to_bitstring(rolls, bitstring_len=n_bits) enough_rolls = True except entropy.InsufficientEntropyError: print "INFO: {0} rolls was insufficient. Adding another roll.".format( len(rolls)) rolls.append(rand.randint(1, 6)) return bitstring
def _main(): wordlist = bip39.get_wordlist() wordset = set(wordlist) assert len(wordlist) == 2048 mnemonic = str( raw_input('Enter your BIP39 mnemonic in using the ' 'canonical English dictionary: ')) print "You entered: '{0}'".format(mnemonic) words = [str(word) for word in mnemonic.split(' ')] if not _is_canonical_mnemonic(words, wordset): sys.exit(1) if len(words) not in NORMAL_MNEMONIC_LEN: print "WARNING: Length of menonic you provided ({0}) is atypical.".format( len(words)) binstring = None try: binstring = bip39.mnemonic2binstring(mnemonic) except bip39.FailedCheckSumError: print( "ERROR: Mnemonic failed checksum. It may be an invalid BIP39 " "mnemonic -- be careful!!! Stopping.") sys.exit(1) print "Mnemonic as binary string: {0}".format(binstring) print "Note: The mnemonic passes a checksum test!" buf = "Re-deriving mnemonic from binary string for sanity check... {result}" mnemonic_calc = bip39.binstring2mnemonic(binstring) if mnemonic == mnemonic_calc: print buf.format(result="PASSED!") else: print buf.format( result="FAILED! Stopping. Please report issue on GitHub.") sys.exit(1) if is_valid_entropy(binstring): print "The mnemonic appears to conform to a valid bip39 entropy format." else: print( "ERROR: The mnemonic doesn't appear to conform to bip39 entropy " "format -- be careful!!! Stopping.") sys.exit(1) urandom_rounds = int( raw_input(('Enter the number of times entropy should be mixed in from ' '/dev/urandom (0 to skip): '))) latest_entropy_binstring = binstring latest_mnemonic = mnemonic n_bits = len(binstring) for _ in repeat(None, urandom_rounds): new_entropy = entropy.get_entropy(n_bits) combined_entropy = entropy.xor(latest_entropy_binstring, new_entropy) combined_mnemonic = bip39.binstring2mnemonic(combined_entropy) print "====" print "old: {old_hex} {old_mnemonic}".format( old_hex=bip39.bin2hex(latest_entropy_binstring), old_mnemonic=latest_mnemonic) print "new: {new_hex} {new_mnemonic}".format( new_hex=bip39.bin2hex(new_entropy), new_mnemonic=bip39.binstring2mnemonic(new_entropy)) print "xor: {xor_hex} {xor_mnemonic}".format( xor_hex=bip39.bin2hex(combined_entropy), xor_mnemonic=combined_mnemonic) print( "Manually validate:\n" "\t1. Old hex and mnemonic match previous versions.\n" "\t2. Entering new and xor'd hex into alternative tool such as " "Ian Coleman bip39 tool derives to correct mnemonics.\n" "\t3. Confirm old XOR new = xor'd version hex char at a time.") latest_entropy_binstring = combined_entropy latest_mnemonic = combined_mnemonic min_estimated_rolls = int( math.ceil(entropy.die_rolls_per_bits(n_bits=n_bits) * (4.0 / 3))) entropy_gathered = False rolls_str = '' rolls = [] while True: rolls_str = str( raw_input(( 'Roll a die at least {minimum} times to provide entropy to mix ' 'in, with each roll represented by 1 to 6 and a space separating ' 'each roll: ').format(minimum=min_estimated_rolls))) rolls = [int(roll) for roll in rolls_str.split()] if len(rolls) >= min_estimated_rolls: break dice_bitstring = None while True: try: dice_bitstring = entropy.die_rolls_to_bitstring( dice_vals=rolls, bitstring_len=n_bits) print "Dice rolls as bitstring: {0}".format(dice_bitstring) break except entropy.InsufficientEntropyError: more_rolls_str = str( raw_input(( "More entropy needed. {num} rolls saved so far. Please enter " "another roll: ").format(num=len(rolls)))) rolls.extend([int(roll) for roll in more_rolls_str.split()]) combined_entropy = entropy.xor(latest_entropy_binstring, dice_bitstring) combined_mnemonic = bip39.binstring2mnemonic(combined_entropy) print "====" print "old: {old_hex} {old_mnemonic}".format( old_hex=bip39.bin2hex(latest_entropy_binstring), old_mnemonic=latest_mnemonic) print "new: {new_hex} {new_mnemonic}".format( new_hex=bip39.bin2hex(dice_bitstring), new_mnemonic=bip39.binstring2mnemonic(dice_bitstring)) print "xor: {xor_hex} {xor_mnemonic}".format( xor_hex=bip39.bin2hex(combined_entropy), xor_mnemonic=combined_mnemonic)
def test_die_rolls_to_bitstring_invalid(self): """Invoke function with invalid args""" #bad die rolls with self.assertRaises(ValueError): entropy.die_rolls_to_bitstring(dice_vals=[0, 1, 1], bitstring_len=2) with self.assertRaises(ValueError): entropy.die_rolls_to_bitstring(dice_vals=[7, 1, 1], bitstring_len=2) #rolls not a list or not int with self.assertRaises(TypeError): entropy.die_rolls_to_bitstring(dice_vals='1', bitstring_len=2) with self.assertRaises(TypeError): entropy.die_rolls_to_bitstring(dice_vals=['1'], bitstring_len=2) with self.assertRaises(TypeError): entropy.die_rolls_to_bitstring(dice_vals=[1], bitstring_len='2') #invalid length with self.assertRaises(ValueError): entropy.die_rolls_to_bitstring(dice_vals=[0], bitstring_len=0) with self.assertRaises(entropy.InsufficientEntropyError): entropy.die_rolls_to_bitstring(dice_vals=[], bitstring_len=2) #bitstring length should be a multiple of 2 with self.assertRaises(ValueError): entropy.die_rolls_to_bitstring(dice_vals=[1], bitstring_len=1)
def test_die_rolls_to_bitstring_valid_2bit(self): """Test function with various roll and 2-bit lenth, result manually computed""" #2 bits self.assertEqual( entropy.die_rolls_to_bitstring(dice_vals=[1], bitstring_len=2), '01') self.assertEqual( entropy.die_rolls_to_bitstring(dice_vals=[2], bitstring_len=2), '10') self.assertEqual( entropy.die_rolls_to_bitstring(dice_vals=[3], bitstring_len=2), '11') self.assertEqual( entropy.die_rolls_to_bitstring(dice_vals=[6], bitstring_len=2), '00') #a second roll for 2 bits won't impact those 2 bits for roll2 in [1, 2, 3, 6]: self.assertEqual( entropy.die_rolls_to_bitstring(dice_vals=[1, roll2], bitstring_len=2), '01') # "xxx01"[-2:] = "01" self.assertEqual( entropy.die_rolls_to_bitstring(dice_vals=[2, roll2], bitstring_len=2), '10') # "xxx10"[-2:] = "10" self.assertEqual( entropy.die_rolls_to_bitstring(dice_vals=[3, roll2], bitstring_len=2), '11') # "xxx11"[-2:] = "11" self.assertEqual( entropy.die_rolls_to_bitstring(dice_vals=[6, roll2], bitstring_len=2), '00') # "xxx00"[-2:] = "00" for roll2 in [1, 2, 3, 6]: for roll3 in [1, 2, 3, 6]: self.assertEqual( entropy.die_rolls_to_bitstring(dice_vals=[1, roll2, roll3], bitstring_len=2), '01') # "xxx01"[-2:] = "01" self.assertEqual( entropy.die_rolls_to_bitstring(dice_vals=[2, roll2, roll3], bitstring_len=2), '10') # "xxx10"[-2:] = "10" self.assertEqual( entropy.die_rolls_to_bitstring(dice_vals=[3, roll2, roll3], bitstring_len=2), '11') # "xxx11"[-2:] = "11" self.assertEqual( entropy.die_rolls_to_bitstring(dice_vals=[6, roll2, roll3], bitstring_len=2), '00') # "xxx00"[-2:] = "00"