Skip to content

Commit dc65078

Browse files
Merge branch 'develop' into feat/btc-only/base
2 parents 60b008a + c9fbd50 commit dc65078

77 files changed

Lines changed: 10589 additions & 747 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/btc_family/btc/btc_app.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ const btc_config_t btc_app = {
106106
.legacy_xpub_ver = 0x0488b21e,
107107
.segwit_xpub_ver = 0x049d7cb2,
108108
.nsegwit_xpub_ver = 0x04b24746,
109+
.taproot_xpub_ver = 0x04b24746,
109110
.bech32_hrp = "bc",
110111
.lunit_name = "BTC",
111112
.name = "Bitcoin",

apps/btc_family/btc_context.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ typedef struct {
6060
uint32_t legacy_xpub_ver;
6161
uint32_t segwit_xpub_ver;
6262
uint32_t nsegwit_xpub_ver;
63-
63+
uint32_t taproot_xpub_ver;
6464
/** The human-readable prefix for Bech32 encoded addresses. Applicable for
6565
* Segwit and Taproot addresses. Ref:
6666
* https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#segwit-address-format

apps/btc_family/btc_helpers.c

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,18 @@
6262

6363
#include "btc_helpers.h"
6464

65+
#include <stdint.h>
66+
#include <string.h>
67+
68+
#include "bignum.h"
6569
#include "btc_priv.h"
6670
#include "coin_utils.h"
71+
#include "ecdsa.h"
6772
#include "flash_config.h"
73+
#include "secp256k1.h"
6874
#include "segwit_addr.h"
75+
#include "sha2.h"
76+
#include "utils.h"
6977

7078
/*****************************************************************************
7179
* EXTERN VARIABLES
@@ -95,6 +103,74 @@
95103
* STATIC FUNCTIONS
96104
*****************************************************************************/
97105

106+
// Computes sha256(tap_tweak_hash + tap_tweak_hash + x_only_public_key +
107+
// root_hash) and stores the result in tweak_key_hash.
108+
static bool bip340_tweak_key_hash(const uint8_t *x_only_public_key,
109+
const uint8_t *root_hash,
110+
uint8_t tweak_key_hash[32]) {
111+
if (NULL == x_only_public_key || NULL == tweak_key_hash) {
112+
return false;
113+
}
114+
115+
size_t payload_size = 32; // x_only_public_key
116+
if (root_hash != NULL) {
117+
payload_size += 32;
118+
}
119+
120+
uint8_t payload[payload_size];
121+
memzero(payload, payload_size);
122+
// Prepare the data for hashing
123+
memcpy(payload, x_only_public_key, 32);
124+
if (root_hash != NULL) {
125+
memcpy(payload + 32, root_hash, 32);
126+
}
127+
128+
bip340_tagged_hash("TapTweak", tweak_key_hash, payload, payload_size);
129+
return true;
130+
}
131+
132+
// Calculates result = (public_key_point + tweak_key_hash * G).x
133+
static bool bip340_point_add_tweak(const ecdsa_curve *curve,
134+
const uint8_t *public_key,
135+
const uint8_t *tweak_key_hash,
136+
uint8_t result[32]) {
137+
if (NULL == public_key || NULL == tweak_key_hash || NULL == result) {
138+
return false;
139+
}
140+
141+
curve_point public_key_point = {0};
142+
if (!ecdsa_read_pubkey(curve, public_key, &public_key_point)) {
143+
return false;
144+
}
145+
146+
// Negate y-coordinate if it's odd
147+
if (bn_is_odd(&public_key_point.y)) {
148+
bn_subtract(&curve->prime, &public_key_point.y, &public_key_point.y);
149+
bn_mod(&public_key_point.y, &curve->prime);
150+
}
151+
152+
bignum256 tweak_hash_bn = {0};
153+
bn_read_be(tweak_key_hash, &tweak_hash_bn);
154+
bn_mod(&tweak_hash_bn, &curve->order);
155+
156+
if (bn_is_zero(&tweak_hash_bn)) {
157+
return false;
158+
}
159+
160+
curve_point result_point = {0};
161+
point_multiply(curve,
162+
&tweak_hash_bn,
163+
&curve->G,
164+
&result_point); // result_point = tweak_hash_bn * G
165+
point_add(curve,
166+
&public_key_point,
167+
&result_point); // result_point = public_key_point + result_point
168+
169+
// take the x-coordinate of the result point
170+
bn_write_be(&result_point.x, result);
171+
return true;
172+
}
173+
98174
/*****************************************************************************
99175
* GLOBAL FUNCTIONS
100176
*****************************************************************************/
@@ -162,6 +238,9 @@ bool btc_get_version(uint32_t purpose_index, uint32_t *xpub_ver) {
162238
case PURPOSE_NSEGWIT:
163239
*xpub_ver = g_btc_app->nsegwit_xpub_ver;
164240
break;
241+
case PURPOSE_TAPROOT:
242+
*xpub_ver = g_btc_app->taproot_xpub_ver;
243+
break;
165244
default:
166245
status = false;
167246
}
@@ -208,3 +287,108 @@ void format_value(const uint64_t value_in_sat,
208287
snprintf(
209288
msg, msg_len, "%0.*f %s", precision, fee_in_btc, g_btc_app->lunit_name);
210289
}
290+
291+
// BIP340 tagged hash implementation
292+
void bip340_tagged_hash(const char *tag,
293+
uint8_t *out,
294+
const uint8_t *data,
295+
size_t data_len) {
296+
uint8_t tag_hash[32];
297+
Hasher hasher;
298+
299+
// Hash the tag
300+
hasher_Init(&hasher, HASHER_SHA2);
301+
hasher_Update(&hasher, (const uint8_t *)tag, strlen(tag));
302+
hasher_Final(&hasher, tag_hash);
303+
304+
// tagged_hash = SHA256(SHA256(tag) || SHA256(tag) || data)
305+
hasher_Init(&hasher, HASHER_SHA2);
306+
hasher_Update(&hasher, tag_hash, 32);
307+
hasher_Update(&hasher, tag_hash, 32);
308+
hasher_Update(&hasher, data, data_len);
309+
hasher_Final(&hasher, out);
310+
}
311+
312+
// implementation of BIP-340 tweak public key for taproot without using
313+
// secp256k1 library
314+
bool bip340_tweak_public_key(const uint8_t *public_key,
315+
const uint8_t *root_hash,
316+
uint8_t *tweaked_public_key) {
317+
if (NULL == public_key || NULL == tweaked_public_key) {
318+
return false;
319+
}
320+
321+
uint8_t tweak_key_hash[32] = {0};
322+
if (!bip340_tweak_key_hash(public_key + 1, root_hash, tweak_key_hash)) {
323+
return false;
324+
}
325+
326+
if (!bip340_point_add_tweak(
327+
&secp256k1, public_key, tweak_key_hash, tweaked_public_key)) {
328+
return false;
329+
}
330+
331+
return true;
332+
}
333+
334+
// BIP341 private key tweaking for Taproot
335+
bool bip340_tweak_private_key(const uint8_t *private_key,
336+
const uint8_t *public_key,
337+
const uint8_t *root_hash,
338+
uint8_t *tweaked_private_key) {
339+
if (NULL == private_key || NULL == tweaked_private_key) {
340+
return false;
341+
}
342+
343+
// Compute the tweak hash
344+
uint8_t tweak_hash[32] = {0};
345+
if (!bip340_tweak_key_hash(public_key + 1, root_hash, tweak_hash)) {
346+
return false;
347+
}
348+
349+
bignum256 sk = {0}, t = {0}, result = {0};
350+
bn_read_be(private_key, &sk);
351+
const ecdsa_curve *curve = &secp256k1;
352+
curve_point public_key_point = {0};
353+
if (!ecdsa_read_pubkey(curve, public_key, &public_key_point)) {
354+
return false;
355+
}
356+
357+
// Negate private key if public key y is odd
358+
if (bn_is_odd(&public_key_point.y)) {
359+
bn_subtract(&curve->order, &sk, &sk);
360+
bn_mod(&sk, &curve->order);
361+
}
362+
363+
// Compute tweaked private key: sk' = sk + t (mod n)
364+
bn_read_be(tweak_hash, &t);
365+
bn_mod(&t, &curve->order);
366+
bn_copy(&sk, &result);
367+
bn_add(&result, &t);
368+
bn_mod(&result, &curve->order);
369+
370+
bn_write_be(&result, tweaked_private_key);
371+
372+
// Clear sensitive data
373+
memzero(&sk, sizeof(sk));
374+
memzero(&t, sizeof(t));
375+
memzero(&result, sizeof(result));
376+
memzero(tweak_hash, sizeof(tweak_hash));
377+
378+
return true;
379+
}
380+
381+
int btc_get_taproot_address(uint8_t *public_key,
382+
const char *hrp,
383+
char *address) {
384+
if (NULL == public_key || NULL == hrp || NULL == address) {
385+
return 0;
386+
}
387+
388+
uint8_t tweaked_public_key[32] = {0};
389+
if (!bip340_tweak_public_key(public_key, NULL, tweaked_public_key)) {
390+
return 0;
391+
}
392+
393+
return segwit_addr_encode(address, hrp, 1, tweaked_public_key, 32);
394+
}

apps/btc_family/btc_helpers.h

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,4 +137,73 @@ bool btc_derivation_path_guard(const uint32_t *path, uint32_t depth);
137137
*/
138138
void format_value(uint64_t value_in_sat, char *msg, size_t msg_len);
139139

140+
/**
141+
* @brief Returns the taproot key path address string (Bech32M encoding)
142+
* @details The functions assumes public key is compressed
143+
*
144+
* @param [in] public_key 33 Byte array representation of public key.
145+
* @param [in] hrp HRP value for bech32M encoding "bc" for mainnet and
146+
* "tb" for testnet.
147+
* @param [out] address char array to store taproot address.
148+
*
149+
* @return 1 if successful and 0 if failure.
150+
*/
151+
int btc_get_taproot_address(uint8_t *public_key,
152+
const char *hrp,
153+
char *address);
154+
155+
/**
156+
* @brief Tweaks a private key for Taproot key path spending.
157+
* @details This function implements BIP341 private key tweaking for Taproot.
158+
* The tweaked private key is computed as sk' = sk + t (mod n) where t is the
159+
* tweak hash.
160+
*
161+
* @param private_key The internal private key (32 bytes)
162+
* @param root_hash The Merkle root hash (32 bytes, can be NULL for key path
163+
* only)
164+
* @param public_key The public key (33 bytes, compressed format)
165+
* @param tweaked_private_key Output buffer for the tweaked private key (32
166+
* bytes)
167+
*
168+
* @return bool Status code
169+
* @retval true Success
170+
* @retval false Error (invalid inputs, tweaking failed)
171+
*/
172+
bool bip340_tweak_private_key(const uint8_t *private_key,
173+
const uint8_t *public_key,
174+
const uint8_t *root_hash,
175+
uint8_t *tweaked_private_key);
176+
177+
/**
178+
* @brief Tweaks a public key for Taproot key path spending.
179+
* @details This function implements BIP341 public key tweaking for Taproot.
180+
* The tweaked public key is computed as P' = P + t*G where t is the tweak hash.
181+
*
182+
* @param public_key The internal public key (33 bytes, compressed format)
183+
* @param root_hash The Merkle root hash (32 bytes, can be NULL for key path
184+
* only)
185+
* @param tweaked_public_key Output buffer for the tweaked public key
186+
* x-coordinate (32 bytes)
187+
*
188+
* @return bool Status code
189+
* @retval true Success
190+
* @retval false Error (invalid inputs, tweaking failed)
191+
*/
192+
bool bip340_tweak_public_key(const uint8_t *public_key,
193+
const uint8_t *root_hash,
194+
uint8_t *tweaked_public_key);
195+
196+
/**
197+
* @brief BIP340 tagged hash implementation
198+
* @details This function implements BIP340 tagged hash for Taproot.
199+
*
200+
* @param tag The tag to be hashed
201+
* @param out The output buffer for the hashed data
202+
* @param data The data to be hashed
203+
* @param data_len The length of the data
204+
*/
205+
void bip340_tagged_hash(const char *tag,
206+
uint8_t *out,
207+
const uint8_t *data,
208+
size_t data_len);
140209
#endif

apps/btc_family/btc_priv.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,17 @@ typedef struct {
3434
uint8_t hash_outputs[32];
3535
} btc_segwit_cache_t;
3636

37+
typedef struct {
38+
bool filled;
39+
uint8_t sha_prevouts[32];
40+
uint8_t sha_amounts[32];
41+
uint8_t sha_scriptpubkeys[32];
42+
uint8_t sha_sequences[32];
43+
uint8_t sha_outputs[32];
44+
uint8_t sha_annex[32];
45+
uint8_t sha_single_output[32];
46+
} btc_taproot_cache_t;
47+
3748
typedef struct {
3849
pb_byte_t prev_txn_hash[32];
3950
uint32_t prev_output_index;
@@ -69,6 +80,8 @@ typedef struct {
6980
btc_sign_txn_metadata_t metadata;
7081
// Populated for segwit transactions
7182
btc_segwit_cache_t segwit_cache;
83+
// Populated for taproot transactions
84+
btc_taproot_cache_t taproot_cache;
7285

7386
/**
7487
* The structure holds the outputs (TxOut) of the transaction. Refer

apps/btc_family/btc_pub_key.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ static size_t btc_get_address(const uint8_t *seed,
206206
uint8_t *public_key,
207207
char *address) {
208208
HDNode node = {0};
209-
char addr[50] = "";
209+
char addr[70] = "";
210210
size_t address_length = 0;
211211

212212
if (!derive_hdnode_from_path(
@@ -237,7 +237,9 @@ static size_t btc_get_address(const uint8_t *seed,
237237
36);
238238
break;
239239

240-
// TODO: add support for taproot
240+
case PURPOSE_TAPROOT:
241+
btc_get_taproot_address(node.public_key, g_btc_app->bech32_hrp, addr);
242+
break;
241243
default:
242244
break;
243245
}

apps/btc_family/btc_script.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464

6565
#include "base58.h"
6666
#include "bip32.h"
67+
#include "btc_helpers.h"
6768
#include "btc_priv.h"
6869
#include "memzero.h"
6970
#include "ripemd160.h"
@@ -240,12 +241,21 @@ bool btc_check_script_address(const uint8_t *script,
240241
uint8_t digest[HASHER_DIGEST_LENGTH] = {0};
241242
btc_script_type_e type = btc_get_script_type(script, script_len);
242243
if (SCRIPT_TYPE_P2PKH != type && SCRIPT_TYPE_P2WPKH != type &&
243-
SCRIPT_TYPE_P2SH != type) {
244+
SCRIPT_TYPE_P2SH != type && SCRIPT_TYPE_P2TR != type) {
244245
// allow only p2pkh and p2wpkh and p2sh-p2wpkh for change output
245246
return false;
246247
}
247248
uint8_t offset = (SCRIPT_TYPE_P2PKH == type) ? 3 : 2;
248249

250+
if (SCRIPT_TYPE_P2TR == type) {
251+
offset = 2;
252+
uint8_t tweaked_public_key[32] = {0};
253+
if (!bip340_tweak_public_key(public_key, NULL, tweaked_public_key)) {
254+
return false;
255+
}
256+
return memcmp(tweaked_public_key, script + offset, 32) == 0;
257+
}
258+
249259
hasher_Raw(HASHER_SHA2_RIPEMD, public_key, BTC_SHORT_PUB_KEY_SIZE, digest);
250260

251261
if (SCRIPT_TYPE_P2SH == type) {

0 commit comments

Comments
 (0)