Skip to content

Commit 894d354

Browse files
committed
keystore: use xpub from Rust when computing bip86 pubkey
The goal use xpub from the Rust keystore, so that we can impement xpub caching in Rust, calling to C only for utility functions that are hard to port. If the xpub is derived in C.
1 parent 47315c5 commit 894d354

File tree

6 files changed

+83
-32
lines changed

6 files changed

+83
-32
lines changed

src/keystore.c

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -691,23 +691,12 @@ static void _tagged_hash(const char* tag, const uint8_t* msg, size_t msg_len, ui
691691
rust_sha256_finish(&hash_ctx, hash_out);
692692
}
693693

694-
bool keystore_secp256k1_schnorr_bip86_pubkey(
695-
const uint32_t* keypath,
696-
size_t keypath_len,
697-
uint8_t* pubkey_out)
694+
bool keystore_secp256k1_schnorr_bip86_pubkey(const uint8_t* pubkey33, uint8_t* pubkey_out)
698695
{
699-
if (keystore_is_locked()) {
700-
return false;
701-
}
702-
struct ext_key xpub __attribute__((__cleanup__(keystore_zero_xkey))) = {0};
703-
if (!keystore_get_xpub(keypath, keypath_len, &xpub)) {
704-
return false;
705-
}
706-
707696
const secp256k1_context* ctx = wally_get_secp_context();
708697

709698
secp256k1_pubkey pubkey = {0};
710-
if (!secp256k1_ec_pubkey_parse(ctx, &pubkey, xpub.pub_key, sizeof(xpub.pub_key))) {
699+
if (!secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkey33, 33)) {
711700
return false;
712701
}
713702
secp256k1_xonly_pubkey xonly_pubkey = {0};

src/keystore.h

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -255,21 +255,18 @@ USE_RESULT bool keystore_encode_xpub_at_keypath(
255255
uint8_t* out);
256256

257257
/**
258-
* Return the tweaked taproot pubkey at the given keypath.
258+
* Return the tweaked taproot pubkey.
259259
*
260-
* Instead of returning the original pubkey at the keypath directly, it is tweaked with the hash of
261-
* the pubkey.
260+
* Instead of returning the original pubkey directly, it is tweaked with the hash of the pubkey.
262261
*
263262
* See
264263
* https://github.com/bitcoin/bips/blob/edffe529056f6dfd33d8f716fb871467c3c09263/bip-0086.mediawiki#address-derivation
265264
*
266-
* @param[in] keypath derivation keypath
267-
* @param[in] keypath_len number of elements in keypath
265+
* @param[in] pubkey33 33 byte compressed pubkey.
268266
* @param[out] pubkey_out 32 byte x-only pubkey (see BIP-340 for details).
269267
*/
270268
USE_RESULT bool keystore_secp256k1_schnorr_bip86_pubkey(
271-
const uint32_t* keypath,
272-
size_t keypath_len,
269+
const uint8_t* pubkey33,
273270
uint8_t* pubkey_out);
274271

275272
/**

src/rust/bitbox02-rust/src/hww/api/bitcoin/common.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
use super::pb;
1616
use super::Error;
1717

18-
use bitbox02::keystore;
18+
use crate::keystore;
1919

2020
use alloc::string::String;
2121
use alloc::vec::Vec;
@@ -77,7 +77,7 @@ impl Payload {
7777
) -> Result<Self, Error> {
7878
match simple_type {
7979
SimpleType::P2wpkh => Ok(Payload {
80-
data: crate::keystore::secp256k1_pubkey_hash160(keypath)?,
80+
data: keystore::secp256k1_pubkey_hash160(keypath)?,
8181
output_type: BtcOutputType::P2wpkh,
8282
}),
8383
SimpleType::P2wpkhP2sh => {

src/rust/bitbox02-rust/src/keystore.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,18 @@ pub fn root_fingerprint() -> Result<Vec<u8>, ()> {
4848
Ok(secp256k1_pubkey_hash160(&[])?.get(..4).ok_or(())?.to_vec())
4949
}
5050

51+
/// Return the tweaked taproot pubkey at the given keypath.
52+
///
53+
/// Instead of returning the original pubkey at the keypath directly, it is tweaked with the hash of
54+
/// the pubkey.
55+
///
56+
/// See
57+
/// https://github.com/bitcoin/bips/blob/edffe529056f6dfd33d8f716fb871467c3c09263/bip-0086.mediawiki#address-derivation
58+
pub fn secp256k1_schnorr_bip86_pubkey(keypath: &[u32]) -> Result<[u8; 32], ()> {
59+
let xpub = get_xpub(keypath)?;
60+
keystore::secp256k1_schnorr_bip86_pubkey(&xpub.public_key)
61+
}
62+
5163
#[cfg(test)]
5264
mod tests {
5365
use super::*;
@@ -155,4 +167,51 @@ mod tests {
155167
*b"\x04\x77\xa4\x4a\xa9\xe8\xc8\xfb\x51\x05\xef\x5e\xe2\x39\x4e\x8a\xed\x89\xad\x73\xfc\x74\x36\x14\x25\xf0\x63\x47\xec\xfe\x32\x61\x31\xe1\x33\x93\x67\xee\x3c\xbe\x87\x71\x92\x85\xa0\x7f\x77\x4b\x17\xeb\x93\x3e\xcf\x0b\x9b\x82\xac\xeb\xc1\x95\x22\x6d\x63\x42\x44",
156168
);
157169
}
170+
171+
#[test]
172+
fn test_secp2e56k1_schnorr_bip86_pubkey() {
173+
// Test vectors from:
174+
// https://github.com/bitcoin/bips/blob/edffe529056f6dfd33d8f716fb871467c3c09263/bip-0086.mediawiki#test-vectors
175+
// Here we only test the creation of the tweaked pubkkey. See `Payload::from_simple` for address generation.
176+
177+
keystore::lock();
178+
assert_eq!(
179+
secp256k1_schnorr_bip86_pubkey(&[86 + HARDENED, 0 + HARDENED, 0 + HARDENED, 0, 0]),
180+
Err(())
181+
);
182+
183+
mock_unlocked_using_mnemonic("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about");
184+
assert_eq!(secp256k1_schnorr_bip86_pubkey(&[
185+
86 + HARDENED,
186+
0 + HARDENED,
187+
0 + HARDENED,
188+
0,
189+
0
190+
])
191+
.unwrap(),
192+
*b"\xa6\x08\x69\xf0\xdb\xcf\x1d\xc6\x59\xc9\xce\xcb\xaf\x80\x50\x13\x5e\xa9\xe8\xcd\xc4\x87\x05\x3f\x1d\xc6\x88\x09\x49\xdc\x68\x4c",
193+
);
194+
195+
assert_eq!(secp256k1_schnorr_bip86_pubkey(&[
196+
86 + HARDENED,
197+
0 + HARDENED,
198+
0 + HARDENED,
199+
0,
200+
1
201+
])
202+
.unwrap(),
203+
*b"\xa8\x2f\x29\x94\x4d\x65\xb8\x6a\xe6\xb5\xe5\xcc\x75\xe2\x94\xea\xd6\xc5\x93\x91\xa1\xed\xc5\xe0\x16\xe3\x49\x8c\x67\xfc\x7b\xbb",
204+
);
205+
206+
assert_eq!(secp256k1_schnorr_bip86_pubkey(&[
207+
86 + HARDENED,
208+
0 + HARDENED,
209+
0 + HARDENED,
210+
1,
211+
0,
212+
])
213+
.unwrap(),
214+
*b"\x88\x2d\x74\xe5\xd0\x57\x2d\x5a\x81\x6c\xef\x00\x41\xa9\x6b\x6c\x1d\xe8\x32\xf6\xf9\x67\x6d\x96\x05\xc4\x4d\x5e\x9a\x97\xd3\xdc",
215+
);
216+
}
158217
}

src/rust/bitbox02/src/keystore.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -314,12 +314,11 @@ pub fn secp256k1_schnorr_bip86_sign(keypath: &[u32], msg: &[u8; 32]) -> Result<[
314314
}
315315
}
316316

317-
pub fn secp256k1_schnorr_bip86_pubkey(keypath: &[u32]) -> Result<[u8; 32], ()> {
317+
pub fn secp256k1_schnorr_bip86_pubkey(pubkey33: &[u8]) -> Result<[u8; 32], ()> {
318318
let mut pubkey = [0u8; 32];
319319
match unsafe {
320320
bitbox02_sys::keystore_secp256k1_schnorr_bip86_pubkey(
321-
keypath.as_ptr(),
322-
keypath.len() as _,
321+
pubkey33.as_ptr(),
323322
pubkey.as_mut_ptr(),
324323
)
325324
} {

test/unit-test/test_keystore.c

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -628,9 +628,7 @@ static void _test_keystore_secp256k1_schnorr_bip86_pubkey(void** state)
628628
{
629629
// Test vectors from:
630630
// https://github.com/bitcoin/bips/blob/edffe529056f6dfd33d8f716fb871467c3c09263/bip-0086.mediawiki#test-vectors
631-
// Here we only test the creation of the tweaked pubkkey. See
632-
// `test_btc_common_address_from_payload()` for the actual address generation, which takes this
633-
// pubkey as input.
631+
// Here we only test the creation of the tweaked pubkkey.
634632
_mock_with_mnemonic(
635633
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon "
636634
"about",
@@ -643,8 +641,10 @@ static void _test_keystore_secp256k1_schnorr_bip86_pubkey(void** state)
643641
0,
644642
0,
645643
};
644+
struct ext_key xpub = {0};
645+
assert_true(keystore_get_xpub(keypath, 5, &xpub));
646646
uint8_t pubkey[32] = {0};
647-
assert_true(keystore_secp256k1_schnorr_bip86_pubkey(keypath, 5, pubkey));
647+
assert_true(keystore_secp256k1_schnorr_bip86_pubkey(xpub.pub_key, pubkey));
648648
const uint8_t expected_pubkey[32] =
649649
"\xa6\x08\x69\xf0\xdb\xcf\x1d\xc6\x59\xc9\xce\xcb\xaf\x80\x50\x13\x5e\xa9\xe8\xcd\xc4"
650650
"\x87\x05\x3f\x1d\xc6\x88\x09\x49\xdc\x68\x4c";
@@ -658,8 +658,10 @@ static void _test_keystore_secp256k1_schnorr_bip86_pubkey(void** state)
658658
0,
659659
1,
660660
};
661+
struct ext_key xpub = {0};
662+
assert_true(keystore_get_xpub(keypath, 5, &xpub));
661663
uint8_t pubkey[32] = {0};
662-
assert_true(keystore_secp256k1_schnorr_bip86_pubkey(keypath, 5, pubkey));
664+
assert_true(keystore_secp256k1_schnorr_bip86_pubkey(xpub.pub_key, pubkey));
663665
const uint8_t expected_pubkey[32] =
664666
"\xa8\x2f\x29\x94\x4d\x65\xb8\x6a\xe6\xb5\xe5\xcc\x75\xe2\x94\xea\xd6\xc5\x93\x91\xa1"
665667
"\xed\xc5\xe0\x16\xe3\x49\x8c\x67\xfc\x7b\xbb";
@@ -673,8 +675,10 @@ static void _test_keystore_secp256k1_schnorr_bip86_pubkey(void** state)
673675
1,
674676
0,
675677
};
678+
struct ext_key xpub = {0};
679+
assert_true(keystore_get_xpub(keypath, 5, &xpub));
676680
uint8_t pubkey[32] = {0};
677-
assert_true(keystore_secp256k1_schnorr_bip86_pubkey(keypath, 5, pubkey));
681+
assert_true(keystore_secp256k1_schnorr_bip86_pubkey(xpub.pub_key, pubkey));
678682
const uint8_t expected_pubkey[32] =
679683
"\x88\x2d\x74\xe5\xd0\x57\x2d\x5a\x81\x6c\xef\x00\x41\xa9\x6b\x6c\x1d\xe8\x32\xf6\xf9"
680684
"\x67\x6d\x96\x05\xc4\x4d\x5e\x9a\x97\xd3\xdc";
@@ -696,7 +700,10 @@ static void _test_keystore_secp256k1_schnorr_bip86_sign(void** state)
696700
0,
697701
0,
698702
};
699-
assert_true(keystore_secp256k1_schnorr_bip86_pubkey(keypath, 5, pubkey));
703+
struct ext_key xpub = {0};
704+
assert_true(keystore_get_xpub(keypath, 5, &xpub));
705+
706+
assert_true(keystore_secp256k1_schnorr_bip86_pubkey(xpub.pub_key, pubkey));
700707
uint8_t msg[32] = {0};
701708
memset(msg, 0x88, sizeof(msg));
702709
uint8_t sig[64] = {0};

0 commit comments

Comments
 (0)