diff --git a/common/tests/fixtures/cardano/get_base_address.derivations.json b/common/tests/fixtures/cardano/get_base_address.derivations.json new file mode 100644 index 000000000..7d3d21bcc --- /dev/null +++ b/common/tests/fixtures/cardano/get_base_address.derivations.json @@ -0,0 +1,50 @@ +{ + "setup": { + "mnemonic": "very improve such purity recipe deer giggle shuffle bamboo sorry galaxy damp confirm note easy pause coast purse often hint west angle spare north", + "passphrase": "" + }, + "tests": [ + { + "name": "ledger-derivation", + "parameters": { + "derivation_type": "LEDGER", + "path": "m/1852'/1815'/0'/0/0", + "address_type": "base", + "staking_path": "m/1852'/1815'/0'/2/0", + "network_id": 1, + "protocol_magic": 764824073 + }, + "result": { + "expected_address": "addr1q8afdsv8q9hr4z8qljl3qz3rvm048g8j506j6umrpg63nytg4txp8fd25x5lnmkdh5p9wudkh4llu4dxyc3jyjef0gwqa5dzfd" + } + }, + { + "name": "icarus-derivation", + "parameters": { + "derivation_type": "ICARUS", + "path": "m/1852'/1815'/0'/0/0", + "address_type": "base", + "staking_path": "m/1852'/1815'/0'/2/0", + "network_id": 1, + "protocol_magic": 764824073 + }, + "result": { + "expected_address": "addr1q996av5n0379tp8yllaxtgj3ffntmlrsl9sah237h65x6yt76a24dxtkgy59n8tvk00jf5dwfp8cz50g0uheuzfddcnqxp4njq" + } + }, + { + "name": "icarus-trezor-derivation", + "parameters": { + "derivation_type": "ICARUS_TREZOR", + "path": "m/1852'/1815'/0'/0/0", + "address_type": "base", + "staking_path": "m/1852'/1815'/0'/2/0", + "network_id": 1, + "protocol_magic": 764824073 + }, + "result": { + "expected_address": "addr1q8g9th06vccxzl96nr905al0vgg2t0fqfxrxmv7ecye3x2e66sl9kvzd905ad5natzd5wghy2w3a9lm0y7u7c5sv0c2snql3c4" + } + } + ] +} diff --git a/common/tests/fixtures/cardano/get_public_key.derivations.json b/common/tests/fixtures/cardano/get_public_key.derivations.json new file mode 100644 index 000000000..e8310c356 --- /dev/null +++ b/common/tests/fixtures/cardano/get_public_key.derivations.json @@ -0,0 +1,41 @@ +{ + "setup": { + "mnemonic": "very improve such purity recipe deer giggle shuffle bamboo sorry galaxy damp confirm note easy pause coast purse often hint west angle spare north", + "passphrase": "" + }, + "tests": [ + { + "name": "ledger-derivation", + "parameters": { + "path": "m/1852'/1815'/0'", + "derivation_type": "LEDGER" + }, + "result": { + "public_key": "09a48c3390586bf1b5141e141fd2045b54f0a100af4254abb9bcf124eb82b078", + "chain_code": "abb4f1731e9bb4151fb8bbe4a63c64b84a6e6193ef2b97edd4176136d55c235f" + } + }, + { + "name": "icarus-derivation", + "parameters": { + "path": "m/1852'/1815'/0'", + "derivation_type": "ICARUS" + }, + "result": { + "public_key": "8946b80aaa2ab0839276ca373d0b7845803ae39e452ac2469986bf8b360b217f", + "chain_code": "85e096c7789c5aa29012633044750b1a79ab5ac2eb7189c91113e8ffbadd550f" + } + }, + { + "name": "icarus-trezor-derivation", + "parameters": { + "path": "m/1852'/1815'/0'", + "derivation_type": "ICARUS_TREZOR" + }, + "result": { + "public_key": "8a97a956fadbc79e09a867ed1e829af4f66a34903c9194de44c87262dfe19111", + "chain_code": "dd97903ff01f4fcb6375c8252b31a59d9b170c1b61f5786304dc676270f44a90" + } + } + ] +} diff --git a/tests/device_tests/cardano/test_address_public_key.py b/tests/device_tests/cardano/test_address_public_key.py index ffb4d7092..890ef6f46 100644 --- a/tests/device_tests/cardano/test_address_public_key.py +++ b/tests/device_tests/cardano/test_address_public_key.py @@ -22,8 +22,7 @@ from trezorlib.cardano import ( get_public_key, parse_optional_bytes, ) -from trezorlib.exceptions import TrezorFailure -from trezorlib.messages import CardanoAddressType +from trezorlib.messages import CardanoAddressType, CardanoDerivationType from trezorlib.tools import parse_path from ...common import parametrize_using_common_fixtures @@ -44,10 +43,15 @@ pytestmark = [ "cardano/get_enterprise_address.json", "cardano/get_pointer_address.json", "cardano/get_reward_address.json", + "cardano/get_base_address.derivations.json", ) def test_cardano_get_address(client, parameters, result): client.init_device(new_session=True, derive_cardano=True) + derivation_type = CardanoDerivationType.__members__[ + parameters.get("derivation_type", "ICARUS_TREZOR") + ] + address = get_address( client, address_parameters=create_address_parameters( @@ -74,6 +78,7 @@ def test_cardano_get_address(client, parameters, result): protocol_magic=parameters["protocol_magic"], network_id=parameters["network_id"], show_display=True, + derivation_type=derivation_type, ) assert address == result["expected_address"] @@ -81,22 +86,16 @@ def test_cardano_get_address(client, parameters, result): @parametrize_using_common_fixtures( "cardano/get_public_key.json", "cardano/get_public_key.slip39.json", + "cardano/get_public_key.derivations.json", ) def test_cardano_get_public_key(client, parameters, result): client.init_device(new_session=True, derive_cardano=True) - key = get_public_key(client, parse_path(parameters["path"])) + derivation_type = CardanoDerivationType.__members__[ + parameters.get("derivation_type", "ICARUS_TREZOR") + ] + key = get_public_key(client, parse_path(parameters["path"]), derivation_type) assert key.node.public_key.hex() == result["public_key"] assert key.node.chain_code.hex() == result["chain_code"] assert key.xpub == result["public_key"] + result["chain_code"] - - -def test_bad_session(client): - client.init_device(new_session=True) - with pytest.raises(TrezorFailure, match="not enabled"): - get_public_key(client, parse_path("m/1852'/1815'/0'")) - - client.init_device(new_session=True, derive_cardano=False) - with pytest.raises(TrezorFailure, match="not enabled"): - get_public_key(client, parse_path("m/1852'/1815'/0'")) diff --git a/tests/device_tests/cardano/test_derivations.py b/tests/device_tests/cardano/test_derivations.py new file mode 100644 index 000000000..95ddc98bd --- /dev/null +++ b/tests/device_tests/cardano/test_derivations.py @@ -0,0 +1,59 @@ +# This file is part of the Trezor project. +# +# Copyright (C) 2012-2019 SatoshiLabs and contributors +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the License along with this library. +# If not, see . + +import pytest + +from trezorlib.cardano import get_public_key +from trezorlib.exceptions import TrezorFailure +from trezorlib.messages import CardanoDerivationType as D +from trezorlib.tools import parse_path + +from ...common import MNEMONIC_SLIP39_BASIC_20_3of6 + +pytestmark = [ + pytest.mark.altcoin, + pytest.mark.cardano, + pytest.mark.skip_t1, +] + +ADDRESS_N = parse_path("m/1852'/1815'/0'") + + +def test_bad_session(client): + client.init_device(new_session=True) + with pytest.raises(TrezorFailure, match="not enabled"): + get_public_key(client, ADDRESS_N, derivation_type=D.ICARUS) + + client.init_device(new_session=True, derive_cardano=False) + with pytest.raises(TrezorFailure, match="not enabled"): + get_public_key(client, ADDRESS_N, derivation_type=D.ICARUS) + + +def test_ledger_available_always(client): + client.init_device(new_session=True, derive_cardano=False) + get_public_key(client, ADDRESS_N, derivation_type=D.LEDGER) + + client.init_device(new_session=True, derive_cardano=True) + get_public_key(client, ADDRESS_N, derivation_type=D.LEDGER) + + +@pytest.mark.setup_client(mnemonic=MNEMONIC_SLIP39_BASIC_20_3of6) +@pytest.mark.parametrize("derivation_type", D) # try ALL derivation types +def test_derivation_irrelevant_on_slip39(client, derivation_type): + client.init_device(new_session=True, derive_cardano=False) + pubkey = get_public_key(client, ADDRESS_N, derivation_type=D.ICARUS) + test_pubkey = get_public_key(client, ADDRESS_N, derivation_type=derivation_type) + assert pubkey == test_pubkey diff --git a/tests/device_tests/test_session.py b/tests/device_tests/test_session.py index 47b928ac2..06726c7ee 100644 --- a/tests/device_tests/test_session.py +++ b/tests/device_tests/test_session.py @@ -16,7 +16,7 @@ import pytest -from trezorlib import messages +from trezorlib import cardano, messages from trezorlib.btc import get_public_node from trezorlib.exceptions import TrezorFailure from trezorlib.tools import parse_path @@ -154,3 +154,69 @@ def test_session_recycling(client): client.use_passphrase("TREZOR") client.init_device(session_id=session_id_orig) assert address == get_test_address(client) + + +@pytest.mark.altcoin +@pytest.mark.cardano +@pytest.mark.skip_t1 +def test_derive_cardano_empty_session(client): + # start new session + client.init_device(new_session=True) + session_id = client.session_id + + # restarting same session should go well + client.init_device() + assert session_id == client.session_id + + # restarting same session should go well with any setting + client.init_device(derive_cardano=False) + assert session_id == client.session_id + client.init_device(derive_cardano=True) + assert session_id == client.session_id + + +@pytest.mark.altcoin +@pytest.mark.cardano +@pytest.mark.skip_t1 +def test_derive_cardano_running_session(client): + # start new session + client.init_device(new_session=True) + session_id = client.session_id + # force derivation of seed + get_test_address(client) + + # session should not have Cardano capability + with pytest.raises(TrezorFailure, match="not enabled"): + cardano.get_public_key(client, parse_path("m/44h/1815h/0h")) + + # restarting same session should go well + client.init_device() + assert session_id == client.session_id + + # restarting same session should go well if we _don't_ want to derive cardano + client.init_device(derive_cardano=False) + assert session_id == client.session_id + + # restarting with derive_cardano=True should kill old session and create new one + client.init_device(derive_cardano=True) + assert session_id != client.session_id + + session_id = client.session_id + + # new session should have Cardano capability + cardano.get_public_key(client, parse_path("m/44h/1815h/0h")) + + # restarting with derive_cardano=True should keep same session + client.init_device(derive_cardano=True) + assert session_id == client.session_id + + # restarting with no setting should keep same session + client.init_device() + assert session_id == client.session_id + + # restarting with derive_cardano=False should kill old session and create new one + client.init_device(derive_cardano=False) + assert session_id != client.session_id + + with pytest.raises(TrezorFailure, match="not enabled"): + cardano.get_public_key(client, parse_path("m/44h/1815h/0h")) diff --git a/tests/device_tests/test_session_id_and_passphrase.py b/tests/device_tests/test_session_id_and_passphrase.py index eb4cd03ca..5ab4795c5 100644 --- a/tests/device_tests/test_session_id_and_passphrase.py +++ b/tests/device_tests/test_session_id_and_passphrase.py @@ -35,6 +35,7 @@ XPUB_PASSPHRASES = { "J": "xpub6CVeYPTG57D4tm9BvwCcakppwGJstbXyK8Yd611agusZuHmx7og3dNvr6pjMN6e4BoaNc5MZA4TjMLjMT2h2vJRU8rYLvHFUwrEL9zDbuqe", } XPUB_PASSPHRASE_NONE = "xpub6BiVtCpG9fQPxnPmHXG8PhtzQdWC2Su4qWu6XW9tpWFYhxydCLJGrWBJZ5H6qTAHdPQ7pQhtpjiYZVZARo14qHiay2fvrX996oEP42u8wZy" +XPUB_CARDANO_PASSPHRASE_A = "d37eba66d6183547b11b4d0c3e08e761da9f07c3ef32183f8b79360b2b66850e47e8eb3865251784c3c471a854ee40dfc067f7f3afe47d093388ea45239606fd" XPUB_CARDANO_PASSPHRASE_B = "d80e770f6dfc3edb58eaab68aa091b2c27b08a47583471e93437ac5f8baa61880c7af4938a941c084c19731e6e57a5710e6ad1196263291aea297ce0eec0f177" ADDRESS_N = parse_path("44h/0h/0h") @@ -43,9 +44,11 @@ XPUB_REQUEST = messages.GetPublicKey(address_n=ADDRESS_N, coin_name="Bitcoin") SESSIONS_STORED = 10 -def _init_session(client, session_id=None): +def _init_session(client, session_id=None, derive_cardano=False): """Call Initialize, check and return the session ID.""" - response = client.call(messages.Initialize(session_id=session_id)) + response = client.call( + messages.Initialize(session_id=session_id, derive_cardano=derive_cardano) + ) assert isinstance(response, messages.Features) assert len(response.session_id) == 32 return response.session_id @@ -372,7 +375,10 @@ def test_passphrase_length(client): def _get_xpub_cardano(client, passphrase): - msg = messages.CardanoGetPublicKey(address_n=parse_path("44'/1815'/0'/0/0")) + msg = messages.CardanoGetPublicKey( + address_n=parse_path("44'/1815'/0'/0/0"), + derivation_type=messages.CardanoDerivationType.ICARUS, + ) response = client.call_raw(msg) if passphrase is not None: assert isinstance(response, messages.PassphraseRequest) @@ -385,36 +391,35 @@ def _get_xpub_cardano(client, passphrase): @pytest.mark.altcoin @pytest.mark.setup_client(passphrase=True) def test_cardano_passphrase(client): - # Cardano uses a variation of BIP-39 so we need to ask for the passphrase again. + # Cardano has a separate derivation method that needs to access the plaintext + # of the passphrase. + # Historically, Cardano calls would ask for passphrase again. Now, they should not. - session_id = _init_session(client) + session_id = _init_session(client, derive_cardano=True) # GetPublicKey requires passphrase and since it is not cached, # Trezor will prompt for it. - assert _get_xpub(client, passphrase="A") == XPUB_PASSPHRASES["A"] + assert _get_xpub(client, passphrase="B") == XPUB_PASSPHRASES["B"] # The passphrase is now cached for non-Cardano coins. - assert _get_xpub(client, passphrase=None) == XPUB_PASSPHRASES["A"] - - # Cardano will prompt for it again. - assert _get_xpub_cardano(client, passphrase="B") == XPUB_CARDANO_PASSPHRASE_B + assert _get_xpub(client, passphrase=None) == XPUB_PASSPHRASES["B"] - # But now also Cardano has it cached. + # The passphrase should be cached for Cardano as well assert _get_xpub_cardano(client, passphrase=None) == XPUB_CARDANO_PASSPHRASE_B - # And others behaviour did not change. - assert _get_xpub(client, passphrase=None) == XPUB_PASSPHRASES["A"] - # Initialize with the session id does not destroy the state - _init_session(client, session_id=session_id) - assert _get_xpub(client, passphrase=None) == XPUB_PASSPHRASES["A"] + _init_session(client, session_id=session_id, derive_cardano=True) + assert _get_xpub(client, passphrase=None) == XPUB_PASSPHRASES["B"] assert _get_xpub_cardano(client, passphrase=None) == XPUB_CARDANO_PASSPHRASE_B # New session will destroy the state - _init_session(client) + _init_session(client, derive_cardano=True) - # GetPublicKey must ask for passphrase again - assert _get_xpub(client, passphrase="A") == XPUB_PASSPHRASES["A"] + # Cardano must ask for passphrase again + assert _get_xpub_cardano(client, passphrase="A") == XPUB_CARDANO_PASSPHRASE_A + + # Passphrase is now cached for Cardano + assert _get_xpub_cardano(client, passphrase=None) == XPUB_CARDANO_PASSPHRASE_A - # Cardano must also ask for passphrase again - assert _get_xpub_cardano(client, passphrase="B") == XPUB_CARDANO_PASSPHRASE_B + # Passphrase is cached for non-Cardano coins too + assert _get_xpub(client, passphrase=None) == XPUB_PASSPHRASES["A"] diff --git a/tests/ui_tests/fixtures.json b/tests/ui_tests/fixtures.json index 07bfe7d17..f0c6477ad 100644 --- a/tests/ui_tests/fixtures.json +++ b/tests/ui_tests/fixtures.json @@ -1,5 +1,7 @@ { -"cardano-test_address_public_key.py::test_bad_session": "c09de07fbbf1e047442180e2facb5482d06a1a428891b875b7dd93c9e4704ae1", +"cardano-test_address_public_key.py::test_cardano_get_address[icarus-derivation]": "61bf57f65a018283d5e314f95f5580fe751eb3f09cd175a8f9e47f677419cbf2", +"cardano-test_address_public_key.py::test_cardano_get_address[icarus-trezor-derivation]": "d2b12b3a3fe426785e5dad64d7b48e8e266983011f8a837abccc2f4a8f9c43e6", +"cardano-test_address_public_key.py::test_cardano_get_address[ledger-derivation]": "08b2ef82b653af85b33253a45faca36669e4fb044596c4595890efaa48c84595", "cardano-test_address_public_key.py::test_cardano_get_address[parameters0-result0]": "e3be7c64097574e795bad1852f3eb7b9d5c4577ed88a845329bacc7350338d74", "cardano-test_address_public_key.py::test_cardano_get_address[parameters1-result1]": "50b6d3e772e77d6f52c7883f89c3b5f196e43865982c6c5b5171a72546e68164", "cardano-test_address_public_key.py::test_cardano_get_address[parameters10-result10]": "31b0bb5d4ab8cbd5293cbdc8c44f722e5e39c1f633eaa1db2186cb138d88dd5c", @@ -37,6 +39,9 @@ "cardano-test_address_public_key.py::test_cardano_get_address[parameters7-result7]": "d1e4b84dcafcf246d14f7093694df1e14831387e2fd519150949cbff077b82cd", "cardano-test_address_public_key.py::test_cardano_get_address[parameters8-result8]": "124fdbdf78ad7cbb4e5bd81abe8d8b5738be7544b7fd939de800df5e3fde71ac", "cardano-test_address_public_key.py::test_cardano_get_address[parameters9-result9]": "4b12964477d051513257ea78693ca3d0262be7f05d29db54a92398eb7add5ed1", +"cardano-test_address_public_key.py::test_cardano_get_public_key[icarus-derivation]": "095af81ec79e9b510c90d9fa34fed343f3840807190c67bc237af885695ae687", +"cardano-test_address_public_key.py::test_cardano_get_public_key[icarus-trezor-derivation]": "095af81ec79e9b510c90d9fa34fed343f3840807190c67bc237af885695ae687", +"cardano-test_address_public_key.py::test_cardano_get_public_key[ledger-derivation]": "095af81ec79e9b510c90d9fa34fed343f3840807190c67bc237af885695ae687", "cardano-test_address_public_key.py::test_cardano_get_public_key[parameters0-result0]": "095af81ec79e9b510c90d9fa34fed343f3840807190c67bc237af885695ae687", "cardano-test_address_public_key.py::test_cardano_get_public_key[parameters1-result1]": "095af81ec79e9b510c90d9fa34fed343f3840807190c67bc237af885695ae687", "cardano-test_address_public_key.py::test_cardano_get_public_key[parameters10-result10]": "095af81ec79e9b510c90d9fa34fed343f3840807190c67bc237af885695ae687", @@ -54,6 +59,11 @@ "cardano-test_address_public_key.py::test_cardano_get_public_key[parameters7-result7]": "095af81ec79e9b510c90d9fa34fed343f3840807190c67bc237af885695ae687", "cardano-test_address_public_key.py::test_cardano_get_public_key[parameters8-result8]": "095af81ec79e9b510c90d9fa34fed343f3840807190c67bc237af885695ae687", "cardano-test_address_public_key.py::test_cardano_get_public_key[parameters9-result9]": "095af81ec79e9b510c90d9fa34fed343f3840807190c67bc237af885695ae687", +"cardano-test_derivations.py::test_bad_session": "c09de07fbbf1e047442180e2facb5482d06a1a428891b875b7dd93c9e4704ae1", +"cardano-test_derivations.py::test_derivation_irrelevant_on_slip39[CardanoDerivationType.ICA-3b0af713": "c09de07fbbf1e047442180e2facb5482d06a1a428891b875b7dd93c9e4704ae1", +"cardano-test_derivations.py::test_derivation_irrelevant_on_slip39[CardanoDerivationType.ICARUS]": "c09de07fbbf1e047442180e2facb5482d06a1a428891b875b7dd93c9e4704ae1", +"cardano-test_derivations.py::test_derivation_irrelevant_on_slip39[CardanoDerivationType.LEDGER]": "c09de07fbbf1e047442180e2facb5482d06a1a428891b875b7dd93c9e4704ae1", +"cardano-test_derivations.py::test_ledger_available_always": "c09de07fbbf1e047442180e2facb5482d06a1a428891b875b7dd93c9e4704ae1", "cardano-test_get_native_script_hash.py::test_cardano_get_native_script_hash[all_script]": "96dfeb79b33a0b49b761fbd694423e57567ee932da5e75514315159aaad3288b", "cardano-test_get_native_script_hash.py::test_cardano_get_native_script_hash[all_script_cont-aae1e6c3": "aef99839b7f2706cfb76aed84bfb862647bdd6d0013326aad1fc90d704c98e9e", "cardano-test_get_native_script_hash.py::test_cardano_get_native_script_hash[all_script_cont-e4ca0ea5": "c488d5e29487e9a75f2146694c11699af6c2c2893f03773434996386f01469ea", @@ -816,10 +826,12 @@ "test_sdcard.py::test_sd_protect_unlock": "bc654959615d17f6d8d6dda4625b4ecf7798a7d8c05ff438fc1546d0f8547432", "test_session.py::test_cannot_resume_ended_session": "c09de07fbbf1e047442180e2facb5482d06a1a428891b875b7dd93c9e4704ae1", "test_session.py::test_clear_session": "a749e7ff817a859da0670e7810d683909b9c4db415594098981183adb9c6be2e", +"test_session.py::test_derive_cardano_empty_session": "c09de07fbbf1e047442180e2facb5482d06a1a428891b875b7dd93c9e4704ae1", +"test_session.py::test_derive_cardano_running_session": "c09de07fbbf1e047442180e2facb5482d06a1a428891b875b7dd93c9e4704ae1", "test_session.py::test_end_session": "c09de07fbbf1e047442180e2facb5482d06a1a428891b875b7dd93c9e4704ae1", "test_session.py::test_end_session_only_current": "c09de07fbbf1e047442180e2facb5482d06a1a428891b875b7dd93c9e4704ae1", "test_session.py::test_session_recycling": "54fe7196c39e3f70734be72fc45a121670e891852354057b4d8ab094ced4b493", -"test_session_id_and_passphrase.py::test_cardano_passphrase": "cb1d3bb493357a2f07f3efdbe9695b9e0820165a78bb3265c7c7c4abdb535e92", +"test_session_id_and_passphrase.py::test_cardano_passphrase": "c35e3d1e5a374a3c55a062659f9ccf51cf950dfb11ef61fc08f8c6ce827bcef5", "test_session_id_and_passphrase.py::test_max_sessions_with_passphrases": "a8e25f2a9ef380c9390568977c68de5167e75cc39989bec7939dcf2dd908da8b", "test_session_id_and_passphrase.py::test_multiple_passphrases": "90bffec62c5f070b14d0ce7b505b1aa455b379c3c3c0a0a5d4f27af7a78325df", "test_session_id_and_passphrase.py::test_multiple_sessions": "c09de07fbbf1e047442180e2facb5482d06a1a428891b875b7dd93c9e4704ae1",