diff --git a/CHANGELOG.md b/CHANGELOG.md index a8a51cac9..20f6a9578 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ customers cannot upgrade their bootloader, its changes are recorded separately. - The BackupData.length field is now obsolete and always set to 0 - Port remaining protobuf code to Rust, remove the C nanopb protobuf dependency. - Ethereum: replace ERC20 token names with their unit codes in the receive screen +- SetPassword now returns the UserAbort error instead of the Generic error if the user cancelled ### 9.12.0 - Ethereum: add support for EIP-712 structured data signing diff --git a/src/rust/bitbox02-rust/src/hww/api/error.rs b/src/rust/bitbox02-rust/src/hww/api/error.rs index 1376a1dc0..349cbb4df 100644 --- a/src/rust/bitbox02-rust/src/hww/api/error.rs +++ b/src/rust/bitbox02-rust/src/hww/api/error.rs @@ -48,6 +48,21 @@ impl core::convert::From for Error { } } +impl core::convert::From for Error { + fn from(error: crate::workflow::password::EnterTwiceError) -> Self { + match error { + crate::workflow::password::EnterTwiceError::DoNotMatch => { + // For backwards compatibility. + Error::Generic + } + crate::workflow::password::EnterTwiceError::Cancelled => { + // Added in v9.13.0. + Error::UserAbort + } + } + } +} + impl core::convert::From for Error { fn from(_error: crate::workflow::confirm::UserAbort) -> Self { Error::UserAbort diff --git a/src/rust/bitbox02-rust/src/hww/api/restore.rs b/src/rust/bitbox02-rust/src/hww/api/restore.rs index 23f33c0c5..241d5c79e 100644 --- a/src/rust/bitbox02-rust/src/hww/api/restore.rs +++ b/src/rust/bitbox02-rust/src/hww/api/restore.rs @@ -115,14 +115,15 @@ pub async fn from_mnemonic( // the process immediately. We break out only if the user confirms. let password = loop { match password::enter_twice().await { - Err(()) => { - let params = confirm::Params { + Err(password::EnterTwiceError::DoNotMatch) => { + confirm::confirm(&confirm::Params { title: "", body: "Passwords\ndo not match.\nTry again?", ..Default::default() - }; - confirm::confirm(¶ms).await?; + }) + .await?; } + Err(password::EnterTwiceError::Cancelled) => return Err(Error::UserAbort), Ok(password) => break password, } }; diff --git a/src/rust/bitbox02-rust/src/workflow/cancel.rs b/src/rust/bitbox02-rust/src/workflow/cancel.rs index 451981816..70e7c5c50 100644 --- a/src/rust/bitbox02-rust/src/workflow/cancel.rs +++ b/src/rust/bitbox02-rust/src/workflow/cancel.rs @@ -47,7 +47,6 @@ pub async fn with_cancel( component: &mut bitbox02::ui::Component<'_>, result_cell: &ResultCell, ) -> Result { - *result_cell.borrow_mut() = None; component.screen_stack_push(); loop { let result = option(result_cell).await; diff --git a/src/rust/bitbox02-rust/src/workflow/password.rs b/src/rust/bitbox02-rust/src/workflow/password.rs index 5c4dad98e..5cf6cd2e4 100644 --- a/src/rust/bitbox02-rust/src/workflow/password.rs +++ b/src/rust/bitbox02-rust/src/workflow/password.rs @@ -15,7 +15,7 @@ use super::{confirm, status, trinary_input_string}; use bitbox02::input::SafeInputString; -pub use trinary_input_string::CanCancel; +pub use trinary_input_string::{CanCancel, Error}; /// If `can_cancel` is `Yes`, the workflow can be cancelled. /// If it is no, the result is always `Ok(())`. @@ -29,7 +29,7 @@ pub async fn enter( title: &str, special_chars: bool, can_cancel: CanCancel, -) -> Result { +) -> Result { let params = trinary_input_string::Params { title, hide: true, @@ -40,6 +40,19 @@ pub async fn enter( trinary_input_string::enter(¶ms, can_cancel, "").await } +pub enum EnterTwiceError { + DoNotMatch, + Cancelled, +} + +impl core::convert::From for EnterTwiceError { + fn from(error: Error) -> Self { + match error { + Error::Cancelled => EnterTwiceError::Cancelled, + } + } +} + /// Prompt the user to enter a password twice. A warning is displayed /// if the password has fewer than 4 chars. Returns `Err` if the two /// passwords do not match, or if the user aborts at the warning. @@ -48,26 +61,35 @@ pub async fn enter( /// ```no_run /// let pw = enter_twice().await.unwrap(); /// // use pw. -pub async fn enter_twice() -> Result { - let password = enter("Set password", false, CanCancel::No) - .await - .expect("not cancelable"); - let password_repeat = enter("Repeat password", false, CanCancel::No) - .await - .expect("not cancelable"); +pub async fn enter_twice() -> Result { + let password = enter("Set password", false, CanCancel::Yes).await?; + let password_repeat = enter("Repeat password", false, CanCancel::Yes).await?; if password.as_str() != password_repeat.as_str() { status::status("Passwords\ndo not match", false).await; - return Err(()); + return Err(EnterTwiceError::DoNotMatch); } if password.as_str().len() < 4 { - let params = confirm::Params { - title: "WARNING", - body: "Your password\n has fewer than\n 4 characters.\nContinue?", - longtouch: true, - ..Default::default() - }; - - confirm::confirm(¶ms).await.or(Err(()))?; + loop { + match confirm::confirm(&confirm::Params { + title: "WARNING", + body: "Your password\n has fewer than\n 4 characters.\nContinue?", + longtouch: true, + ..Default::default() + }) + .await + { + Ok(()) => break, + Err(confirm::UserAbort) => match confirm::confirm(&confirm::Params { + body: "Do you really\nwant to cancel?", + ..Default::default() + }) + .await + { + Ok(()) => return Err(EnterTwiceError::Cancelled), + Err(confirm::UserAbort) => {} + }, + } + } } status::status("Success", true).await; Ok(password) diff --git a/src/rust/bitbox02-rust/src/workflow/trinary_input_string.rs b/src/rust/bitbox02-rust/src/workflow/trinary_input_string.rs index 1f80f7ca4..bb3210b17 100644 --- a/src/rust/bitbox02-rust/src/workflow/trinary_input_string.rs +++ b/src/rust/bitbox02-rust/src/workflow/trinary_input_string.rs @@ -12,10 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -pub use super::cancel::Error; +pub use super::cancel::{cancel, set_result, with_cancel, Error}; pub use bitbox02::ui::TrinaryInputStringParams as Params; -use crate::bb02_async::option; use bitbox02::input::SafeInputString; use core::cell::RefCell; @@ -35,20 +34,17 @@ pub async fn enter( can_cancel: CanCancel, preset: &str, ) -> Result { - let result = RefCell::new(None as Option>); // Err means cancelled. + let result = RefCell::new(None); let mut component = bitbox02::ui::trinary_input_string_create( params, - |string| *result.borrow_mut() = Some(Ok(string)), + |string| set_result(&result, string), match can_cancel { - CanCancel::Yes => Some(Box::new(|| *result.borrow_mut() = Some(Err(())))), + CanCancel::Yes => Some(Box::new(|| cancel(&result))), CanCancel::No => None, }, ); if !preset.is_empty() { bitbox02::ui::trinary_input_string_set_input(&mut component, preset); } - component.screen_stack_push(); - option(&result) - .await - .or(Err(super::cancel::Error::Cancelled)) + with_cancel("", &mut component, &result).await }