diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h index 9533cd6ac3..01bbe6a805 100644 --- a/core/embed/rust/librust_qstr.h +++ b/core/embed/rust/librust_qstr.h @@ -782,6 +782,7 @@ static void _librust_qstrs(void) { MP_QSTR_words__amount; MP_QSTR_words__are_you_sure; MP_QSTR_words__array_of; + MP_QSTR_words__authenticate; MP_QSTR_words__blockhash; MP_QSTR_words__buying; MP_QSTR_words__cancel_and_exit; diff --git a/core/embed/rust/src/translations/generated/translated_string.rs b/core/embed/rust/src/translations/generated/translated_string.rs index 6578807e3c..e317543789 100644 --- a/core/embed/rust/src/translations/generated/translated_string.rs +++ b/core/embed/rust/src/translations/generated/translated_string.rs @@ -499,7 +499,7 @@ pub enum TranslatedString { #[cfg(feature = "universal_fw")] fido__device_not_registered = 301, // "This device is not registered with this application." #[cfg(feature = "universal_fw")] - fido__does_not_belong = 302, // "The credential you are trying to import does\nnot belong to this authenticator." + fido__does_not_belong = 302, // {"Bolt": "The credential you are trying to import does\nnot belong to this authenticator.", "Caesar": "The credential you are trying to import does\nnot belong to this authenticator.", "Delizia": "The credential you are trying to import does\nnot belong to this authenticator.", "Eckhart": "The credential you are trying to import does not belong to this authenticator."} #[cfg(feature = "universal_fw")] fido__erase_credentials = 303, // "erase all credentials?" #[cfg(feature = "universal_fw")] @@ -1419,6 +1419,7 @@ pub enum TranslatedString { send__sign_cancelled = 1005, // "Sign cancelled." words__send = 1006, // "Send" words__wallet = 1007, // "Wallet" + words__authenticate = 1008, // "Authenticate" } impl TranslatedString { @@ -1921,8 +1922,18 @@ impl TranslatedString { #[cfg(feature = "universal_fw")] Self::fido__device_not_registered => "This device is not registered with this application.", #[cfg(feature = "universal_fw")] + #[cfg(feature = "layout_bolt")] Self::fido__does_not_belong => "The credential you are trying to import does\nnot belong to this authenticator.", #[cfg(feature = "universal_fw")] + #[cfg(feature = "layout_caesar")] + Self::fido__does_not_belong => "The credential you are trying to import does\nnot belong to this authenticator.", + #[cfg(feature = "universal_fw")] + #[cfg(feature = "layout_delizia")] + Self::fido__does_not_belong => "The credential you are trying to import does\nnot belong to this authenticator.", + #[cfg(feature = "universal_fw")] + #[cfg(feature = "layout_eckhart")] + Self::fido__does_not_belong => "The credential you are trying to import does not belong to this authenticator.", + #[cfg(feature = "universal_fw")] Self::fido__erase_credentials => "erase all credentials?", #[cfg(feature = "universal_fw")] Self::fido__export_credentials => "Export information about the credentials stored on this device?", @@ -2939,6 +2950,7 @@ impl TranslatedString { Self::send__sign_cancelled => "Sign cancelled.", Self::words__send => "Send", Self::words__wallet => "Wallet", + Self::words__authenticate => "Authenticate", } } @@ -4353,6 +4365,7 @@ impl TranslatedString { Qstr::MP_QSTR_send__sign_cancelled => Some(Self::send__sign_cancelled), Qstr::MP_QSTR_words__send => Some(Self::words__send), Qstr::MP_QSTR_words__wallet => Some(Self::words__wallet), + Qstr::MP_QSTR_words__authenticate => Some(Self::words__authenticate), _ => None, } } diff --git a/core/embed/rust/src/ui/layout_eckhart/firmware/fido.rs b/core/embed/rust/src/ui/layout_eckhart/firmware/fido.rs new file mode 100644 index 0000000000..af08fec383 --- /dev/null +++ b/core/embed/rust/src/ui/layout_eckhart/firmware/fido.rs @@ -0,0 +1,95 @@ +use crate::{ + strutil::TString, + ui::{ + component::{ + image::Image, + paginated::SinglePage, + text::{ + paragraphs::{Paragraph, ParagraphSource, ParagraphVecShort, Paragraphs}, + TextStyle, + }, + Component, Event, EventCtx, LineBreaking, + }, + geometry::{Insets, LinearPlacement, Offset, Rect}, + shape::Renderer, + }, +}; + +use super::super::{firmware::fido_icons::get_fido_icon_data, theme}; + +pub struct FidoCredential TString<'static>> { + app_icon: Option, + text: Paragraphs>, + get_account: F, +} + +impl TString<'static>> FidoCredential { + const ICON_SIZE: i16 = 32; + const SPACING: i16 = 24; + + pub fn new( + icon_name: Option>, + app_name: TString<'static>, + get_account: F, + ) -> Self { + const STYLE: TextStyle = + theme::TEXT_REGULAR.with_line_breaking(LineBreaking::BreakWordsNoHyphen); + let app_icon = get_fido_icon_data(icon_name).map(Image::new); + let text = ParagraphVecShort::from_iter([ + Paragraph::new(&theme::TEXT_MEDIUM_GREY, app_name), + Paragraph::new(&STYLE, (get_account)()), + ]) + .into_paragraphs() + .with_placement(LinearPlacement::vertical()) + .with_spacing(Self::SPACING); + Self { + app_icon, + text, + get_account, + } + } +} + +impl TString<'static>> Component for FidoCredential { + type Msg = (); + + fn place(&mut self, bounds: Rect) -> Rect { + let icon_size = self.app_icon.map_or(Offset::zero(), |i| i.toif.size()); + let (icon_area, text_area) = bounds.split_top(icon_size.y); + let text_area = text_area.inset(Insets::top(Self::SPACING)); + self.text.place(text_area); + // let text_height = self.text.area().height(); + // let vertical_space = bounds.height() - icon_size.y - Self::SPACING - + // text_height; let off = Offset::y(vertical_space / 2); + + // let icon_area = icon_area.with_width(icon_size.x).translate(off); + // let text_area = text_area.with_height(text_height).translate(off); + self.app_icon.place(icon_area); + self.text.place(text_area); + bounds + } + + fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option { + if let Event::Attach(_) = event { + self.text.mutate(|p| p[1].update((self.get_account)())); + ctx.request_paint(); + } + self.app_icon.event(ctx, event); + self.text.event(ctx, event); + None + } + + fn render<'s>(&'s self, target: &mut impl Renderer<'s>) { + self.app_icon.render(target); + self.text.render(target); + } +} + +impl TString<'static>> SinglePage for FidoCredential {} + +#[cfg(feature = "ui_debug")] +impl TString<'static>> crate::trace::Trace for FidoCredential { + fn trace(&self, t: &mut dyn crate::trace::Tracer) { + t.component("FidoCredential"); + } +} diff --git a/core/embed/rust/src/ui/layout_eckhart/firmware/fido_icons.rs b/core/embed/rust/src/ui/layout_eckhart/firmware/fido_icons.rs new file mode 100644 index 0000000000..9248427f0d --- /dev/null +++ b/core/embed/rust/src/ui/layout_eckhart/firmware/fido_icons.rs @@ -0,0 +1,80 @@ +//! generated from webauthn_icons.rs.mako +//! (by running `make templates` in `core`) +//! do not edit manually! + + +use crate::strutil::TString; +use crate::ui::util::include_res; + + +const ICON_APPLE: &[u8] = include_res!("layout_eckhart/res/fido/icon_apple.toif"); +const ICON_AWS: &[u8] = include_res!("layout_eckhart/res/fido/icon_aws.toif"); +const ICON_BINANCE: &[u8] = include_res!("layout_eckhart/res/fido/icon_binance.toif"); +const ICON_BITBUCKET: &[u8] = include_res!("layout_eckhart/res/fido/icon_bitbucket.toif"); +const ICON_BITFINEX: &[u8] = include_res!("layout_eckhart/res/fido/icon_bitfinex.toif"); +const ICON_BITWARDEN: &[u8] = include_res!("layout_eckhart/res/fido/icon_bitwarden.toif"); +const ICON_CLOUDFLARE: &[u8] = include_res!("layout_eckhart/res/fido/icon_cloudflare.toif"); +const ICON_COINBASE: &[u8] = include_res!("layout_eckhart/res/fido/icon_coinbase.toif"); +const ICON_DASHLANE: &[u8] = include_res!("layout_eckhart/res/fido/icon_dashlane.toif"); +const ICON_DROPBOX: &[u8] = include_res!("layout_eckhart/res/fido/icon_dropbox.toif"); +const ICON_DUO: &[u8] = include_res!("layout_eckhart/res/fido/icon_duo.toif"); +const ICON_FACEBOOK: &[u8] = include_res!("layout_eckhart/res/fido/icon_facebook.toif"); +const ICON_FASTMAIL: &[u8] = include_res!("layout_eckhart/res/fido/icon_fastmail.toif"); +const ICON_FEDORA: &[u8] = include_res!("layout_eckhart/res/fido/icon_fedora.toif"); +const ICON_GANDI: &[u8] = include_res!("layout_eckhart/res/fido/icon_gandi.toif"); +const ICON_GEMINI: &[u8] = include_res!("layout_eckhart/res/fido/icon_gemini.toif"); +const ICON_GITHUB: &[u8] = include_res!("layout_eckhart/res/fido/icon_github.toif"); +const ICON_GITLAB: &[u8] = include_res!("layout_eckhart/res/fido/icon_gitlab.toif"); +const ICON_GOOGLE: &[u8] = include_res!("layout_eckhart/res/fido/icon_google.toif"); +const ICON_INVITY: &[u8] = include_res!("layout_eckhart/res/fido/icon_invity.toif"); +const ICON_KEEPER: &[u8] = include_res!("layout_eckhart/res/fido/icon_keeper.toif"); +const ICON_KRAKEN: &[u8] = include_res!("layout_eckhart/res/fido/icon_kraken.toif"); +const ICON_LOGIN_GOV: &[u8] = include_res!("layout_eckhart/res/fido/icon_login.gov.toif"); +const ICON_MICROSOFT: &[u8] = include_res!("layout_eckhart/res/fido/icon_microsoft.toif"); +const ICON_MOJEID: &[u8] = include_res!("layout_eckhart/res/fido/icon_mojeid.toif"); +const ICON_NAMECHEAP: &[u8] = include_res!("layout_eckhart/res/fido/icon_namecheap.toif"); +const ICON_PROTON: &[u8] = include_res!("layout_eckhart/res/fido/icon_proton.toif"); +const ICON_SLUSHPOOL: &[u8] = include_res!("layout_eckhart/res/fido/icon_slushpool.toif"); +const ICON_STRIPE: &[u8] = include_res!("layout_eckhart/res/fido/icon_stripe.toif"); +const ICON_TUTANOTA: &[u8] = include_res!("layout_eckhart/res/fido/icon_tutanota.toif"); + +/// Translates icon name into its data. +pub fn get_fido_icon_data(icon_name: Option>) -> Option< &'static [u8]> { + if let Some(icon_name) = icon_name { + icon_name.map(|c| match c { + "apple" => Some(ICON_APPLE), + "aws" => Some(ICON_AWS), + "binance" => Some(ICON_BINANCE), + "bitbucket" => Some(ICON_BITBUCKET), + "bitfinex" => Some(ICON_BITFINEX), + "bitwarden" => Some(ICON_BITWARDEN), + "cloudflare" => Some(ICON_CLOUDFLARE), + "coinbase" => Some(ICON_COINBASE), + "dashlane" => Some(ICON_DASHLANE), + "dropbox" => Some(ICON_DROPBOX), + "duo" => Some(ICON_DUO), + "facebook" => Some(ICON_FACEBOOK), + "fastmail" => Some(ICON_FASTMAIL), + "fedora" => Some(ICON_FEDORA), + "gandi" => Some(ICON_GANDI), + "gemini" => Some(ICON_GEMINI), + "github" => Some(ICON_GITHUB), + "gitlab" => Some(ICON_GITLAB), + "google" => Some(ICON_GOOGLE), + "invity" => Some(ICON_INVITY), + "keeper" => Some(ICON_KEEPER), + "kraken" => Some(ICON_KRAKEN), + "login.gov" => Some(ICON_LOGIN_GOV), + "microsoft" => Some(ICON_MICROSOFT), + "mojeid" => Some(ICON_MOJEID), + "namecheap" => Some(ICON_NAMECHEAP), + "proton" => Some(ICON_PROTON), + "slushpool" => Some(ICON_SLUSHPOOL), + "stripe" => Some(ICON_STRIPE), + "tutanota" => Some(ICON_TUTANOTA), + _ => None, + }) + } else { + None + } +} diff --git a/core/embed/rust/src/ui/layout_eckhart/firmware/fido_icons.rs.mako b/core/embed/rust/src/ui/layout_eckhart/firmware/fido_icons.rs.mako new file mode 100644 index 0000000000..607c9896d7 --- /dev/null +++ b/core/embed/rust/src/ui/layout_eckhart/firmware/fido_icons.rs.mako @@ -0,0 +1,35 @@ +//! generated from webauthn_icons.rs.mako +//! (by running `make templates` in `core`) +//! do not edit manually! + + +use crate::strutil::TString; +use crate::ui::util::include_res; + +<% +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!("layout_eckhart/res/fido/icon_${icon_name}.toif"); +% endfor + +/// Translates icon name into its data. +pub fn get_fido_icon_data(icon_name: Option>) -> Option< &'static [u8]> { + if let Some(icon_name) = icon_name { + icon_name.map(|c| match c { +% for icon_name, var_name in icons: + "${icon_name}" => Some(ICON_${var_name}), +% endfor + _ => None, + }) + } else { + None + } +} diff --git a/core/embed/rust/src/ui/layout_eckhart/firmware/mod.rs b/core/embed/rust/src/ui/layout_eckhart/firmware/mod.rs index 9c575f8341..ac5d30d48f 100644 --- a/core/embed/rust/src/ui/layout_eckhart/firmware/mod.rs +++ b/core/embed/rust/src/ui/layout_eckhart/firmware/mod.rs @@ -2,6 +2,9 @@ mod action_bar; mod brightness_screen; mod confirm_homescreen; mod device_menu_screen; +mod fido; +#[rustfmt::skip] +mod fido_icons; mod header; mod hint; mod hold_to_confirm; @@ -19,6 +22,7 @@ pub use action_bar::{ActionBar, ActionBarMsg}; pub use brightness_screen::SetBrightnessScreen; pub use confirm_homescreen::{ConfirmHomescreen, ConfirmHomescreenMsg}; pub use device_menu_screen::{DeviceMenuMsg, DeviceMenuScreen}; +pub use fido::FidoCredential; pub use header::{Header, HeaderMsg}; pub use hint::Hint; pub use hold_to_confirm::HoldToConfirmAnim; diff --git a/core/embed/rust/src/ui/layout_eckhart/flow/confirm_fido.rs b/core/embed/rust/src/ui/layout_eckhart/flow/confirm_fido.rs new file mode 100644 index 0000000000..86ce96d803 --- /dev/null +++ b/core/embed/rust/src/ui/layout_eckhart/flow/confirm_fido.rs @@ -0,0 +1,168 @@ +use crate::{ + error, + micropython::{gc::Gc, list::List}, + strutil::TString, + translations::TR, + ui::{ + component::{ + base::ComponentExt, + text::paragraphs::{Paragraph, ParagraphSource}, + }, + flow::{ + base::{Decision, DecisionBuilder as _}, + FlowController, FlowMsg, SwipeFlow, + }, + geometry::{Direction, LinearPlacement}, + }, +}; + +use super::super::{ + component::Button, + firmware::{ + ActionBar, FidoCredential, Header, TextScreen, TextScreenMsg, VerticalMenu, + VerticalMenuScreen, VerticalMenuScreenMsg, + }, + theme, +}; + +use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; + +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum ConfirmFido { + Intro, + ChooseCredential, + Authenticate, + Menu, +} + +static CRED_SELECTED: AtomicUsize = AtomicUsize::new(0); +static SINGLE_CRED: AtomicBool = AtomicBool::new(false); +const EXTRA_PADDING: i16 = 6; + +impl FlowController for ConfirmFido { + #[inline] + fn index(&'static self) -> usize { + *self as usize + } + + fn handle_swipe(&'static self, _direction: Direction) -> Decision { + self.do_nothing() + } + + fn handle_event(&'static self, msg: FlowMsg) -> Decision { + match (self, msg) { + (Self::Intro, FlowMsg::Confirmed) => Self::ChooseCredential.goto(), + (Self::ChooseCredential, FlowMsg::Choice(i)) => { + CRED_SELECTED.store(i, Ordering::Relaxed); + Self::Authenticate.goto() + } + (_, FlowMsg::Info) => Self::Menu.goto(), + (Self::Authenticate, FlowMsg::Cancelled) => Self::ChooseCredential.goto(), + (Self::Authenticate, FlowMsg::Confirmed) => { + self.return_msg(FlowMsg::Choice(CRED_SELECTED.load(Ordering::Relaxed))) + } + (Self::Menu, FlowMsg::Choice(0)) => self.return_msg(FlowMsg::Cancelled), + (Self::Menu, FlowMsg::Cancelled) => { + if single_cred() { + Self::Authenticate.goto() + } else { + Self::Intro.goto() + } + } + _ => self.do_nothing(), + } + } +} + +fn single_cred() -> bool { + SINGLE_CRED.load(Ordering::Relaxed) +} + +pub fn new_confirm_fido( + title: TString<'static>, + app_name: TString<'static>, + icon_name: Option>, + accounts: Gc, +) -> Result { + let num_accounts = accounts.len(); + SINGLE_CRED.store(num_accounts <= 1, Ordering::Relaxed); + CRED_SELECTED.store(0, Ordering::Relaxed); + + let content_intro = TextScreen::new( + Paragraph::new::(&theme::TEXT_REGULAR, TR::fido__select_intro.into()) + .into_paragraphs() + .with_placement(LinearPlacement::vertical()), + ) + .with_header(Header::new(title).with_menu_button()) + .with_action_bar(ActionBar::new_single(Button::with_text( + TR::buttons__continue.into(), + ))) + .map(|msg| match msg { + TextScreenMsg::Confirmed => Some(FlowMsg::Confirmed), + TextScreenMsg::Menu => Some(FlowMsg::Info), + _ => None, + }); + + let mut credentials = VerticalMenu::empty(); + for i in 0..num_accounts { + let account = unwrap!(accounts.get(i)); + let label = account + .try_into() + .unwrap_or_else(|_| TString::from_str("-")); + credentials = credentials.item(Button::with_text(label)); + } + let content_choose_credential = VerticalMenuScreen::new(credentials) + .with_header(Header::new(TR::fido__title_select_credential.into())) + .map(|msg| match msg { + VerticalMenuScreenMsg::Selected(i) => Some(FlowMsg::Choice(i)), + VerticalMenuScreenMsg::Close => Some(FlowMsg::Cancelled), + _ => None, + }); + + let get_account = move || { + let current = CRED_SELECTED.load(Ordering::Relaxed); + let account = unwrap!(accounts.get(current)); + account.try_into().unwrap_or_else(|_| TString::from_str("")) + }; + + let mut auth_header = Header::new(TR::fido__title_credential_details.into()); + auth_header = if single_cred() { + auth_header.with_menu_button() + } else { + auth_header.with_close_button() + }; + + let content_authenticate = + TextScreen::new(FidoCredential::new(icon_name, app_name, get_account)) + .with_header(auth_header) + .with_action_bar(ActionBar::new_single( + Button::with_text(TR::words__authenticate.into()).styled(theme::button_confirm()), + )) + .map(|msg| match msg { + TextScreenMsg::Confirmed => Some(FlowMsg::Confirmed), + TextScreenMsg::Menu => Some(FlowMsg::Info), + _ => None, + }); + + let content_menu = VerticalMenuScreen::new(VerticalMenu::empty().item( + Button::with_text(TR::buttons__cancel.into()).styled(theme::menu_item_title_orange()), + )) + .with_header(Header::new(TR::fido__title_authenticate.into()).with_close_button()) + .map(|msg| match msg { + VerticalMenuScreenMsg::Selected(0) => Some(FlowMsg::Choice(0)), + VerticalMenuScreenMsg::Menu => Some(FlowMsg::Info), + _ => None, + }); + + let initial_page = if single_cred() { + &ConfirmFido::Authenticate + } else { + &ConfirmFido::Intro + }; + let mut flow = SwipeFlow::new(initial_page)?; + flow.add_page(&ConfirmFido::Intro, content_intro)? + .add_page(&ConfirmFido::ChooseCredential, content_choose_credential)? + .add_page(&ConfirmFido::Authenticate, content_authenticate)? + .add_page(&ConfirmFido::Menu, content_menu)?; + Ok(flow) +} diff --git a/core/embed/rust/src/ui/layout_eckhart/flow/mod.rs b/core/embed/rust/src/ui/layout_eckhart/flow/mod.rs index aea7c2b349..f05a1c6d73 100644 --- a/core/embed/rust/src/ui/layout_eckhart/flow/mod.rs +++ b/core/embed/rust/src/ui/layout_eckhart/flow/mod.rs @@ -1,3 +1,4 @@ +pub mod confirm_fido; pub mod confirm_output; pub mod confirm_reset; pub mod confirm_set_new_pin; @@ -9,6 +10,7 @@ pub mod request_passphrase; pub mod show_danger; pub mod show_share_words; +pub use confirm_fido::new_confirm_fido; pub use confirm_output::new_confirm_output; pub use confirm_reset::new_confirm_reset; pub use confirm_set_new_pin::new_set_new_pin; diff --git a/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_apple.toif b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_apple.toif new file mode 100644 index 0000000000..8c188f3203 Binary files /dev/null and b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_apple.toif differ diff --git a/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_aws.toif b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_aws.toif new file mode 100644 index 0000000000..b429a082eb Binary files /dev/null and b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_aws.toif differ diff --git a/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_binance.toif b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_binance.toif new file mode 100644 index 0000000000..af00c7d0c9 Binary files /dev/null and b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_binance.toif differ diff --git a/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_bitbucket.toif b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_bitbucket.toif new file mode 100644 index 0000000000..1cce809928 Binary files /dev/null and b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_bitbucket.toif differ diff --git a/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_bitfinex.toif b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_bitfinex.toif new file mode 100644 index 0000000000..9a4d7732ef Binary files /dev/null and b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_bitfinex.toif differ diff --git a/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_bitwarden.toif b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_bitwarden.toif new file mode 100644 index 0000000000..db3e51d6f6 Binary files /dev/null and b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_bitwarden.toif differ diff --git a/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_cloudflare.toif b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_cloudflare.toif new file mode 100644 index 0000000000..b15eabdbeb Binary files /dev/null and b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_cloudflare.toif differ diff --git a/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_coinbase.toif b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_coinbase.toif new file mode 100644 index 0000000000..2e4a566690 Binary files /dev/null and b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_coinbase.toif differ diff --git a/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_dashlane.toif b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_dashlane.toif new file mode 100644 index 0000000000..d4d024276e Binary files /dev/null and b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_dashlane.toif differ diff --git a/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_dropbox.toif b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_dropbox.toif new file mode 100644 index 0000000000..0d27802443 Binary files /dev/null and b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_dropbox.toif differ diff --git a/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_duo.toif b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_duo.toif new file mode 100644 index 0000000000..25d4a42e34 Binary files /dev/null and b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_duo.toif differ diff --git a/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_facebook.toif b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_facebook.toif new file mode 100644 index 0000000000..966466f6cb Binary files /dev/null and b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_facebook.toif differ diff --git a/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_fastmail.toif b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_fastmail.toif new file mode 100644 index 0000000000..11eda901fc Binary files /dev/null and b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_fastmail.toif differ diff --git a/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_fedora.toif b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_fedora.toif new file mode 100644 index 0000000000..fb05ae9681 Binary files /dev/null and b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_fedora.toif differ diff --git a/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_gandi.toif b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_gandi.toif new file mode 100644 index 0000000000..def0c44fbc Binary files /dev/null and b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_gandi.toif differ diff --git a/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_gemini.toif b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_gemini.toif new file mode 100644 index 0000000000..3006a28fba Binary files /dev/null and b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_gemini.toif differ diff --git a/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_github.toif b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_github.toif new file mode 100644 index 0000000000..d95a79db0c Binary files /dev/null and b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_github.toif differ diff --git a/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_gitlab.toif b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_gitlab.toif new file mode 100644 index 0000000000..143ce303f9 Binary files /dev/null and b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_gitlab.toif differ diff --git a/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_google.toif b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_google.toif new file mode 100644 index 0000000000..3ae8dc4910 Binary files /dev/null and b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_google.toif differ diff --git a/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_invity.toif b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_invity.toif new file mode 100644 index 0000000000..e268c10aa6 Binary files /dev/null and b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_invity.toif differ diff --git a/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_keeper.toif b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_keeper.toif new file mode 100644 index 0000000000..23479ed1ee Binary files /dev/null and b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_keeper.toif differ diff --git a/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_kraken.toif b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_kraken.toif new file mode 100644 index 0000000000..b1e1ae2bf4 Binary files /dev/null and b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_kraken.toif differ diff --git a/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_login.gov.toif b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_login.gov.toif new file mode 100644 index 0000000000..573ca214db Binary files /dev/null and b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_login.gov.toif differ diff --git a/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_microsoft.toif b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_microsoft.toif new file mode 100644 index 0000000000..363741cf72 Binary files /dev/null and b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_microsoft.toif differ diff --git a/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_mojeid.toif b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_mojeid.toif new file mode 100644 index 0000000000..74a13d1c30 Binary files /dev/null and b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_mojeid.toif differ diff --git a/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_namecheap.toif b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_namecheap.toif new file mode 100644 index 0000000000..d5a4d0f53c Binary files /dev/null and b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_namecheap.toif differ diff --git a/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_proton.toif b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_proton.toif new file mode 100644 index 0000000000..7e1a8c58e5 Binary files /dev/null and b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_proton.toif differ diff --git a/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_slushpool.toif b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_slushpool.toif new file mode 100644 index 0000000000..1fb0a57fdd Binary files /dev/null and b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_slushpool.toif differ diff --git a/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_stripe.toif b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_stripe.toif new file mode 100644 index 0000000000..e86d66eb09 Binary files /dev/null and b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_stripe.toif differ diff --git a/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_tutanota.toif b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_tutanota.toif new file mode 100644 index 0000000000..5f42ae2ca3 Binary files /dev/null and b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_tutanota.toif differ diff --git a/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_webauthn.toif b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_webauthn.toif new file mode 100644 index 0000000000..58817cf1dc Binary files /dev/null and b/core/embed/rust/src/ui/layout_eckhart/res/fido/icon_webauthn.toif differ diff --git a/core/embed/rust/src/ui/layout_eckhart/theme/firmware.rs b/core/embed/rust/src/ui/layout_eckhart/theme/firmware.rs index 83e0cb30b2..f6420588cd 100644 --- a/core/embed/rust/src/ui/layout_eckhart/theme/firmware.rs +++ b/core/embed/rust/src/ui/layout_eckhart/theme/firmware.rs @@ -53,6 +53,10 @@ pub const TEXT_MEDIUM: TextStyle = TextStyle::new( GREY_LIGHT, GREY_LIGHT, ); + +pub const TEXT_MEDIUM_GREY: TextStyle = + TextStyle::new(fonts::FONT_SATOSHI_MEDIUM_26, GREY, BG, GREY, GREY); + /// TT Satoshi Regular - 22 (Screen title, Hint, PageCounter, Secondary info) /// with negative line spacing to make it more compact pub const TEXT_SMALL: TextStyle = diff --git a/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs b/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs index 6e9f5fd43c..214b05d8e6 100644 --- a/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs +++ b/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs @@ -158,13 +158,13 @@ impl FirmwareUI for UIEckhart { } fn confirm_fido( - _title: TString<'static>, - _app_name: TString<'static>, - _icon: Option>, - _accounts: Gc, + title: TString<'static>, + app_name: TString<'static>, + icon: Option>, + accounts: Gc, ) -> Result { #[cfg(feature = "universal_fw")] - return Err::, Error>(Error::ValueError(c"not implemented")); + return flow::confirm_fido::new_confirm_fido(title, app_name, icon, accounts); #[cfg(not(feature = "universal_fw"))] Err::, Error>(Error::ValueError( c"confirm_fido not used in bitcoin-only firmware", @@ -727,8 +727,6 @@ impl FirmwareUI for UIEckhart { .with_placement(LinearPlacement::vertical()); let action_bar = if allow_cancel { - ActionBar::new_single(Button::with_text(button)) - } else { ActionBar::new_double( Button::with_icon(theme::ICON_CLOSE), Button::with_text(button), diff --git a/core/mocks/trezortranslate_keys.pyi b/core/mocks/trezortranslate_keys.pyi index c22bfad06e..e8d3dd1ff2 100644 --- a/core/mocks/trezortranslate_keys.pyi +++ b/core/mocks/trezortranslate_keys.pyi @@ -950,6 +950,7 @@ class TR: words__amount: str = "Amount" words__are_you_sure: str = "Are you sure?" words__array_of: str = "Array of" + words__authenticate: str = "Authenticate" words__blockhash: str = "Blockhash" words__buying: str = "Buying" words__cancel_and_exit: str = "Cancel and exit" diff --git a/core/src/apps/webauthn/add_resident_credential.py b/core/src/apps/webauthn/add_resident_credential.py index c61e869984..88dc70607f 100644 --- a/core/src/apps/webauthn/add_resident_credential.py +++ b/core/src/apps/webauthn/add_resident_credential.py @@ -8,8 +8,7 @@ async def add_resident_credential(msg: WebAuthnAddResidentCredential) -> Success import storage.device as storage_device from trezor import TR, wire from trezor.messages import Success - from trezor.ui.layouts import show_error_and_raise - from trezor.ui.layouts.fido import confirm_fido + from trezor.ui.layouts.fido import confirm_fido, credential_warning from .credential import Fido2Credential from .resident_credentials import store_resident_credential @@ -22,7 +21,7 @@ async def add_resident_credential(msg: WebAuthnAddResidentCredential) -> Success try: cred = Fido2Credential.from_cred_id(bytes(msg.credential_id), None) except Exception: - await show_error_and_raise( + await credential_warning( "warning_credential", TR.fido__does_not_belong, ) diff --git a/core/src/trezor/ui/layouts/bolt/fido.py b/core/src/trezor/ui/layouts/bolt/fido.py index 64940470d7..515a4c4ffe 100644 --- a/core/src/trezor/ui/layouts/bolt/fido.py +++ b/core/src/trezor/ui/layouts/bolt/fido.py @@ -1,9 +1,15 @@ +from typing import TYPE_CHECKING + import trezorui_api from trezor import ui from trezor.enums import ButtonRequestType +from trezor.ui.layouts import show_error_and_raise from ..common import interact +if TYPE_CHECKING: + from typing import NoReturn + async def confirm_fido( header: str, @@ -60,3 +66,10 @@ async def confirm_fido_reset() -> bool: ) ) return (await confirm.get_result()) is trezorui_api.CONFIRMED + + +async def credential_warning(br_name: str, content: str) -> NoReturn: + await show_error_and_raise( + br_name=br_name, + content=content, + ) diff --git a/core/src/trezor/ui/layouts/caesar/fido.py b/core/src/trezor/ui/layouts/caesar/fido.py index 5e4005c8d5..e6d174b419 100644 --- a/core/src/trezor/ui/layouts/caesar/fido.py +++ b/core/src/trezor/ui/layouts/caesar/fido.py @@ -1,9 +1,15 @@ +from typing import TYPE_CHECKING + import trezorui_api from trezor import ui from trezor.enums import ButtonRequestType +from trezor.ui.layouts import show_error_and_raise from ..common import interact +if TYPE_CHECKING: + from typing import NoReturn + async def confirm_fido( header: str, @@ -42,3 +48,10 @@ async def confirm_fido_reset() -> bool: verb=TR.buttons__confirm, ) return (await ui.Layout(confirm).get_result()) is trezorui_api.CONFIRMED + + +async def credential_warning(br_name: str, content: str) -> NoReturn: + await show_error_and_raise( + br_name=br_name, + content=content, + ) diff --git a/core/src/trezor/ui/layouts/delizia/fido.py b/core/src/trezor/ui/layouts/delizia/fido.py index a4b54e0636..1d068455fd 100644 --- a/core/src/trezor/ui/layouts/delizia/fido.py +++ b/core/src/trezor/ui/layouts/delizia/fido.py @@ -1,9 +1,15 @@ +from typing import TYPE_CHECKING + import trezorui_api from trezor import ui from trezor.enums import ButtonRequestType +from trezor.ui.layouts import show_error_and_raise from ..common import interact +if TYPE_CHECKING: + from typing import NoReturn + async def confirm_fido( header: str, @@ -52,3 +58,10 @@ async def confirm_fido_reset() -> bool: ) ) return (await confirm.get_result()) is trezorui_api.CONFIRMED + + +async def credential_warning(br_name: str, content: str) -> NoReturn: + await show_error_and_raise( + br_name=br_name, + content=content, + ) diff --git a/core/src/trezor/ui/layouts/eckhart/fido.py b/core/src/trezor/ui/layouts/eckhart/fido.py index a4b54e0636..1df7ebd2cf 100644 --- a/core/src/trezor/ui/layouts/eckhart/fido.py +++ b/core/src/trezor/ui/layouts/eckhart/fido.py @@ -1,9 +1,15 @@ +from typing import TYPE_CHECKING + import trezorui_api -from trezor import ui +from trezor import ui, TR from trezor.enums import ButtonRequestType +from trezor.ui.layouts import show_error_and_raise from ..common import interact +if TYPE_CHECKING: + from typing import NoReturn + async def confirm_fido( header: str, @@ -52,3 +58,11 @@ async def confirm_fido_reset() -> bool: ) ) return (await confirm.get_result()) is trezorui_api.CONFIRMED + + +async def credential_warning(br_name: str, content: str) -> NoReturn: + await show_error_and_raise( + br_name=br_name, + content=content, + subheader=TR.words__pay_attention, + ) diff --git a/core/translations/en.json b/core/translations/en.json index 3ffdb788c8..f84bd26209 100644 --- a/core/translations/en.json +++ b/core/translations/en.json @@ -364,7 +364,12 @@ "fido__device_already_registered": "This device is already registered with this application.", "fido__device_already_registered_with_template": "This device is already registered with {0}.", "fido__device_not_registered": "This device is not registered with this application.", - "fido__does_not_belong": "The credential you are trying to import does\nnot belong to this authenticator.", + "fido__does_not_belong": { + "Bolt": "The credential you are trying to import does\nnot belong to this authenticator.", + "Caesar": "The credential you are trying to import does\nnot belong to this authenticator.", + "Delizia": "The credential you are trying to import does\nnot belong to this authenticator.", + "Eckhart": "The credential you are trying to import does not belong to this authenticator." + }, "fido__erase_credentials": "erase all credentials?", "fido__export_credentials": "Export information about the credentials stored on this device?", "fido__more_credentials": "More credentials", @@ -1022,6 +1027,7 @@ "words__amount": "Amount", "words__are_you_sure": "Are you sure?", "words__array_of": "Array of", + "words__authenticate": "Authenticate", "words__blockhash": "Blockhash", "words__buying": "Buying", "words__cancel_and_exit": "Cancel and exit", diff --git a/core/translations/order.json b/core/translations/order.json index 92bb04145b..ee45b3f7b3 100644 --- a/core/translations/order.json +++ b/core/translations/order.json @@ -1006,5 +1006,6 @@ "1004": "send__send_in_the_app", "1005": "send__sign_cancelled", "1006": "words__send", - "1007": "words__wallet" + "1007": "words__wallet", + "1008": "words__authenticate" } diff --git a/core/translations/signatures.json b/core/translations/signatures.json index ccc199ee1a..a9a1aabd7f 100644 --- a/core/translations/signatures.json +++ b/core/translations/signatures.json @@ -1,8 +1,8 @@ { "current": { - "merkle_root": "32a4bda1a1ee4fdcef587ba5a1ab26ce37902bc1714e87e77cda9c98bb913c76", - "datetime": "2025-04-08T10:31:11.342473", - "commit": "b6db7571e9e73304b7229b03970662c117b3a1df" + "merkle_root": "51fbfb5399c7ad544c547c2b3b435d854c4dc0de8b8c8cf474f727964ae482e7", + "datetime": "2025-04-11T10:27:48.914997", + "commit": "4f65836acfde34c5898396b22d15298554d79de5" }, "history": [ {