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)
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.''')