mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-02-08 05:32:39 +00:00
feat(core): increase maximum passphrase length to 128 bytes
Legacy is still limited to 50 bytes. Also, deduplicate common passphrase test vectors definitions.
This commit is contained in:
parent
e9aca68612
commit
a0282add7f
1
core/.changelog.d/4084.changed
Normal file
1
core/.changelog.d/4084.changed
Normal file
@ -0,0 +1 @@
|
|||||||
|
Increase maximum passphrase length to 128 bytes.
|
@ -1,7 +1,7 @@
|
|||||||
use heapless::Vec;
|
use heapless::{String, Vec};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
strutil::{ShortString, TString},
|
strutil::TString,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
ui::{
|
ui::{
|
||||||
button_request::{ButtonRequest, ButtonRequestCode},
|
button_request::{ButtonRequest, ButtonRequestCode},
|
||||||
@ -592,6 +592,8 @@ impl EventCtx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type FlowMsgText = String<128>;
|
||||||
|
|
||||||
/// Component::Msg for component parts of a swipe flow. Converting results of
|
/// Component::Msg for component parts of a swipe flow. Converting results of
|
||||||
/// different screens to a shared type makes things easier to work with.
|
/// different screens to a shared type makes things easier to work with.
|
||||||
///
|
///
|
||||||
@ -603,7 +605,7 @@ pub enum FlowMsg {
|
|||||||
Cancelled,
|
Cancelled,
|
||||||
Info,
|
Info,
|
||||||
Choice(usize),
|
Choice(usize),
|
||||||
Text(ShortString),
|
Text(FlowMsgText),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "micropython")]
|
#[cfg(feature = "micropython")]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::ui::{
|
||||||
strutil::ShortString,
|
component::{base::FlowMsgText, EventCtx},
|
||||||
ui::{component::EventCtx, util::ResultExt},
|
util::ResultExt,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Reified editing operations of `TextBox`.
|
/// Reified editing operations of `TextBox`.
|
||||||
@ -16,13 +16,13 @@ pub enum TextEdit {
|
|||||||
/// operations over it. Text ops usually take a `EventCtx` to request a paint
|
/// operations over it. Text ops usually take a `EventCtx` to request a paint
|
||||||
/// pass in case of any state modification.
|
/// pass in case of any state modification.
|
||||||
pub struct TextBox {
|
pub struct TextBox {
|
||||||
text: ShortString,
|
text: FlowMsgText,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextBox {
|
impl TextBox {
|
||||||
/// Create a new `TextBox` with content `text`.
|
/// Create a new `TextBox` with content `text`.
|
||||||
pub fn new(text: &str, max_len: usize) -> Self {
|
pub fn new(text: &str, max_len: usize) -> Self {
|
||||||
let text = unwrap!(ShortString::try_from(text));
|
let text = unwrap!(FlowMsgText::try_from(text));
|
||||||
debug_assert!(text.capacity() >= max_len);
|
debug_assert!(text.capacity() >= max_len);
|
||||||
Self { text }
|
Self { text }
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ const KEYBOARD: [[&str; KEY_COUNT]; PAGE_COUNT] = [
|
|||||||
["_<>", ".:@", "/|\\", "!()", "+%&", "-[]", "?{}", ",'`", ";\"~", "$^="],
|
["_<>", ".:@", "/|\\", "!()", "+%&", "-[]", "?{}", ",'`", ";\"~", "$^="],
|
||||||
];
|
];
|
||||||
|
|
||||||
const MAX_LENGTH: usize = 50;
|
const MAX_LENGTH: usize = 128;
|
||||||
const INPUT_AREA_HEIGHT: i16 = ScrollBar::DOT_SIZE + 9;
|
const INPUT_AREA_HEIGHT: i16 = ScrollBar::DOT_SIZE + 9;
|
||||||
|
|
||||||
impl PassphraseKeyboard {
|
impl PassphraseKeyboard {
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
use crate::{
|
use crate::ui::{
|
||||||
strutil::ShortString,
|
component::{base::FlowMsgText, Component, Event, EventCtx, Never, Pad},
|
||||||
ui::{
|
display::Font,
|
||||||
component::{Component, Event, EventCtx, Never, Pad},
|
geometry::{Alignment, Point, Rect},
|
||||||
display::Font,
|
shape::{self, Renderer},
|
||||||
geometry::{Alignment, Point, Rect},
|
util::long_line_content_with_ellipsis,
|
||||||
shape::{self, Renderer},
|
|
||||||
util::long_line_content_with_ellipsis,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::theme;
|
use super::theme;
|
||||||
@ -16,7 +13,7 @@ use super::theme;
|
|||||||
/// and without being affected by other components.
|
/// and without being affected by other components.
|
||||||
pub struct ChangingTextLine {
|
pub struct ChangingTextLine {
|
||||||
pad: Pad,
|
pad: Pad,
|
||||||
text: ShortString,
|
text: FlowMsgText,
|
||||||
font: Font,
|
font: Font,
|
||||||
/// Whether to show the text. Can be disabled.
|
/// Whether to show the text. Can be disabled.
|
||||||
show_content: bool,
|
show_content: bool,
|
||||||
@ -29,7 +26,7 @@ pub struct ChangingTextLine {
|
|||||||
|
|
||||||
impl ChangingTextLine {
|
impl ChangingTextLine {
|
||||||
pub fn new(text: &str, font: Font, alignment: Alignment, max_len: usize) -> Self {
|
pub fn new(text: &str, font: Font, alignment: Alignment, max_len: usize) -> Self {
|
||||||
let text = unwrap!(ShortString::try_from(text));
|
let text = unwrap!(FlowMsgText::try_from(text));
|
||||||
debug_assert!(text.capacity() >= max_len);
|
debug_assert!(text.capacity() >= max_len);
|
||||||
Self {
|
Self {
|
||||||
pad: Pad::with_background(theme::BG),
|
pad: Pad::with_background(theme::BG),
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
strutil::{ShortString, TString},
|
strutil::TString,
|
||||||
translations::TR,
|
translations::TR,
|
||||||
trezorhal::random,
|
trezorhal::random,
|
||||||
ui::{
|
ui::{
|
||||||
component::{text::common::TextBox, Child, Component, ComponentExt, Event, EventCtx},
|
component::{
|
||||||
|
base::FlowMsgText, text::common::TextBox, Child, Component, ComponentExt, Event,
|
||||||
|
EventCtx,
|
||||||
|
},
|
||||||
display::Icon,
|
display::Icon,
|
||||||
geometry::Rect,
|
geometry::Rect,
|
||||||
shape::Renderer,
|
shape::Renderer,
|
||||||
@ -26,7 +29,7 @@ enum ChoiceCategory {
|
|||||||
SpecialSymbol,
|
SpecialSymbol,
|
||||||
}
|
}
|
||||||
|
|
||||||
const MAX_PASSPHRASE_LENGTH: usize = 50;
|
const MAX_PASSPHRASE_LENGTH: usize = 128;
|
||||||
|
|
||||||
const DIGITS: &str = "0123456789";
|
const DIGITS: &str = "0123456789";
|
||||||
const LOWERCASE_LETTERS: &str = "abcdefghijklmnopqrstuvwxyz";
|
const LOWERCASE_LETTERS: &str = "abcdefghijklmnopqrstuvwxyz";
|
||||||
@ -291,17 +294,17 @@ impl PassphraseEntry {
|
|||||||
|
|
||||||
fn update_passphrase_dots(&mut self, ctx: &mut EventCtx) {
|
fn update_passphrase_dots(&mut self, ctx: &mut EventCtx) {
|
||||||
debug_assert!({
|
debug_assert!({
|
||||||
let s = ShortString::new();
|
let s = FlowMsgText::new();
|
||||||
s.capacity() >= MAX_PASSPHRASE_LENGTH
|
s.capacity() >= MAX_PASSPHRASE_LENGTH
|
||||||
});
|
});
|
||||||
|
|
||||||
let text_to_show = if self.show_plain_passphrase {
|
let text_to_show = if self.show_plain_passphrase {
|
||||||
unwrap!(ShortString::try_from(self.passphrase()))
|
unwrap!(FlowMsgText::try_from(self.passphrase()))
|
||||||
} else if self.is_empty() {
|
} else if self.is_empty() {
|
||||||
unwrap!(ShortString::try_from(""))
|
unwrap!(FlowMsgText::try_from(""))
|
||||||
} else {
|
} else {
|
||||||
// Showing asterisks and possibly the last digit.
|
// Showing asterisks and possibly the last digit.
|
||||||
let mut dots = ShortString::new();
|
let mut dots = FlowMsgText::new();
|
||||||
for _ in 0..self.textbox.len() - 1 {
|
for _ in 0..self.textbox.len() - 1 {
|
||||||
unwrap!(dots.push('*'));
|
unwrap!(dots.push('*'));
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
strutil::{ShortString, TString},
|
strutil::TString,
|
||||||
translations::TR,
|
translations::TR,
|
||||||
ui::{
|
ui::{
|
||||||
component::{
|
component::{
|
||||||
base::ComponentExt, swipe_detect::SwipeConfig, text::common::TextBox, Component, Event,
|
base::{ComponentExt, FlowMsgText},
|
||||||
EventCtx, Label, Maybe, Never, Swipe,
|
swipe_detect::SwipeConfig,
|
||||||
|
text::common::TextBox,
|
||||||
|
Component, Event, EventCtx, Label, Maybe, Never, Swipe,
|
||||||
},
|
},
|
||||||
display,
|
display,
|
||||||
geometry::{Alignment, Direction, Grid, Insets, Offset, Rect},
|
geometry::{Alignment, Direction, Grid, Insets, Offset, Rect},
|
||||||
@ -27,7 +29,7 @@ use core::cell::Cell;
|
|||||||
use num_traits::ToPrimitive;
|
use num_traits::ToPrimitive;
|
||||||
|
|
||||||
pub enum PassphraseKeyboardMsg {
|
pub enum PassphraseKeyboardMsg {
|
||||||
Confirmed(ShortString),
|
Confirmed(FlowMsgText),
|
||||||
Cancelled,
|
Cancelled,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +103,7 @@ const KEYBOARD: [[&str; KEY_COUNT]; PAGE_COUNT] = [
|
|||||||
["_<>", ".:@", "/|\\", "!()", "+%&", "-[]", "?{}", ",'`", ";\"~", "$^="],
|
["_<>", ".:@", "/|\\", "!()", "+%&", "-[]", "?{}", ",'`", ";\"~", "$^="],
|
||||||
];
|
];
|
||||||
|
|
||||||
const MAX_LENGTH: usize = 50;
|
const MAX_LENGTH: usize = 128;
|
||||||
|
|
||||||
const CONFIRM_BTN_INSETS: Insets = Insets::new(5, 0, 5, 0);
|
const CONFIRM_BTN_INSETS: Insets = Insets::new(5, 0, 5, 0);
|
||||||
const CONFIRM_EMPTY_BTN_MARGIN_RIGHT: i16 = 7;
|
const CONFIRM_EMPTY_BTN_MARGIN_RIGHT: i16 = 7;
|
||||||
@ -338,12 +340,12 @@ impl Component for PassphraseKeyboard {
|
|||||||
// Confirm button was clicked, we're done.
|
// Confirm button was clicked, we're done.
|
||||||
if let Some(ButtonMsg::Clicked) = self.confirm_empty_btn.event(ctx, event) {
|
if let Some(ButtonMsg::Clicked) = self.confirm_empty_btn.event(ctx, event) {
|
||||||
return Some(PassphraseKeyboardMsg::Confirmed(unwrap!(
|
return Some(PassphraseKeyboardMsg::Confirmed(unwrap!(
|
||||||
ShortString::try_from(self.passphrase())
|
FlowMsgText::try_from(self.passphrase())
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
if let Some(ButtonMsg::Clicked) = self.confirm_btn.event(ctx, event) {
|
if let Some(ButtonMsg::Clicked) = self.confirm_btn.event(ctx, event) {
|
||||||
return Some(PassphraseKeyboardMsg::Confirmed(unwrap!(
|
return Some(PassphraseKeyboardMsg::Confirmed(unwrap!(
|
||||||
ShortString::try_from(self.passphrase())
|
FlowMsgText::try_from(self.passphrase())
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
if let Some(ButtonMsg::Clicked) = self.cancel_btn.event(ctx, event) {
|
if let Some(ButtonMsg::Clicked) = self.cancel_btn.event(ctx, event) {
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
error,
|
error,
|
||||||
strutil::ShortString,
|
|
||||||
translations::TR,
|
translations::TR,
|
||||||
ui::{
|
ui::{
|
||||||
component::ComponentExt,
|
component::{base::FlowMsgText, ComponentExt},
|
||||||
flow::{
|
flow::{
|
||||||
base::{Decision, DecisionBuilder as _},
|
base::{Decision, DecisionBuilder as _},
|
||||||
FlowController, FlowMsg, SwipeFlow,
|
FlowController, FlowMsg, SwipeFlow,
|
||||||
@ -44,7 +43,7 @@ impl FlowController for RequestPassphrase {
|
|||||||
(Self::Keypad, FlowMsg::Cancelled) => self.return_msg(FlowMsg::Cancelled),
|
(Self::Keypad, FlowMsg::Cancelled) => self.return_msg(FlowMsg::Cancelled),
|
||||||
(Self::ConfirmEmpty, FlowMsg::Cancelled) => Self::Keypad.goto(),
|
(Self::ConfirmEmpty, FlowMsg::Cancelled) => Self::Keypad.goto(),
|
||||||
(Self::ConfirmEmpty, FlowMsg::Confirmed) => {
|
(Self::ConfirmEmpty, FlowMsg::Confirmed) => {
|
||||||
self.return_msg(FlowMsg::Text(ShortString::new()))
|
self.return_msg(FlowMsg::Text(FlowMsgText::new()))
|
||||||
}
|
}
|
||||||
_ => self.do_nothing(),
|
_ => self.do_nothing(),
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ from micropython import const
|
|||||||
import storage.device as storage_device
|
import storage.device as storage_device
|
||||||
from trezor.wire import DataError
|
from trezor.wire import DataError
|
||||||
|
|
||||||
_MAX_PASSPHRASE_LEN = const(50)
|
_MAX_PASSPHRASE_LEN = const(128)
|
||||||
|
|
||||||
|
|
||||||
def is_enabled() -> bool:
|
def is_enabled() -> bool:
|
||||||
|
@ -38,7 +38,7 @@ MT = TypeVar("MT", bound=MessageType)
|
|||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
MAX_PASSPHRASE_LENGTH = 50
|
MAX_PASSPHRASE_LENGTH = 128
|
||||||
MAX_PIN_LENGTH = 50
|
MAX_PIN_LENGTH = 50
|
||||||
|
|
||||||
PASSPHRASE_ON_DEVICE = object()
|
PASSPHRASE_ON_DEVICE = object()
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import itertools
|
||||||
import typing as t
|
import typing as t
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
@ -27,6 +28,32 @@ class CommonPass:
|
|||||||
|
|
||||||
EMPTY_ADDRESS = "mvbu1Gdy8SUjTenqerxUaZyYjmveZvt33q"
|
EMPTY_ADDRESS = "mvbu1Gdy8SUjTenqerxUaZyYjmveZvt33q"
|
||||||
|
|
||||||
|
LONGEST = "".join(itertools.islice(itertools.cycle("1234567890"), 128))
|
||||||
|
LONGEST_ADDRESS = "msHs5d865WetSd7Uai8roAu2eCaZgx3oqg"
|
||||||
|
|
||||||
|
ALMOST_LONGEST = LONGEST[:-1]
|
||||||
|
ALMOST_LONGEST_ADDRESS = "mkRvtfidQoPhNQx1eesDNMXCRiZeUK5kLB"
|
||||||
|
|
||||||
|
TOO_LONG = LONGEST + "x"
|
||||||
|
TOO_LONG_ADDRESS = LONGEST_ADDRESS
|
||||||
|
|
||||||
|
VECTORS = ( # passphrase, address
|
||||||
|
(SHORT, SHORT_ADDRESS),
|
||||||
|
(WITH_SPACE, WITH_SPACE_ADDRESS),
|
||||||
|
(RANDOM_25, RANDOM_25_ADDRESS),
|
||||||
|
(ALMOST_LONGEST, ALMOST_LONGEST_ADDRESS),
|
||||||
|
(LONGEST, LONGEST_ADDRESS),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
assert len(CommonPass.LONGEST) == 128
|
||||||
|
assert len(CommonPass.ALMOST_LONGEST) == 127
|
||||||
|
assert len(CommonPass.TOO_LONG) == 129
|
||||||
|
assert CommonPass.LONGEST_ADDRESS != CommonPass.ALMOST_LONGEST_ADDRESS
|
||||||
|
assert CommonPass.LONGEST_ADDRESS == CommonPass.TOO_LONG_ADDRESS
|
||||||
|
# TODO: show some UI message when length reaches the limit?
|
||||||
|
# (it currently disabled typing and greys out the buttons)
|
||||||
|
|
||||||
|
|
||||||
class PassphraseCategory(Enum):
|
class PassphraseCategory(Enum):
|
||||||
MENU = "MENU"
|
MENU = "MENU"
|
||||||
|
@ -45,24 +45,6 @@ TT_CATEGORIES = [
|
|||||||
TT_CATEGORY = PassphraseCategory.LOWERCASE
|
TT_CATEGORY = PassphraseCategory.LOWERCASE
|
||||||
TT_COORDS_PREV: buttons.Coords = (0, 0)
|
TT_COORDS_PREV: buttons.Coords = (0, 0)
|
||||||
|
|
||||||
# Testing the maximum length is really 50
|
|
||||||
# TODO: show some UI message when length reaches 50?
|
|
||||||
# (it currently disabled typing and greys out the buttons)
|
|
||||||
|
|
||||||
DA_50 = 25 * "da"
|
|
||||||
DA_50_ADDRESS = "mg5L2i8HZKUvceK1sfmGHhE4gichFSsdvm"
|
|
||||||
assert len(DA_50) == 50
|
|
||||||
|
|
||||||
DA_49 = DA_50[:-1]
|
|
||||||
DA_49_ADDRESS = "mxrB75ydMS3ZzqmYKK28fj4bNMEx7dDw6e"
|
|
||||||
assert len(DA_49) == 49
|
|
||||||
assert DA_49_ADDRESS != DA_50_ADDRESS
|
|
||||||
|
|
||||||
DA_51 = DA_50 + "d"
|
|
||||||
DA_51_ADDRESS = DA_50_ADDRESS
|
|
||||||
assert len(DA_51) == 51
|
|
||||||
assert DA_51_ADDRESS == DA_50_ADDRESS
|
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def prepare_passphrase_dialogue(
|
def prepare_passphrase_dialogue(
|
||||||
@ -151,16 +133,7 @@ def delete_char(debug: "DebugLink") -> None:
|
|||||||
debug.click(coords)
|
debug.click(coords)
|
||||||
|
|
||||||
|
|
||||||
VECTORS = ( # passphrase, address
|
@pytest.mark.parametrize("passphrase, address", CommonPass.VECTORS)
|
||||||
(CommonPass.SHORT, CommonPass.SHORT_ADDRESS),
|
|
||||||
(CommonPass.WITH_SPACE, CommonPass.WITH_SPACE_ADDRESS),
|
|
||||||
(CommonPass.RANDOM_25, CommonPass.RANDOM_25_ADDRESS),
|
|
||||||
(DA_49, DA_49_ADDRESS),
|
|
||||||
(DA_50, DA_50_ADDRESS),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("passphrase, address", VECTORS)
|
|
||||||
@pytest.mark.setup_client(passphrase=True)
|
@pytest.mark.setup_client(passphrase=True)
|
||||||
def test_passphrase_input(
|
def test_passphrase_input(
|
||||||
device_handler: "BackgroundDeviceHandler", passphrase: str, address: str
|
device_handler: "BackgroundDeviceHandler", passphrase: str, address: str
|
||||||
@ -171,10 +144,10 @@ def test_passphrase_input(
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.setup_client(passphrase=True)
|
@pytest.mark.setup_client(passphrase=True)
|
||||||
def test_passphrase_input_over_50_chars(device_handler: "BackgroundDeviceHandler"):
|
def test_passphrase_input_too_long(device_handler: "BackgroundDeviceHandler"):
|
||||||
with prepare_passphrase_dialogue(device_handler, DA_51_ADDRESS) as debug: # type: ignore
|
with prepare_passphrase_dialogue(device_handler, CommonPass.TOO_LONG_ADDRESS) as debug: # type: ignore
|
||||||
input_passphrase(debug, DA_51, check=False)
|
input_passphrase(debug, CommonPass.TOO_LONG, check=False)
|
||||||
assert debug.read_layout().passphrase() == DA_50
|
assert debug.read_layout().passphrase() == CommonPass.LONGEST
|
||||||
enter_passphrase(debug)
|
enter_passphrase(debug)
|
||||||
|
|
||||||
|
|
||||||
@ -290,9 +263,10 @@ def test_passphrase_dollar_sign_deletion(
|
|||||||
def test_cycle_through_last_character(
|
def test_cycle_through_last_character(
|
||||||
device_handler: "BackgroundDeviceHandler",
|
device_handler: "BackgroundDeviceHandler",
|
||||||
):
|
):
|
||||||
# Checks that we can cycle through the last (50th) passphrase character
|
# Checks that we can cycle through the last (128th) passphrase character
|
||||||
# (was a bug previously)
|
# (was a bug previously)
|
||||||
with prepare_passphrase_dialogue(device_handler) as debug:
|
with prepare_passphrase_dialogue(device_handler) as debug:
|
||||||
passphrase = DA_49 + "i" # for i we need to cycle through "ghi" three times
|
# for "i" we need to cycle through "ghi" three times
|
||||||
|
passphrase = CommonPass.ALMOST_LONGEST + "i"
|
||||||
input_passphrase(debug, passphrase)
|
input_passphrase(debug, passphrase)
|
||||||
enter_passphrase(debug)
|
enter_passphrase(debug)
|
||||||
|
@ -37,24 +37,6 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
pytestmark = pytest.mark.models("safe3")
|
pytestmark = pytest.mark.models("safe3")
|
||||||
|
|
||||||
# Testing the maximum length is really 50
|
|
||||||
# TODO: show some UI message when length reaches 50?
|
|
||||||
|
|
||||||
AAA_50 = 50 * "a"
|
|
||||||
AAA_50_ADDRESS = "miPeCUxf1Ufh5DtV3AuBopNM8YEDvnQZMh"
|
|
||||||
assert len(AAA_50) == 50
|
|
||||||
|
|
||||||
AAA_49 = AAA_50[:-1]
|
|
||||||
AAA_49_ADDRESS = "n2MPUjAB86MuVmyYe8HCgdznJS1FXk3qvg"
|
|
||||||
assert len(AAA_49) == 49
|
|
||||||
assert AAA_49_ADDRESS != AAA_50_ADDRESS
|
|
||||||
|
|
||||||
AAA_51 = AAA_50 + "a"
|
|
||||||
AAA_51_ADDRESS = "miPeCUxf1Ufh5DtV3AuBopNM8YEDvnQZMh"
|
|
||||||
assert len(AAA_51) == 51
|
|
||||||
assert AAA_51_ADDRESS == AAA_50_ADDRESS
|
|
||||||
|
|
||||||
|
|
||||||
BACK = "inputs__back"
|
BACK = "inputs__back"
|
||||||
SHOW = "inputs__show"
|
SHOW = "inputs__show"
|
||||||
ENTER = "inputs__enter"
|
ENTER = "inputs__enter"
|
||||||
@ -186,16 +168,7 @@ def cancel(debug: "DebugLink") -> None:
|
|||||||
delete_char(debug)
|
delete_char(debug)
|
||||||
|
|
||||||
|
|
||||||
VECTORS = ( # passphrase, address
|
@pytest.mark.parametrize("passphrase, address", CommonPass.VECTORS)
|
||||||
(CommonPass.SHORT, CommonPass.SHORT_ADDRESS),
|
|
||||||
(CommonPass.WITH_SPACE, CommonPass.WITH_SPACE_ADDRESS),
|
|
||||||
(CommonPass.RANDOM_25, CommonPass.RANDOM_25_ADDRESS),
|
|
||||||
(AAA_49, AAA_49_ADDRESS),
|
|
||||||
(AAA_50, AAA_50_ADDRESS),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("passphrase, address", VECTORS)
|
|
||||||
@pytest.mark.setup_client(passphrase=True)
|
@pytest.mark.setup_client(passphrase=True)
|
||||||
def test_passphrase_input(
|
def test_passphrase_input(
|
||||||
device_handler: "BackgroundDeviceHandler", passphrase: str, address: str
|
device_handler: "BackgroundDeviceHandler", passphrase: str, address: str
|
||||||
@ -207,22 +180,22 @@ def test_passphrase_input(
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.setup_client(passphrase=True)
|
@pytest.mark.setup_client(passphrase=True)
|
||||||
def test_passphrase_input_over_50_chars(device_handler: "BackgroundDeviceHandler"):
|
def test_passphrase_input_too_long(device_handler: "BackgroundDeviceHandler"):
|
||||||
with prepare_passphrase_dialogue(device_handler, AAA_51_ADDRESS) as debug: # type: ignore
|
with prepare_passphrase_dialogue(device_handler, CommonPass.TOO_LONG_ADDRESS) as debug: # type: ignore
|
||||||
# First 50 chars
|
# First 128 chars
|
||||||
input_passphrase(debug, AAA_51[:-1])
|
input_passphrase(debug, CommonPass.TOO_LONG[:-1])
|
||||||
layout = debug.read_layout()
|
layout = debug.read_layout()
|
||||||
assert AAA_51[:-1] in layout.passphrase()
|
assert CommonPass.TOO_LONG[:-1] in layout.passphrase()
|
||||||
|
|
||||||
show_passphrase(debug)
|
show_passphrase(debug)
|
||||||
|
|
||||||
# Over-limit character
|
# Over-limit character
|
||||||
press_char(debug, AAA_51[-1])
|
press_char(debug, CommonPass.TOO_LONG[-1])
|
||||||
|
|
||||||
# No change
|
# No change
|
||||||
layout = debug.read_layout()
|
layout = debug.read_layout()
|
||||||
assert AAA_51[:-1] in layout.passphrase()
|
assert CommonPass.TOO_LONG[:-1] in layout.passphrase()
|
||||||
assert AAA_51 not in layout.passphrase()
|
assert CommonPass.TOO_LONG not in layout.passphrase()
|
||||||
|
|
||||||
show_passphrase(debug)
|
show_passphrase(debug)
|
||||||
enter_passphrase(debug)
|
enter_passphrase(debug)
|
||||||
|
@ -54,22 +54,6 @@ PASSPHRASE_SPECIAL = ("_<>", ".:@", "/|\\", "!()", "+%&", "-[]", "?{}", ",'`", "
|
|||||||
KEYBOARD_CATEGORY = PassphraseCategory.LOWERCASE
|
KEYBOARD_CATEGORY = PassphraseCategory.LOWERCASE
|
||||||
COORDS_PREV: buttons.Coords = (0, 0)
|
COORDS_PREV: buttons.Coords = (0, 0)
|
||||||
|
|
||||||
# Testing the maximum length is really 50
|
|
||||||
|
|
||||||
DA_50 = 25 * "da"
|
|
||||||
DA_50_ADDRESS = "mg5L2i8HZKUvceK1sfmGHhE4gichFSsdvm"
|
|
||||||
assert len(DA_50) == 50
|
|
||||||
|
|
||||||
DA_49 = DA_50[:-1]
|
|
||||||
DA_49_ADDRESS = "mxrB75ydMS3ZzqmYKK28fj4bNMEx7dDw6e"
|
|
||||||
assert len(DA_49) == 49
|
|
||||||
assert DA_49_ADDRESS != DA_50_ADDRESS
|
|
||||||
|
|
||||||
DA_51 = DA_50 + "d"
|
|
||||||
DA_51_ADDRESS = DA_50_ADDRESS
|
|
||||||
assert len(DA_51) == 51
|
|
||||||
assert DA_51_ADDRESS == DA_50_ADDRESS
|
|
||||||
|
|
||||||
|
|
||||||
def get_passphrase_choices(char: str) -> tuple[str, ...]:
|
def get_passphrase_choices(char: str) -> tuple[str, ...]:
|
||||||
if char in " *#":
|
if char in " *#":
|
||||||
@ -182,16 +166,7 @@ def delete_char(debug: "DebugLink") -> None:
|
|||||||
debug.click(coords)
|
debug.click(coords)
|
||||||
|
|
||||||
|
|
||||||
VECTORS = ( # passphrase, address
|
@pytest.mark.parametrize("passphrase, address", CommonPass.VECTORS)
|
||||||
(CommonPass.SHORT, CommonPass.SHORT_ADDRESS),
|
|
||||||
(CommonPass.WITH_SPACE, CommonPass.WITH_SPACE_ADDRESS),
|
|
||||||
(CommonPass.RANDOM_25, CommonPass.RANDOM_25_ADDRESS),
|
|
||||||
(DA_49, DA_49_ADDRESS),
|
|
||||||
(DA_50, DA_50_ADDRESS),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("passphrase, address", VECTORS)
|
|
||||||
@pytest.mark.setup_client(passphrase=True)
|
@pytest.mark.setup_client(passphrase=True)
|
||||||
def test_passphrase_input(
|
def test_passphrase_input(
|
||||||
device_handler: "BackgroundDeviceHandler", passphrase: str, address: str
|
device_handler: "BackgroundDeviceHandler", passphrase: str, address: str
|
||||||
@ -202,10 +177,10 @@ def test_passphrase_input(
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.setup_client(passphrase=True)
|
@pytest.mark.setup_client(passphrase=True)
|
||||||
def test_passphrase_input_over_50_chars(device_handler: "BackgroundDeviceHandler"):
|
def test_passphrase_input_over_max_length(device_handler: "BackgroundDeviceHandler"):
|
||||||
with prepare_passphrase_dialogue(device_handler, DA_51_ADDRESS) as debug: # type: ignore
|
with prepare_passphrase_dialogue(device_handler, CommonPass.TOO_LONG_ADDRESS) as debug: # type: ignore
|
||||||
input_passphrase(debug, DA_51, check=False)
|
input_passphrase(debug, CommonPass.TOO_LONG, check=False)
|
||||||
assert debug.read_layout().passphrase() == DA_50
|
assert debug.read_layout().passphrase() == CommonPass.LONGEST
|
||||||
enter_passphrase(debug)
|
enter_passphrase(debug)
|
||||||
|
|
||||||
|
|
||||||
@ -323,9 +298,10 @@ def test_passphrase_dollar_sign_deletion(
|
|||||||
def test_cycle_through_last_character(
|
def test_cycle_through_last_character(
|
||||||
device_handler: "BackgroundDeviceHandler",
|
device_handler: "BackgroundDeviceHandler",
|
||||||
):
|
):
|
||||||
# Checks that we can cycle through the last (50th) passphrase character
|
# Checks that we can cycle through the last (128th) passphrase character
|
||||||
# (was a bug previously)
|
# (was a bug previously)
|
||||||
with prepare_passphrase_dialogue(device_handler) as debug:
|
with prepare_passphrase_dialogue(device_handler) as debug:
|
||||||
passphrase = DA_49 + "i" # for i we need to cycle through "ghi" three times
|
# for "i" we need to cycle through "ghi" three times
|
||||||
|
passphrase = CommonPass.ALMOST_LONGEST + "i"
|
||||||
input_passphrase(debug, passphrase)
|
input_passphrase(debug, passphrase)
|
||||||
enter_passphrase(debug)
|
enter_passphrase(debug)
|
||||||
|
@ -18,7 +18,7 @@ import random
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from trezorlib import device, exceptions, messages
|
from trezorlib import device, exceptions, messages, models
|
||||||
from trezorlib.debuglink import LayoutType
|
from trezorlib.debuglink import LayoutType
|
||||||
from trezorlib.debuglink import TrezorClientDebugLink as Client
|
from trezorlib.debuglink import TrezorClientDebugLink as Client
|
||||||
from trezorlib.exceptions import TrezorFailure
|
from trezorlib.exceptions import TrezorFailure
|
||||||
@ -369,14 +369,17 @@ def test_passphrase_length(client: Client):
|
|||||||
assert expected_result is False, "Call should have succeeded"
|
assert expected_result is False, "Call should have succeeded"
|
||||||
assert e.code == FailureType.DataError
|
assert e.code == FailureType.DataError
|
||||||
|
|
||||||
# 50 is ok
|
max_len = 50 if client.model is models.T1B1 else 128
|
||||||
call(passphrase="A" * 50, expected_result=True)
|
|
||||||
# 51 is not
|
# N is ok
|
||||||
call(passphrase="A" * 51, expected_result=False)
|
call(passphrase="A" * max_len, expected_result=True)
|
||||||
# "š" has two bytes - 48x A and "š" should be fine (50 bytes)
|
# N+1 is not
|
||||||
call(passphrase="A" * 48 + "š", expected_result=True)
|
call(passphrase="A" * (max_len + 1), expected_result=False)
|
||||||
# "š" has two bytes - 49x A and "š" should not (51 bytes)
|
|
||||||
call(passphrase="A" * 49 + "š", expected_result=False)
|
# "š" has two bytes - (N-2)x A and "š" should be fine (N bytes)
|
||||||
|
call(passphrase="A" * (max_len - 2) + "š", expected_result=True)
|
||||||
|
# "š" has two bytes - (N-1)x A and "š" should not (N+1 bytes)
|
||||||
|
call(passphrase="A" * (max_len - 1) + "š", expected_result=False)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.models("core")
|
@pytest.mark.models("core")
|
||||||
|
Loading…
Reference in New Issue
Block a user