1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-14 03:30:02 +00:00

core/webauthn: Allow new workflow to be set after a command response is sent, so that in device tests the next test doesn't fail with ERR_CHANNEL_BUSY while the previous workflow is closing.

This commit is contained in:
Andrew Kozlik 2019-11-20 15:10:55 +01:00 committed by matejcik
parent 5e7dd41613
commit 203853faed
2 changed files with 36 additions and 18 deletions

View File

@ -118,7 +118,7 @@ _KEEPALIVE_INTERVAL_MS = const(80) # interval between keepalive commands
_CTAP_HID_TIMEOUT_MS = const(500)
_U2F_CONFIRM_TIMEOUT_MS = const(10 * 1000)
_FIDO2_CONFIRM_TIMEOUT_MS = const(60 * 1000)
_POPUP_TIMEOUT_MS = const(4 * 1000) if not __debug__ else const(0)
_POPUP_TIMEOUT_MS = const(4 * 1000)
# CBOR object signing and encryption algorithms and keys
_COSE_ALG_KEY = const(3)
@ -561,6 +561,7 @@ class State:
def __init__(self, cid: int, iface: io.HID) -> None:
self.cid = cid
self.iface = iface
self.finished = False
def keepalive_status(self) -> Optional[int]:
return None
@ -668,10 +669,12 @@ class Fido2State(State):
async def on_confirm(self) -> None:
cmd = cbor_error(self.cid, _ERR_OPERATION_DENIED)
await send_cmd(cmd, self.iface)
self.finished = True
async def on_decline(self) -> None:
cmd = cbor_error(self.cid, _ERR_OPERATION_DENIED)
await send_cmd(cmd, self.iface)
self.finished = True
async def on_timeout(self) -> None:
await self.on_decline()
@ -679,6 +682,7 @@ class Fido2State(State):
async def on_cancel(self) -> None:
cmd = cbor_error(self.cid, _ERR_KEEPALIVE_CANCEL)
await send_cmd(cmd, self.iface)
self.finished = True
class Fido2ConfirmMakeCredential(Fido2State, ConfirmInfo):
@ -731,6 +735,7 @@ class Fido2ConfirmMakeCredential(Fido2State, ConfirmInfo):
if not store_resident_credential(self._cred):
cmd = cbor_error(self.cid, _ERR_KEY_STORE_FULL)
await send_cmd(cmd, self.iface)
self.finished = True
class Fido2ConfirmExcluded(Fido2ConfirmMakeCredential):
@ -740,6 +745,7 @@ class Fido2ConfirmExcluded(Fido2ConfirmMakeCredential):
async def on_confirm(self) -> None:
cmd = cbor_error(self.cid, _ERR_CREDENTIAL_EXCLUDED)
await send_cmd(cmd, self.iface)
self.finished = True
text = Text("FIDO2 Register", ui.ICON_WRONG, ui.RED)
text.bold("Already registered.")
@ -813,11 +819,13 @@ class Fido2ConfirmGetAssertion(Fido2State, ConfirmInfo, Pageable):
cmd = cbor_error(self.cid, _ERR_OPERATION_DENIED)
await send_cmd(cmd, self.iface)
self.finished = True
class Fido2ConfirmNoPin(State):
def __init__(self, cid: int, iface: io.HID) -> None:
super().__init__(cid, iface)
self.finished = True
def timeout_ms(self) -> int:
return _FIDO2_CONFIRM_TIMEOUT_MS
@ -841,6 +849,7 @@ class Fido2ConfirmNoCredentials(Fido2ConfirmGetAssertion):
async def on_confirm(self) -> None:
cmd = cbor_error(self.cid, _ERR_NO_CREDENTIALS)
await send_cmd(cmd, self.iface)
self.finished = True
text = Text("FIDO2 Authenticate", ui.ICON_WRONG, ui.RED)
text.bold("Not registered.")
@ -865,6 +874,7 @@ class Fido2ConfirmReset(Fido2State):
storage.resident_credentials.delete_all()
cmd = Cmd(self.cid, _CMD_CBOR, bytes([_ERR_NONE]))
await send_cmd(cmd, self.iface)
self.finished = True
class DialogManager:
@ -909,7 +919,12 @@ class DialogManager:
return True
def set_state(self, state: State) -> bool:
if self.is_busy():
if self.workflow is not None:
if self.state is None or self.state.finished:
self.reset()
else:
return False
elif workflow.tasks:
return False
self.state = state
@ -942,23 +957,24 @@ class DialogManager:
try:
workflow.on_start(self.workflow)
if await self.state.confirm_dialog():
self.result = _RESULT_CONFIRM
else:
self.result = _RESULT_DECLINE
try:
if await self.state.confirm_dialog():
self.result = _RESULT_CONFIRM
else:
self.result = _RESULT_DECLINE
finally:
if self.keepalive is not None:
loop.close(self.keepalive)
if self.result == _RESULT_CONFIRM:
await self.state.on_confirm()
elif self.result == _RESULT_CANCEL:
await self.state.on_cancel()
elif self.result == _RESULT_TIMEOUT:
await self.state.on_timeout()
else:
await self.state.on_decline()
finally:
if self.keepalive is not None:
loop.close(self.keepalive)
if self.result == _RESULT_CONFIRM:
await self.state.on_confirm()
elif self.result == _RESULT_CANCEL:
await self.state.on_cancel()
elif self.result == _RESULT_TIMEOUT:
await self.state.on_timeout()
else:
await self.state.on_decline()
workflow.on_close(self.workflow)
self.workflow = None

View File

@ -28,6 +28,8 @@ def on_start(workflow: loop.Task) -> None:
make sure to always call `on_close` when the task is finished.
"""
# Take note that this workflow task is running.
if __debug__:
log.debug(__name__, "start: %s", workflow)
tasks.add(workflow)