firmware/shared/countdowns.py
2021-12-15 14:12:51 -05:00

132 lines
3.5 KiB
Python

# (c) Copyright 2021 by Coinkite Inc. This file is covered by license found in COPYING-CC.
#
# countdowns.py - various details and chooser menus for setting/showing countdown times
#
from glob import settings
from nvstore import SettingsObject
from menu import MenuItem
from ux import ux_show_story, ux_dramatic_pause
# Login countdown length, stored in minutes
#
lgto_map = {
0: 'Disabled',
5: ' 5 minutes',
15: '15 minutes',
30: '30 minutes',
60: ' 1 hour',
2*60: ' 2 hours',
4*60: ' 4 hours',
8*60: ' 8 hours',
12*60: '12 hours',
24*60: '24 hours',
48*60: '48 hours',
3*24*60: ' 3 days',
7*24*60: ' 1 week',
28*24*60: '28 days later' }
lgto_va = list(lgto_map.keys())
lgto_ch = list(lgto_map.values())
def real_countdown_chooser(tag, offset, def_to):
# 'disabled' choice not appropriate for cd_lgto case
ch = lgto_ch[offset:]
va = lgto_va[offset:]
s = SettingsObject()
timeout = s.get(tag, def_to) # in minutes
try:
which = va.index(timeout)
except ValueError:
which = 0
def set_it(idx, text):
# save on key0, not normal settings
s = SettingsObject()
s.set(tag, va[idx])
s.save()
del s
return which, ch, set_it
def countdown_chooser():
return real_countdown_chooser('lgto', 0, 0)
def cd_countdown_chooser():
return real_countdown_chooser('cd_lgto', 1, 60)
# Mk3 only
async def set_countdown_pin(_1, _2, menu_item):
# Accept a new PIN to be used to enable this feature
from login import LoginUX
lll = LoginUX()
lll.reset()
lll.subtitle = "Countdown PIN"
pin = await lll.get_new_pin(None, allow_clear=True) # a string
s = SettingsObject()
from pincodes import pa
if pin == pa.pin.decode():
# can't compare to others like duress/brickme but will override them
await ux_show_story("Must be a unique PIN value!")
return
elif not pin:
# X on first screen does this (better than CLEAR_PIN thing)
s.remove_key('cd_pin')
msg = 'PIN Cleared.'
menu_item.label = "Enable Feature"
else:
s.set('cd_pin', pin)
msg = 'PIN Set.'
menu_item.label = "PIN is Set!"
s.save()
await ux_dramatic_pause(msg, 3)
# Mk3 only
def set_countdown_pin_mode():
# cd_mode = various harm levels
s = SettingsObject()
which = s.get('cd_mode', 0) # default is brick
del s
ch = ['Brick', 'Final PIN', 'Test Mode']
def set(idx, text):
# save it, but "outside" of login PIN
s = SettingsObject()
s.set('cd_mode', idx)
s.save()
del s
return which, ch, set
# Mk3 only
async def countdown_pin_submenu(*a):
# Background and settings for duress-countdown pin
s = SettingsObject()
pin_set = bool(s.get('cd_pin', 0))
if not pin_set:
ok = await ux_show_story('''\
This special PIN will immediately and silently brick the Coldcard, \
but as it does that, it shows a normal-looking countdown timer for login. \
At the end of the countdown, the Coldcard crashes with a vague error. \
Instead of complete brick, you may select a test mode (no harm done) or \
to consume all but the final PIN attempt.\
''')
if not ok: return
return [
MenuItem('PIN is Set!' if pin_set else 'Enable Feature', f=set_countdown_pin),
MenuItem('Countdown Time', chooser=cd_countdown_chooser),
MenuItem('Brick Mode', chooser=set_countdown_pin_mode),
]
# EOF