mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-04-20 00:59:02 +00:00
feat(eckhart): add FIDO2 support
FidoCredential component, confirm_fido flow and icons
This commit is contained in:
parent
fda73e8314
commit
2ade8f2217
@ -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;
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
95
core/embed/rust/src/ui/layout_eckhart/firmware/fido.rs
Normal file
95
core/embed/rust/src/ui/layout_eckhart/firmware/fido.rs
Normal file
@ -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<F: Fn() -> TString<'static>> {
|
||||
app_icon: Option<Image>,
|
||||
text: Paragraphs<ParagraphVecShort<'static>>,
|
||||
get_account: F,
|
||||
}
|
||||
|
||||
impl<F: Fn() -> TString<'static>> FidoCredential<F> {
|
||||
const ICON_SIZE: i16 = 32;
|
||||
const SPACING: i16 = 24;
|
||||
|
||||
pub fn new(
|
||||
icon_name: Option<TString<'static>>,
|
||||
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<F: Fn() -> TString<'static>> Component for FidoCredential<F> {
|
||||
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<Self::Msg> {
|
||||
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<F: Fn() -> TString<'static>> SinglePage for FidoCredential<F> {}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<F: Fn() -> TString<'static>> crate::trace::Trace for FidoCredential<F> {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.component("FidoCredential");
|
||||
}
|
||||
}
|
80
core/embed/rust/src/ui/layout_eckhart/firmware/fido_icons.rs
Normal file
80
core/embed/rust/src/ui/layout_eckhart/firmware/fido_icons.rs
Normal file
@ -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<TString<'static>>) -> 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
|
||||
}
|
||||
}
|
@ -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<TString<'static>>) -> 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
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
168
core/embed/rust/src/ui/layout_eckhart/flow/confirm_fido.rs
Normal file
168
core/embed/rust/src/ui/layout_eckhart/flow/confirm_fido.rs
Normal file
@ -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<TString<'static>>,
|
||||
accounts: Gc<List>,
|
||||
) -> Result<SwipeFlow, error::Error> {
|
||||
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::<TString>(&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)
|
||||
}
|
@ -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;
|
||||
|
BIN
core/embed/rust/src/ui/layout_eckhart/res/fido/icon_apple.toif
Normal file
BIN
core/embed/rust/src/ui/layout_eckhart/res/fido/icon_apple.toif
Normal file
Binary file not shown.
BIN
core/embed/rust/src/ui/layout_eckhart/res/fido/icon_aws.toif
Normal file
BIN
core/embed/rust/src/ui/layout_eckhart/res/fido/icon_aws.toif
Normal file
Binary file not shown.
BIN
core/embed/rust/src/ui/layout_eckhart/res/fido/icon_binance.toif
Normal file
BIN
core/embed/rust/src/ui/layout_eckhart/res/fido/icon_binance.toif
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
core/embed/rust/src/ui/layout_eckhart/res/fido/icon_dropbox.toif
Normal file
BIN
core/embed/rust/src/ui/layout_eckhart/res/fido/icon_dropbox.toif
Normal file
Binary file not shown.
BIN
core/embed/rust/src/ui/layout_eckhart/res/fido/icon_duo.toif
Normal file
BIN
core/embed/rust/src/ui/layout_eckhart/res/fido/icon_duo.toif
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
core/embed/rust/src/ui/layout_eckhart/res/fido/icon_fedora.toif
Normal file
BIN
core/embed/rust/src/ui/layout_eckhart/res/fido/icon_fedora.toif
Normal file
Binary file not shown.
BIN
core/embed/rust/src/ui/layout_eckhart/res/fido/icon_gandi.toif
Normal file
BIN
core/embed/rust/src/ui/layout_eckhart/res/fido/icon_gandi.toif
Normal file
Binary file not shown.
BIN
core/embed/rust/src/ui/layout_eckhart/res/fido/icon_gemini.toif
Normal file
BIN
core/embed/rust/src/ui/layout_eckhart/res/fido/icon_gemini.toif
Normal file
Binary file not shown.
BIN
core/embed/rust/src/ui/layout_eckhart/res/fido/icon_github.toif
Normal file
BIN
core/embed/rust/src/ui/layout_eckhart/res/fido/icon_github.toif
Normal file
Binary file not shown.
BIN
core/embed/rust/src/ui/layout_eckhart/res/fido/icon_gitlab.toif
Normal file
BIN
core/embed/rust/src/ui/layout_eckhart/res/fido/icon_gitlab.toif
Normal file
Binary file not shown.
BIN
core/embed/rust/src/ui/layout_eckhart/res/fido/icon_google.toif
Normal file
BIN
core/embed/rust/src/ui/layout_eckhart/res/fido/icon_google.toif
Normal file
Binary file not shown.
BIN
core/embed/rust/src/ui/layout_eckhart/res/fido/icon_invity.toif
Normal file
BIN
core/embed/rust/src/ui/layout_eckhart/res/fido/icon_invity.toif
Normal file
Binary file not shown.
BIN
core/embed/rust/src/ui/layout_eckhart/res/fido/icon_keeper.toif
Normal file
BIN
core/embed/rust/src/ui/layout_eckhart/res/fido/icon_keeper.toif
Normal file
Binary file not shown.
BIN
core/embed/rust/src/ui/layout_eckhart/res/fido/icon_kraken.toif
Normal file
BIN
core/embed/rust/src/ui/layout_eckhart/res/fido/icon_kraken.toif
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
core/embed/rust/src/ui/layout_eckhart/res/fido/icon_mojeid.toif
Normal file
BIN
core/embed/rust/src/ui/layout_eckhart/res/fido/icon_mojeid.toif
Normal file
Binary file not shown.
Binary file not shown.
BIN
core/embed/rust/src/ui/layout_eckhart/res/fido/icon_proton.toif
Normal file
BIN
core/embed/rust/src/ui/layout_eckhart/res/fido/icon_proton.toif
Normal file
Binary file not shown.
Binary file not shown.
BIN
core/embed/rust/src/ui/layout_eckhart/res/fido/icon_stripe.toif
Normal file
BIN
core/embed/rust/src/ui/layout_eckhart/res/fido/icon_stripe.toif
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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 =
|
||||
|
@ -158,13 +158,13 @@ impl FirmwareUI for UIEckhart {
|
||||
}
|
||||
|
||||
fn confirm_fido(
|
||||
_title: TString<'static>,
|
||||
_app_name: TString<'static>,
|
||||
_icon: Option<TString<'static>>,
|
||||
_accounts: Gc<List>,
|
||||
title: TString<'static>,
|
||||
app_name: TString<'static>,
|
||||
icon: Option<TString<'static>>,
|
||||
accounts: Gc<List>,
|
||||
) -> Result<impl LayoutMaybeTrace, Error> {
|
||||
#[cfg(feature = "universal_fw")]
|
||||
return Err::<RootComponent<Empty, ModelUI>, Error>(Error::ValueError(c"not implemented"));
|
||||
return flow::confirm_fido::new_confirm_fido(title, app_name, icon, accounts);
|
||||
#[cfg(not(feature = "universal_fw"))]
|
||||
Err::<RootComponent<Empty, ModelUI>, 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),
|
||||
|
@ -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"
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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",
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -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": [
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user