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:
parent
5e7dd41613
commit
203853faed
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user