-
Notifications
You must be signed in to change notification settings - Fork 149
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
BIP-85: Derive Entropy (from existing seed) #39
Merged
Changes from 14 commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
e6b2640
Generalize 'view seed' to support xprv/master secrets
doc-hex 575aed2
Bugfix: do not blank passed-in secret
doc-hex 7e97d93
Remove warning, because not applicable to all cases, and we're deep i…
doc-hex ac939cf
Correct comment
doc-hex 0c756c8
Bugfix: show master secrets as hex
doc-hex c62a98c
Add Derive Entropy item, remove need for bip39 words for lock-down/vi…
doc-hex a66ae3c
Some cleanup
doc-hex a142463
Add shortcut for testing Derive Entropy
doc-hex d90f3ba
Fix line breaks, unwanted
doc-hex 71fa2af
Switch to XPRV->XPRV w/o HMAC512; bugfixes
doc-hex 027635d
Correct key vs. message in HMAC512 step
doc-hex 76ae681
Rename HDSEED to WIF, text cleanups, etc
doc-hex ea7c31d
Derived seed test code
doc-hex cbe8374
XPRV created from entropy, not BIP32 process; more tests
doc-hex 48672d3
Merge branch 'master' into deventro
doc-hex db64610
Merge branch 'master' into deventro
doc-hex 74fd6fb
Merge branch 'master' into deventro
doc-hex e3a41d6
Comments/docs updates
doc-hex 67ab364
Merge branch 'master' into deventro
doc-hex 4f83de9
Add bip number
doc-hex e19c412
Merge branch 'master' into deventro
doc-hex File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
# (c) Copyright 2020 by Coinkite Inc. This file is part of Coldcard <coldcardwallet.com> | ||
# and is covered by GPLv3 license found in COPYING. | ||
# | ||
# BIP-(tbd): Deterministic Entropy From BIP32 Keychains | ||
# | ||
# Using the system's BIP32 master key, safely derive seeds phrases/entropy for other systems. | ||
# | ||
# see <https://gist.github.com/ethandrv_entro/268c52f018b94bea29a6e809381c05d6> | ||
# idea from: Ethan Kosakovsky <ethankosakovsky@protonmail.com> | ||
# | ||
# | ||
import stash, tcc, hmac, chains | ||
from ux import ux_show_story, ux_enter_number, the_ux, ux_confirm | ||
from menu import MenuItem, MenuSystem | ||
from ubinascii import hexlify as b2a_hex | ||
from serializations import hash160 | ||
|
||
def drv_entro_start(*a): | ||
|
||
# UX entry | ||
ch = await ux_show_story('''\ | ||
Create Entropy for Other Wallets | ||
|
||
This feature derives "entropy" based mathematically on this wallet's seed value. \ | ||
This will be displayed as a 12 or 24 word seed phrase, \ | ||
or formatted in other ways to make it easy to import into \ | ||
other wallet systems. | ||
|
||
You can recreate this value later, based \ | ||
only the seed-phrase or backup of this Coldcard. | ||
|
||
There is no way to reverse the process, should the other wallet system be compromised, \ | ||
so the other wallet is effectively segregated from the Coldcard and yet \ | ||
still backed-up.''') | ||
if ch != 'y': return | ||
|
||
if stash.bip39_passphrase: | ||
if not await ux_confirm('''You have a BIP39 passphrase set right now and so that will become wrapped into the new secret.'''): | ||
return | ||
|
||
choices = [ '12 words', '18 words', '24 words', 'WIF (privkey)', | ||
'XPRV (BIP32)', '32-bytes hex', '64-bytes hex'] | ||
|
||
m = MenuSystem([MenuItem(c, f=drv_entro_step2) for c in choices]) | ||
the_ux.push(m) | ||
|
||
def drv_entro_step2(_1, picked, _2): | ||
from main import dis | ||
from files import CardSlot, CardMissingError | ||
|
||
the_ux.pop() | ||
|
||
index = await ux_enter_number("Index Number?", 9999) | ||
|
||
if picked in (0,1,2): | ||
# BIP39 seed phrases (we only support English) | ||
num_words = (12, 18, 24)[picked] | ||
width = (16, 24, 32)[picked] # of bytes | ||
path = "m/83696968'/39'/0'/{num_words}'/{index}'".format(num_words=num_words, index=index) | ||
s_mode = 'words' | ||
elif picked == 3: | ||
# HDSeed for Bitcoin Core: but really a WIF of a private key, can be used anywhere | ||
s_mode = 'wif' | ||
path = "m/83696968'/2'/{index}'".format(index=index) | ||
width = 32 | ||
elif picked == 4: | ||
# New XPRV | ||
path = "m/83696968'/32'/{index}'".format(index=index) | ||
s_mode = 'xprv' | ||
width = 64 | ||
elif picked in (5, 6): | ||
width = 32 if picked == 5 else 64 | ||
path = "m/83696968'/128169'/{width}'/{index}'".format(width=width, index=index) | ||
s_mode = 'hex' | ||
else: | ||
raise ValueError(picked) | ||
|
||
dis.fullscreen("Working...") | ||
encoded = None | ||
|
||
with stash.SensitiveValues() as sv: | ||
node = sv.derive_path(path) | ||
entropy = hmac.HMAC(b'bip-entropy-from-k', node.private_key(), tcc.sha512).digest() | ||
sv.register(entropy) | ||
|
||
# truncate for this application | ||
new_secret = entropy[0:width] | ||
|
||
|
||
# only "new_secret" is interesting past here (node already blanked at this point) | ||
del node | ||
|
||
# Reveal to user! | ||
chain = chains.current_chain() | ||
|
||
if s_mode == 'words': | ||
# BIP39 seed phrase, various lengths | ||
words = tcc.bip39.from_data(new_secret).split(' ') | ||
|
||
msg = 'Seed words (%d):\n' % len(words) | ||
msg += '\n'.join('%2d: %s' % (i+1, w) for i,w in enumerate(words)) | ||
|
||
encoded = stash.SecretStash.encode(seed_phrase=new_secret) | ||
|
||
elif s_mode == 'wif': | ||
# for Bitcoin Core: a 32-byte of secret exponent, base58 w/ prefix 0x80 | ||
# - always "compressed", so has suffix of 0x01 (inside base58) | ||
# - we're not checking it's on curve | ||
# - we have no way to represent this internally, since we rely on bip32 | ||
|
||
# append 0x01 to indicate it's a compressed private key | ||
pk = new_secret + b'\x01' | ||
|
||
msg = 'WIF (privkey):\n' + tcc.codecs.b58_encode(chain.b58_privkey + pk) | ||
|
||
elif s_mode == 'xprv': | ||
# Raw XPRV value. | ||
ch, pk = new_secret[0:32], new_secret[32:64] | ||
master_node = tcc.bip32.HDNode(chain_code=ch, private_key=pk, | ||
child_num=0, depth=0, fingerprint=0) | ||
|
||
encoded = stash.SecretStash.encode(xprv=master_node) | ||
|
||
msg = 'Derived XPRV:\n' + chain.serialize_private(master_node) | ||
|
||
elif s_mode == 'hex': | ||
# Random hex number for whatever purpose | ||
msg = ('Hex (%d bytes):\n' % width) + str(b2a_hex(new_secret), 'ascii') | ||
|
||
stash.blank_object(new_secret) | ||
new_secret = None # no need to print it again | ||
else: | ||
raise ValueError(s_mode) | ||
|
||
msg += '\n\nPath Used (index=%d):\n %s' % (index, path) | ||
|
||
if new_secret: | ||
msg += '\n\nRaw Entropy:\n' + str(b2a_hex(new_secret), 'ascii') | ||
|
||
print(msg) # XXX debug | ||
|
||
prompt = '\n\nPress 1 to save to MicroSD card' | ||
if encoded is not None: | ||
prompt += ', 2 to switch to derived secret.' | ||
|
||
while 1: | ||
ch = await ux_show_story(msg+prompt, sensitive=True, escape='12') | ||
|
||
if ch == '1': | ||
# write to SD card: simple text file | ||
try: | ||
with CardSlot() as card: | ||
fname, out_fn = card.pick_filename('drv-%s-idx%d.txt' % (s_mode, index)) | ||
|
||
with open(fname, 'wt') as fp: | ||
fp.write(msg) | ||
fp.write('\n') | ||
except CardMissingError: | ||
await needs_microsd() | ||
continue | ||
except Exception as e: | ||
await ux_show_story('Failed to write!\n\n\n'+str(e)) | ||
continue | ||
|
||
await ux_show_story("Filename is:\n\n%s" % out_fn, title='Saved') | ||
else: | ||
break | ||
|
||
if new_secret is not None: | ||
stash.blank_object(new_secret) | ||
stash.blank_object(msg) | ||
|
||
if ch == '2' and (encoded is not None): | ||
from main import pa, settings, dis | ||
from pincodes import AE_SECRET_LEN | ||
|
||
# switch over to new secret! | ||
dis.fullscreen("Applying...") | ||
|
||
stash.bip39_passphrase = '' | ||
tmp_secret = encoded + bytes(AE_SECRET_LEN - len(encoded)) | ||
|
||
# monkey-patch to block SE access, and just use new secret | ||
pa.fetch = lambda *a, **k: bytearray(tmp_secret) | ||
pa.change = lambda *a, **k: None | ||
pa.ls_fetch = pa.change | ||
pa.ls_change = pa.change | ||
|
||
# copies system settings to new encrypted-key value, calculates | ||
# XFP, XPUB and saves into that, and starts using them. | ||
pa.new_main_secret(pa.fetch()) | ||
|
||
await ux_show_story("New master key in effect until next power down.") | ||
|
||
if encoded is not None: | ||
stash.blank_object(encoded) | ||
|
||
# EOF |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# load up the simulator w/ indicated encoded secret. could be xprv/words/etc. | ||
import tcc, main | ||
from sim_settings import sim_defaults | ||
import stash, chains | ||
from h import b2a_hex | ||
from main import settings, pa | ||
from stash import SecretStash, SensitiveValues | ||
from utils import xfp2str | ||
|
||
settings.current = sim_defaults | ||
settings.overrides.clear() | ||
|
||
raw = main.ENCODED_SECRET | ||
pa.change(new_secret=raw) | ||
pa.new_main_secret(raw) | ||
|
||
print("New key in effect: %s" % settings.get('xpub', 'MISSING')) | ||
print("Fingerprint: %s" % xfp2str(settings.get('xfp', 0))) | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
link is 404