diff --git a/legacy/firmware/.changelog.d/3043.fixed b/legacy/firmware/.changelog.d/3043.fixed new file mode 100644 index 0000000000..d95f0eb015 --- /dev/null +++ b/legacy/firmware/.changelog.d/3043.fixed @@ -0,0 +1 @@ +Allow showing XPUB using a QR code. diff --git a/legacy/firmware/fsm_msg_coin.h b/legacy/firmware/fsm_msg_coin.h index b767859b76..0f6bf7c200 100644 --- a/legacy/firmware/fsm_msg_coin.h +++ b/legacy/firmware/fsm_msg_coin.h @@ -114,13 +114,16 @@ void fsm_msgGetPublicKey(const GetPublicKey *msg) { } if (msg->has_show_display && msg->show_display) { - for (int page = 0; page < 2; page++) { - layoutXPUB(resp->xpub, page); - if (!protectButton(ButtonRequestType_ButtonRequest_PublicKey, true)) { - memzero(resp, sizeof(PublicKey)); - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; + int page = 0; + bool qrcode = false; + + while (page < 2) { + layoutXPUB(resp->xpub, page, qrcode); + if (protectButton(ButtonRequestType_ButtonRequest_PublicKey, false)) { + page += 1; // advance to the next page + qrcode = false; // switch to XPUB text + } else { + qrcode = !qrcode; // switch to and from QR } } } diff --git a/legacy/firmware/layout2.c b/legacy/firmware/layout2.c index abebeced1f..00ef281003 100644 --- a/legacy/firmware/layout2.c +++ b/legacy/firmware/layout2.c @@ -985,17 +985,21 @@ static void _layout_xpub(const char *xpub, const char *desc, int page) { } } -void layoutXPUB(const char *xpub, int page) { +void layoutXPUB(const char *xpub, int page, bool qrcode) { if (layoutLast != layoutAddress && layoutLast != layoutXPUB) { layoutSwipe(); } else { oledClear(); } layoutLast = layoutXPUB; - char desc[] = "XPUB _/2"; - desc[5] = '1' + page; - _layout_xpub(xpub, desc, page); - layoutButtonNo(_("Cancel"), &bmp_btn_cancel); + if (qrcode) { + renderQR(xpub); + } else { + char desc[] = "XPUB _/2"; + desc[5] = '1' + page; + _layout_xpub(xpub, desc, page); + layoutButtonNo(_("QR Code"), NULL); + } layoutButtonYes(_("Confirm"), &bmp_btn_confirm); oledRefresh(); } diff --git a/legacy/firmware/layout2.h b/legacy/firmware/layout2.h index 7b9076259f..17ea8ef5dc 100644 --- a/legacy/firmware/layout2.h +++ b/legacy/firmware/layout2.h @@ -92,7 +92,7 @@ void layoutAddress(const char *address, const char *desc, bool qrcode, bool ignorecase, const uint32_t *address_n, size_t address_n_count, bool address_is_account); void layoutPublicKey(const uint8_t *pubkey); -void layoutXPUB(const char *xpub, int page); +void layoutXPUB(const char *xpub, int page, bool qrcode); void layoutXPUBMultisig(const char *xpub, int index, int page, bool ours); void layoutSignIdentity(const IdentityType *identity, const char *challenge); void layoutDecryptIdentity(const IdentityType *identity); diff --git a/tests/device_tests/bitcoin/test_getpublickey.py b/tests/device_tests/bitcoin/test_getpublickey.py index e8b90cbb48..be0c43e535 100644 --- a/tests/device_tests/bitcoin/test_getpublickey.py +++ b/tests/device_tests/bitcoin/test_getpublickey.py @@ -134,6 +134,39 @@ def test_invalid_path(client: Client, coin_name, path): btc.get_public_node(client, path, coin_name=coin_name) +@pytest.mark.models("legacy") +@pytest.mark.parametrize("coin_name, xpub_magic, path, xpub", VECTORS_BITCOIN) +def test_get_public_node_show_legacy(client: Client, coin_name, xpub_magic, path, xpub): + def input_flow(): + yield + client.debug.press_no() # show QR code + yield + client.debug.press_no() # back to text + yield + client.debug.press_no() # show QR code + yield + client.debug.press_yes() # next xpub page + yield + client.debug.press_no() # show QR code again + yield + client.debug.press_no() # back to text + yield + client.debug.press_yes() # finish the flow + yield + + with client: + # test XPUB display flow (without showing QR code) + res = btc.get_public_node(client, path, coin_name=coin_name, show_display=True) + assert res.xpub == xpub + assert bip32.serialize(res.node, xpub_magic) == xpub + + # test XPUB QR code display using the input flow above + client.set_input_flow(input_flow) + res = btc.get_public_node(client, path, coin_name=coin_name, show_display=True) + assert res.xpub == xpub + assert bip32.serialize(res.node, xpub_magic) == xpub + + def test_slip25_path(client: Client): # Ensure that CoinJoin XPUBs are inaccessible without user authorization. with pytest.raises(TrezorFailure, match="Forbidden key path"): diff --git a/tests/ui_tests/fixtures.json b/tests/ui_tests/fixtures.json index b5789dbbbd..d8ec870419 100644 --- a/tests/ui_tests/fixtures.json +++ b/tests/ui_tests/fixtures.json @@ -31,24 +31,24 @@ "T1B1_en_bitcoin-test_decred.py::test_decred_multisig_change": "8f95b7d2e65a76068196ab46c8189ba5a137795b43e88699d85ad7e2c164d645", "T1B1_en_bitcoin-test_decred.py::test_send_decred": "50fa7dc09dbf22fc4c1e62ee2870e9047e29dd050edbdcf34341a5483d52cdac", "T1B1_en_bitcoin-test_decred.py::test_send_decred_change": "b27eb08c244e00f9a2c5fecad0a8efc65fa2eba15f72ad0eb3b42a3ac16d465d", -"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Bitcoin-0-10025-InputScriptType.SPE-6c24ae6f": "d1ac62d128ed78eef5947994e9c9d70194a203eadb5a50f9ca9af66b42ba333d", -"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Bitcoin-0-44-InputScriptType.SPENDA-fc66840d": "9b30c98b35338d933fe992e4a47fb259a54f0f6f204610f63c6e6cbff427dbb9", -"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Bitcoin-0-49-InputScriptType.SPENDP-3f679311": "09d1f2688240677ac55f2677b62973ee7fe4ca788e9e139b9f6de5a1a66d6205", -"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Bitcoin-0-84-InputScriptType.SPENDW-dedbd46d": "565050e8908a69cd9866a8696cdc103c19d1299486c682211f608e63b6597648", -"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Bitcoin-0-86-InputScriptType.SPENDT-9b717395": "f71944d71edb2531faef0a177c2eda4f88bcb0ef77883db18f655d706759b5b0", -"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Bitcoin-1-44-InputScriptType.SPENDA-908dd45b": "c7987e946ff4f7f80f2aef9acd226f50ab2439837a188c0601861d0f15ca14c3", -"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Bitcoin-1-49-InputScriptType.SPENDP-07c408b2": "fd3a4f48f9ea6b02a88db9ec8ab21ad72a4aee1e983e4e626b420834b375e190", -"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Bitcoin-1-84-InputScriptType.SPENDW-ae5dad46": "0c3090e26ede1019969dee87ce0ea88d46e54ec587c54cf975e1947fe90b5a0e", -"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Bitcoin-1-86-InputScriptType.SPENDT-f5c0cb2f": "c7a98dbc8fdf8b8a62d3ececef56e7ff2613a9491e6734b5683c1e2a5a074bed", -"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Testnet-0-10025-InputScriptType.SPE-ad017c81": "ec6aa625a688e2d54857aafca83b513d6509b13898b0fb79166e5d85c74a414b", -"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Testnet-0-44-InputScriptType.SPENDA-87568704": "a03f1c3753c5a53e05ea9737c7f2583c2935a1a68684d37c0230a1a4d9453bea", -"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Testnet-0-49-InputScriptType.SPENDP-08c818f5": "84ee5c5a45dbff7bb3d2d79b48e2323c84ff589540f6806b32b286f6bc5ce4e8", -"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Testnet-0-84-InputScriptType.SPENDW-3a2da005": "f56e158c4fe862875dc4d655e0a5f203312a6509bf7c16bf462a237feaf78b7b", -"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Testnet-0-86-InputScriptType.SPENDT-0d2d3911": "5f990152867980f9b129e85f9fa1963006895c49a33045e2a9363cb9fa493f26", -"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Testnet-1-44-InputScriptType.SPENDA-06441aab": "bf7d2992e2732384f0e574732e36342b09ef6a8b50641a29c5d05a13c026fd7b", -"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Testnet-1-49-InputScriptType.SPENDP-2341fa5a": "a381684066de8a00048ab82491599140c8e36a8eed9b9c31284e76275b7d396c", -"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Testnet-1-84-InputScriptType.SPENDW-59aa0a79": "cd384fcf3379759dd5fc3f2737ddfb9185a55e506d9d0869f8a85ba31ed1d973", -"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Testnet-1-86-InputScriptType.SPENDT-af95048b": "f24b6c201efc2efbb174a48766df03f5964ea95908abca791714e22bcf9b635d", +"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Bitcoin-0-10025-InputScriptType.SPE-6c24ae6f": "104401402fa93edc328c73d3c6ac3f185d55bc2515c4c6f74f518ecb2c247048", +"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Bitcoin-0-44-InputScriptType.SPENDA-fc66840d": "8ce74bf750fa077f3349cce10fabfe945bd3c44a957de62949ccb33b9f9dd0b8", +"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Bitcoin-0-49-InputScriptType.SPENDP-3f679311": "35e777790592f18aa33e3ca9d7bc8de386f3e079966e914c836c88c30ff85fd6", +"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Bitcoin-0-84-InputScriptType.SPENDW-dedbd46d": "7c9abf10389dba7937e29b9d494639589e0bff2698e54cef177e3b6da0c92e18", +"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Bitcoin-0-86-InputScriptType.SPENDT-9b717395": "eda93660418b28f718f1370691abc58427f382d0cbb6fc1d51e2bda02ad4dba4", +"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Bitcoin-1-44-InputScriptType.SPENDA-908dd45b": "1fc0840d4f3da3dcd2d0267a5f8c1578fabc92af6f675ba7d90ae1d72191bb8d", +"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Bitcoin-1-49-InputScriptType.SPENDP-07c408b2": "54991fdd4d36a72f0948611b20f6e07b7cab07854a4ad538981ac54ebedc6961", +"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Bitcoin-1-84-InputScriptType.SPENDW-ae5dad46": "e532ad6eee6ed465436d2265de6216c9194ba04f03706df70b69524a78ec8d17", +"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Bitcoin-1-86-InputScriptType.SPENDT-f5c0cb2f": "5548f9742257256c208b3901d9c76c25f8287a44b4083d267f0d7f75603f9120", +"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Testnet-0-10025-InputScriptType.SPE-ad017c81": "60d72b0cf0980fe5d5779e561b4d32c18ec088206d9a9c3ec1e0b1d3954df53f", +"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Testnet-0-44-InputScriptType.SPENDA-87568704": "45323e5eaab0c214c44b4b6667dfeb6870a7c22da1e23e4bfcdf3945c11ae45a", +"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Testnet-0-49-InputScriptType.SPENDP-08c818f5": "9f147172e3cd4911bacf3c18754fb46f7c6dc7c46d080b9e9328fb48d276df30", +"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Testnet-0-84-InputScriptType.SPENDW-3a2da005": "c10d0063e773ecee0ed5f844ac5b3000ccf762e8dc5ccaed188f0a84f9f788d3", +"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Testnet-0-86-InputScriptType.SPENDT-0d2d3911": "7febe50f7f780da4275d4e3bcca421a91a93e327fcb9d108ca5404b1e4500deb", +"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Testnet-1-44-InputScriptType.SPENDA-06441aab": "7b204973f3ca702ad0f69799b795f601d1441a56be38bdf9166aada439227674", +"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Testnet-1-49-InputScriptType.SPENDP-2341fa5a": "cfe83fe0b30062606ccd54403c4dc9c256ebfb5ca60474c128f9d04380dac9f4", +"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Testnet-1-84-InputScriptType.SPENDW-59aa0a79": "6e6dab3036adda56b78ceafe68239522c27f4ee3fec74f0113371c5cfa85ca93", +"T1B1_en_bitcoin-test_descriptors.py::test_descriptors_trezorlib[Testnet-1-86-InputScriptType.SPENDT-af95048b": "32fce016e30f21c81a33514e2895972ac58dceb5e61b55fa4990552910144408", "T1B1_en_bitcoin-test_firo.py::test_spend_lelantus": "cbb87e77f54c351584dd713a8eaabb99b65a515e57c30bfb9e7a38660ca6895b", "T1B1_en_bitcoin-test_fujicoin.py::test_send_p2tr": "bfa5dc1a494ea17fd43d52a315a7f1abe9ad80d7fdaa8b3ec5f92c4118638d1e", "T1B1_en_bitcoin-test_getaddress.py::test_bch": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", @@ -133,6 +133,17 @@ "T1B1_en_bitcoin-test_getpublickey.py::test_get_public_node[Litecoin-27108450-path9-Ltub2dTvwC4v7GNe-8d6d95fb": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "T1B1_en_bitcoin-test_getpublickey.py::test_get_public_node[Testnet-70617039-path4-tpubDDKn3FtHc74Ca-f3b70aff": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "T1B1_en_bitcoin-test_getpublickey.py::test_get_public_node[Testnet-70617039-path5-tpubDGwNSs8z8jZU2-8b5efa13": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", +"T1B1_en_bitcoin-test_getpublickey.py::test_get_public_node_show_legacy[Bitcoin-76067358-path0-xpub6-bf232aa0": "9432d50e05621eaf35b55bed19fd73227733b286324f9080310dc0c98a2df03e", +"T1B1_en_bitcoin-test_getpublickey.py::test_get_public_node_show_legacy[Bitcoin-76067358-path1-xpub6-42fe2535": "7b2f01d7e2744b837be4606aec7ef5361b230299fb2d3a515bfa69ac2c9591d9", +"T1B1_en_bitcoin-test_getpublickey.py::test_get_public_node_show_legacy[Bitcoin-76067358-path2-xpub6-7d53be7d": "97e473eaf4a28afb6531460e9c282f1a3edf572b8044986158bf5d230fc480c9", +"T1B1_en_bitcoin-test_getpublickey.py::test_get_public_node_show_legacy[Bitcoin-76067358-path3-xpub6-40ff3d1a": "cba39be984a08e13e2554269bb6f7a2a3cf73928f734a22401239dcef0964118", +"T1B1_en_bitcoin-test_getpublickey.py::test_get_public_node_show_legacy[Bitcoin-76067358-path6-xpub6-2233337c": "55a3b51f784add99ad325a0da0a5b0be4c9efa4ac7248bc4a43881bdb7133900", +"T1B1_en_bitcoin-test_getpublickey.py::test_get_public_node_show_legacy[Litecoin-27108450-path10-Ltu-5c9baf11": "f90c7e41611ad5b441d2f92cc13a8e3ebb561fa3311ce546aa1f2801330059f0", +"T1B1_en_bitcoin-test_getpublickey.py::test_get_public_node_show_legacy[Litecoin-27108450-path7-Ltub-40e7a24f": "883985d5141042aad58f34b4e3e4b9d7ae50e00052502843700320f223c90648", +"T1B1_en_bitcoin-test_getpublickey.py::test_get_public_node_show_legacy[Litecoin-27108450-path8-Ltub-c105cc05": "ab637d75e41769d72fbb829c7434ba1a4673da0e3503520d7ddadda3ba5cb1df", +"T1B1_en_bitcoin-test_getpublickey.py::test_get_public_node_show_legacy[Litecoin-27108450-path9-Ltub-deb2b005": "8c473abbeab271b11a1dd81e47fd61a5b3727663023882184116a1632f996ceb", +"T1B1_en_bitcoin-test_getpublickey.py::test_get_public_node_show_legacy[Testnet-70617039-path4-tpubD-3ff0907e": "ff4370b8a9dab8e3fe8443574a6a63d9806ecb051f43f614605c216db7ea1136", +"T1B1_en_bitcoin-test_getpublickey.py::test_get_public_node_show_legacy[Testnet-70617039-path5-tpubD-92239b2c": "1e2d268042077f65eb4df4ba99d6908cc4cc00e97d4b5fd5ae216db0376c3c1a", "T1B1_en_bitcoin-test_getpublickey.py::test_invalid_path[Bcash-path5]": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "T1B1_en_bitcoin-test_getpublickey.py::test_invalid_path[Bitcoin-path0]": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "T1B1_en_bitcoin-test_getpublickey.py::test_invalid_path[Bitcoin-path2]": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",