Skip to content

Commit e9386f7

Browse files
committed
Merge branch 'keystore-rust'
2 parents 7b104fe + 894d354 commit e9386f7

File tree

9 files changed

+126
-71
lines changed

9 files changed

+126
-71
lines changed

src/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ add_custom_target(rust-bindgen
312312
--allowlist-function keystore_get_bip39_mnemonic
313313
--allowlist-function keystore_get_bip39_word
314314
--allowlist-function keystore_get_ed25519_seed
315-
--allowlist-function keystore_secp256k1_pubkey_uncompressed
315+
--allowlist-function keystore_secp256k1_compressed_to_uncompressed
316316
--allowlist-function keystore_secp256k1_nonce_commit
317317
--allowlist-function keystore_secp256k1_sign
318318
--allowlist-function keystore_secp256k1_schnorr_bip86_sign

src/keystore.c

Lines changed: 5 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -539,11 +539,9 @@ bool keystore_get_bip39_word(uint16_t idx, char** word_out)
539539
return bip39_get_word(NULL, idx, word_out) == WALLY_OK;
540540
}
541541

542-
// Reformats xpub from compressed 33 bytes to uncompressed 65 bytes (<0x04><64 bytes X><64 bytes
543-
// Y>),
544-
// pubkey must be 33 bytes
545-
// uncompressed_out must be 65 bytes.
546-
static bool _compressed_to_uncompressed(const uint8_t* pubkey_bytes, uint8_t* uncompressed_out)
542+
bool keystore_secp256k1_compressed_to_uncompressed(
543+
const uint8_t* pubkey_bytes,
544+
uint8_t* uncompressed_out)
547545
{
548546
const secp256k1_context* ctx = wally_get_secp_context();
549547
secp256k1_pubkey pubkey;
@@ -558,18 +556,6 @@ static bool _compressed_to_uncompressed(const uint8_t* pubkey_bytes, uint8_t* un
558556
return true;
559557
}
560558

561-
bool keystore_secp256k1_pubkey_uncompressed(
562-
const uint32_t* keypath,
563-
size_t keypath_len,
564-
uint8_t* pubkey_out)
565-
{
566-
struct ext_key xpub __attribute__((__cleanup__(keystore_zero_xkey))) = {0};
567-
if (!keystore_get_xpub(keypath, keypath_len, &xpub)) {
568-
return false;
569-
}
570-
return _compressed_to_uncompressed(xpub.pub_key, pubkey_out);
571-
}
572-
573559
bool keystore_secp256k1_nonce_commit(
574560
const uint32_t* keypath,
575561
size_t keypath_len,
@@ -706,23 +692,12 @@ static void _tagged_hash(const char* tag, const uint8_t* msg, size_t msg_len, ui
706692
rust_sha256_finish(&hash_ctx, hash_out);
707693
}
708694

709-
bool keystore_secp256k1_schnorr_bip86_pubkey(
710-
const uint32_t* keypath,
711-
size_t keypath_len,
712-
uint8_t* pubkey_out)
695+
bool keystore_secp256k1_schnorr_bip86_pubkey(const uint8_t* pubkey33, uint8_t* pubkey_out)
713696
{
714-
if (keystore_is_locked()) {
715-
return false;
716-
}
717-
struct ext_key xpub __attribute__((__cleanup__(keystore_zero_xkey))) = {0};
718-
if (!keystore_get_xpub(keypath, keypath_len, &xpub)) {
719-
return false;
720-
}
721-
722697
const secp256k1_context* ctx = wally_get_secp_context();
723698

724699
secp256k1_pubkey pubkey = {0};
725-
if (!secp256k1_ec_pubkey_parse(ctx, &pubkey, xpub.pub_key, sizeof(xpub.pub_key))) {
700+
if (!secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkey33, 33)) {
726701
return false;
727702
}
728703
secp256k1_xonly_pubkey xonly_pubkey = {0};

src/keystore.h

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -166,17 +166,13 @@ void keystore_zero_xkey(struct ext_key* xkey);
166166
*/
167167
USE_RESULT bool keystore_get_bip39_word(uint16_t idx, char** word_out);
168168

169-
/**
170-
* Return the serialized secp256k1 public key at the keypath, in uncompressed format.
171-
* @param[in] keypath derivation keypath
172-
* @param[in] keypath_len size of keypath buffer
173-
* @param[out] pubkey_out serialized output. Must be EC_PUBLIC_KEY_UNCOMPRESSED_LEN bytes.
174-
* @return true on success, false if the keystore is locked or the input is invalid.
175-
*/
176-
USE_RESULT bool keystore_secp256k1_pubkey_uncompressed(
177-
const uint32_t* keypath,
178-
size_t keypath_len,
179-
uint8_t* pubkey_out);
169+
// Reformats pubkey from compressed 33 bytes to uncompressed 65 bytes (<0x04><64 bytes X><64 bytes
170+
// Y>),
171+
// pubkey must be 33 bytes
172+
// uncompressed_out must be 65 bytes.
173+
USE_RESULT bool keystore_secp256k1_compressed_to_uncompressed(
174+
const uint8_t* pubkey_bytes,
175+
uint8_t* uncompressed_out);
180176

181177
/**
182178
* Get a commitment to the original nonce before tweaking it with the host nonce. This is part of
@@ -259,21 +255,18 @@ USE_RESULT bool keystore_encode_xpub_at_keypath(
259255
uint8_t* out);
260256

261257
/**
262-
* Return the tweaked taproot pubkey at the given keypath.
258+
* Return the tweaked taproot pubkey.
263259
*
264-
* Instead of returning the original pubkey at the keypath directly, it is tweaked with the hash of
265-
* the pubkey.
260+
* Instead of returning the original pubkey directly, it is tweaked with the hash of the pubkey.
266261
*
267262
* See
268263
* https://github.com/bitcoin/bips/blob/edffe529056f6dfd33d8f716fb871467c3c09263/bip-0086.mediawiki#address-derivation
269264
*
270-
* @param[in] keypath derivation keypath
271-
* @param[in] keypath_len number of elements in keypath
265+
* @param[in] pubkey33 33 byte compressed pubkey.
272266
* @param[out] pubkey_out 32 byte x-only pubkey (see BIP-340 for details).
273267
*/
274268
USE_RESULT bool keystore_secp256k1_schnorr_bip86_pubkey(
275-
const uint32_t* keypath,
276-
size_t keypath_len,
269+
const uint8_t* pubkey33,
277270
uint8_t* pubkey_out);
278271

279272
/**

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/hww/api/ethereum/pubrequest.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ async fn process_address(request: &pb::EthPubRequest) -> Result<Response, Error>
4747
if !super::keypath::is_valid_keypath_address(&request.keypath) {
4848
return Err(Error::InvalidInput);
4949
}
50-
let pubkey = bitbox02::keystore::secp256k1_pubkey_uncompressed(&request.keypath)
50+
let pubkey = crate::keystore::secp256k1_pubkey_uncompressed(&request.keypath)
5151
.or(Err(Error::InvalidInput))?;
5252
let address = super::address::from_pubkey(&pubkey);
5353

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

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,32 @@ pub fn secp256k1_pubkey_hash160(keypath: &[u32]) -> Result<Vec<u8>, ()> {
3434
Ok(bitbox02::hash160(&xpub.public_key).to_vec())
3535
}
3636

37+
pub fn secp256k1_pubkey_uncompressed(
38+
keypath: &[u32],
39+
) -> Result<[u8; keystore::EC_PUBLIC_KEY_UNCOMPRESSED_LEN], ()> {
40+
let xpub = get_xpub(keypath)?;
41+
keystore::secp256k1_pubkey_compressed_to_uncompressed(&xpub.public_key)
42+
}
43+
3744
/// Returns fingerprint of the root public key at m/, which are the first four bytes of its hash160
3845
/// according to:
3946
/// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#serialization-format
4047
pub fn root_fingerprint() -> Result<Vec<u8>, ()> {
4148
Ok(secp256k1_pubkey_hash160(&[])?.get(..4).ok_or(())?.to_vec())
4249
}
4350

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+
4463
#[cfg(test)]
4564
mod tests {
4665
use super::*;
@@ -134,4 +153,65 @@ mod tests {
134153
);
135154
assert_eq!(root_fingerprint(), Ok(vec![0xf4, 0x0b, 0x46, 0x9a]));
136155
}
156+
157+
#[test]
158+
fn test_secp256k1_pubkey_uncompressed() {
159+
let keypath = &[44 + HARDENED, 0 + HARDENED, 0 + HARDENED, 1, 2];
160+
161+
keystore::lock();
162+
assert_eq!(secp256k1_pubkey_uncompressed(keypath), Err(()));
163+
164+
mock_unlocked_using_mnemonic("sleep own lobster state clean thrive tail exist cactus bitter pass soccer clinic riot dream turkey before sport action praise tunnel hood donate man");
165+
assert_eq!(
166+
secp256k1_pubkey_uncompressed(keypath).unwrap(),
167+
*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",
168+
);
169+
}
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+
}
137217
}

src/rust/bitbox02/src/keystore.rs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -183,14 +183,13 @@ pub fn get_bip39_wordlist() -> Result<Bip39Wordlist, ()> {
183183
Ok(result)
184184
}
185185

186-
pub fn secp256k1_pubkey_uncompressed(
187-
keypath: &[u32],
186+
pub fn secp256k1_pubkey_compressed_to_uncompressed(
187+
compressed_pubkey: &[u8],
188188
) -> Result<[u8; EC_PUBLIC_KEY_UNCOMPRESSED_LEN], ()> {
189189
let mut pubkey = [0u8; EC_PUBLIC_KEY_UNCOMPRESSED_LEN];
190190
match unsafe {
191-
bitbox02_sys::keystore_secp256k1_pubkey_uncompressed(
192-
keypath.as_ptr(),
193-
keypath.len() as _,
191+
bitbox02_sys::keystore_secp256k1_compressed_to_uncompressed(
192+
compressed_pubkey.as_ptr(),
194193
pubkey.as_mut_ptr(),
195194
)
196195
} {
@@ -315,12 +314,11 @@ pub fn secp256k1_schnorr_bip86_sign(keypath: &[u32], msg: &[u8; 32]) -> Result<[
315314
}
316315
}
317316

318-
pub fn secp256k1_schnorr_bip86_pubkey(keypath: &[u32]) -> Result<[u8; 32], ()> {
317+
pub fn secp256k1_schnorr_bip86_pubkey(pubkey33: &[u8]) -> Result<[u8; 32], ()> {
319318
let mut pubkey = [0u8; 32];
320319
match unsafe {
321320
bitbox02_sys::keystore_secp256k1_schnorr_bip86_pubkey(
322-
keypath.as_ptr(),
323-
keypath.len() as _,
321+
pubkey33.as_ptr(),
324322
pubkey.as_mut_ptr(),
325323
)
326324
} {

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};

test/unit-test/test_keystore_functional.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,8 @@ static bool _encode_xpub(const struct ext_key* xpub, char* out, size_t out_len)
120120

121121
static void _check_pubs(const char* expected_xpub, const char* expected_pubkey_uncompressed_hex)
122122
{
123-
struct ext_key __attribute__((__cleanup__(keystore_zero_xkey))) xpub;
123+
struct ext_key __attribute__((__cleanup__(keystore_zero_xkey))) xpub_3;
124+
struct ext_key __attribute__((__cleanup__(keystore_zero_xkey))) xpub_5;
124125
uint32_t keypath[] = {
125126
44 + BIP32_INITIAL_HARDENED_CHILD,
126127
0 + BIP32_INITIAL_HARDENED_CHILD,
@@ -129,13 +130,14 @@ static void _check_pubs(const char* expected_xpub, const char* expected_pubkey_u
129130
2,
130131
};
131132

132-
assert_true(keystore_get_xpub(keypath, 3, &xpub));
133+
assert_true(keystore_get_xpub(keypath, 3, &xpub_3));
134+
assert_true(keystore_get_xpub(keypath, 5, &xpub_5));
133135
char xpub_serialized[120];
134-
assert_true(_encode_xpub(&xpub, xpub_serialized, sizeof(xpub_serialized)));
136+
assert_true(_encode_xpub(&xpub_3, xpub_serialized, sizeof(xpub_serialized)));
135137
assert_string_equal(xpub_serialized, expected_xpub);
136138

137139
uint8_t pubkey_uncompressed[EC_PUBLIC_KEY_UNCOMPRESSED_LEN];
138-
assert_true(keystore_secp256k1_pubkey_uncompressed(keypath, 5, pubkey_uncompressed));
140+
assert_true(keystore_secp256k1_compressed_to_uncompressed(xpub_5.pub_key, pubkey_uncompressed));
139141
_assert_equal_memory_hex(
140142
pubkey_uncompressed, sizeof(pubkey_uncompressed), expected_pubkey_uncompressed_hex);
141143
}

0 commit comments

Comments
 (0)