Example #1
0
async def make_new_wallet():
    # Pick a new random seed.

    await ux_dramatic_pause('Generating...', 4)

    # always full 24-word (256 bit) entropy
    seed = random.bytes(32)

    assert len(set(seed)) > 4       # TRNG failure

    # hash to mitigate possible bias in TRNG
    seed = ngu.hash.sha256s(seed)

    await approve_word_list(seed)
Example #2
0
async def xor_split_start(*a):

    ch = await ux_show_story('''\
Seed XOR Split

This feature splits your BIP-39 seed phrase into multiple parts. \
Each part is 24 words and looks and functions as a normal BIP-39 wallet.

We recommend spliting into just two parts, but permit up to four.

If ANY ONE of the parts is lost, then ALL FUNDS are lost and the original \
seed phrase cannot be reconstructed.

Finding a single part does not help an attacker construct the original seed.

Press 2, 3 or 4 to select number of parts to split into. ''',
                             strict_escape=True,
                             escape='234x')
    if ch == 'x': return

    num_parts = int(ch)

    ch = await ux_show_story('''\
Split Into {n} Parts

On the following screen you will be shown {n} lists of 24-words. \
The new words, when reconstructed, will re-create the seed already \
in use on this Coldcard.

The new parts are generated deterministically from your seed, so if you \
repeat this process later, the same {t} words will be shown.

If you would prefer a random split using the TRNG, press (2). \
Otherwise, press OK to continue.'''.format(n=num_parts, t=num_parts * 24),
                             escape='2')

    use_rng = (ch == '2')
    if ch == 'x': return

    await ux_dramatic_pause('Generating...', 2)

    raw_secret = bytes(32)
    try:
        with stash.SensitiveValues() as sv:
            words = None
            if sv.mode == 'words':
                words = bip39.b2a_words(sv.raw).split(' ')

            if not words or len(words) != 24:
                await ux_show_story("Need 24-seed words for this feature.")
                return

            # checksum of target result is useful.
            chk_word = words[-1]
            del words

            # going to need the secret
            raw_secret = bytearray(sv.raw)
            assert len(raw_secret) == 32

        parts = []
        for i in range(num_parts - 1):
            if use_rng:
                here = random.bytes(32)
                assert len(set(here)) > 4  # TRNG failure?
                mask = ngu.hash.sha256d(here)
            else:
                mask = ngu.hash.sha256d(b'Batshitoshi ' + raw_secret +
                                        b'%d of %d parts' % (i, num_parts))
            parts.append(mask)

        parts.append(xor32(raw_secret, *parts))

        assert xor32(*parts) == raw_secret  # selftest

    finally:
        stash.blank_object(raw_secret)

    word_parts = [bip39.b2a_words(p).split(' ') for p in parts]

    while 1:
        ch = await show_n_parts(word_parts, chk_word)
        if ch == 'x':
            if not use_rng: return
            if await ux_confirm("Stop and forget those words?"):
                return
            continue

        for ws, part in enumerate(word_parts):
            ch = await word_quiz(part, title='Word %s%%d is?' % chr(65 + ws))
            if ch == 'x': break
        else:
            break

    await ux_show_story('''\
Quiz Passed!\n
You have confirmed the details of the new split.''')