|
62 | 62 |
|
63 | 63 | #include "btc_helpers.h" |
64 | 64 |
|
| 65 | +#include <stdint.h> |
| 66 | +#include <string.h> |
| 67 | + |
| 68 | +#include "bignum.h" |
65 | 69 | #include "btc_priv.h" |
66 | 70 | #include "coin_utils.h" |
| 71 | +#include "ecdsa.h" |
67 | 72 | #include "flash_config.h" |
| 73 | +#include "secp256k1.h" |
68 | 74 | #include "segwit_addr.h" |
| 75 | +#include "sha2.h" |
| 76 | +#include "utils.h" |
69 | 77 |
|
70 | 78 | /***************************************************************************** |
71 | 79 | * EXTERN VARIABLES |
|
95 | 103 | * STATIC FUNCTIONS |
96 | 104 | *****************************************************************************/ |
97 | 105 |
|
| 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 | + |
98 | 174 | /***************************************************************************** |
99 | 175 | * GLOBAL FUNCTIONS |
100 | 176 | *****************************************************************************/ |
@@ -162,6 +238,9 @@ bool btc_get_version(uint32_t purpose_index, uint32_t *xpub_ver) { |
162 | 238 | case PURPOSE_NSEGWIT: |
163 | 239 | *xpub_ver = g_btc_app->nsegwit_xpub_ver; |
164 | 240 | break; |
| 241 | + case PURPOSE_TAPROOT: |
| 242 | + *xpub_ver = g_btc_app->taproot_xpub_ver; |
| 243 | + break; |
165 | 244 | default: |
166 | 245 | status = false; |
167 | 246 | } |
@@ -208,3 +287,108 @@ void format_value(const uint64_t value_in_sat, |
208 | 287 | snprintf( |
209 | 288 | msg, msg_len, "%0.*f %s", precision, fee_in_btc, g_btc_app->lunit_name); |
210 | 289 | } |
| 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 | +} |
0 commit comments