diff --git a/core/ChangeLog b/core/ChangeLog index 135c11d436..6ad61f32f6 100644 --- a/core/ChangeLog +++ b/core/ChangeLog @@ -4,6 +4,7 @@ Version 2.x.x [not yet released] Version 2.3.0 [not yet released] * Passphrase redesign +* Properly limit passphrase to 50 bytes and not 50 characters Version 2.2.0 [Jan 2020] * Remove unused ButtonRequest.data field diff --git a/core/src/apps/common/passphrase.py b/core/src/apps/common/passphrase.py index 9f59f5af53..6a418d48f0 100644 --- a/core/src/apps/common/passphrase.py +++ b/core/src/apps/common/passphrase.py @@ -33,8 +33,8 @@ async def _request_from_user(ctx: wire.Context) -> str: passphrase = await _request_on_device(ctx) else: passphrase = await _request_on_host(ctx) - if len(passphrase) > _MAX_PASSPHRASE_LEN: - raise wire.DataError("Maximum passphrase length is %d" % _MAX_PASSPHRASE_LEN) + if len(passphrase.encode()) > _MAX_PASSPHRASE_LEN: + raise wire.DataError("Maximum passphrase length is %d bytes" % _MAX_PASSPHRASE_LEN) return passphrase diff --git a/tests/device_tests/test_session_id_and_passphrase.py b/tests/device_tests/test_session_id_and_passphrase.py index 0ec35e44df..3f401d6fcd 100644 --- a/tests/device_tests/test_session_id_and_passphrase.py +++ b/tests/device_tests/test_session_id_and_passphrase.py @@ -230,6 +230,30 @@ def test_passphrase_missing(client): assert response.code == FailureType.DataError +@pytest.mark.skip_ui +@pytest.mark.setup_client(passphrase=True) +def test_passphrase_length(client): + def call(passphrase: str, expected_result: bool): + _init_session(client) + response = client.call_raw(XPUB_REQUEST) + assert isinstance(response, messages.PassphraseRequest) + response = client.call_raw(messages.PassphraseAck(passphrase)) + if expected_result: + assert isinstance(response, messages.PublicKey) + else: + assert isinstance(response, messages.Failure) + assert response.code == FailureType.DataError + + # 50 is ok + call(passphrase="A" * 50, expected_result=True) + # 51 is not + call(passphrase="A" * 51, expected_result=False) + # "š" has two bytes - 48x A and "š" should be fine (50 bytes) + call(passphrase="A" * 48 + "š", expected_result=True) + # "š" has two bytes - 49x A and "š" should not (51 bytes) + call(passphrase="A" * 49 + "š", expected_result=False) + + def _get_xpub_cardano(client, passphrase): msg = messages.CardanoGetPublicKey(address_n=parse_path("44'/1815'/0'/0/0")) response = client.call_raw(msg)