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
Exemple #6
0
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"