From 0a13f7a441b084d574a06405b12d34e0a82a002d Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Tue, 11 Feb 2020 15:39:00 +0000 Subject: [PATCH] core: properly limit passphrase to 50 bytes --- core/ChangeLog | 1 + core/src/apps/common/passphrase.py | 4 ++-- .../test_session_id_and_passphrase.py | 24 +++++++++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/core/ChangeLog b/core/ChangeLog index 135c11d43..6ad61f32f 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 9f59f5af5..6a418d48f 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 0ec35e44d..3f401d6fc 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)