Skip to content
Open
4 changes: 4 additions & 0 deletions docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ With the secure element you will have three options:

At the moment, we have implementation for the first two options. Last seems to be the most secure, but then you need to trust proprietary crypto implementation. The second option saves the private key on the secure element under pin protection, and it can be encrypted, so the secure element never knows the private keys.

## *What happens if I lock my Specter-Javacard by entering the wrong PIN too many times?*

When the Specter-Javacard applet reaches its PIN retry limit the card is permanently locked and the applet becomes unusable. The only recovery is to uninstall the Specter-Javacard applet and then install it again. This reset process cannot be performed directly on the Specter-DIY hardware, but you can complete it with the [SeedSigner smartcard-compatible fork](https://github.com/3rdIteration/seedsigner) or any computer that has a USB smartcard reader.

# Troubleshooting questions

# I can't flash my device via Mini-USB ?
Expand Down
4 changes: 2 additions & 2 deletions src/gui/async_gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,9 @@ async def qr_alert(
await self.load_screen(alert)
return await alert.result()

async def error(self, msg, popup=False):
async def error(self, msg, popup=False, button_text="OK"):
"""Shows an error"""
alert = Alert("Error!", msg, button_text="OK")
alert = Alert("Error!", msg, button_text=button_text)
if popup:
await self.open_popup(alert)
else:
Expand Down
34 changes: 29 additions & 5 deletions src/keystore/memorycard.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from .ram import RAMKeyStore
from .javacard.applets.memorycard import MemoryCardApplet, SecureError
from .javacard.util import get_connection
from platform import CriticalErrorWipeImmediately
import platform
from embit import bip39
from helpers import tagged_hash, aead_encrypt, aead_decrypt
Expand All @@ -14,6 +13,20 @@
import lvgl as lv


SMARTCARD_BLOCKED_MESSAGE = (
"No more PIN attempts!\n"
"\n"
"Inserted Specter-Javacard is bricked...\n"
"\n"
"Reinstall the Specter-Javacard applet using the SeedSigner smartcard-compatible fork "
"or a PC with a USB smartcard reader."
)


class SmartcardLockedError(PinError):
NAME = "Smartcard locked"


class MemoryCard(RAMKeyStore):
"""
KeyStore that stores secrets on a smartcard
Expand Down Expand Up @@ -47,6 +60,10 @@ def __init__(self):
self.connected = False


def _raise_blocked_card(self):
raise SmartcardLockedError(SMARTCARD_BLOCKED_MESSAGE)


@classmethod
def is_available(cls):
if not cls.connection.isCardInserted():
Expand Down Expand Up @@ -111,8 +128,8 @@ def is_ready(self):

def _unlock(self, pin):
"""
Unlock the keystore, raises PinError if PIN is invalid.
Raises CriticalErrorWipeImmediately if no attempts left.
Unlock the keystore, raises PinError if the PIN is invalid
or if the card was permanently locked.
"""
try:
self.applet.unlock(pin)
Expand All @@ -123,8 +140,7 @@ def _unlock(self, pin):
% (self.pin_attempts_left, self.pin_attempts_max)
)
elif str(e) == "0503": # bricked
# wipe is happening automatically on this exception
raise CriticalErrorWipeImmediately("No more PIN attempts!\nWipe!")
self._raise_blocked_card()
else:
raise e
self.check_saved()
Expand Down Expand Up @@ -327,6 +343,8 @@ async def check_card(self, check_pin=False):
self.applet.open_secure_channel()
self.connected = True
self.applet.get_pin_status()
if self.is_locked and self.pin_attempts_left == 0:
self._raise_blocked_card()
if check_pin and self.is_locked:
pin = await self.get_pin()
self._unlock(pin)
Expand All @@ -352,6 +370,12 @@ async def init(self, show_fn, show_loader):
# the rest can be done with parent
await super().init(show_fn, show_loader)

async def unlock(self):
self.applet.get_pin_status()
if self.is_locked and self.pin_attempts_left == 0:
self._raise_blocked_card()
await super().unlock()

@property
def hexid(self):
return hexlify(tagged_hash("smartcard/pubkey", self.applet.card_pubkey)[:4]).decode()
Expand Down
5 changes: 4 additions & 1 deletion src/specter.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,10 @@ async def handle_exception(self, exception, next_fn):
raise exception
except CriticalErrorWipeImmediately as e:
# show error
await self.gui.error("Critical error, the device will be wiped.\n\n%s" % e)
await self.gui.error(
"Critical error, the device will be wiped.\n\n%s" % e,
button_text="Wipe Specter Device",
)
self.gui.show_loader(title="Wiping the device...")
# wipe everything and reboot
self.wipe()
Expand Down
Loading