From fb240bf48a8df55bbc37bfd2c95f3ced1943b04d Mon Sep 17 00:00:00 2001 From: Martin Milata Date: Mon, 26 Feb 2024 17:41:29 +0100 Subject: [PATCH] WIP feat(legacy): send BIP-380 descriptor in GetPublicKey response [skip_ci] --- legacy/emulator/strl.c | 8 ++ legacy/emulator/strl.h | 2 + legacy/firmware/fsm_msg_coin.h | 78 +++++++++++++++++++ .../firmware/protob/messages-bitcoin.options | 2 +- .../device_tests/bitcoin/test_descriptors.py | 6 +- 5 files changed, 92 insertions(+), 4 deletions(-) diff --git a/legacy/emulator/strl.c b/legacy/emulator/strl.c index 966322ac5..96a0298b8 100644 --- a/legacy/emulator/strl.c +++ b/legacy/emulator/strl.c @@ -42,3 +42,11 @@ size_t strlcat(char *dst, const char *src, size_t size) { } #endif + +char *itoa(int i, char *dest, int base) { + if (base != 10) { + return dest; + } + int n = sprintf(dest, "%d", i); + return dest + n; +} diff --git a/legacy/emulator/strl.h b/legacy/emulator/strl.h index 7fa7d5f08..6bb37dfe1 100644 --- a/legacy/emulator/strl.h +++ b/legacy/emulator/strl.h @@ -27,4 +27,6 @@ size_t strlcpy(char *dst, const char *src, size_t size); size_t strlcat(char *dst, const char *src, size_t size); #endif +char *itoa(int i, char *dest, int base) { + #endif diff --git a/legacy/firmware/fsm_msg_coin.h b/legacy/firmware/fsm_msg_coin.h index 692671cf5..c71e19eec 100644 --- a/legacy/firmware/fsm_msg_coin.h +++ b/legacy/firmware/fsm_msg_coin.h @@ -81,6 +81,56 @@ void fsm_msgGetPublicKey(const GetPublicKey *msg) { resp->node.public_key.bytes[0] = 0; } + bool descriptor = true; + size_t descriptor_len = 0; + + if (script_type == InputScriptType_SPENDADDRESS) { + strlcpy(resp->descriptor, "pkh([", sizeof(resp->descriptor)); + descriptor_len += 5; + } else if (script_type == InputScriptType_SPENDP2SHWITNESS) { + strlcpy(resp->descriptor, "sh(wpkh([", sizeof(resp->descriptor)); + descriptor_len += 9; + } else if (script_type == InputScriptType_SPENDWITNESS) { + strlcpy(resp->descriptor, "wpkh([", sizeof(resp->descriptor)); + descriptor_len += 6; + } else if (script_type == InputScriptType_SPENDTAPROOT) { + strlcpy(resp->descriptor, "tr([", sizeof(resp->descriptor)); + descriptor_len += 4; + } else { + descriptor = false; + } + if (descriptor) { + // FIXME this needs to be lowercase + uint32hex(root_fingerprint, resp->descriptor + descriptor_len); + descriptor_len += 8; + + for (size_t i = 0; i < msg->address_n_count; i++) { + strlcpy(resp->descriptor + descriptor_len, "/", + sizeof(resp->descriptor) - descriptor_len); + descriptor_len++; + + uint32_t unhardened = msg->address_n[i] & PATH_UNHARDEN_MASK; + + if (descriptor_len + 17 > sizeof(resp->descriptor)) { + descriptor = false; + break; + } + + char *result = itoa(unhardened, resp->descriptor + descriptor_len, 10); + descriptor_len += result - (resp->descriptor + descriptor_len); + + if (msg->address_n[i] & PATH_HARDENED) { + strlcpy(resp->descriptor + descriptor_len, "'", + sizeof(resp->descriptor) - descriptor_len); + descriptor_len++; + } + } + + strlcpy(resp->descriptor + descriptor_len, "]", + sizeof(resp->descriptor) - descriptor_len); + descriptor_len++; + } + if (coin->xpub_magic && (script_type == InputScriptType_SPENDADDRESS || script_type == InputScriptType_SPENDMULTISIG)) { hdnode_serialize_public(node, fingerprint, coin->xpub_magic, resp->xpub, @@ -113,9 +163,37 @@ void fsm_msgGetPublicKey(const GetPublicKey *msg) { layoutHome(); return; } + if (descriptor) { + if (coin->xpub_magic) { + char tmp[XPUB_MAXLEN] = {0}; + hdnode_serialize_public(node, fingerprint, coin->xpub_magic, tmp, + sizeof(tmp)); + strlcpy(resp->descriptor + descriptor_len, tmp, + sizeof(resp->descriptor) - descriptor_len); + descriptor_len += strlen(tmp); + + strlcpy(resp->descriptor + descriptor_len, "/<0;1>/*)", + sizeof(resp->descriptor) - descriptor_len); + descriptor_len += 9; + + if (script_type == InputScriptType_SPENDP2SHWITNESS) { + strlcpy(resp->descriptor + descriptor_len, ")", + sizeof(resp->descriptor) - descriptor_len); + descriptor_len++; + } + + strlcpy(resp->descriptor + descriptor_len, "#00000000", + sizeof(resp->descriptor) - descriptor_len); + descriptor_len += 9; + } else { + descriptor = false; + } + } + resp->has_descriptor = descriptor; if (msg->has_show_display && msg->show_display) { for (int page = 0; page < 2; page++) { + // TODO show descriptor layoutXPUB(resp->xpub, page); if (!protectButton(ButtonRequestType_ButtonRequest_PublicKey, true)) { memzero(resp, sizeof(PublicKey)); diff --git a/legacy/firmware/protob/messages-bitcoin.options b/legacy/firmware/protob/messages-bitcoin.options index 6d235d2a4..e79dc22dd 100644 --- a/legacy/firmware/protob/messages-bitcoin.options +++ b/legacy/firmware/protob/messages-bitcoin.options @@ -3,7 +3,7 @@ GetPublicKey.ecdsa_curve_name max_size:32 GetPublicKey.coin_name max_size:21 PublicKey.xpub max_size:113 -PublicKey.descriptor type:FT_IGNORE +PublicKey.descriptor max_size:180 GetAddress.address_n max_count:8 GetAddress.coin_name max_size:21 diff --git a/tests/device_tests/bitcoin/test_descriptors.py b/tests/device_tests/bitcoin/test_descriptors.py index e501b8083..5b2f22840 100644 --- a/tests/device_tests/bitcoin/test_descriptors.py +++ b/tests/device_tests/bitcoin/test_descriptors.py @@ -161,14 +161,14 @@ def _address_n(purpose, coin, account, script_type): return res -@pytest.mark.skip_t1 @pytest.mark.parametrize( "coin, account, purpose, script_type, descriptors", VECTORS_DESCRIPTORS ) def test_descriptors(client: Client, coin, account, purpose, script_type, descriptors): with client: - IF = InputFlowShowXpubQRCode(client) - client.set_input_flow(IF.get()) + if client.model != models.T1B1: + IF = InputFlowShowXpubQRCode(client) + client.set_input_flow(IF.get()) address_n = _address_n(purpose, coin, account, script_type) res = btc.get_public_node(