parent
3e2ae5e469
commit
b89a9dc590
@ -0,0 +1,54 @@
|
||||
from trezor import ui, wire
|
||||
from trezor.messages.Success import Success
|
||||
from trezor.messages.WebAuthnAddResidentCredential import WebAuthnAddResidentCredential
|
||||
from trezor.ui.text import Text
|
||||
|
||||
from apps.common.confirm import require_confirm
|
||||
from apps.common.storage.webauthn import store_resident_credential
|
||||
from apps.webauthn.confirm import ConfirmContent, ConfirmInfo
|
||||
from apps.webauthn.credential import Fido2Credential
|
||||
|
||||
if False:
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class ConfirmAddCredential(ConfirmInfo):
|
||||
def __init__(self, cred: Fido2Credential):
|
||||
self._cred = cred
|
||||
self.load_icon(cred.rp_id_hash)
|
||||
|
||||
def get_header(self) -> str:
|
||||
return "Import credential"
|
||||
|
||||
def app_name(self) -> str:
|
||||
return self._cred.app_name()
|
||||
|
||||
def account_name(self) -> Optional[str]:
|
||||
return self._cred.account_name()
|
||||
|
||||
|
||||
async def add_resident_credential(
|
||||
ctx: wire.Context, msg: WebAuthnAddResidentCredential
|
||||
) -> Success:
|
||||
if not msg.credential_id:
|
||||
raise wire.ProcessError("Missing credential ID parameter.")
|
||||
|
||||
cred = Fido2Credential.from_cred_id(msg.credential_id, None)
|
||||
if cred is None:
|
||||
text = Text("Import credential", ui.ICON_WRONG, ui.RED)
|
||||
text.normal(
|
||||
"The credential you are",
|
||||
"trying to import does",
|
||||
"not belong to this",
|
||||
"authenticator.",
|
||||
)
|
||||
await require_confirm(ctx, text, confirm=None)
|
||||
raise wire.ActionCancelled("Cancelled")
|
||||
|
||||
content = ConfirmContent(ConfirmAddCredential(cred))
|
||||
await require_confirm(ctx, content)
|
||||
|
||||
if store_resident_credential(cred):
|
||||
return Success(message="Credential added")
|
||||
else:
|
||||
raise wire.ProcessError("Internal credential storage is full.")
|
@ -0,0 +1,62 @@
|
||||
from trezor import log, ui
|
||||
from trezor.ui.text import text_center_trim_left, text_center_trim_right
|
||||
|
||||
if False:
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class ConfirmInfo:
|
||||
def __init__(self) -> None:
|
||||
self.app_icon = None # type: Optional[bytes]
|
||||
|
||||
def get_header(self) -> str:
|
||||
raise NotImplementedError
|
||||
|
||||
def app_name(self) -> str:
|
||||
raise NotImplementedError
|
||||
|
||||
def account_name(self) -> Optional[str]:
|
||||
return None
|
||||
|
||||
def load_icon(self, rp_id_hash: bytes) -> None:
|
||||
from trezor import res
|
||||
from apps.webauthn import knownapps
|
||||
|
||||
try:
|
||||
namepart = knownapps.knownapps[rp_id_hash].lower().replace(" ", "_")
|
||||
icon = res.load("apps/webauthn/res/icon_%s.toif" % namepart)
|
||||
except Exception as e:
|
||||
icon = res.load("apps/webauthn/res/icon_webauthn.toif")
|
||||
if __debug__:
|
||||
log.exception(__name__, e)
|
||||
self.app_icon = icon
|
||||
|
||||
|
||||
class ConfirmContent(ui.Component):
|
||||
def __init__(self, info: ConfirmInfo) -> None:
|
||||
self.info = info
|
||||
self.repaint = True
|
||||
|
||||
def on_render(self) -> None:
|
||||
if self.repaint:
|
||||
header = self.info.get_header()
|
||||
ui.header(header, ui.ICON_DEFAULT, ui.GREEN, ui.BG, ui.GREEN)
|
||||
|
||||
if self.info.app_icon is not None:
|
||||
ui.display.image((ui.WIDTH - 64) // 2, 48, self.info.app_icon)
|
||||
|
||||
app_name = self.info.app_name()
|
||||
account_name = self.info.account_name()
|
||||
|
||||
# Dummy requests usually have some text as both app_name and account_name,
|
||||
# in that case show the text only once.
|
||||
if account_name is not None:
|
||||
if app_name != account_name:
|
||||
text_center_trim_left(ui.WIDTH // 2, 140, app_name)
|
||||
text_center_trim_right(ui.WIDTH // 2, 172, account_name)
|
||||
else:
|
||||
text_center_trim_right(ui.WIDTH // 2, 156, account_name)
|
||||
else:
|
||||
text_center_trim_left(ui.WIDTH // 2, 156, app_name)
|
||||
|
||||
self.repaint = False
|
@ -0,0 +1,38 @@
|
||||
from trezor import wire
|
||||
from trezor.messages.WebAuthnCredential import WebAuthnCredential
|
||||
from trezor.messages.WebAuthnCredentials import WebAuthnCredentials
|
||||
from trezor.messages.WebAuthnListResidentCredentials import (
|
||||
WebAuthnListResidentCredentials,
|
||||
)
|
||||
from trezor.ui.text import Text
|
||||
|
||||
from apps.common.confirm import require_confirm
|
||||
from apps.common.storage.webauthn import get_resident_credentials
|
||||
|
||||
|
||||
async def list_resident_credentials(
|
||||
ctx: wire.Context, msg: WebAuthnListResidentCredentials
|
||||
) -> WebAuthnCredentials:
|
||||
text = Text("List credentials")
|
||||
text.normal(
|
||||
"Do you want to export",
|
||||
"information about the",
|
||||
"resident credentials",
|
||||
"stored on this device?",
|
||||
)
|
||||
await require_confirm(ctx, text)
|
||||
creds = [
|
||||
WebAuthnCredential(
|
||||
index=cred.index,
|
||||
id=cred.id,
|
||||
rp_id=cred.rp_id,
|
||||
rp_name=cred.rp_name,
|
||||
user_id=cred.user_id,
|
||||
user_name=cred.user_name,
|
||||
user_display_name=cred.user_display_name,
|
||||
creation_time=cred._creation_time,
|
||||
hmac_secret=cred.hmac_secret,
|
||||
)
|
||||
for cred in get_resident_credentials()
|
||||
]
|
||||
return WebAuthnCredentials(creds)
|
@ -0,0 +1,48 @@
|
||||
from trezor import wire
|
||||
from trezor.messages.Success import Success
|
||||
from trezor.messages.WebAuthnRemoveResidentCredential import (
|
||||
WebAuthnRemoveResidentCredential,
|
||||
)
|
||||
|
||||
from apps.common.confirm import require_confirm
|
||||
from apps.common.storage.webauthn import (
|
||||
erase_resident_credential,
|
||||
get_resident_credential,
|
||||
)
|
||||
from apps.webauthn.confirm import ConfirmContent, ConfirmInfo
|
||||
from apps.webauthn.credential import Fido2Credential
|
||||
|
||||
if False:
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class ConfirmRemoveCredential(ConfirmInfo):
|
||||
def __init__(self, cred: Fido2Credential):
|
||||
self._cred = cred
|
||||
self.load_icon(cred.rp_id_hash)
|
||||
|
||||
def get_header(self) -> str:
|
||||
return "Remove credential"
|
||||
|
||||
def app_name(self) -> str:
|
||||
return self._cred.app_name()
|
||||
|
||||
def account_name(self) -> Optional[str]:
|
||||
return self._cred.account_name()
|
||||
|
||||
|
||||
async def remove_resident_credential(
|
||||
ctx: wire.Context, msg: WebAuthnRemoveResidentCredential
|
||||
) -> Success:
|
||||
if msg.index is None:
|
||||
raise wire.ProcessError("Missing credential index parameter.")
|
||||
|
||||
cred = get_resident_credential(msg.index)
|
||||
if cred is None:
|
||||
raise wire.ProcessError("Invalid credential index.")
|
||||
|
||||
content = ConfirmContent(ConfirmRemoveCredential(cred))
|
||||
await require_confirm(ctx, content)
|
||||
|
||||
erase_resident_credential(msg.index)
|
||||
return Success(message="Credential removed")
|
Loading…
Reference in new issue