refinements
This commit is contained in:
parent
6cbde58b02
commit
4fd23830e5
@ -40,6 +40,29 @@ TC_COUNTDOWN = const(0x0040) # tc_arg = minutes of delay
|
||||
|
||||
# special "pin" used as catch-all for wrong pins
|
||||
WRONG_PIN_CODE = '!p'
|
||||
|
||||
def validate_delta_pin(true_pin, proposed_delta_pin):
|
||||
# Check delta pin proposal works w/ limitations and
|
||||
# provide error msg, and/or calc required tc_arg value.
|
||||
right = true_pin.replace('-', '')
|
||||
fake = proposed_delta_pin.replace('-', '')
|
||||
|
||||
if (len(right) != len(fake)) or (right[0:-4] != fake[0:-4]):
|
||||
prob = '''\
|
||||
Trick PIN must be same length (%d) as true PIN and \
|
||||
up to last four digits can be different between true PIN and trick.''' % len(right)
|
||||
return prob, 0
|
||||
|
||||
a = 0
|
||||
for i in range(4):
|
||||
dx = -(1+i)
|
||||
if right[dx] == fake[dx]:
|
||||
# no need to reveal this digit to SE2 hacker if same
|
||||
a |= 0xf << (i*4)
|
||||
else:
|
||||
a |= (ord(right[-(1+i)]) - 0x30) << (i*4)
|
||||
|
||||
return None, a
|
||||
|
||||
def make_slot():
|
||||
b = bytearray(uctypes.sizeof(TRICK_SLOT_LAYOUT))
|
||||
@ -100,6 +123,7 @@ class TrickPinMgmt:
|
||||
record = (slot.slot_num, slot.tc_flags,
|
||||
0xffff if slot.tc_flags & TC_DELTA_MODE else slot.tc_arg)
|
||||
self.tp[new_pin] = record
|
||||
self.save_record()
|
||||
|
||||
return True
|
||||
|
||||
@ -169,7 +193,7 @@ class TrickPinMgmt:
|
||||
sn = self.find_empty_slots(1 if not secret else 1+(len(secret)//32))
|
||||
if sn == None:
|
||||
# we are full
|
||||
raise RuntimeError("no space")
|
||||
raise RuntimeError("no space left")
|
||||
|
||||
slot.slot_num = sn
|
||||
|
||||
@ -199,7 +223,7 @@ class TrickPinMgmt:
|
||||
slot.xdata[0:64] = secret
|
||||
|
||||
# Save config for later
|
||||
# - never document real pin digits
|
||||
# - deltamode: don't document real pin digits
|
||||
record = (slot.slot_num, slot.tc_flags,
|
||||
0xffff if slot.tc_flags & TC_DELTA_MODE else slot.tc_arg)
|
||||
|
||||
@ -270,8 +294,8 @@ class TrickPinMenu(MenuSystem):
|
||||
if not has_wrong:
|
||||
rv.append(MenuItem('Add If Wrong', f=self.set_any_wrong))
|
||||
|
||||
if tricks:
|
||||
rv.append(MenuItem('Delete All', f=self.clear_all))
|
||||
# even if menu "looks" empty, many times we need this anyway
|
||||
rv.append(MenuItem('Delete All', f=self.clear_all))
|
||||
|
||||
return rv
|
||||
|
||||
@ -321,24 +345,10 @@ class TrickPinMenu(MenuSystem):
|
||||
if flags & TC_DELTA_MODE:
|
||||
# Calculate the value needed for args: BCD encoded final 4 digits
|
||||
# of the true PIN!
|
||||
right = self.current_pin.replace('-', '')
|
||||
fake = self.proposed_pin.replace('-', '')
|
||||
prob = None
|
||||
if (len(right) != len(fake)) or (right[0:-4] != fake[0:-4]):
|
||||
prob = '''\
|
||||
Trick PIN must be same length (%d) as true PIN and \
|
||||
up to last four digits can be different between true PIN and trick.''' % len(right)
|
||||
prob, a = validate_delta_pin(self.current_pin, self.proposed_pin)
|
||||
if prob:
|
||||
await ux_show_story(prob, 'Sorry!')
|
||||
return
|
||||
|
||||
a = 0
|
||||
for i in range(4):
|
||||
dx = -(1+i)
|
||||
if right[dx] == fake[dx]:
|
||||
# no need to reveal this digit to SE2 hacker if same
|
||||
a |= 0xf << (i*4)
|
||||
else:
|
||||
a |= (ord(right[-(1+i)]) - 0x30) << (i*4)
|
||||
tc_arg = a
|
||||
|
||||
msg += "Ok?"
|
||||
@ -353,10 +363,11 @@ up to last four digits can be different between true PIN and trick.''' % len(rig
|
||||
await ux_dramatic_pause("Saved.", 1)
|
||||
except BaseException as exc:
|
||||
sys.print_exception(exc)
|
||||
await ux_show_story("Failed.")
|
||||
await ux_show_story("Failed: %s" % exc)
|
||||
|
||||
self.update_contents()
|
||||
|
||||
|
||||
async def get_new_pin(self, existing_pin=None):
|
||||
# get a new PIN code and check not a dup
|
||||
# - show msg if aborted
|
||||
@ -384,7 +395,8 @@ up to last four digits can be different between true PIN and trick.''' % len(rig
|
||||
return
|
||||
|
||||
# check if we "forgot" this pin, and read it back if we did.
|
||||
# - important this is after the above checks so we don't reveal a deltamode pin in use
|
||||
# - important this is after the above checks so we don't reveal any trick pin used
|
||||
# to get here
|
||||
if tp.restore_pin(new_pin):
|
||||
await ux_show_story("Hmm. I remember that PIN now.")
|
||||
self.update_contents()
|
||||
@ -462,8 +474,6 @@ differ only in final 4 positions (ignoring dash).\
|
||||
m.goto_idx(1)
|
||||
the_ux.push(m)
|
||||
|
||||
|
||||
|
||||
|
||||
async def set_any_wrong(self, *a):
|
||||
ch = await ux_show_story('''\
|
||||
@ -511,7 +521,7 @@ setting) the Coldcard will always brick after 13 failed PIN attempts.''')
|
||||
pin, slot_num, flags = item.arg
|
||||
|
||||
if flags & TC_DELTA_MODE:
|
||||
await ux_show_story('''Delta mode PIN will be hidden when this menu is shown \
|
||||
await ux_show_story('''Delta mode PIN will be hidden if trick PIN menu is shown \
|
||||
to attacker, and we need to update this record if the main PIN is changed, so we don't support \
|
||||
hiding this item.''')
|
||||
return
|
||||
@ -537,29 +547,34 @@ You can restore it by trying to re-add the same PIN (%s) again later.''' % pin
|
||||
|
||||
async def change_pin(self, m,l, item):
|
||||
# Change existing PIN code.
|
||||
old_pin, slot_num, flags = item.arg
|
||||
old_pin, slot_num, flags, tc_arg = item.arg
|
||||
|
||||
new_pin = await self.get_new_pin(old_pin)
|
||||
if new_pin is None:
|
||||
return
|
||||
|
||||
# TODO XXX chcek if delta mode ... must apply rules to new PIN
|
||||
#if flags & TC_DELTA_MODE:
|
||||
if flags & TC_DELTA_MODE:
|
||||
# if delta mode ... must apply rules to new PIN
|
||||
prob, a = validate_delta_pin(self.current_pin, new_pin)
|
||||
if prob:
|
||||
await ux_show_story(prob, 'Sorry!')
|
||||
return
|
||||
tc_arg = a
|
||||
|
||||
try:
|
||||
tp.update_slot(old_pin.encode(), new_pin=new_pin.encode())
|
||||
tp.update_slot(old_pin.encode(), new_pin=new_pin.encode(), tc_arg=tc_arg)
|
||||
await ux_dramatic_pause("Changed.", 1)
|
||||
|
||||
self.pop_submenu() # too lazy to get redraw right
|
||||
except BaseException as exc:
|
||||
sys.print_exception(exc)
|
||||
await ux_show_story("Failed.")
|
||||
await ux_show_story("Failed: %s" % exc)
|
||||
|
||||
async def delete_pin(self, m,l, item):
|
||||
pin, slot_num, flags = item.arg
|
||||
|
||||
if flags & (TC_WORD_WALLET | TC_XPRV_WALLET):
|
||||
if not await ux_confirm("The funds on this duress wallet have been moved already?"):
|
||||
if not await ux_confirm("Any funds on this duress wallet have been moved already?"):
|
||||
return
|
||||
|
||||
if pin == WRONG_PIN_CODE:
|
||||
@ -627,11 +642,9 @@ normal operation.''')
|
||||
|
||||
pin, flags, arg = item.arg
|
||||
|
||||
# arg can be out-of-date, if they edited timer value after parent was
|
||||
# rendered, where arg was captured into item.arg
|
||||
# "arg" can be out-of-date, if they edited timer value after parent was
|
||||
# rendered, where arg was captured into item.arg ... so don't use it.
|
||||
cd_val = tp.tp[pin][2]
|
||||
if arg != cd_val:
|
||||
print("gotcha")
|
||||
|
||||
msg = 'Shows login countdown (%s)' % lgto_map.get(cd_val, '???').strip()
|
||||
if flags & TC_WIPE:
|
||||
@ -737,7 +750,7 @@ Wallet is XPRV-based and derived from a fixed path.''' % pin
|
||||
])
|
||||
if pin != WRONG_PIN_CODE:
|
||||
rv.append(
|
||||
MenuItem('Change PIN', f=self.change_pin, arg=(pin, slot_num, flags)),
|
||||
MenuItem('Change PIN', f=self.change_pin, arg=(pin, slot_num, flags, arg)),
|
||||
)
|
||||
|
||||
return rv
|
||||
@ -757,7 +770,7 @@ class StoryMenuItem(MenuItem):
|
||||
# drill down more
|
||||
return await super().activate(menu, idx)
|
||||
|
||||
# pop all, and note the path used
|
||||
# pop some levels, and note the drill-down path that was used
|
||||
parents = []
|
||||
while 1:
|
||||
the_ux.pop()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user