Skip to content

Commit

Permalink
Merge pull request #90 from cryptoadvance/user-entropy
Browse files Browse the repository at this point in the history
add user entropy for mnemonic generation
  • Loading branch information
stepansnigirev authored Oct 21, 2020
2 parents b937022 + dd77c90 commit 2af0c4d
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 22 deletions.
3 changes: 3 additions & 0 deletions src/gui/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ def init_styles(dark=True):
lv.style_copy(th.style.btnm.btn.pr, th.style.btnm.btn.rel)
th.style.btnm.btn.pr.body.main_color = chl
th.style.btnm.btn.pr.body.grad_color = chl
# button map toggled
lv.style_copy(th.style.btnm.btn.tgl_pr, th.style.btnm.btn.pr)
lv.style_copy(th.style.btnm.btn.tgl_rel, th.style.btnm.btn.pr)
# button map inactive
lv.style_copy(th.style.btnm.btn.ina, th.style.btnm.btn.rel)
th.style.btnm.btn.ina.text.opa = 80
Expand Down
20 changes: 10 additions & 10 deletions src/gui/screens/input.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ def __init__(
self.note.align(self.title, lv.ALIGN.OUT_BOTTOM_MID, 0, 5)

self.kb = HintKeyboard(self)
self.kb.set_map(type(self).CHARSET)
self.kb.set_map(self.CHARSET)
self.kb.set_width(HOR_RES)
self.kb.set_height(VER_RES // 3)
self.kb.align(self, lv.ALIGN.IN_BOTTOM_MID, 0, 0)
Expand All @@ -131,21 +131,21 @@ def cb(self, obj, event):
if c == lv.SYMBOL.LEFT:
self.ta.del_char()
elif c == lv.SYMBOL.UP or c == lv.SYMBOL.DOWN:
for i, ch in enumerate(type(self).CHARSET):
for i, ch in enumerate(self.CHARSET):
if ch.isalpha():
if c == lv.SYMBOL.UP:
type(self).CHARSET[i] = type(self).CHARSET[i].upper()
self.CHARSET[i] = self.CHARSET[i].upper()
else:
type(self).CHARSET[i] = type(self).CHARSET[i].lower()
self.CHARSET[i] = self.CHARSET[i].lower()
elif ch == lv.SYMBOL.UP:
type(self).CHARSET[i] = lv.SYMBOL.DOWN
self.CHARSET[i] = lv.SYMBOL.DOWN
elif ch == lv.SYMBOL.DOWN:
type(self).CHARSET[i] = lv.SYMBOL.UP
self.kb.set_map(type(self).CHARSET)
self.CHARSET[i] = lv.SYMBOL.UP
self.kb.set_map(self.CHARSET)
elif c == "#@":
self.kb.set_map(type(self).CHARSET_EXTRA)
self.kb.set_map(self.CHARSET_EXTRA)
elif c == "aA":
self.kb.set_map(type(self).CHARSET)
self.kb.set_map(self.CHARSET)
elif c[0] == lv.SYMBOL.CLOSE:
self.ta.set_text("")
elif c[0] == lv.SYMBOL.OK:
Expand Down Expand Up @@ -264,7 +264,7 @@ def __init__(self, title="Enter derivation path"):
super().__init__()
self.title = add_label(title, scr=self, y=PADDING, style="title")
self.kb = lv.btnm(self)
self.kb.set_map(type(self).PATH_CHARSET)
self.kb.set_map(self.PATH_CHARSET)
self.kb.set_width(HOR_RES)
self.kb.set_height(VER_RES // 2)
self.kb.align(self, lv.ALIGN.IN_BOTTOM_MID, 0, 0)
Expand Down
85 changes: 83 additions & 2 deletions src/gui/screens/mnemonic.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,19 @@ class NewMnemonicScreen(MnemonicScreen):
def __init__(
self,
generator,
wordlist,
fixer,
title="Your recovery phrase:",
note="Write it down and never show to anybody",
note="Write it down and never show to anybody.",
):
self.fixer = fixer
self.wordlist = wordlist
mnemonic = generator(12)
super().__init__(mnemonic, title, note)
self.table.align(self.title, lv.ALIGN.OUT_BOTTOM_MID, 0, 50)
self.table.set_event_cb(self.on_word_click)
# enable callbacks
self.table.set_click(True)

self.close_label.set_text(lv.SYMBOL.LEFT + " Back")
self.next_button = add_button(scr=self, callback=on_release(self.confirm))
Expand All @@ -41,10 +48,12 @@ def __init__(
self.next_label.set_text("Next " + lv.SYMBOL.RIGHT)
align_button_pair(self.close_button, self.next_button)

# toggle switch 12-24 words
lbl = lv.label(self)
lbl.set_text("Use 24 words")
lbl.align(self.table, lv.ALIGN.OUT_BOTTOM_MID, 0, 60)
lbl.align(self.table, lv.ALIGN.OUT_BOTTOM_MID, 0, 40)
lbl.set_x(120)
self.switch_lbl = lbl

self.switch = lv.sw(self)
self.switch.off(lv.ANIM.OFF)
Expand All @@ -56,6 +65,78 @@ def cb():

self.switch.set_event_cb(on_release(cb))

# fix mnemonic components
self.kb = lv.btnm(self)
self.kb.set_map(["1", "2", "4", "8", "16", "32", "\n",
"64", "128", "256", "512", "1024", ""])
self.kb.set_ctrl_map([lv.btnm.CTRL.TGL_ENABLE for i in range(11)])
self.kb.set_width(HOR_RES)
self.kb.set_height(100)
self.kb.align(self.table, lv.ALIGN.OUT_BOTTOM_MID, 0, 5)
self.kb.set_hidden(True)

self.instruction = add_label("Hint: click on any word above to edit it.", scr=self, style="hint")
self.instruction.align(self.kb, lv.ALIGN.OUT_BOTTOM_MID, 0, 15)


def on_word_click(self, obj, evt):
if evt != lv.EVENT.RELEASED:
return
# get coordinates
point = lv.point_t()
indev = lv.indev_get_act()
lv.indev_get_point(indev, point)
# get offsets
dx = point.x - obj.get_x()
dy = point.y - obj.get_y()
# get index
idx = 12*int(dx > obj.get_width()//2) + int(12*dy/obj.get_height())
self.change_word(idx)

def change_word(self, idx):
if idx >= len(self.table.words):
return
word = self.table.words[idx]
self.instruction.set_text(
"Changing word number %d:\n%s (%d in wordlist)"
% (idx+1, word.upper(), self.wordlist.index(word))
)
# hide switch
if not self.switch.get_hidden():
self.switch.set_hidden(True)
self.switch_lbl.set_hidden(True)
self.kb.set_hidden(False)
word_idx = self.wordlist.index(word)
self.kb.set_ctrl_map([
lv.btnm.CTRL.TGL_ENABLE | (lv.btnm.CTRL.TGL_STATE if ((word_idx>>i)&1) else 0)
for i in range(11)
])
# callback on toggle
def cb(obj, event):
if event != lv.EVENT.RELEASED:
return
c = obj.get_active_btn_text()
if c is None:
return
bits = [obj.get_btn_ctrl(i, lv.btnm.CTRL.TGL_STATE) for i in range(11)]
num = 0
for i, bit in enumerate(reversed(bits)):
num = num << 1
if bit:
num += 1
# change word
word = self.wordlist[num]
self.table.words[idx] = word
# fix mnemonic
mnemonic = " ".join(self.table.words)
self.table.set_mnemonic(self.fixer(mnemonic))
self.instruction.set_text(
"Changing word number %d:\n%s (%d in wordlist)"
% (idx+1, word.upper(), self.wordlist.index(word))
)
self.kb.set_event_cb(cb)


def confirm(self):
self.set_value(self.table.get_mnemonic())

Expand Down
4 changes: 2 additions & 2 deletions src/gui/specter.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ async def show_mnemonic(self, mnemonic: str):
await self.load_screen(scr)
return await scr.result()

async def new_mnemonic(self, generator):
async def new_mnemonic(self, generator, wordlist, fix):
"""
Generates a new mnemonic and shows it on the screen
"""
scr = NewMnemonicScreen(generator)
scr = NewMnemonicScreen(generator, wordlist, fix)
await self.load_screen(scr)
return await scr.result()

Expand Down
6 changes: 5 additions & 1 deletion src/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,17 @@
IV_SIZE = 16
AES_CBC = 2


def gen_mnemonic(num_words: int) -> str:
"""Generates a mnemonic with num_words"""
if num_words < 12 or num_words > 24 or num_words % 3 != 0:
raise RuntimeError("Invalid word count")
return bip39.mnemonic_from_bytes(rng.get_random_bytes(num_words * 4 // 3))

def fix_mnemonic(phrase):
"""Fixes checksum of invalid mnemonic"""
entropy = bip39.mnemonic_to_bytes(phrase, ignore_checksum=True)
return bip39.mnemonic_from_bytes(entropy)


def tagged_hash(tag: str, data: bytes) -> bytes:
"""BIP-Schnorr tag-specific key derivation"""
Expand Down
9 changes: 2 additions & 7 deletions src/specter.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from bitcoin.networks import NETWORKS

# small helper functions
from helpers import gen_mnemonic, load_apps
from helpers import gen_mnemonic, fix_mnemonic, load_apps
from errors import BaseError


Expand Down Expand Up @@ -189,7 +189,7 @@ async def initmenu(self):

# process the menu button:
if menuitem == 0:
mnemonic = await self.gui.new_mnemonic(gen_mnemonic)
mnemonic = await self.gui.new_mnemonic(gen_mnemonic, bip39.WORDLIST, fix_mnemonic)
if mnemonic is not None:
# load keys using mnemonic and empty password
self.keystore.set_mnemonic(mnemonic.strip(), "")
Expand All @@ -198,11 +198,6 @@ async def initmenu(self):
return self.mainmenu
# recover
elif menuitem == 1:
# a small function that fixes checksum of invalid mnemonic
def fix_mnemonic(phrase):
entropy = bip39.mnemonic_to_bytes(phrase, ignore_checksum=True)
return bip39.mnemonic_from_bytes(entropy)

mnemonic = await self.gui.recover(
bip39.mnemonic_is_valid, bip39.find_candidates, fix_mnemonic
)
Expand Down

0 comments on commit 2af0c4d

Please sign in to comment.