-
Notifications
You must be signed in to change notification settings - Fork 17
/
seed.py
173 lines (129 loc) · 4.83 KB
/
seed.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# SPDX-FileCopyrightText: 2020 Foundation Devices, Inc. <[email protected]>
# SPDX-License-Identifier: GPL-3.0-or-later
#
# SPDX-FileCopyrightText: 2018 Coinkite, Inc. <coldcardwallet.com>
# SPDX-License-Identifier: GPL-3.0-only
#
# (c) Copyright 2018 by Coinkite Inc. This file is part of Coldcard <coldcardwallet.com>
# and is covered by GPLv3 license found in COPYING.
#
# seed.py - bip39 seeds and words
#
# references:
# - <https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki>
# - <https://iancoleman.io/bip39/#english>
# - zero values:
# - 'abandon' * 23 + 'art'
# - 'abandon' * 17 + 'agent'
# - 'abandon' * 11 + 'about'
#
from common import noise
import trezorcrypto
import uctypes
from pincodes import SE_SECRET_LEN
from stash import SecretStash, SensitiveValues
from ubinascii import hexlify as b2a_hex
from utils import pop_count, xfp2str
from ux import ux_show_story
# seed words lengths we support: 24=>256 bits, and recommended
VALID_LENGTHS = (24, 18, 12)
async def create_new_wallet_seed():
from noise_source import NoiseSource
from common import dis, system
from uasyncio import sleep_ms
# Pick a new random seed
dis.fullscreen('Generating Seed...')
await sleep_ms(1000)
# always full 24-word (256 bit) entropy
seed = bytearray(32)
noise.random_bytes(seed, NoiseSource.ALL)
# hash to mitigate any potential bias in Avalanche RNG
seed = trezorcrypto.sha256(seed).digest()
# print('create_new_wallet_seed(): New seed = {}'.format(b2a_hex(seed)))
return seed
async def save_wallet_seed(seed_bits):
from common import dis, pa, settings, system
dis.fullscreen('Saving Seed...')
system.show_busy_bar()
# encode it for our limited secret space
nv = SecretStash.encode(seed_bits=seed_bits)
pa.change(new_secret=nv)
# re-read settings since key is now different
# - also captures xfp, xpub at this point
await pa.new_main_secret(nv)
# check and reload secret
pa.reset()
pa.login()
system.hide_busy_bar()
def set_bip39_passphrase(pw):
# apply bip39 passphrase for now (volatile)
# - return None or error msg
import stash
from common import system
from utils import bytes_to_hex_str
stash.bip39_passphrase = pw
# Create a hash from the passphrase
if len(stash.bip39_passphrase) > 0:
digest = bytearray(32)
system.sha256(stash.bip39_passphrase, digest)
digest_hex = bytes_to_hex_str(digest)
stash.bip39_hash = digest_hex[:8] # Take first 8 characters (32-bits)
# print('stash.bip39_hash={}'.format(stash.bip39_hash))
else:
stash.bip39_hash = ''
with stash.SensitiveValues() as sv:
if sv.mode != 'words':
# can't do it without original seed words
return 'No BIP39 seed words'
sv.capture_xpub()
async def remember_bip39_passphrase():
# Compute current xprv and switch to using that as root secret.
import stash
from common import dis, pa, system
dis.fullscreen('Check...')
with stash.SensitiveValues() as sv:
# GIT: https://github.com/Coldcard/firmware/commit/7e97d93153aee1a6878702145410ff9a6106119a
# If this message is deemed unnecessary, we could consider if the above commit fixes it
if sv.mode != 'words':
# not a BIP39 derived secret, so cannot work.
await ux_show_story('''The wallet secret was not based on a seed phrase, so we cannot add a BIP39 passphrase at this time.''', title='Failed')
return
nv = SecretStash.encode(xprv=sv.node)
# Important: won't write new XFP to nvram if pw still set
stash.bip39_passphrase = ''
system.show_busy_bar()
dis.fullscreen('Saving...')
pa.change(new_secret=nv)
# re-read settings since key is now different
# - also captures xfp, xpub at this point
await pa.new_main_secret(nv)
system.hide_busy_bar()
# check and reload secret
pa.reset()
pa.login()
async def erase_wallet(restart=True):
from common import dis, pa, settings,system
import utime
import version
dis.fullscreen('Erasing Wallet...')
system.show_busy_bar();
# Remove wallet-related settings, but leave other settings alone like terms_ok, validated_ok
settings.remove('xfp')
settings.remove('xpub')
settings.remove('words')
settings.remove('multisig')
settings.remove('accounts')
settings.remove('backup_quiz')
settings.remove('enable_passphrase')
# save a blank secret (all zeros is a special case)
nv = bytes(SE_SECRET_LEN)
pa.change(new_secret=nv)
await settings.save()
system.hide_busy_bar();
if restart:
dis.fullscreen('Restarting...')
utime.sleep(1)
# security: need to reboot to really be sure to clear the secrets from main memory.
from machine import reset
reset()
# EOF