From 61277bd80ade99f7ea8b0564793fdcdf6f4f739e Mon Sep 17 00:00:00 2001 From: grdddj Date: Wed, 12 Oct 2022 15:30:53 +0200 Subject: [PATCH] feat(core/ui): implement webauthn layouts for UI2 [no changelog] --- core/SConscript.firmware | 4 +- core/SConscript.unix | 4 +- core/embed/rust/librust_qstr.h | 4 + .../rust/src/ui/model_tt/component/fido.rs | 210 ++++++++++++++++++ .../src/ui/model_tt/component/fido_icons.rs | 77 +++++++ .../ui/model_tt/component/fido_icons.rs.mako | 34 +++ .../rust/src/ui/model_tt/component/mod.rs | 3 + core/embed/rust/src/ui/model_tt/layout.rs | 70 +++++- .../src/ui/model_tt/res/fido/icon_aws.toif | Bin 0 -> 1264 bytes .../ui/model_tt/res/fido/icon_binance.toif | Bin 0 -> 555 bytes .../ui/model_tt/res/fido/icon_bitbucket.toif | Bin 0 -> 865 bytes .../ui/model_tt/res/fido/icon_bitfinex.toif | Bin 0 -> 1151 bytes .../ui/model_tt/res/fido/icon_bitwarden.toif | Bin 0 -> 617 bytes .../ui/model_tt/res/fido/icon_cloudflare.toif | Bin 0 -> 615 bytes .../ui/model_tt/res/fido/icon_coinbase.toif | Bin 0 -> 568 bytes .../ui/model_tt/res/fido/icon_dashlane.toif | Bin 0 -> 2158 bytes .../ui/model_tt/res/fido/icon_dropbox.toif | Bin 0 -> 852 bytes .../src/ui/model_tt/res/fido/icon_duo.toif | Bin 0 -> 463 bytes .../ui/model_tt/res/fido/icon_facebook.toif | Bin 0 -> 747 bytes .../ui/model_tt/res/fido/icon_fastmail.toif | Bin 0 -> 1185 bytes .../src/ui/model_tt/res/fido/icon_fedora.toif | Bin 0 -> 1185 bytes .../src/ui/model_tt/res/fido/icon_gandi.toif | Bin 0 -> 1237 bytes .../src/ui/model_tt/res/fido/icon_gemini.toif | Bin 0 -> 1042 bytes .../src/ui/model_tt/res/fido/icon_github.toif | Bin 0 -> 1092 bytes .../src/ui/model_tt/res/fido/icon_gitlab.toif | Bin 0 -> 952 bytes .../src/ui/model_tt/res/fido/icon_google.toif | Bin 0 -> 1071 bytes .../src/ui/model_tt/res/fido/icon_invity.toif | Bin 0 -> 136 bytes .../src/ui/model_tt/res/fido/icon_keeper.toif | Bin 0 -> 1507 bytes .../src/ui/model_tt/res/fido/icon_kraken.toif | Bin 0 -> 654 bytes .../ui/model_tt/res/fido/icon_login.gov.toif | Bin 0 -> 651 bytes .../ui/model_tt/res/fido/icon_microsoft.toif | Bin 0 -> 165 bytes .../src/ui/model_tt/res/fido/icon_mojeid.toif | Bin 0 -> 1330 bytes .../ui/model_tt/res/fido/icon_namecheap.toif | Bin 0 -> 867 bytes .../src/ui/model_tt/res/fido/icon_proton.toif | Bin 0 -> 587 bytes .../ui/model_tt/res/fido/icon_slushpool.toif | Bin 0 -> 1021 bytes .../src/ui/model_tt/res/fido/icon_stripe.toif | Bin 0 -> 668 bytes .../ui/model_tt/res/fido/icon_tutanota.toif | Bin 0 -> 646 bytes .../ui/model_tt/res/fido/icon_webauthn.toif | Bin 0 -> 1135 bytes core/mocks/generated/trezorui2.pyi | 13 ++ core/src/all_modules.py | 10 +- .../apps/webauthn/add_resident_credential.py | 30 +-- core/src/apps/webauthn/credential.py | 6 + core/src/apps/webauthn/fido2.py | 98 ++++---- core/src/apps/webauthn/knownapps.py | 88 ++++---- core/src/apps/webauthn/knownapps.py.mako | 10 +- .../webauthn/remove_resident_credential.py | 30 +-- .../trezor/ui/components/common/webauthn.py | 25 --- core/src/trezor/ui/layouts/fido.py | 1 + core/src/trezor/ui/layouts/tt_v2/fido.py | 96 ++++++++ core/src/trezor/ui/layouts/tt_v2/webauthn.py | 49 ---- core/src/trezor/ui/layouts/webauthn.py | 1 - core/tools/build_icons.py | 4 +- core/tools/build_templates | 3 +- tests/ui_tests/fixtures.json | 2 +- 54 files changed, 635 insertions(+), 237 deletions(-) create mode 100644 core/embed/rust/src/ui/model_tt/component/fido.rs create mode 100644 core/embed/rust/src/ui/model_tt/component/fido_icons.rs create mode 100644 core/embed/rust/src/ui/model_tt/component/fido_icons.rs.mako create mode 100644 core/embed/rust/src/ui/model_tt/res/fido/icon_aws.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/fido/icon_binance.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/fido/icon_bitbucket.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/fido/icon_bitfinex.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/fido/icon_bitwarden.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/fido/icon_cloudflare.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/fido/icon_coinbase.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/fido/icon_dashlane.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/fido/icon_dropbox.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/fido/icon_duo.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/fido/icon_facebook.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/fido/icon_fastmail.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/fido/icon_fedora.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/fido/icon_gandi.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/fido/icon_gemini.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/fido/icon_github.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/fido/icon_gitlab.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/fido/icon_google.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/fido/icon_invity.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/fido/icon_keeper.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/fido/icon_kraken.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/fido/icon_login.gov.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/fido/icon_microsoft.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/fido/icon_mojeid.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/fido/icon_namecheap.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/fido/icon_proton.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/fido/icon_slushpool.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/fido/icon_stripe.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/fido/icon_tutanota.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/fido/icon_webauthn.toif delete mode 100644 core/src/trezor/ui/components/common/webauthn.py create mode 100644 core/src/trezor/ui/layouts/fido.py create mode 100644 core/src/trezor/ui/layouts/tt_v2/fido.py delete mode 100644 core/src/trezor/ui/layouts/tt_v2/webauthn.py delete mode 100644 core/src/trezor/ui/layouts/webauthn.py diff --git a/core/SConscript.firmware b/core/SConscript.firmware index 92b1d6762..0353e7804 100644 --- a/core/SConscript.firmware +++ b/core/SConscript.firmware @@ -579,13 +579,13 @@ if FROZEN: SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/reset.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/recovery.py')) if EVERYTHING: - SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/webauthn.py')) + SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/fido.py')) if TREZOR_MODEL in ('T',): SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/tt_v2/__init__.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/tt_v2/reset.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/tt_v2/recovery.py')) if EVERYTHING: - SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/tt_v2/webauthn.py')) + SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/tt_v2/fido.py')) elif TREZOR_MODEL in ('1',): SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/t1.py')) elif TREZOR_MODEL in ('R',): diff --git a/core/SConscript.unix b/core/SConscript.unix index 03f23ba0c..6f9c8e91e 100644 --- a/core/SConscript.unix +++ b/core/SConscript.unix @@ -533,13 +533,13 @@ if FROZEN: SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/reset.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/recovery.py')) if EVERYTHING: - SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/webauthn.py')) + SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/fido.py')) if TREZOR_MODEL in ('T',): SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/tt_v2/__init__.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/tt_v2/reset.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/tt_v2/recovery.py')) if EVERYTHING: - SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/tt_v2/webauthn.py')) + SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/tt_v2/fido.py')) elif TREZOR_MODEL in ('1',): SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/t1.py')) elif TREZOR_MODEL in ('R',): diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h index d9851623e..e2902214b 100644 --- a/core/embed/rust/librust_qstr.h +++ b/core/embed/rust/librust_qstr.h @@ -20,6 +20,7 @@ static void _librust_qstrs(void) { MP_QSTR_confirm_blob; MP_QSTR_confirm_properties; MP_QSTR_confirm_coinjoin; + MP_QSTR_confirm_fido; MP_QSTR_confirm_joint_total; MP_QSTR_confirm_modify_fee; MP_QSTR_confirm_modify_output; @@ -91,4 +92,7 @@ static void _librust_qstrs(void) { MP_QSTR_active; MP_QSTR_info_button; MP_QSTR_time_ms; + MP_QSTR_app_name; + MP_QSTR_icon_name; + MP_QSTR_accounts; } diff --git a/core/embed/rust/src/ui/model_tt/component/fido.rs b/core/embed/rust/src/ui/model_tt/component/fido.rs new file mode 100644 index 000000000..eacabdcfc --- /dev/null +++ b/core/embed/rust/src/ui/model_tt/component/fido.rs @@ -0,0 +1,210 @@ +use core::ops::Deref; + +use crate::ui::{ + component::{Child, Component, Event, EventCtx, Image, Label}, + display, + geometry::{Alignment, Insets, Rect}, + model_tt::component::{ + fido_icons::get_fido_icon_data, + swipe::{Swipe, SwipeDirection}, + theme, ScrollBar, + }, +}; + +use super::CancelConfirmMsg; + +const ICON_HEIGHT: i16 = 70; +const SCROLLBAR_INSET_TOP: i16 = 5; +const SCROLLBAR_HEIGHT: i16 = 10; +const APP_NAME_PADDING: i16 = 12; +const APP_NAME_HEIGHT: i16 = 30; + +pub enum FidoMsg { + Confirmed(usize), + Cancelled, +} + +pub struct FidoConfirm T, T, U> { + page_swipe: Swipe, + app_name: Label, + account_name: Label, + icon: Child, + /// Function/closure that will return appropriate page on demand. + get_account: F, + scrollbar: ScrollBar, + fade: bool, + controls: U, +} + +impl FidoConfirm +where + F: Fn(usize) -> T, + T: Deref + From<&'static str>, + U: Component, +{ + pub fn new( + app_name: T, + get_account: F, + page_count: usize, + icon_name: Option, + controls: U, + ) -> Self { + let icon_data = get_fido_icon_data(icon_name.as_deref()); + + // Preparing scrollbar and setting its page-count. + let mut scrollbar = ScrollBar::horizontal(); + scrollbar.set_count_and_active_page(page_count, 0); + + // Preparing swipe component and setting possible initial + // swipe directions according to number of pages. + let mut page_swipe = Swipe::horizontal(); + page_swipe.allow_right = scrollbar.has_previous_page(); + page_swipe.allow_left = scrollbar.has_next_page(); + + Self { + app_name: Label::new(app_name, Alignment::Center, theme::TEXT_BOLD), + account_name: Label::new("".into(), Alignment::Center, theme::TEXT_BOLD), + page_swipe, + icon: Child::new(Image::new(icon_data)), + get_account, + scrollbar, + fade: false, + controls, + } + } + + fn on_page_swipe(&mut self, ctx: &mut EventCtx, swipe: SwipeDirection) { + // Change the page number. + match swipe { + SwipeDirection::Left if self.scrollbar.has_next_page() => { + self.scrollbar.go_to_next_page(); + } + SwipeDirection::Right if self.scrollbar.has_previous_page() => { + self.scrollbar.go_to_previous_page(); + } + _ => {} // page did not change + }; + + // Disable swipes on the boundaries. Not allowing carousel effect. + self.page_swipe.allow_right = self.scrollbar.has_previous_page(); + self.page_swipe.allow_left = self.scrollbar.has_next_page(); + + // Redraw the page. + ctx.request_paint(); + + // Reset backlight to normal level on next paint. + self.fade = true; + } + + fn active_page(&self) -> usize { + self.scrollbar.active_page + } +} + +impl Component for FidoConfirm +where + F: Fn(usize) -> T, + T: Deref + From<&'static str>, + U: Component, +{ + type Msg = FidoMsg; + + fn place(&mut self, bounds: Rect) -> Rect { + self.page_swipe.place(bounds); + + // Place the control buttons. + let controls_area = self.controls.place(bounds); + + // Get the image and content areas. + let content_area = bounds.inset(Insets::bottom(controls_area.height())); + let (image_area, content_area) = content_area.split_top(ICON_HEIGHT); + + // In case of showing a scrollbar, getting its area and placing it. + let remaining_area = if self.scrollbar.page_count > 1 { + let (scrollbar_area, remaining_area) = content_area + .inset(Insets::top(SCROLLBAR_INSET_TOP)) + .split_top(SCROLLBAR_HEIGHT); + self.scrollbar.place(scrollbar_area); + remaining_area + } else { + content_area + }; + + // Place the icon image. + self.icon.place(image_area); + + // Place the text labels. + let (app_name_area, account_name_area) = remaining_area + .inset(Insets::top(APP_NAME_PADDING)) + .split_top(APP_NAME_HEIGHT); + + self.app_name.place(app_name_area); + self.account_name.place(account_name_area); + + bounds + } + + fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option { + if let Some(swipe) = self.page_swipe.event(ctx, event) { + // Swipe encountered, update the page. + self.on_page_swipe(ctx, swipe); + } + if let Some(msg) = self.controls.event(ctx, event) { + // Some button was clicked, send results. + match msg { + CancelConfirmMsg::Confirmed => return Some(FidoMsg::Confirmed(self.active_page())), + CancelConfirmMsg::Cancelled => return Some(FidoMsg::Cancelled), + } + } + None + } + + fn paint(&mut self) { + self.icon.paint(); + self.controls.paint(); + self.app_name.paint(); + + if self.scrollbar.page_count > 1 { + self.scrollbar.paint(); + } + + let current_account = (self.get_account)(self.active_page()); + + // Erasing the old text content before writing the new one. + let account_name_area = self.account_name.area(); + let real_area = account_name_area + .with_height(account_name_area.height() + self.account_name.font().text_baseline() + 1); + display::rect_fill(real_area, theme::BG); + + // Account name is optional. + // Showing it only if it differs from app name. + // (Dummy requests usually have some text as both app_name and account_name.) + if !current_account.is_empty() && current_account.deref() != self.app_name.text().deref() { + self.account_name.set_text(current_account); + self.account_name.paint(); + } + + if self.fade { + self.fade = false; + // Note that this is blocking and takes some time. + display::fade_backlight(theme::BACKLIGHT_NORMAL); + } + } + + fn bounds(&self, sink: &mut dyn FnMut(Rect)) { + self.icon.bounds(sink); + self.app_name.bounds(sink); + self.account_name.bounds(sink); + } +} + +#[cfg(feature = "ui_debug")] +impl crate::trace::Trace for FidoConfirm +where + F: Fn(usize) -> T, +{ + fn trace(&self, t: &mut dyn crate::trace::Tracer) { + t.open("FidoPaginatedPage"); + t.close(); + } +} diff --git a/core/embed/rust/src/ui/model_tt/component/fido_icons.rs b/core/embed/rust/src/ui/model_tt/component/fido_icons.rs new file mode 100644 index 000000000..386154516 --- /dev/null +++ b/core/embed/rust/src/ui/model_tt/component/fido_icons.rs @@ -0,0 +1,77 @@ +//! generated from webauthn_icons.rs.mako +//! (by running `make templates` in `core`) +//! do not edit manually! + +const ICON_AWS: &[u8] = include_res!("model_tt/res/fido/icon_aws.toif"); +const ICON_BINANCE: &[u8] = include_res!("model_tt/res/fido/icon_binance.toif"); +const ICON_BITBUCKET: &[u8] = include_res!("model_tt/res/fido/icon_bitbucket.toif"); +const ICON_BITFINEX: &[u8] = include_res!("model_tt/res/fido/icon_bitfinex.toif"); +const ICON_BITWARDEN: &[u8] = include_res!("model_tt/res/fido/icon_bitwarden.toif"); +const ICON_CLOUDFLARE: &[u8] = include_res!("model_tt/res/fido/icon_cloudflare.toif"); +const ICON_COINBASE: &[u8] = include_res!("model_tt/res/fido/icon_coinbase.toif"); +const ICON_DASHLANE: &[u8] = include_res!("model_tt/res/fido/icon_dashlane.toif"); +const ICON_DROPBOX: &[u8] = include_res!("model_tt/res/fido/icon_dropbox.toif"); +const ICON_DUO: &[u8] = include_res!("model_tt/res/fido/icon_duo.toif"); +const ICON_FACEBOOK: &[u8] = include_res!("model_tt/res/fido/icon_facebook.toif"); +const ICON_FASTMAIL: &[u8] = include_res!("model_tt/res/fido/icon_fastmail.toif"); +const ICON_FEDORA: &[u8] = include_res!("model_tt/res/fido/icon_fedora.toif"); +const ICON_GANDI: &[u8] = include_res!("model_tt/res/fido/icon_gandi.toif"); +const ICON_GEMINI: &[u8] = include_res!("model_tt/res/fido/icon_gemini.toif"); +const ICON_GITHUB: &[u8] = include_res!("model_tt/res/fido/icon_github.toif"); +const ICON_GITLAB: &[u8] = include_res!("model_tt/res/fido/icon_gitlab.toif"); +const ICON_GOOGLE: &[u8] = include_res!("model_tt/res/fido/icon_google.toif"); +const ICON_INVITY: &[u8] = include_res!("model_tt/res/fido/icon_invity.toif"); +const ICON_KEEPER: &[u8] = include_res!("model_tt/res/fido/icon_keeper.toif"); +const ICON_KRAKEN: &[u8] = include_res!("model_tt/res/fido/icon_kraken.toif"); +const ICON_LOGIN_GOV: &[u8] = include_res!("model_tt/res/fido/icon_login.gov.toif"); +const ICON_MICROSOFT: &[u8] = include_res!("model_tt/res/fido/icon_microsoft.toif"); +const ICON_MOJEID: &[u8] = include_res!("model_tt/res/fido/icon_mojeid.toif"); +const ICON_NAMECHEAP: &[u8] = include_res!("model_tt/res/fido/icon_namecheap.toif"); +const ICON_PROTON: &[u8] = include_res!("model_tt/res/fido/icon_proton.toif"); +const ICON_SLUSHPOOL: &[u8] = include_res!("model_tt/res/fido/icon_slushpool.toif"); +const ICON_STRIPE: &[u8] = include_res!("model_tt/res/fido/icon_stripe.toif"); +const ICON_TUTANOTA: &[u8] = include_res!("model_tt/res/fido/icon_tutanota.toif"); +/// Default icon when app does not have its own +const ICON_WEBAUTHN: &[u8] = include_res!("model_tt/res/fido/icon_webauthn.toif"); + +/// Translates icon name into its data. +/// Returns default `ICON_WEBAUTHN` when the icon is not found or name not +/// supplied. +pub fn get_fido_icon_data>(icon_name: Option) -> &'static [u8] { + if let Some(icon_name) = icon_name { + match icon_name.as_ref() { + "aws" => ICON_AWS, + "binance" => ICON_BINANCE, + "bitbucket" => ICON_BITBUCKET, + "bitfinex" => ICON_BITFINEX, + "bitwarden" => ICON_BITWARDEN, + "cloudflare" => ICON_CLOUDFLARE, + "coinbase" => ICON_COINBASE, + "dashlane" => ICON_DASHLANE, + "dropbox" => ICON_DROPBOX, + "duo" => ICON_DUO, + "facebook" => ICON_FACEBOOK, + "fastmail" => ICON_FASTMAIL, + "fedora" => ICON_FEDORA, + "gandi" => ICON_GANDI, + "gemini" => ICON_GEMINI, + "github" => ICON_GITHUB, + "gitlab" => ICON_GITLAB, + "google" => ICON_GOOGLE, + "invity" => ICON_INVITY, + "keeper" => ICON_KEEPER, + "kraken" => ICON_KRAKEN, + "login.gov" => ICON_LOGIN_GOV, + "microsoft" => ICON_MICROSOFT, + "mojeid" => ICON_MOJEID, + "namecheap" => ICON_NAMECHEAP, + "proton" => ICON_PROTON, + "slushpool" => ICON_SLUSHPOOL, + "stripe" => ICON_STRIPE, + "tutanota" => ICON_TUTANOTA, + _ => ICON_WEBAUTHN, + } + } else { + ICON_WEBAUTHN + } +} diff --git a/core/embed/rust/src/ui/model_tt/component/fido_icons.rs.mako b/core/embed/rust/src/ui/model_tt/component/fido_icons.rs.mako new file mode 100644 index 000000000..3de5e4af8 --- /dev/null +++ b/core/embed/rust/src/ui/model_tt/component/fido_icons.rs.mako @@ -0,0 +1,34 @@ +//! generated from webauthn_icons.rs.mako +//! (by running `make templates` in `core`) +//! do not edit manually! +<% +icons: list[tuple[str, str]] = [] +for app in fido: + if app.icon is not None: + # Variable names cannot have a dot in themselves + icon_name = app.key + var_name = icon_name.replace(".", "_").upper() + icons.append((icon_name, var_name)) +%>\ + +% for icon_name, var_name in icons: +const ICON_${var_name}: &[u8] = include_res!("model_tt/res/fido/icon_${icon_name}.toif"); +% endfor +/// Default icon when app does not have its own +const ICON_WEBAUTHN: &[u8] = include_res!("model_tt/res/fido/icon_webauthn.toif"); + +/// Translates icon name into its data. +/// Returns default `ICON_WEBAUTHN` when the icon is not found or name not +/// supplied. +pub fn get_fido_icon_data>(icon_name: Option) -> &'static [u8] { + if let Some(icon_name) = icon_name { + match icon_name.as_ref() { +% for icon_name, var_name in icons: + "${icon_name}" => ICON_${var_name}, +% endfor + _ => ICON_WEBAUTHN, + } + } else { + ICON_WEBAUTHN + } +} diff --git a/core/embed/rust/src/ui/model_tt/component/mod.rs b/core/embed/rust/src/ui/model_tt/component/mod.rs index e3e789138..55f40330d 100644 --- a/core/embed/rust/src/ui/model_tt/component/mod.rs +++ b/core/embed/rust/src/ui/model_tt/component/mod.rs @@ -1,5 +1,7 @@ mod button; mod dialog; +mod fido; +mod fido_icons; mod frame; mod hold_to_confirm; mod keyboard; @@ -14,6 +16,7 @@ pub use button::{ CancelInfoConfirmMsg, SelectWordMsg, }; pub use dialog::{Dialog, DialogMsg, IconDialog}; +pub use fido::{FidoConfirm, FidoMsg}; pub use frame::{Frame, NotificationFrame}; pub use hold_to_confirm::{HoldToConfirm, HoldToConfirmMsg}; pub use keyboard::{ diff --git a/core/embed/rust/src/ui/model_tt/layout.rs b/core/embed/rust/src/ui/model_tt/layout.rs index 393175b6a..191e8e08b 100644 --- a/core/embed/rust/src/ui/model_tt/layout.rs +++ b/core/embed/rust/src/ui/model_tt/layout.rs @@ -6,7 +6,9 @@ use crate::{ error::Error, micropython::{ buffer::StrBuffer, + gc::Gc, iter::{Iter, IterBuf}, + list::List, map::Map, module::Module, obj::Obj, @@ -37,11 +39,11 @@ use crate::{ use super::{ component::{ Bip39Input, Button, ButtonMsg, ButtonStyleSheet, CancelConfirmMsg, CancelInfoConfirmMsg, - Dialog, DialogMsg, Frame, HoldToConfirm, HoldToConfirmMsg, IconDialog, MnemonicInput, - MnemonicKeyboard, MnemonicKeyboardMsg, NotificationFrame, NumberInputDialog, - NumberInputDialogMsg, PassphraseKeyboard, PassphraseKeyboardMsg, PinKeyboard, - PinKeyboardMsg, SelectWordCount, SelectWordCountMsg, SelectWordMsg, Slip39Input, - SwipeHoldPage, SwipePage, + Dialog, DialogMsg, FidoConfirm, FidoMsg, Frame, HoldToConfirm, HoldToConfirmMsg, + IconDialog, MnemonicInput, MnemonicKeyboard, MnemonicKeyboardMsg, NotificationFrame, + NumberInputDialog, NumberInputDialogMsg, PassphraseKeyboard, PassphraseKeyboardMsg, + PinKeyboard, PinKeyboardMsg, SelectWordCount, SelectWordCountMsg, SelectWordMsg, + Slip39Input, SwipeHoldPage, SwipePage, }, theme, }; @@ -89,6 +91,20 @@ impl TryFrom for Obj { } } +impl ComponentMsgObj for FidoConfirm +where + F: Fn(usize) -> T, + T: Deref + From<&'static str>, + U: Component, +{ + fn msg_try_into_obj(&self, msg: Self::Msg) -> Result { + match msg { + FidoMsg::Confirmed(page) => Ok((page as u8).into()), + FidoMsg::Cancelled => Ok(CANCELLED.as_obj()), + } + } +} + impl ComponentMsgObj for Dialog where T: ComponentMsgObj, @@ -661,6 +677,37 @@ extern "C" fn new_show_error(n_args: usize, args: *const Obj, kwargs: *mut Map) unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } } +extern "C" fn new_confirm_fido(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { + let block = move |_args: &[Obj], kwargs: &Map| { + let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; + let app_name: StrBuffer = kwargs.get(Qstr::MP_QSTR_app_name)?.try_into()?; + let icon: Option = kwargs.get(Qstr::MP_QSTR_icon_name)?.try_into_option()?; + let accounts: Gc = kwargs.get(Qstr::MP_QSTR_accounts)?.try_into()?; + + // Cache the page count so that we can move `accounts` into the closure. + let page_count = accounts.len(); + // Closure to lazy-load the information on given page index. + // Done like this to allow arbitrarily many pages without + // the need of any allocation here in Rust. + let get_page = move |page_index| { + let account = unwrap!(accounts.get(page_index)); + account.try_into().unwrap_or_else(|_| "".into()) + }; + + let controls = Button::cancel_confirm( + Button::with_icon(theme::ICON_CANCEL).styled(theme::button_cancel()), + Button::with_text("CONFIRM").styled(theme::button_confirm()), + 2, + ); + + let fido_page = FidoConfirm::new(app_name, get_page, page_count, icon, controls); + + let obj = LayoutObj::new(Frame::new(title, fido_page).with_border(theme::borders()))?; + Ok(obj.into()) + }; + unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } +} + extern "C" fn new_show_warning(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { let block = move |_args: &[Obj], kwargs: &Map| { let icon = BlendedImage::new( @@ -1172,6 +1219,19 @@ pub static mp_module_trezorui2: Module = obj_module! { /// """Decrease or increase transaction fee.""" Qstr::MP_QSTR_confirm_modify_fee => obj_fn_kw!(0, new_confirm_modify_fee).as_obj(), + /// def confirm_fido( + /// *, + /// title: str, + /// app_name: str, + /// icon_name: str | None, + /// accounts: list[str | None], + /// ) -> int | object: + /// """FIDO confirmation. + /// + /// Returns page index in case of confirmation and CANCELLED otherwise. + /// """ + Qstr::MP_QSTR_confirm_fido => obj_fn_kw!(0, new_confirm_fido).as_obj(), + /// def show_error( /// *, /// title: str, diff --git a/core/embed/rust/src/ui/model_tt/res/fido/icon_aws.toif b/core/embed/rust/src/ui/model_tt/res/fido/icon_aws.toif new file mode 100644 index 0000000000000000000000000000000000000000..898afe3e0353d7ddfdb56562013ca69735431427 GIT binary patch literal 1264 zcmVlnJ6?Q>7`ZiL^HCgIjRI$Ay=L||efoMgHYJqdR)^iy4cZ;Y zVCQ3RO*PT)R1v=b&O&Llo+_gCr=!=`-|@oC-~gECo8^Fx$!=3ZER1*Xi|rnYp3R`O zCk5`9bHB9iA}g5x%Wk?pR4iz4b{U~b#4-=Uzu#B>3 z)v+R7U$CZwT*w>$8afApDAtvTN85Rvk4q8P&vs6-tpBO%U8$z&wpg@$3H_YMG1Fs3 z#1*Lteq9NYob%Cz9-S$70osg$^}~(v4wpI%uLPUYd3>)jSpP7Lc9&Jm?O#hMy4lk8 z?-z)lf`-`kv2Kh@A%~ zhhaJjQ|kI1k0YyQq!%CWkV*GB6bq#_qqkd|+ytN+oo{%4E=6JuezpTqqSze|Dh+al z_M0uFjaH>OA4XHsE&WD;T%_v-jrBu6T?tp$^|ds)m%G5x9WRcx^EgK9m%E6x=A+;d z;XPji-r1&zGzO870!?Dq@o_}LDbWxUhi?bSJs z=@bguml(s$xog_(fGSwSy$XqeD!LwN(fPX8yfpaJJN|KhOip-GdzH)TSaCjzeYE3U zb`ReA>J0>4{(WvZ76=Yc=3@iFPGPHtD!XUk%RvmWL}BLNj1kDs5x;<^ifAM zg71B63%h7xXqO1%^-6oTbdQxRv1FcxIQfi#Se%jP;_yeqUs5 zJQ@&xSnBkZ2J`tXx~RLvEv&gv5>V9xgZ;AoZIM+nv{Ti#(RV=PUS3z`5}|rVxW<{p zdnII@+>f$9)N#4qr$437{KFgw>vBL5p0270Mz`z20dq~iPdp@cG2e2aj=22|RF7C1 z?16lSMoqaalnlRQ(5L2v1G!vpV(kXM#DlU+>nM~G4|Ugb7*vFW>6zDmYMWUf)Xq@l z;VJIzJgMeh-i6vuBvsF7Hi3QK>?80B>te~CVcI8Z+fXG!^Zm9k*ql)!;+C>&s9rKU aDj^>vlFVnr?Jj|F;J|?c2M!$k9{&N%8GAwi literal 0 HcmV?d00001 diff --git a/core/embed/rust/src/ui/model_tt/res/fido/icon_binance.toif b/core/embed/rust/src/ui/model_tt/res/fido/icon_binance.toif new file mode 100644 index 0000000000000000000000000000000000000000..8d9ba3649866cd8f60d22e8faed3ad2af8f68c40 GIT binary patch literal 555 zcmV+`0@VFfPf12V06+jA0ssKT(yeX+K@^7Jg&csZ=WMbUNgWIR*G&Z3J=Y7;x-7`nb%JbNFA(dZ0M%6jR96e+dMcpm zlz^(!0$l$SG+%4CANbSxaoCnmD>b_Z>b&c|&3)}o(SCXr1XzIUDnYq#voA?K9$7%w zDFI!_g0wCR)O93K*JXiN7X|uyR-mto0=b?FP<`F2tZE@8K=sX}F{1>yeilS~RF8jk zLkMs^c45JGOVxF^h6J6Es#5~H5w`{j==$XXv2G#|>z4`Sx=tY1-37R=65zV4VDUtq zh6QweclzFULVLlRg+N`OR^2whRKQa=74X$f1ibZM!9r1Z=dSDSfWPh{$hU4Q$iHqQ t;HjGm`06GC-nve}UsnnI)G2|#IuiJ;X9fQ2{{_X=6G3tHcp9@y>pvn~2H^kz literal 0 HcmV?d00001 diff --git a/core/embed/rust/src/ui/model_tt/res/fido/icon_bitbucket.toif b/core/embed/rust/src/ui/model_tt/res/fido/icon_bitbucket.toif new file mode 100644 index 0000000000000000000000000000000000000000..61c4b67f2fca6df9086f4415bfb37d1933dda611 GIT binary patch literal 865 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 R27XmZK!c@aYmnzX{|7MWRww`f literal 0 HcmV?d00001 diff --git a/core/embed/rust/src/ui/model_tt/res/fido/icon_bitwarden.toif b/core/embed/rust/src/ui/model_tt/res/fido/icon_bitwarden.toif new file mode 100644 index 0000000000000000000000000000000000000000..6779de1d2fd2b12359641d0d91fbbc4addbe5aa1 GIT binary patch literal 617 zcmV-v0+#($Pf12V06+j;0ssK*(lKb;KokaGErpa!9x_gC3WXbMHx|0-n=>2b# z?%pTeJ%*w2^N#6qJ^tVbf~>0vo`nX#&z2)}iLC(m%Jn3CEui%V4?#YqbGy{t8gSbu zpQrBD`$BN)Dj;wFN$@AJ%R!KT9j{NIC!pRh4?V=Oh2Vt9&@s-Z~gLtxF*_+!yuquJ;+(SF-XHV9;xFV) zy>1M}G=BUYY_8q>T5GZ-Oh2oDVJv3Z$6&I^~Lp9 zXE2EJ|9qazx1TF&d{=xQg6qCDNBN)5+Q#;?iCygA^d#{isCG2l!ARXOWxS4L{_2*c z2tF7p9KiWN>STZ&|Xf9 zA4q%j1kZ2qq&*?rQUM4QZdU~S+^z4+lGziM#7pl>y(P4>V`tbivGa~>()#V6i;5q?k|$8d${rH z*s7mgKVBcdKV6=@H}6`Ccm=PJi=|nHiABb*V?Eqt?>s0rnw`JoGq>JP)|c{=)a~WT zYX913vd(e)LWWcw`{OY;)&yA}$@&8s;MdWaqYvM&ygL-R!05hLAld(-RDf4IF4Xej z;eDw{uI`Yd;O^;TSJ&oU=KX`0!iuTnLZHkLM_fbgBx*AQAC-0Ci%j?Q+ zS2+MvrY~T1Z6iVU-5Jg)Mtc{onYOVDHK}Mt-y!2SP=T7t4|RDvUbmuemv6RV$hmj+ zFYH*ee?`0ZQFUJ3Dp|AtIqm-aXmtNj*z}MSPf12V06+jN0ssK*lA(^=Fc5~Ng@J}WV30>BEHGGT8otEvpbJ-iir{&R zrDlP7g@J*7gsFRxR;}tu?AUj!N|~V^fB*dR!v)Sh`|Pv-qedYIFl6Oj-pPWI1Bovp zYZw($q7=eN`t!)XLz^c5rD8$ryY_TvT%gQy0ZdD_>fJC0#io!om;m4^D`Lv zbXC5A@$>Gs+W(j5PuEOuZZPCbqexHtIhXI}^baI5{X=G6*Z5bE z;F0-ZL9y?iTA2HdxqM_cAi6#Lw)2 literal 0 HcmV?d00001 diff --git a/core/embed/rust/src/ui/model_tt/res/fido/icon_dashlane.toif b/core/embed/rust/src/ui/model_tt/res/fido/icon_dashlane.toif new file mode 100644 index 0000000000000000000000000000000000000000..02d618c4068e8d4ba68f6b8af34e395fd775bd6c GIT binary patch literal 2158 zcmV-!2$A#1Ke8pYLCp+0EzK=XnsswYrO}g3uCuZ0)vkdLz9C|MD8YGQV&%FTTsf^E>SJ7ck)uPGa$WhJ> z?gT2}O`z5pM+Ml*m~6^^7qMtC_wIt^6r_{092htZPrzi#-iYs(mp2$pG^nsX^N~|x z`(1p7?&R_~&dOEDF#l~&F~$bB>L8x@OX^jPfwnEV!LRfO>1JW-c^~ak z8BFC3iSpO*F)M!qZ#@R%={fE1^0`c2w&`G1OtP}jKrgrqw7Mxb^&P_6e;a!hv+>E( zmJZ%aIG|D)=|!&J+Ak|UCcSyk8jNF$6_B@`5wTB9sDYG?Y3@d9?7P97)ReFa zvZBEi{c*bwHsvR&96#GY9%p=!l`kRT1_nrJsq^|Rjo_(1hW?psH92~^(#Yn+oiqK$ zK-T|F;jEsdn($-!;Hkq5u8W_J;zgCg3im&B=WAQV6%oLn}Svp_4)nqOaKElV=|2~l2fIiFKu6<(|{TUzH*;M}#Y2SQqCbBwY_(*Hck7VQwJJo7wSGWLPGyrAH+_rAX)#6{Mk2ELw!=#Pau+2v`Z!I z=q4FIB!zF!FA)kum<>>^M(a19w4Ww7VG@v*f>smlVhIX?PkDrNQ33XNIk-V- zX&G5Y@#wpS>6J#^-eWS*fvi(@$j{NYpI@6FnH-iQz6Ul$ zu8|uze!J}K()yq4T7Gg}H_#!9WAVr0Ye3vKI-Y$v(l1>fxW@A9r|4xDmtMTzc)t<5 z=pVQUqYPB#c=6Q+v#_Pv}vUe~^&yLuFET9hh2B~X%kLix-igm?W zuOBpX>diN!55Jrbvps)%cHn|^W(&SU7m9nCfF3-q4>;I^hTeZ^$?+wJmmGc>CSSV& zS%eC3KXN8RpRP~uP47LbJF4~iZ>3%R^zG=1p_4;@108_0|MksaWYJ2yf4ilFbX~fx zM%Q@HdK2ac^%pzJJIaU3K?fj_2fpw{7Pand{+%vLpQcai1<|GHIzbQY9LgTlw^i4H zRJFsDx0gpG1t5!G0baaMqXwh&AiYsc4@KJZ-}Bpw)J2U&jn$1z4|SrGoM7a+)i;|D zU->=gAyOaN3j%sb(}Jke)Ub=xy1_jG?#OelwQ*;!i3UY_5T*wv+DJ9}@%vuL3o1_B k2N&E1-Sli1_pWL8zwe8@;5r!e<-x<(&@b1}MD({mjTZ zn9KKX>#^t>c#E;0i=(NpCD`g{ro0m!UpnvsOUmBE?VgRNN?*}e5`Bf9g04xJsH0i% zSx)gG>zV!ujZ_;?i`|oagPlZcqbGSbp0U`8enBn%d)1s@%FRh`VbjR(w$R7D z)(S0l_xk6fs^uH(;4o#GDy^~LvmhZQ#U_QUC3?VxmAgWfKGNbS_zFE_(Zq^u!4gJ)%zGvhC z4;8l9g=sw8@R1#3U&KUS49~u9!i)4o4E5Wl$!(5|{A}xo+SY}&*PppKTHe9so}Wlj zLziW`r=tmYp?r}g36A_L3rgNc^~lC^OHZk@Y5t0Cj((O!s*WaPE%PgC5@z}-%c;JL zR;G=o!KXyu!K0FYpnqg-j77z+XThG>2fnA}o3eWiGYqJ%*VE@{$M>~;o5PiLVQJfV z=HkG?Tul8$iuh%$qj{IS#%9lW`3ys}jaCSFAv(Q1oB<1)5(t{wUol_a-ADBOI57OijYvH5Tk`PhMaCKV(^A|3k?)1*!3Ho4G=RPn&;sc3%CS zzM`*g|Cyd^2@=-``D1f(8!rd%9eq`NE2UIl7AVDmb+p0_z0I#P44B;tH^E=gg-&*N z33ws#$u2YR_g#t`|F+{_SrBN)##kaf7boXtcjsfTA|@+8*`7&1U5vKPs^sqd@mBF& zk1)hJQj<`5_h_cdUCU1gk6L@j^FaR$yZ6Lo@AWKLKlv7XJkWIi3ca(uKv|~RX9!q2 zTLS-KFuFep!B={%!i5psU@s+RqulWOXAG?bH literal 0 HcmV?d00001 diff --git a/core/embed/rust/src/ui/model_tt/res/fido/icon_duo.toif b/core/embed/rust/src/ui/model_tt/res/fido/icon_duo.toif new file mode 100644 index 0000000000000000000000000000000000000000..173f1d0372d78c29fcfd69d09cce3412d1b777e0 GIT binary patch literal 463 zcmV;=0WkhlPf12V06+l40RRB)lEG@iKoEu}6v|$6+}p;Wls)b>7jaQMt?3+-jT|G2aNmt+D!5ClOG1VIpgN2-dVWynQta>;Jz zLT;E^=CfqiHwA=T%T!I}${Od+M$zm}`hMiZndyYz&a9Q)Sa|s){BkR1U8nD7@3h?P z`F;QC8KQywjo+5n?sK*}K>3epI*VE6O``$#ge-@?k5&uAzw+=ml^p7ILtm|L0 z>gK(|`Df9GuIry`%zKR)&OeJ@Ssj0X?43%i-)_|L?<`zv%f<)%T%pVSy_`B{)FRNQ+0d0I8$kuc>h4+T6-?mpKt+vAL|b}uC?pn=H9$E z0L(SkFJOr3-B~#QULEw%;aEQaX0tjk|AAqSCD1VNg5rxC<=V9Ccz6Agcc?v&Ju?7ualj%PjaU!I z_@c+_deO(&!-#3rA`Uc{6bx!(tnV)y4Vu<0n~;!ldG{+4V%B7Ls4r!2%f}9_`VArR zlc67bEnw-rFo^=Ud55PxxxqmXhyGb){R#&_? zdpRa$j!t{Ue-&%66AXJ$B(POqZMSiS@pLSq^Cq4VuyDU@nJv4Xxj`ZC> zo@JT3AL||Z@$cm?Z=O{1{SOmJq<23#&9Z8KtSA3(z?R?rZFS$qjh=$6UOfHF=h=Dx zWG2`pee(?}?>{Yg`szxqi$T?+)6lXf$Gn^dr{8L;5`&AJiY?i+)I7O|Q=vtI79FAvbAV6HxQ#iHI(8e`MyHYwjg6aHrUc@lC6KOtM+ z1RvY>eKg@c9on|vp&$751Uzih_jdk1Pe|JILjqs2WNrCN2Al*ES6IdOF#vvC(uVI6 zD9n&H?GGvJO~jh?BL*cIz?3!RPuT#B^_tC@jVljVn=h?;LVR|2F)FO^_zkf~wb~gY3 literal 0 HcmV?d00001 diff --git a/core/embed/rust/src/ui/model_tt/res/fido/icon_fastmail.toif b/core/embed/rust/src/ui/model_tt/res/fido/icon_fastmail.toif new file mode 100644 index 0000000000000000000000000000000000000000..859d3b6f609a31a78921c933e90d0c21200c7a2d GIT binary patch literal 1185 zcmV;S1YY}8Pf12V06+kh1ONcll0Rr7SscfI^M+i8G%*J*%x$Bo+!4;K2QFl`SQQRD z8*>cV!m&|ib4V<<*@eR*B+FgksIKS8#uIGxq>>kQ^p5Y4GYq6KzMptXILonjJu$MjJLTjbX zN_mifi?-2-YtDMaZKJm+Xz7C6L=M6Hy#$||ZS{;XafZvG1Y_QztF$Lf^Hk=Nws(_I zHqC2-k8n=u#@n|pvz*~>(RvdMAz_lBuWR<88Y8R zCGb)u=u$7XQHXh(cC3a{;I&dvL-aPyPhJ77-8+3T0q#NnpL)1dHy)Dk zRRN!1i*SIC=-)Ou>+Id}KRI z_JcfF^DSLiDQEEk@>l}eX*zu`ksY7q0;!zMKUC-hPB4DV8QZVSQ_kk+0j{lHQn&%E|@E4^KasK0=@D7xc%uD7TY<0UH=Dv{qUU? z-Kqb0{h?B`X=vwC4`=AIfu|_O%PaFZzYBui^BLYIf=h7Rs01SBt((P!#$qss4!J?cO)! zxz~#K35vpNg?#luW@ofRP1;wzZQ;E^dq+)LoF4@88hL(STR(~C;y(3SUK<4L?o_Ee zjY>{Vx@IRKC)4j#9ckIxZ5SpH3sK1pJt~O>`i+LRwd?Hm)~?ZmMrEoVOGW#5DDoO(xJ^`8OMtE=@CeDMY1iwrYAW?>a?;Ku0xuY0j^c&gQI16LSXt9N+4oehW+SnP#?Wd?K!;yaW$f3|gplJ86)kc5-iW+x%?q01A#9G`;BvF_ZD z^HXFq5q|O@_s@5?<00Z3OBRC6bDxQ4_wSw=wmR(oTfMe8XFC&wJ^xo$>YWiRu4!-~H@!0iS^Nr9PZM z0kQd-V%LAuyienPZ1{Wr^8})RP3MKZw{}0f?|nLq`DxpisITntLa%D~KkRy+zAxo$hoAf%X^HsO-CoZNs;eLC-+%Sfzk0d0!BVf!v+FedK;$X@5u(}F z{kuzS`d}hIERLSNn_t297Wv-rQQ3`gbed15X3dwshxrxJEnj8AcV>3>0lR+KeK(wc zn36iHQ_${LL|Sgvifnq@=;a3gygr}yurE4j^J4-{U+C}d=frF=yKnY!757fGU*1}M zr??w$i3j2+dC+>m@^47a%+hBUso8x^;RU}fbEd!Sc>RuRYI@Zv`L_TteKL9O`tSmLk1&m%DOE8_UmpO#yPkvq zkbXzRZ;*ERZ*d!c{c)95KSBsN{WLKF`VC0kFkU2BeVGFkpBKUsd4rB<&%sIppZrC= zZ2HZ;(_bbg@NoT6?W4BU=v9xaH*0s#3`((PFE|5$o6id^TL$(v+Qz8*u*;}J0K9x& zxISWgn!Y!#I_Fro^`{r@vd;hd&)cQs`-w-RUD zxQg6%j#reDfl0f7>KqumqMkEg>Rb8#tD}IuN?a1E=!m)1ewR@S2B991Id=DdeG_vt literal 0 HcmV?d00001 diff --git a/core/embed/rust/src/ui/model_tt/res/fido/icon_gandi.toif b/core/embed/rust/src/ui/model_tt/res/fido/icon_gandi.toif new file mode 100644 index 0000000000000000000000000000000000000000..19c8063166b2d24d58caa0dcde3e01b37a83fcbb GIT binary patch literal 1237 zcmV;`1Sb9fWi!n4v=khZ{QF;E;cdwaIC$ zwMqPqckkWvzR&vr@H-gLgdXf1@8r;xkTUyaviCYjdVbl@pu z&RtCzaqVE14{OHR%d83A(+{N0UblnG)1$QELIW3r?&&`YhW^=bmG*Og60J^UOhw(( z&+=xc2lVn(Gi{}T-Av$hOf@C%vebkO@UUIUyp>nlJ$(=BHDyhGPheMC%ybja{Rrir zJ_9-x{Toz$&N-=83{G;LN}M-5&G^ZANxD)Vt}2^w&i$*vf)-+asXphzpKjldi^0|T zh!c^udQUN`Ug@s1%!-I6x!-hfLhSl%xblDjcL^&GuEy;z^%Qm7GHx2!7u{b!kS6~> zZW-R5`=|A=rheYYxTj2VgRdP$C|FR5w>PEz^~=I~wx)HX0|t25`^2O0r%)DrQA7Jf z{ZWuH`F2|0e*?Z~1Fv^*+!xQKZ*{ns>bmS=!Hd{Ju;b|a#=ZT00Ata7l6#q2KOg!(dx(|QB{ zTwBI}A2NR6AQ9?1Mi1p5_0Us}(kTi!P9EMT{yu!~gNXiBJS=bt=G!#NxGHHO2Dqnw zln!gw+Nypwyh}tV*TmlwFv(eauj(}=kj_H@+*P+upI1Y<0S#OxkhYq=fsb1mKX4r_ zWVQF2;_k**G$B3Q-1%|K!-9j5<<$+}VIf5wM!gAZ%DQ@-XEy3mR1-F#^IeTNb)5n@HtZ=qg&uY)zNm9uFyORBPXFJ7`F93TQQViYxkAQW=_$lGSuW} zd)F}|bu~rbL8oGO2a_DB-wv*BPS_;cr61PpI)Jw74`{DOrv7YrJ6_L2oVTZ67Pk4% zfN9;Fwxe{4%EOxyssC1YHh;*JKM&iLmbYGQ&M}{7h5z3B!G1wq7P7hP2|MKuI zfpT%)*m55Bleo|YcYboq%OCS_d;NTEU#Bg`UI4&FOvYpe{}i^( zl<|{p>H4E!Q~rIU^pN~h#(xT%TV;S2x*e014cra_+l)BUfe#O=Qaflk8=*4(o05q1 zHL8-m?Jr4DRY~7KL{$mp0_n1FQ+nUWQNSgduq^Zxb<5l@Ggxri z_e}=$oI7p3%#ujP>bt6p!&*&wtB<$=&49zefIQ6jspv(_7u7;1Y>GORveWzzxBq(+ literal 0 HcmV?d00001 diff --git a/core/embed/rust/src/ui/model_tt/res/fido/icon_gemini.toif b/core/embed/rust/src/ui/model_tt/res/fido/icon_gemini.toif new file mode 100644 index 0000000000000000000000000000000000000000..10e81eab143d6600e33be76b28efe226609ea274 GIT binary patch literal 1042 zcmV+t1nv7&Pf12V06+i+1ONctk}GiINDzkGaHdK`_>812je+s1@8Dq~ORr#)mfJNPRr ze+IMm)rc@{F3lHGCS7B}PR;^HxY%=xM^+BI0hfHc_dk544@gh=aX?mj$hT+oxm5yv zo}fE8WcPdCy6_ttB_?PaMJG6`%WmJWRqr2p-v_;}>Jai`cbA<;NNBpMCiRpF^84U3 zk(+d|TT5TrCAOm43(3d7GvXc|nzd}T-I~uwf732-H95+~-akXC0 zr(a8W*Fh`y^bYoO`p1QlP+*c#L7&H!Uvm2wV94SwZ6iya1%bG|n;#GNIs3ekc`a8oN=VIzHDaOAMN}i-}K!4-=NA|=$;aeL!h0+$Cp3Ot# z22ALQ5s262r;YpB8auC${GQGp{tH=}5mukO_n)#ung=*KkU@%9(n-%B{AYpBw|OUq z#>T`sqSmF?k_&3^o|OXF)S?*Gb?f0Daja##6R9zPV63#xv< zW$Nb1f93IKAU&e)_ZN|_b8tYm>&oI!#g=WweW$QC zird?0DZaNA*X^z>gMUHgmKu}aCsb@Gq#`eS6C*p?=7r0=}!b?tA)MlvlRP6Xoz{FlFlI5w;C$t0olF-H@{Q4vvY& zPK+SIj_`?FA8MY3`#c=&P3e(QidVZbz*}}`=4x|kK9Ix8R+pWPvl>TlRz(IF@+;an zWEtiD_GZ!|DV6ka&Tko^JvbgAkE?D76pAs4JI#6es#g|QX{;E3rN^D789H6ixGLnu z_)^KY3`)8X2)Eo9px-NftW1?{;lN>m3k<~_{fs(J#04De=cM<+!MID4W~)5~ThzAd z`S`7R++l5IuhxWi*{A;=O)BdrIqDm*Hsg8^Ka*_L-I^!p*G4}59!Ma1*x1c5L6^wu z1(_<7ajV?GOMQhVmAgl%F(?o|mz}=hS-pSM^R0TI(9h~nC@lEJh?IUK9J1M-w=R5P zBUE3{c>{`u1y4LmbPX`%D}6wEKVI};KvsIl15Ca(U-)rL! MK4dS%f}bP)1HK0b^#A|> literal 0 HcmV?d00001 diff --git a/core/embed/rust/src/ui/model_tt/res/fido/icon_github.toif b/core/embed/rust/src/ui/model_tt/res/fido/icon_github.toif new file mode 100644 index 0000000000000000000000000000000000000000..7092c7eaf0fc64b8365a1985ab1ae8b52375aa66 GIT binary patch literal 1092 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 literal 0 HcmV?d00001 diff --git a/core/embed/rust/src/ui/model_tt/res/fido/icon_invity.toif b/core/embed/rust/src/ui/model_tt/res/fido/icon_invity.toif new file mode 100644 index 0000000000000000000000000000000000000000..d13db26677984936a8e50c9699d1325b8392d4c3 GIT binary patch literal 136 zcmV;30C)dXPf12V06+kI0002((#Z{gAP_)N81P89)<6ZeITd*EC@u-IsEPhRSD9x} zV~B`|S_*C+&L=p0eEj;-Z7QX2@-08V4IdxB-dEY4&rNTCe;)r|{@edww84%3s(#$w q>K`lHhL4Ybm#>n0TBWx?`E^^g&--QdzkmFa`2P6ms2Cz5qD>17q(kZe literal 0 HcmV?d00001 diff --git a/core/embed/rust/src/ui/model_tt/res/fido/icon_keeper.toif b/core/embed/rust/src/ui/model_tt/res/fido/icon_keeper.toif new file mode 100644 index 0000000000000000000000000000000000000000..e15958b4f9fc97579dea8a9ec1474a59682eeeff GIT binary patch literal 1507 zcmV<91swWRPf12V06+lO1polul0Rr0NfgGXIJhlRY>`6c7;@n4PUUthaVb`i!dq@N z-oyt|D$As?njs1*ER*6C+$hDIt+)uA6hZE`7>K}yEv`6J3I>ACg%K$Zr8GCQf25VP zD~%la%-u{ivv2nI-uHa~tiC>HiiMARyN93N{PyPOS5KaA=8skIFRdL-ef8qQ(b>`2 z!*{!fU#nj$TfC1y-@e}alDTX4w({h8z}`K)c8Z1Ry;<1Na?0J&k5%Gp1!vpZOp4v%bxNbsIZyeUaJc^ z<@Mgt*^3VszTav3KH50fPqRus$N^j<|FKpqy!h~OugIr=tfkj|fue&zgL#c^zpnUS zcMpq&3%gi=OF9)UgdQmCp>Hcn{z@0@#nT7tT(5-+EU%SYUoz*q|3{wu+|rqj)pPwy z7kniT*W~_b*5v;?unsJl`da9KSvXeL#V`7NvmmEeP`|DWPWVMBKI9n)_~-h4@Pz{b ze=~m{`U7x!C;a#S8Gh}a`8!&~hyMfqj#kT>EAbzPe6x^KZqGFo!vQVY%)7t1KeYyp zzZ$<*unsh=8p>VsvC7J@hX>#WCT{SJf9nzty0{K1xWr7PQwWuH@R4d%Q6CS)hBvEi zI^tb>+wL=W(5zK|eEz$7i`jDl`!@W9j)tIXv{9XuNY0Q_gRGSw-$<9?Q&XPLMx=uq zY@e2C8CKZ|=z;~+c&&Ww0c&1So*tWaQeNa__)Lc1oBWGpsA<^9fi~8W8qHwz=U)*i zKCqVYK`WRWscawiW*oUBCGpizpgWgEQw9&0@M$xs!wwy$=sWzNS zjRM>yS+DadfW0Z*EE&Gxr1SAWI1B-5Pbo4a9`;awz5|kBqgDB60U!E*ATeFh0NOB0 zVos!vbVxH$hi&o8O6;{Q!^fqFPG?Ty>DHi$8jlI8NQLoCqv(xtuf!v9_-D~zxiD@;*Qhiv}Q~B^(6g~?>Dd_HNu?FxQ{(? zOT+ZB5mu*asxo{u;SXc;`^lbU2`2JIMj?pOhw4)=ZVV|tZY1%!vLK!;<{z;)mC#EN zujv`w3w9af*Jb!5kii(U9S@6h3WErt{C`uwRpw*%nmz@U1BwbCFi#3`y}p zD}_H4enP+}jr4S4vU*GyP5g(6EErAn^^7;?s@{KXMxO zum@c3vx`00TbxQ>DY4k4Jb#sq2DE%1d@?lJoU8%KMM`K5d^|v&QHK_TVD+V;_Ue2x zq#e+LRl`O)QowgE>=^w;d^`YM)TT{XMGnmY&G>HO?4lXaLVegH9iz=oa2-@|30Q=) JunN~k`3E)f1?vC+ literal 0 HcmV?d00001 diff --git a/core/embed/rust/src/ui/model_tt/res/fido/icon_kraken.toif b/core/embed/rust/src/ui/model_tt/res/fido/icon_kraken.toif new file mode 100644 index 0000000000000000000000000000000000000000..9aaacd6e1f6b48b0acc45421a0dd50b0abb07a7f GIT binary patch literal 654 zcmV;90&)FRPf12V06+kO0ssK*k}1HO1i8WtL6*(YPzi5z)VkY9rXUV5EEp| ze#7hG_s1(==ex8nx~zaT6o%{{AN_?-@+pAj`z=NoppYzUNe%Gm+E-S$&e$JcV zFZ?5Yj+&s$vZ3U=v?+c@BtPX%@k0i{SJ)K2Ki+TvV%!|RKOVxa(uMJY-A1 z5ZCjYu^BsD^&G^4g)Fbwv97c7^{+XPFnCu0Lsr#yOO753^eQuSsdY4gH8IU8s6N?2 z3?aDC!Z&qZlS_~J63O!u%AkDlds!`-IncJJLl^>|dj zo9fAkzGXVUoW9gjn=&J+N8f=FRXq{bH%;dUcktI5U*7<)<+uEn-|}02%Wr@`x`Y3V z@0AG(VXMbL$kcNYRy|vydU}rPnIP4(^hZ?vqTxZN^gakn^9-Tddye!@YTmm+05{^Y z^TcopfK}-Qw#4LdOE(eBWnsyCS67~5ApHjHtC^$ot1^=A(gJh?$@)#`uHZ|QzkPyhe` literal 0 HcmV?d00001 diff --git a/core/embed/rust/src/ui/model_tt/res/fido/icon_login.gov.toif b/core/embed/rust/src/ui/model_tt/res/fido/icon_login.gov.toif new file mode 100644 index 0000000000000000000000000000000000000000..fc02cd70d467b85c2adf9a3c3d3f5f64c1e6e36a GIT binary patch literal 651 zcmV;60(AXUPf12V06+kL0ssK*(!XmHVHm*iXFPDZK{tn-Ae>tkAq9sdf@DmGN^ok7 zln7Ni2zF6I2%-TcgBZc(Xt7PCV&T`QfZ@Y^vFitCGvm0A`+Ue z=T=E=$NqSxy&xKLqC5BZE0eb%S19|+_zv*#&h%OQdI8T1LHho91K6&F82HM$|Nr>0 zy>s7B|Aqdn>W{xXD4I{vNl(y1pR2L-IsNkp{Z$vWG(1hswFMam&KL3BC_s-Y*GkWo%y}pIiDhuD}6-_(8rIF}f z_@X{%*}+DKO!vY!dXFuCV4kV}!SelY`s{p8?^?G1<&d3!;j8ufy6EaUD?dI&HeIW+ z@?Vea{D&GVzirz28y!~uWjh`nePiQ`x-E}x9I^422ME)xDhq#oi1YI`7QQ*c`7Mow zKX;7tFZNmZAN7O5r{SZKIl65Y&4vE;zSf!VywdWuC71gb0wn&~N$-)}{OQysm{3`v z?Pn8{{Ah@>F98P=lM6KcWEiH!t4(r0=EyH)fS_oY+;23=^&JU#AnM)<4L>3Raq?TQ z{bUTB#FZ;v`q@Qr8rM*~^0R4}C9bS^;m`2xe_kLm3`-?W-tf~eWg;T@(9O6kl~x5;_7A&cR+#_* literal 0 HcmV?d00001 diff --git a/core/embed/rust/src/ui/model_tt/res/fido/icon_microsoft.toif b/core/embed/rust/src/ui/model_tt/res/fido/icon_microsoft.toif new file mode 100644 index 0000000000000000000000000000000000000000..fd54d3ec5c1eacafb994f32ae0c9b66bbf11a0af GIT binary patch literal 165 zcmWIX_jGe$aA26pz`*eKk|Eb21BteWOB5{9dZKS1G``X_!Dp|VU%-mR4-_gUOo>xT zFA~2Z^Dz2%-Frs4A3vkF^JN{JeSeep+&hJ`ars;8R{o2S%UXH(I1;$uE?={Mg>CzO zdB4<<)92pQ%$_N|b@STq`={)V%2ThlU2)d;@BZkYE0%WjBZ2+=_Oni|%3b!$&eife FD*y@aRto?C literal 0 HcmV?d00001 diff --git a/core/embed/rust/src/ui/model_tt/res/fido/icon_mojeid.toif b/core/embed/rust/src/ui/model_tt/res/fido/icon_mojeid.toif new file mode 100644 index 0000000000000000000000000000000000000000..f08b3544ccb0c70639fa99bef039035431d0771f GIT binary patch literal 1330 zcmV-21G&fmC8z_S3>cv^gboNYR8EI9K{illptu9V8H|mAq8q@u zAv!#y`0D5Laev;kITF(@{{F`Mz5BdBKLF~fr=I^e5U?}|!XWhet^d95cQWZffi9Kl zg6$*qd|c?}jn+tFc%2VgJha7#&eGHcT~I1e071NKcwM}4HuCS^u6nP#c_yvarGl-s z@TIu8pNFd4gcsN8LNru6ALUb~(mnW}k$m@lZGDu7>*IvkW@kx0ldipwZs9TZu52I4 z_eXBS7sbWA#iR3W6s89XZr|EIP<2oNr+D6`pk0ye=gONnJ z;RTv|b+M<_d}J@?7x__s)obkTnyJM+X$S9|H}%n}CA*tTgU0eHlsMekx`zN>$gHeb+@AybJ&_I#=Fu`wJ8RdU0o?5o&lTQ*4N`4e(2dRBZ z_Yc!H!0E2nzt$hD#ee=>z@PAsf7xX%{##n*j z@Il;fEX|)}%leXFPHR_2VR&0_hjYLSZj|3%;XWS(;AmO0Wqn-dXFFp#y*~ElCMQRs zEsJ^FU-e49S|1mV50rIq!i3#BoPfpu?Jey1jQbt^A|YEg0AkB!yR7#Jn`Pf^p(A?x zNXT{q&`iBfisG4JAA(c{*XNVyTWndY%RXa`{fXWCDRcnP;*svsxc^s>bo$O|*ugqyCiRki?6RThZ5WNI5yu?ZWs$wk2??*G;=j@wIr> zNsRf;l#3(jwz%{?98z?<&?K01$A6P_zXbs6r1}p={3!=n7scLosbegp)$PL3B{uW> zj8%JfWB$vbjXYUzi_1M@A+=sSeNor?fm8JE9sT-$RP!M?^UWv4iuuJMcLqYzt0UW- z+brp7KPJH9oN!KjhWOx=122oGqrY*bn-{C(?xeC)X)KJrB^>U=M??QkH}rM@?BgrrJWQ{2J-QA<2({4-c{E@^vC#VqftXz>% literal 0 HcmV?d00001 diff --git a/core/embed/rust/src/ui/model_tt/res/fido/icon_namecheap.toif b/core/embed/rust/src/ui/model_tt/res/fido/icon_namecheap.toif new file mode 100644 index 0000000000000000000000000000000000000000..23de6ffcbf2173c9910a090ae3a17bfca2aeede4 GIT binary patch literal 867 zcmV-p1DyO+Pf12V06+j&0{{T+lD|*eKpe#n8YISy9j`zo#*FP2go@IQ{s&G|C>^_% zEpC2PUD?d!1Q4Pd?L@mcA#EjhDokdIuSiJ8cIjkgs4s5F`TRZG39H{*&-Xs>oe|2E zDO09QnKFg7^!$5!`&U!)#(90uEzEG;A}U*_$hOXkdJ!ib#`)Yy!}&1S8A2`%8++4* zYpeL8>W!^2^d_FQa$0w`d><C=n(BAKKIMrHN+`M1|@&pmYN&can`s3KTe zOPgOca__g#RuHz5K8 zZvT?hXpN2-=(5ky9NcGtBIfCV!}(?lI$z#xvo<@R6NXp!TEH|+Pqgp{=urDM>9Q^p z87g%41vG53aK1oLh@RJX2_vlg-(!YDI_m;*`!Rv(_0NE}%6BNwk21`*0B>)3ChRA` zpQ^i_%Ul*^sMw{y(=T8emKRt~SGD)^J&3{GtnW>`fQBtL&W|vt+D|MnytMs44C%faC?jLlTd!po~x01 z0S$MU>-9Z|0d7Az3AYaxSRJYe=GVgQ$KT5g=quFTq@!iLD8;0G`UN!X|Gh#7kpONl zyiSNgv%@FTE#Ml_R}5}MpTYJ_`a>q(lusuwAh#D8p!S+>c~SFpxqZ3?ItF1bgBalU zRc3nJ0K+B%6u*FmyOc2QC;g|aMwf9DCR03$Z69TT+m9C5SLHV^ z+qBKP3}9f%?L`Jv`_Teey+H2vz$b_@%z^DSR`nDFB^0A0ELE6DA0PXdv z7g$cmHj0&wOUea$wBnmnOc>@qLAT(malXtD-#&hUpMzzxe#a2JMzsLlA*PEIN$rys zI3g=VWFRN-9sKp7{N(mY3tYQ7lDlJo+auKPKnoxnrBd7iTcqcX8Q!;4H}o|qKVAEz z1?tYB({rwU3ay~Np??ILrGG)pn=^rnXtwI0* literal 0 HcmV?d00001 diff --git a/core/embed/rust/src/ui/model_tt/res/fido/icon_proton.toif b/core/embed/rust/src/ui/model_tt/res/fido/icon_proton.toif new file mode 100644 index 0000000000000000000000000000000000000000..478a38a570fc20aacb10a25c17151e0b35fd3006 GIT binary patch literal 587 zcmV-R0<`^9Pf12V06+jg0ssK*(y?ySKp4RBFPj&L)h;L1kxaUnskZ21iJWA?;_)Op zxC1AhaDjV(K0+LJB6x)O1se4g(uFb2Go%t7Nf;oiP#SXA>Kyw!3Gw}R|Lo5nz2ui;17g!(brsn4xts@feJ)4D zuHO@{9h*LvQ)btH{0ek_=|BGOx=umD*b@)=o@h1Z|Cy%|t-OIce1P25Z()%&?*qJZ zd)hb1-R6COiLdk95rz*iX6k2+Qyjn4Y@j1>{1#BnALu$5KEO3Iy}*gT$W#qT75^et z`+XNsM&}m<$2Z@9ozsH1#`jm}OnkLY1FS9-|({F0~^Hdx~EifIg zmfatmwxh0NjPHDTJuzlsTo9-@!&LX&8GDn=gM##3UAbH3i#JDg`Il@z{uX{9(D>x; zw!IhMhPUBI1RbE<`1>7kob1J`zK4J^^I)wnvlq#JeAg!*+h1nK$+|Na*Z0Ipaz9+3 z%JcMOBV7NIou`{&`W^8l{k!)B&!4fcTNP+N{k{F|@O)3qvh4ekQ5SZB%~0r2$CX*ML-L|S6t9Sh>froflU$c76C0REK+P?u|-c@s0+1EP?+gI42A05~juoNwI&u*#Sgd_s2oUCYTw%2D$`u%o}uKWYNR4bNpk zWv_1L%L+i!e5wRfJARwr8I&@mjJNhVepKuXMu3VN$HIT{eZ*VqS{Plm0utjd^cgQPxaD+2t!XZ(F#R%GSJ<>&Zj4DdS4dl-K-H_T>Qv2P<2ynRXFZO+7d z%;)R;yr-0*dfYfGvW>z`V%856ax6Sv?9XnWDp8Te%gT8(YuvQhj84R#wZAiX>W95G z%-GdO``@G6H7Eb5S+%xlNfY;YA>8+Es%m|2e%HEl@PF1|%Q{tf{!3!p{HF@fAJeY2 zI-{IvTXpGnZ;~}`2i28-B5VBJ`8&&zuS4Qc+*po~xeoPcTC-oZ?gfc??>wc<;^lok zAo=G0yeFn5O=qR68Pr2O|M6lI9~DfY_on4@c%x!uf_|zW2)zF<|Lbrw|M7y4uZKo! z_@_!_f|k|@@e!=)c0kCEe_DAS#*LVUe+m-**%{yzf0dtyStG4sj{Pv3>0A7`;jC|O zvdV9$cF!@QGr(yEq{yGh>oWS=Er8K1-mVy6I%l3YH}SjHj-TiMD!|B!O=`7|zcplmtWz(@MKEgV-iO=?T(c{s(_^-o&6w`K7SR)}nE*ISp z=6<%r47@_Fdv6ChdRJ$!(An8o{Mz2|eD{PCAEwx97A;yW>d6Oq63 zTrTcBmksr>xSoGqJ`wxjV2aO|v93b(&`ToD`G4OxRNkDMRLb!Dei+`V(Fgr+iRwJx rNA@PLRdp@4MYf(Quw?}#o*&a({{P9Bm3C{pb`>J7T)A@P>Raj`oFNvc literal 0 HcmV?d00001 diff --git a/core/embed/rust/src/ui/model_tt/res/fido/icon_stripe.toif b/core/embed/rust/src/ui/model_tt/res/fido/icon_stripe.toif new file mode 100644 index 0000000000000000000000000000000000000000..a198d7a381dfdbaf898a6f95ae2ce5258b673af2 GIT binary patch literal 668 zcmV;N0%QGDPf12V06+kc0ssK*lA&VTFc5~1O;BtKtDvBu%_=rQ1ubF|YuE}?XjVhR zz=drveFQ_Zx^J*R4=shWW1p>LUe>hBLi8VqqO3)EqprD|jprD|jQ^aAz$mBBY zN^Z^4EuB&4x?9dx^w0U0eja`y%U$(CUWSjgkMT?MWMq899k~quU&)a|t~Il4S{kV| z=N`W_G^TOW#auKi8atwSrZcU`H`(rJK4cU6oqbl%W?NS}^o}D{R2pWRnqT_dTrAvj zFH!;UZ2XfKX$*4`18l?cJJI*G0^W1IO#68VN16>iH|YPKaA#W+FOq2*805s zndQ5e$Gb1;i7vbxu5T5GD@f>EthLWKDra?2pHP}qq~aRJ_zQP09Led=zW;e$t%}+; zvyK2L@_vdr{A!I4)d~5H^I8oP#+@<#TDQ4R{f)Ca%k0ogBh9;8>!Ht?lktZAosWd$ zd;p_xc8VaQl5^2SB{4_%{=BxP?cweGRNzEbble+F00=&_`+I`Fp8wcS2WNZd@}qnJ z4)>z;{2tq3VyE&SdVV3%ttcLG?BK*w$}YNq;A@FX!(Eem%w+=g-`V0x0s)zMUUgN7`>4VRnq4 z3crIpcdL@_L_(kX%(Hp%{HH@FnMQge4nLyBUT;O`ZACUrbImvG`oNtls=44fZO>y2 z72CKvaA&eR<+D*&kkbz}iA<7_>|6Bv>-=8@{-S1A(YwiI_>MqOP*6}%P*70N3-k-) CW>+Wx literal 0 HcmV?d00001 diff --git a/core/embed/rust/src/ui/model_tt/res/fido/icon_tutanota.toif b/core/embed/rust/src/ui/model_tt/res/fido/icon_tutanota.toif new file mode 100644 index 0000000000000000000000000000000000000000..80edb2ca31226a61fd63b70c4c4dfbbe027d1309 GIT binary patch literal 646 zcmV;10(t#ZPf12V06+kG0ssK*(y@!$PyomAq#)Gc;0_!doE(HJWDr_#uy9<#vCxAK zHv|qG?yw+-kWvaM89GFSD{(k5!yClOH{6h|mob7v9~2Jb5O6bE5T4M3S8!>gw6?w4 zOJZKKB;P;a^UHgWVVsXWV9y^&;l-Y_2h2le_3eghuQxEe%YQ$MLSHU8`@t8Y(w&$B zdlIG3p0IBs^&{2e;^N}s;)3XzG?t@Xkd-PbY)8A{O5GZ9a(k}iv*AiPW4I(J$YaKYAkF(R`J-fkIf;vHRjhK)K`T*x9Ub|F&g0D+oE2$v)-6-Li-P&T1@z{P(hDyICOOrcP(UXU&c8Bp@2^vC($x1M(UL@4xck15yk87OI*TU&KMb zM0MoF2V`M)ef)vH^*rN9q$4?uq}|cZS+Vng9R* literal 0 HcmV?d00001 diff --git a/core/embed/rust/src/ui/model_tt/res/fido/icon_webauthn.toif b/core/embed/rust/src/ui/model_tt/res/fido/icon_webauthn.toif new file mode 100644 index 0000000000000000000000000000000000000000..58817cf1dc458c9b51f7eaf32e2aed084547244a GIT binary patch literal 1135 zcmV-#1d#hwPf12V06+j^1ONcFQX!1$N))ZAK#Zt(q5@M_Q9+XxkL0PkPJ;MN5Hvwm zHDpzTAY>gukRQa$=8KmNf@Bd*K>TD^kbp$?i%nQT0s>J{QSn6uVm?E6|FZjNdk5&b z24-&WxpU6pI5zN-U)Hf}1SYbSw?bB6x`vMZGB0ttVWiqPFcmAk0@KDL^}5VkQP#4V zKW`yxTk#s)yrg4fDQ6V2bbP5FvUVQ##}2Y~E_YXT4aK^=T%&tN9tsz}{Id!LCKZN) zjTP&wSJvd$JO!cY_*Q}sfR)b>H#ku^XnMOK8Pq&&6_yK=M$&q(eptB-9X15 z>{}7``o2TiH{|8v)a}c|=CDEEfhn*RS-o$0l>O&@(*4hSx080a$(3qTUix^OEjs9=*@b$hn2y7*^}P z^YyOpQ0M7($Wn5w)?tHrBrd}C`&rl)7xRdow>AfAd3HO$nz8X zw7_Kh;J)w2Q}p`|Y^q=8 zC7A0mdv?O2S-b&E176}dNCIPRZypgK(SJSB-j)YRAGe2{nSh(e`0X%F(VGVUK4f*; znDUR*^sY){uuu1HjTmxGb8r`Cb&XW+fOH3%WBQs>M`7UqJhcH#OtuyL01pHa}z)woe==9AD>-K+TE8eNq;6Y_;BAWPP`i@-}dMrU&C z7@D|rY(-fMOcw6Il(ntMT@@qM#@Sm0rj17`mm7G=FYDMf0uvo69hk16W53L<_kX8I BN^}4K literal 0 HcmV?d00001 diff --git a/core/mocks/generated/trezorui2.pyi b/core/mocks/generated/trezorui2.pyi index 9059097ed..a107947a7 100644 --- a/core/mocks/generated/trezorui2.pyi +++ b/core/mocks/generated/trezorui2.pyi @@ -159,6 +159,19 @@ def confirm_modify_fee( """Decrease or increase transaction fee.""" +# rust/src/ui/model_tt/layout.rs +def confirm_fido( + *, + title: str, + app_name: str, + icon_name: str | None, + accounts: list[str | None], +) -> int | object: + """FIDO confirmation. + Returns page index in case of confirmation and CANCELLED otherwise. + """ + + # rust/src/ui/model_tt/layout.rs def show_error( *, diff --git a/core/src/all_modules.py b/core/src/all_modules.py index b98f6cf13..ee72fdd31 100644 --- a/core/src/all_modules.py +++ b/core/src/all_modules.py @@ -155,6 +155,8 @@ trezor.ui.layouts import trezor.ui.layouts trezor.ui.layouts.common import trezor.ui.layouts.common +trezor.ui.layouts.fido +import trezor.ui.layouts.fido trezor.ui.layouts.recovery import trezor.ui.layouts.recovery trezor.ui.layouts.reset @@ -165,6 +167,8 @@ trezor.ui.layouts.tr import trezor.ui.layouts.tr trezor.ui.layouts.tt_v2 import trezor.ui.layouts.tt_v2 +trezor.ui.layouts.tt_v2.fido +import trezor.ui.layouts.tt_v2.fido trezor.ui.layouts.tt_v2.recovery import trezor.ui.layouts.tt_v2.recovery trezor.ui.layouts.tt_v2.reset @@ -405,12 +409,6 @@ if not utils.BITCOIN_ONLY: import trezor.enums.TezosBallotType trezor.enums.TezosContractType import trezor.enums.TezosContractType - trezor.ui.components.common.webauthn - import trezor.ui.components.common.webauthn - trezor.ui.layouts.tt_v2.webauthn - import trezor.ui.layouts.tt_v2.webauthn - trezor.ui.layouts.webauthn - import trezor.ui.layouts.webauthn apps.binance import apps.binance apps.binance.get_address diff --git a/core/src/apps/webauthn/add_resident_credential.py b/core/src/apps/webauthn/add_resident_credential.py index a0098dd91..6e2da04c4 100644 --- a/core/src/apps/webauthn/add_resident_credential.py +++ b/core/src/apps/webauthn/add_resident_credential.py @@ -1,27 +1,8 @@ from typing import TYPE_CHECKING -from trezor.ui.components.common.webauthn import ConfirmInfo - if TYPE_CHECKING: from trezor.messages import WebAuthnAddResidentCredential, Success from trezor.wire import Context - from .credential import Fido2Credential - - -class ConfirmAddCredential(ConfirmInfo): - def __init__(self, cred: Fido2Credential): - super().__init__() - 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) -> str | None: - return self._cred.account_name() async def add_resident_credential( @@ -30,7 +11,7 @@ async def add_resident_credential( import storage.device as storage_device from trezor import wire from trezor.ui.layouts import show_error_and_raise - from trezor.ui.layouts.webauthn import confirm_webauthn + from trezor.ui.layouts.fido import confirm_fido from trezor.messages import Success from .credential import Fido2Credential from .resident_credentials import store_resident_credential @@ -49,8 +30,13 @@ async def add_resident_credential( "The credential you are trying to import does\nnot belong to this authenticator.", ) - if not await confirm_webauthn(ctx, ConfirmAddCredential(cred)): - raise wire.ActionCancelled + await confirm_fido( + ctx, + "Import credential", + cred.app_name(), + cred.icon_name(), + [cred.account_name()], + ) if store_resident_credential(cred): return Success(message="Credential added") diff --git a/core/src/apps/webauthn/credential.py b/core/src/apps/webauthn/credential.py index aa7eae6c5..0b066bf6b 100644 --- a/core/src/apps/webauthn/credential.py +++ b/core/src/apps/webauthn/credential.py @@ -71,6 +71,12 @@ class Credential: def app_name(self) -> str: raise NotImplementedError + def icon_name(self) -> str | None: + from . import knownapps + + fido_app = knownapps.by_rp_id_hash(self.rp_id_hash) + return None if fido_app is None else fido_app.icon_name + def account_name(self) -> str | None: return None diff --git a/core/src/apps/webauthn/fido2.py b/core/src/apps/webauthn/fido2.py index f669fba58..c62bf247c 100644 --- a/core/src/apps/webauthn/fido2.py +++ b/core/src/apps/webauthn/fido2.py @@ -5,13 +5,10 @@ from micropython import const from typing import TYPE_CHECKING import storage.device as storage_device -from trezor import config, io, log, loop, utils, workflow +from trezor import config, io, log, loop, utils, wire, workflow from trezor.crypto import hashlib from trezor.crypto.curve import nist256p1 -from trezor.ui.components.common.confirm import Pageable -from trezor.ui.components.common.webauthn import ConfirmInfo from trezor.ui.layouts import show_popup -from trezor.ui.layouts.webauthn import confirm_webauthn from apps.base import set_homescreen from apps.common import cbor @@ -20,7 +17,7 @@ from . import common from .credential import Credential, Fido2Credential if TYPE_CHECKING: - from typing import Any, Callable, Coroutine, Iterable, Iterator + from typing import Any, Awaitable, Callable, Coroutine, Iterable, Iterator from .credential import U2fCredential HID = io.HID @@ -587,6 +584,33 @@ async def verify_user(keepalive_callback: KeepaliveCallback) -> bool: return ret +def _confirm_fido_choose(title: str, credentials: list[Credential]) -> Awaitable[int]: + from trezor.ui.layouts.fido import confirm_fido + from . import knownapps + + assert len(credentials) > 0 + repr_credential = credentials[0] + + if __debug__: + for cred in credentials: + assert cred.rp_id_hash == repr_credential.rp_id_hash + + app_name = repr_credential.app_name() + app = knownapps.by_rp_id_hash(repr_credential.rp_id_hash) + icon_name = None if app is None else app.icon_name + return confirm_fido( + None, title, app_name, icon_name, [c.account_name() for c in credentials] + ) + + +async def _confirm_fido(title: str, credential: Credential) -> bool: + try: + await _confirm_fido_choose(title, [credential]) + return True + except wire.ActionCancelled: + return False + + class State: def __init__(self, cid: int, iface: HID) -> None: self.cid = cid @@ -616,13 +640,11 @@ class State: pass -class U2fState(State, ConfirmInfo): +class U2fState(State): def __init__(self, cid: int, iface: HID, req_data: bytes, cred: Credential) -> None: State.__init__(self, cid, iface) - ConfirmInfo.__init__(self) self._cred = cred self._req_data = req_data - self.load_icon(self._cred.rp_id_hash) def timeout_ms(self) -> int: return _U2F_CONFIRM_TIMEOUT_MS @@ -658,10 +680,7 @@ class U2fConfirmRegister(U2fState): ) return False else: - return await confirm_webauthn(None, self) - - def get_header(self) -> str: - return "U2F Register" + return await _confirm_fido("U2F Register", self._cred) def __eq__(self, other: object) -> bool: return ( @@ -675,11 +694,8 @@ class U2fConfirmAuthenticate(U2fState): def __init__(self, cid: int, iface: HID, req_data: bytes, cred: Credential) -> None: super().__init__(cid, iface, req_data, cred) - def get_header(self) -> str: - return "U2F Authenticate" - async def confirm_dialog(self) -> bool: - return await confirm_webauthn(None, self) + return await _confirm_fido("U2F Authenticate", self._cred) def __eq__(self, other: object) -> bool: return ( @@ -767,7 +783,7 @@ class Fido2Unlock(Fido2State): await send_cmd(self.resp, self.iface) -class Fido2ConfirmMakeCredential(Fido2State, ConfirmInfo): +class Fido2ConfirmMakeCredential(Fido2State): def __init__( self, cid: int, @@ -778,24 +794,13 @@ class Fido2ConfirmMakeCredential(Fido2State, ConfirmInfo): user_verification: bool, ) -> None: Fido2State.__init__(self, cid, iface) - ConfirmInfo.__init__(self) self._client_data_hash = client_data_hash self._cred = cred self._resident = resident self._user_verification = user_verification - self.load_icon(cred.rp_id_hash) - - def get_header(self) -> str: - return "FIDO2 Register" - - def app_name(self) -> str: - return self._cred.app_name() - - def account_name(self) -> str | None: - return self._cred.account_name() async def confirm_dialog(self) -> bool: - if not await confirm_webauthn(None, self): + if not await _confirm_fido("FIDO2 Register", self._cred): return False if self._user_verification: return await verify_user(KeepaliveCallback(self.cid, self.iface)) @@ -839,7 +844,7 @@ class Fido2ConfirmExcluded(Fido2ConfirmMakeCredential): ) -class Fido2ConfirmGetAssertion(Fido2State, ConfirmInfo, Pageable): +class Fido2ConfirmGetAssertion(Fido2State): def __init__( self, cid: int, @@ -851,30 +856,21 @@ class Fido2ConfirmGetAssertion(Fido2State, ConfirmInfo, Pageable): user_verification: bool, ) -> None: Fido2State.__init__(self, cid, iface) - ConfirmInfo.__init__(self) - Pageable.__init__(self) self._client_data_hash = client_data_hash self._creds = creds self._hmac_secret = hmac_secret self._resident = resident self._user_verification = user_verification - self.load_icon(self._creds[0].rp_id_hash) - - def get_header(self) -> str: - return "FIDO2 Authenticate" - - def app_name(self) -> str: - return self._creds[self.page()].app_name() - - def account_name(self) -> str | None: - return self._creds[self.page()].account_name() - - def page_count(self) -> int: - return len(self._creds) + self._selected_cred: Credential | None = None async def confirm_dialog(self) -> bool: - if not await confirm_webauthn(None, self, pageable=self): + # There is a choice from more than one credential. + try: + index = await _confirm_fido_choose("FIDO2 Authenticate", self._creds) + except wire.ActionCancelled: return False + + self._selected_cred = self._creds[index] if self._user_verification: return await verify_user(KeepaliveCallback(self.cid, self.iface)) return True @@ -882,13 +878,13 @@ class Fido2ConfirmGetAssertion(Fido2State, ConfirmInfo, Pageable): async def on_confirm(self) -> None: cid = self.cid # local_cache_attribute - cred = self._creds[self.page()] + assert self._selected_cred is not None try: send_cmd_sync(cmd_keepalive(cid, _KEEPALIVE_STATUS_PROCESSING), self.iface) response_data = cbor_get_assertion_sign( self._client_data_hash, - cred.rp_id_hash, - cred, + self._selected_cred.rp_id_hash, + self._selected_cred, self._hmac_secret, self._resident, True, @@ -954,9 +950,9 @@ class Fido2ConfirmReset(Fido2State): super().__init__(cid, iface) async def confirm_dialog(self) -> bool: - from trezor.ui.layouts.webauthn import confirm_webauthn_reset + from trezor.ui.layouts.fido import confirm_fido_reset - return await confirm_webauthn_reset() + return await confirm_fido_reset() async def on_confirm(self) -> None: import storage.resident_credentials diff --git a/core/src/apps/webauthn/knownapps.py b/core/src/apps/webauthn/knownapps.py index 72c62b6cb..096186cf9 100644 --- a/core/src/apps/webauthn/knownapps.py +++ b/core/src/apps/webauthn/knownapps.py @@ -9,12 +9,12 @@ class FIDOApp: def __init__( self, label: str, - icon: str | None, + icon_name: str | None, use_sign_count: bool | None, use_self_attestation: bool | None, ) -> None: self.label = label - self.icon = icon + self.icon_name = icon_name self.use_sign_count = use_sign_count self.use_self_attestation = use_self_attestation @@ -25,7 +25,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # U2F key for Amazon Web Services return FIDOApp( "aws.amazon.com", # label - "apps/webauthn/res/icon_aws.toif", # icon + "aws", # icon_name None, # use_sign_count None, # use_self_attestation ) @@ -33,7 +33,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # WebAuthn key for Binance return FIDOApp( "www.binance.com", # label - "apps/webauthn/res/icon_binance.toif", # icon + "binance", # icon_name False, # use_sign_count True, # use_self_attestation ) @@ -41,7 +41,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # WebAuthn key for Binance return FIDOApp( "binance.com", # label - "apps/webauthn/res/icon_binance.toif", # icon + "binance", # icon_name False, # use_sign_count True, # use_self_attestation ) @@ -49,7 +49,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # U2F key for Bitbucket return FIDOApp( "bitbucket.org", # label - "apps/webauthn/res/icon_bitbucket.toif", # icon + "bitbucket", # icon_name None, # use_sign_count None, # use_self_attestation ) @@ -57,7 +57,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # U2F key for Bitfinex return FIDOApp( "www.bitfinex.com", # label - "apps/webauthn/res/icon_bitfinex.toif", # icon + "bitfinex", # icon_name None, # use_sign_count None, # use_self_attestation ) @@ -65,7 +65,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # U2F key for Bitwarden return FIDOApp( "vault.bitwarden.com", # label - "apps/webauthn/res/icon_bitwarden.toif", # icon + "bitwarden", # icon_name None, # use_sign_count None, # use_self_attestation ) @@ -73,7 +73,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # WebAuthn key for Cloudflare return FIDOApp( "dash.cloudflare.com", # label - "apps/webauthn/res/icon_cloudflare.toif", # icon + "cloudflare", # icon_name None, # use_sign_count None, # use_self_attestation ) @@ -81,7 +81,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # WebAuthn key for Coinbase return FIDOApp( "coinbase.com", # label - "apps/webauthn/res/icon_coinbase.toif", # icon + "coinbase", # icon_name None, # use_sign_count None, # use_self_attestation ) @@ -89,7 +89,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # U2F key for Dashlane return FIDOApp( "www.dashlane.com", # label - "apps/webauthn/res/icon_dashlane.toif", # icon + "dashlane", # icon_name None, # use_sign_count None, # use_self_attestation ) @@ -97,7 +97,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # U2F key for Dropbox return FIDOApp( "www.dropbox.com", # label - "apps/webauthn/res/icon_dropbox.toif", # icon + "dropbox", # icon_name False, # use_sign_count None, # use_self_attestation ) @@ -105,7 +105,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # WebAuthn key for Dropbox return FIDOApp( "www.dropbox.com", # label - "apps/webauthn/res/icon_dropbox.toif", # icon + "dropbox", # icon_name False, # use_sign_count None, # use_self_attestation ) @@ -113,7 +113,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # U2F key for Duo return FIDOApp( "duosecurity.com", # label - "apps/webauthn/res/icon_duo.toif", # icon + "duo", # icon_name None, # use_sign_count None, # use_self_attestation ) @@ -121,7 +121,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # WebAuthn key for Facebook return FIDOApp( "facebook.com", # label - "apps/webauthn/res/icon_facebook.toif", # icon + "facebook", # icon_name None, # use_sign_count None, # use_self_attestation ) @@ -129,7 +129,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # U2F key for FastMail return FIDOApp( "www.fastmail.com", # label - "apps/webauthn/res/icon_fastmail.toif", # icon + "fastmail", # icon_name None, # use_sign_count None, # use_self_attestation ) @@ -137,7 +137,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # WebAuthn key for FastMail return FIDOApp( "fastmail.com", # label - "apps/webauthn/res/icon_fastmail.toif", # icon + "fastmail", # icon_name None, # use_sign_count None, # use_self_attestation ) @@ -145,7 +145,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # U2F key for Fedora return FIDOApp( "fedoraproject.org", # label - "apps/webauthn/res/icon_fedora.toif", # icon + "fedora", # icon_name None, # use_sign_count None, # use_self_attestation ) @@ -153,7 +153,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # U2F key for Gandi return FIDOApp( "gandi.net", # label - "apps/webauthn/res/icon_gandi.toif", # icon + "gandi", # icon_name False, # use_sign_count None, # use_self_attestation ) @@ -161,7 +161,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # WebAuthn key for Gandi return FIDOApp( "gandi.net", # label - "apps/webauthn/res/icon_gandi.toif", # icon + "gandi", # icon_name False, # use_sign_count None, # use_self_attestation ) @@ -169,7 +169,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # WebAuthn key for Gemini return FIDOApp( "gemini.com", # label - "apps/webauthn/res/icon_gemini.toif", # icon + "gemini", # icon_name False, # use_sign_count True, # use_self_attestation ) @@ -177,7 +177,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # U2F key for GitHub return FIDOApp( "github.com", # label - "apps/webauthn/res/icon_github.toif", # icon + "github", # icon_name True, # use_sign_count None, # use_self_attestation ) @@ -185,7 +185,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # WebAuthn key for GitHub return FIDOApp( "github.com", # label - "apps/webauthn/res/icon_github.toif", # icon + "github", # icon_name True, # use_sign_count None, # use_self_attestation ) @@ -193,7 +193,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # U2F key for GitLab return FIDOApp( "gitlab.com", # label - "apps/webauthn/res/icon_gitlab.toif", # icon + "gitlab", # icon_name None, # use_sign_count None, # use_self_attestation ) @@ -201,7 +201,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # U2F key for Google return FIDOApp( "google.com", # label - "apps/webauthn/res/icon_google.toif", # icon + "google", # icon_name None, # use_sign_count None, # use_self_attestation ) @@ -209,7 +209,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # WebAuthn key for Google return FIDOApp( "google.com", # label - "apps/webauthn/res/icon_google.toif", # icon + "google", # icon_name None, # use_sign_count None, # use_self_attestation ) @@ -217,7 +217,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # WebAuthn key for Invity return FIDOApp( "invity.io", # label - "apps/webauthn/res/icon_invity.toif", # icon + "invity", # icon_name None, # use_sign_count None, # use_self_attestation ) @@ -225,7 +225,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # U2F key for Keeper return FIDOApp( "keepersecurity.com", # label - "apps/webauthn/res/icon_keeper.toif", # icon + "keeper", # icon_name None, # use_sign_count None, # use_self_attestation ) @@ -233,7 +233,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # U2F key for Keeper return FIDOApp( "keepersecurity.eu", # label - "apps/webauthn/res/icon_keeper.toif", # icon + "keeper", # icon_name None, # use_sign_count None, # use_self_attestation ) @@ -241,7 +241,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # WebAuthn key for Kraken return FIDOApp( "kraken.com", # label - "apps/webauthn/res/icon_kraken.toif", # icon + "kraken", # icon_name None, # use_sign_count None, # use_self_attestation ) @@ -249,7 +249,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # WebAuthn key for login.gov return FIDOApp( "secure.login.gov", # label - "apps/webauthn/res/icon_login.gov.toif", # icon + "login.gov", # icon_name False, # use_sign_count None, # use_self_attestation ) @@ -257,7 +257,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # WebAuthn key for Microsoft return FIDOApp( "login.microsoft.com", # label - "apps/webauthn/res/icon_microsoft.toif", # icon + "microsoft", # icon_name False, # use_sign_count False, # use_self_attestation ) @@ -265,7 +265,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # WebAuthn key for mojeID return FIDOApp( "mojeid.cz", # label - "apps/webauthn/res/icon_mojeid.toif", # icon + "mojeid", # icon_name None, # use_sign_count None, # use_self_attestation ) @@ -273,7 +273,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # WebAuthn key for Namecheap return FIDOApp( "www.namecheap.com", # label - "apps/webauthn/res/icon_namecheap.toif", # icon + "namecheap", # icon_name None, # use_sign_count None, # use_self_attestation ) @@ -281,7 +281,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # WebAuthn key for Proton return FIDOApp( "proton.me", # label - "apps/webauthn/res/icon_proton.toif", # icon + "proton", # icon_name None, # use_sign_count None, # use_self_attestation ) @@ -289,7 +289,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # U2F key for Slush Pool return FIDOApp( "slushpool.com", # label - "apps/webauthn/res/icon_slushpool.toif", # icon + "slushpool", # icon_name None, # use_sign_count None, # use_self_attestation ) @@ -297,7 +297,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # U2F key for Slush Pool return FIDOApp( "slushpool.com", # label - "apps/webauthn/res/icon_slushpool.toif", # icon + "slushpool", # icon_name None, # use_sign_count None, # use_self_attestation ) @@ -305,7 +305,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # U2F key for Stripe return FIDOApp( "stripe.com", # label - "apps/webauthn/res/icon_stripe.toif", # icon + "stripe", # icon_name None, # use_sign_count None, # use_self_attestation ) @@ -313,7 +313,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # U2F key for Tutanota return FIDOApp( "tutanota.com", # label - "apps/webauthn/res/icon_tutanota.toif", # icon + "tutanota", # icon_name None, # use_sign_count None, # use_self_attestation ) @@ -321,7 +321,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # U2F key for u2f.bin.coffee return FIDOApp( "u2f.bin.coffee", # label - None, # icon + None, # icon_name None, # use_sign_count None, # use_self_attestation ) @@ -329,7 +329,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # WebAuthn key for webauthn.bin.coffee return FIDOApp( "webauthn.bin.coffee", # label - None, # icon + None, # icon_name None, # use_sign_count None, # use_self_attestation ) @@ -337,7 +337,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # WebAuthn key for WebAuthn.io return FIDOApp( "webauthn.io", # label - None, # icon + None, # icon_name None, # use_sign_count None, # use_self_attestation ) @@ -345,7 +345,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # WebAuthn key for WebAuthn.me return FIDOApp( "webauthn.me", # label - None, # icon + None, # icon_name None, # use_sign_count None, # use_self_attestation ) @@ -353,7 +353,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # WebAuthn key for demo.yubico.com return FIDOApp( "demo.yubico.com", # label - None, # icon + None, # icon_name None, # use_sign_count None, # use_self_attestation ) diff --git a/core/src/apps/webauthn/knownapps.py.mako b/core/src/apps/webauthn/knownapps.py.mako index 99102dfdf..93cf9e8f5 100644 --- a/core/src/apps/webauthn/knownapps.py.mako +++ b/core/src/apps/webauthn/knownapps.py.mako @@ -9,12 +9,12 @@ class FIDOApp: def __init__( self, label: str, - icon: str | None, + icon_name: str | None, use_sign_count: bool | None, use_self_attestation: bool | None, ) -> None: self.label = label - self.icon = icon + self.icon_name = icon_name self.use_sign_count = use_sign_count self.use_self_attestation = use_self_attestation @@ -30,9 +30,9 @@ for app in fido: rp_id_hash = sha256(origin.encode()).digest() fido_entries.append((origin, rp_id_hash, "WebAuthn", app)) if app.icon is not None: - app.icon_res = f"apps/webauthn/res/icon_{app.key}.toif" + app.icon_name = app.key else: - app.icon_res = None + app.icon_name = None %>\ # fmt: off def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: @@ -41,7 +41,7 @@ def by_rp_id_hash(rp_id_hash: bytes) -> FIDOApp | None: # ${type} key for ${app.name} return FIDOApp( ${black_repr(label)}, # label - ${black_repr(app.icon_res)}, # icon + ${black_repr(app.icon_name)}, # icon_name ${black_repr(app.use_sign_count)}, # use_sign_count ${black_repr(app.use_self_attestation)}, # use_self_attestation ) diff --git a/core/src/apps/webauthn/remove_resident_credential.py b/core/src/apps/webauthn/remove_resident_credential.py index dc6c380f0..6a5714897 100644 --- a/core/src/apps/webauthn/remove_resident_credential.py +++ b/core/src/apps/webauthn/remove_resident_credential.py @@ -1,29 +1,10 @@ from typing import TYPE_CHECKING -from trezor.ui.components.common.webauthn import ConfirmInfo - if TYPE_CHECKING: from trezor.messages import WebAuthnRemoveResidentCredential, Success - from .credential import Fido2Credential from trezor.wire import Context -class ConfirmRemoveCredential(ConfirmInfo): - def __init__(self, cred: Fido2Credential): - super().__init__() - 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) -> str | None: - return self._cred.account_name() - - async def remove_resident_credential( ctx: Context, msg: WebAuthnRemoveResidentCredential ) -> Success: @@ -31,7 +12,7 @@ async def remove_resident_credential( import storage.resident_credentials from trezor import wire from trezor.messages import Success - from trezor.ui.layouts.webauthn import confirm_webauthn + from trezor.ui.layouts.fido import confirm_fido from .resident_credentials import get_resident_credential if not storage.device.is_initialized(): @@ -43,8 +24,13 @@ async def remove_resident_credential( if cred is None: raise wire.ProcessError("Invalid credential index.") - if not await confirm_webauthn(ctx, ConfirmRemoveCredential(cred)): - raise wire.ActionCancelled + await confirm_fido( + ctx, + "Remove credential", + cred.app_name(), + cred.icon_name(), + [cred.account_name()], + ) assert cred.index is not None storage.resident_credentials.delete(cred.index) diff --git a/core/src/trezor/ui/components/common/webauthn.py b/core/src/trezor/ui/components/common/webauthn.py deleted file mode 100644 index 35b7feae6..000000000 --- a/core/src/trezor/ui/components/common/webauthn.py +++ /dev/null @@ -1,25 +0,0 @@ -DEFAULT_ICON = "apps/webauthn/res/icon_webauthn.toif" - - -class ConfirmInfo: - def __init__(self) -> None: - self.app_icon: bytes | None = None - - def get_header(self) -> str: - raise NotImplementedError - - def app_name(self) -> str: - raise NotImplementedError - - def account_name(self) -> str | None: - return None - - def load_icon(self, rp_id_hash: bytes) -> None: - from trezor import res - from apps.webauthn import knownapps - - fido_app = knownapps.by_rp_id_hash(rp_id_hash) - if fido_app is not None and fido_app.icon is not None: - self.app_icon = res.load(fido_app.icon) - else: - self.app_icon = res.load(DEFAULT_ICON) diff --git a/core/src/trezor/ui/layouts/fido.py b/core/src/trezor/ui/layouts/fido.py new file mode 100644 index 000000000..49d21b2de --- /dev/null +++ b/core/src/trezor/ui/layouts/fido.py @@ -0,0 +1 @@ +from .tt_v2.fido import * # noqa: F401,F403 diff --git a/core/src/trezor/ui/layouts/tt_v2/fido.py b/core/src/trezor/ui/layouts/tt_v2/fido.py new file mode 100644 index 000000000..0e5067289 --- /dev/null +++ b/core/src/trezor/ui/layouts/tt_v2/fido.py @@ -0,0 +1,96 @@ +from typing import TYPE_CHECKING + +from trezor.enums import ButtonRequestType + +import trezorui2 + +from ..common import interact +from . import _RustLayout + +if TYPE_CHECKING: + from trezor.loop import AwaitableTask + from trezor.wire import GenericContext + + +if __debug__: + from trezor import io + from ... import Result + + class _RustFidoLayoutImpl(_RustLayout): + def create_tasks(self) -> tuple[AwaitableTask, ...]: + return ( + self.handle_timers(), + self.handle_input_and_rendering(), + self.handle_swipe(), + self.handle_debug_confirm(), + ) + + async def handle_debug_confirm(self) -> None: + from apps.debug import confirm_signal + + try: + await confirm_signal() + except Result as r: + if r.value is not trezorui2.CONFIRMED: + raise + else: + return + + for event, x, y in ( + (io.TOUCH_START, 220, 220), + (io.TOUCH_END, 220, 220), + ): + msg = self.layout.touch_event(event, x, y) + self.layout.paint() + if msg is not None: + raise Result(msg) + + _RustFidoLayout = _RustFidoLayoutImpl + +else: + _RustFidoLayout = _RustLayout + + +async def confirm_fido( + ctx: GenericContext | None, + header: str, + app_name: str, + icon_name: str | None, + accounts: list[str | None], +) -> int: + """Webauthn confirmation for one or more credentials.""" + confirm = _RustFidoLayout( + trezorui2.confirm_fido( + title=header.upper(), + app_name=app_name, + icon_name=icon_name, + accounts=accounts, + ) + ) + + if ctx is None: + result = await confirm + else: + result = await interact(ctx, confirm, "confirm_fido", ButtonRequestType.Other) + + # The Rust side returns either an int or `CANCELLED`. We detect the int situation + # and assume cancellation otherwise. + if isinstance(result, int): + return result + + # Late import won't get executed on the happy path. + from trezor.wire import ActionCancelled + + raise ActionCancelled + + +async def confirm_fido_reset() -> bool: + confirm = _RustLayout( + trezorui2.confirm_action( + title="FIDO2 RESET", + action="erase all credentials?", + description="Do you really want to", + reverse=True, + ) + ) + return (await confirm) is trezorui2.CONFIRMED diff --git a/core/src/trezor/ui/layouts/tt_v2/webauthn.py b/core/src/trezor/ui/layouts/tt_v2/webauthn.py deleted file mode 100644 index d70caa09f..000000000 --- a/core/src/trezor/ui/layouts/tt_v2/webauthn.py +++ /dev/null @@ -1,49 +0,0 @@ -from typing import TYPE_CHECKING - -from trezor.enums import ButtonRequestType - -import trezorui2 - -from ...components.common.confirm import is_confirmed -from ...components.common.webauthn import ConfirmInfo -from ..common import interact -from . import _RustLayout - -if TYPE_CHECKING: - from trezor.wire import GenericContext - - Pageable = object - - -async def confirm_webauthn( - ctx: GenericContext | None, - info: ConfirmInfo, - pageable: Pageable | None = None, -) -> bool: - if pageable is not None: - raise NotImplementedError - - confirm = _RustLayout( - trezorui2.confirm_blob( - title=info.get_header().upper(), - data=f"{info.app_name()}\n{info.account_name()}", - ) - ) - - if ctx is None: - return is_confirmed(await confirm) - else: - return is_confirmed( - await interact(ctx, confirm, "confirm_webauthn", ButtonRequestType.Other) - ) - - -async def confirm_webauthn_reset() -> bool: - return is_confirmed( - await _RustLayout( - trezorui2.confirm_blob( - title="FIDO2 RESET", - data="Do you really want to\nerase all credentials?", - ) - ) - ) diff --git a/core/src/trezor/ui/layouts/webauthn.py b/core/src/trezor/ui/layouts/webauthn.py deleted file mode 100644 index 3cd18dfb1..000000000 --- a/core/src/trezor/ui/layouts/webauthn.py +++ /dev/null @@ -1 +0,0 @@ -from .tt_v2.webauthn import * # noqa: F401,F403 diff --git a/core/tools/build_icons.py b/core/tools/build_icons.py index 15c0fc635..e4f4f478a 100755 --- a/core/tools/build_icons.py +++ b/core/tools/build_icons.py @@ -12,7 +12,9 @@ HERE = Path(__file__).resolve().parent ROOT = HERE.parent.parent ICON_SIZE = (64, 64) -DESTINATION = ROOT / "core" / "src" / "apps" / "webauthn" / "res" +DESTINATION = ( + ROOT / "core" / "embed" / "rust" / "src" / "ui" / "model_tt" / "res" / "fido" +) EXCLUDE = {"icon_webauthn"} # insert ../../common/tools to sys.path, so that we can import coin_info diff --git a/core/tools/build_templates b/core/tools/build_templates index 41a54210c..e6e9af2e4 100755 --- a/core/tools/build_templates +++ b/core/tools/build_templates @@ -4,7 +4,8 @@ set -e CWD=`dirname "$0"` RENDER="$CWD/../vendor/trezor-common/tools/cointool.py render" -FIND_TEMPLATES="find $CWD/../src -name *.mako -not -name _proto*" +# Search both in `core/src` and `core/embed` +FIND_TEMPLATES="find $CWD/.. -name *.mako -not -name _proto*" check_results() { CHECK_FAIL=0 diff --git a/tests/ui_tests/fixtures.json b/tests/ui_tests/fixtures.json index 257c3b47f..acf09074a 100644 --- a/tests/ui_tests/fixtures.json +++ b/tests/ui_tests/fixtures.json @@ -1667,7 +1667,7 @@ "TT_tezos-test_sign_tx.py::test_tezos_smart_contract_delegation": "683a47bb078a50bf08a16446c90fab70a68a3cec8689abca494fd99b31da2ea6", "TT_tezos-test_sign_tx.py::test_tezos_smart_contract_transfer": "295eac0f3667c0af660575fedefa98cae89ec0882892dc8769adf61751dc12fb", "TT_tezos-test_sign_tx.py::test_tezos_smart_contract_transfer_to_contract": "3e781bd83c0019ba51b9034b8bc53c5a09ea9c5c6fe73570004d46a061ba43ea", -"TT_webauthn-test_msg_webauthn.py::test_add_remove": "7ac75b93373119b69a38c4bacfa51579d7052abc8b10f74f937a78e9a4afb5d9", +"TT_webauthn-test_msg_webauthn.py::test_add_remove": "943e29255d6738cc6824c2565001c05906c2cd032aa2eb09993782564e890a16", "TT_webauthn-test_u2f_counter.py::test_u2f_counter": "c6a8e270ce726c7693e2ff88f9af57c56f2d3d8b1cc9c04b6f1dc71e13fcb88e", "TT_zcash-test_sign_tx.py::test_external_presigned": "8781b601169bd64c90ee4dd9c517af905e2cf5fe10bdb474116d17f3d633e06a", "TT_zcash-test_sign_tx.py::test_one_two": "003f12c0ff194bde070fdeb6c8663bf322efca094e9787b2304231c25938b715",