Skip to content

Commit 7543353

Browse files
committed
keystore: port _test_keystore_secp256k1_sign to Rust
1 parent 6d4781a commit 7543353

File tree

3 files changed

+45
-73
lines changed

3 files changed

+45
-73
lines changed

src/rust/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ resolver = "2"
3030

3131
[workspace.dependencies]
3232
bech32 = { version = "0.11.0", default-features = false }
33-
bitcoin = { version = "0.32.2", default-features = false }
33+
# The secp-recovery feature is currently only needed in tests to make use of `RecoverableSignature`.
34+
# Attempting to enable it conditionally only for tests somehow leads to linking errors (duplicate secp256k1 symbols).
35+
bitcoin = { version = "0.32.2", default-features = false, features = ["secp-recovery"] }
3436
hex = { version = "0.4", default-features = false, features = ["alloc"] }
3537
num-bigint = { version = "0.4.6", default-features = false }
3638
# force-soft-compact reduces the binary size by ~3kB. In future versions of sha2 this will change to

src/rust/bitbox02/src/keystore.rs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -356,10 +356,51 @@ pub fn secp256k1_schnorr_sign(
356356
#[cfg(test)]
357357
mod tests {
358358
use super::*;
359-
use crate::testing::{mock_memory, mock_unlocked_using_mnemonic};
359+
use bitcoin::secp256k1;
360+
361+
use crate::testing::{mock_memory, mock_unlocked, mock_unlocked_using_mnemonic};
360362
use alloc::string::ToString;
361363
use util::bip32::HARDENED;
362364

365+
#[test]
366+
fn test_secp256k1_sign() {
367+
lock();
368+
let keypath = [44 + HARDENED, 0 + HARDENED, 0 + HARDENED, 0, 5];
369+
let msg = [0x88u8; 32];
370+
let host_nonce = [0x56u8; 32];
371+
372+
// Fails because keystore is locked.
373+
assert!(secp256k1_sign(&keypath, &msg, &host_nonce).is_err());
374+
375+
mock_unlocked();
376+
let sign_result = secp256k1_sign(&keypath, &msg, &host_nonce).unwrap();
377+
// Verify signature against expected pubkey.
378+
379+
let secp = secp256k1::Secp256k1::new();
380+
let expected_pubkey = {
381+
let pubkey =
382+
hex::decode("023ffb4a4e41444d40e4e1e4c6cc329bcba2be50d0ef380aea19d490c373be58fb")
383+
.unwrap();
384+
secp256k1::PublicKey::from_slice(&pubkey).unwrap()
385+
};
386+
let msg = secp256k1::Message::from_digest_slice(&msg).unwrap();
387+
// Test recid by recovering the public key from the signature and checking against the
388+
// expected public key.
389+
let recoverable_sig = secp256k1::ecdsa::RecoverableSignature::from_compact(
390+
&sign_result.signature,
391+
secp256k1::ecdsa::RecoveryId::from_i32(sign_result.recid as i32).unwrap(),
392+
)
393+
.unwrap();
394+
395+
let recovered_pubkey = secp.recover_ecdsa(&msg, &recoverable_sig).unwrap();
396+
assert_eq!(recovered_pubkey, expected_pubkey);
397+
398+
// Verify signature.
399+
assert!(secp
400+
.verify_ecdsa(&msg, &recoverable_sig.to_standard(), &expected_pubkey)
401+
.is_ok());
402+
}
403+
363404
#[test]
364405
fn test_bip39_mnemonic_to_seed() {
365406
assert!(bip39_mnemonic_to_seed("invalid").is_err());

test/unit-test/test_keystore.c

Lines changed: 0 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,6 @@ static const uint32_t _keypath[] = {
5959
0,
6060
5,
6161
};
62-
// seckey at the above keypath with the above bip39 seed.
63-
static const uint8_t _expected_seckey[32] = {
64-
0x4e, 0x64, 0xdf, 0xd3, 0x3a, 0xae, 0x66, 0xc4, 0xc7, 0x52, 0x6c, 0xf0, 0x2e, 0xe8, 0xae, 0x3f,
65-
0x58, 0x92, 0x32, 0x9d, 0x67, 0xdf, 0xd4, 0xad, 0x05, 0xe9, 0xc3, 0xd0, 0x6e, 0xdf, 0x74, 0xfb,
66-
};
6762

6863
// Same as Python:
6964
// import hmac, hashlib; hmac.digest(b"unit-test", b"password", hashlib.sha256).hex()
@@ -117,21 +112,6 @@ void _mock_unlocked(const uint8_t* seed, size_t seed_len, const uint8_t* bip39_s
117112
keystore_mock_unlocked(seed, seed_len, bip39_seed);
118113
}
119114

120-
static bool _pubkeys_equal(
121-
const secp256k1_context* ctx,
122-
const secp256k1_pubkey* pubkey1,
123-
const secp256k1_pubkey* pubkey2)
124-
{
125-
uint8_t pubkey1_bytes[33];
126-
uint8_t pubkey2_bytes[33];
127-
size_t len = 33;
128-
assert_true(
129-
secp256k1_ec_pubkey_serialize(ctx, pubkey1_bytes, &len, pubkey1, SECP256K1_EC_COMPRESSED));
130-
assert_true(
131-
secp256k1_ec_pubkey_serialize(ctx, pubkey2_bytes, &len, pubkey2, SECP256K1_EC_COMPRESSED));
132-
return memcmp(pubkey1_bytes, pubkey2_bytes, len) == 0;
133-
}
134-
135115
static void _test_keystore_secp256k1_nonce_commit(void** state)
136116
{
137117
uint8_t msg[32] = {0};
@@ -165,56 +145,6 @@ static void _test_keystore_secp256k1_nonce_commit(void** state)
165145
}
166146
}
167147

168-
static void _test_keystore_secp256k1_sign(void** state)
169-
{
170-
const secp256k1_context* ctx = wally_get_secp_context();
171-
172-
secp256k1_pubkey expected_pubkey;
173-
assert_true(secp256k1_ec_pubkey_create(ctx, &expected_pubkey, _expected_seckey));
174-
175-
uint8_t msg[32] = {0};
176-
memset(msg, 0x88, sizeof(msg));
177-
uint8_t sig[64] = {0};
178-
179-
uint8_t host_nonce[32] = {0};
180-
memset(host_nonce, 0x56, sizeof(host_nonce));
181-
182-
{
183-
_mock_unlocked(NULL, 0, NULL);
184-
// fails because keystore is locked
185-
assert_false(keystore_secp256k1_sign(
186-
_keypath, sizeof(_keypath) / sizeof(uint32_t), msg, host_nonce, sig, NULL));
187-
}
188-
{
189-
_mock_unlocked(_mock_seed, sizeof(_mock_seed), _mock_bip39_seed);
190-
191-
_sign_expected_seckey = _expected_seckey;
192-
_sign_expected_msg = msg;
193-
// check sig by verifying it against the msg.
194-
assert_true(keystore_secp256k1_sign(
195-
_keypath, sizeof(_keypath) / sizeof(uint32_t), msg, host_nonce, sig, NULL));
196-
secp256k1_ecdsa_signature secp256k1_sig = {0};
197-
assert_true(secp256k1_ecdsa_signature_parse_compact(ctx, &secp256k1_sig, sig));
198-
assert_true(secp256k1_ecdsa_verify(ctx, &secp256k1_sig, msg, &expected_pubkey));
199-
}
200-
{ // test recoverable id (recid)
201-
int recid;
202-
assert_true(keystore_secp256k1_sign(
203-
_keypath, sizeof(_keypath) / sizeof(uint32_t), msg, host_nonce, sig, &recid));
204-
assert_int_equal(recid, 1);
205-
206-
// Test recid by recovering the public key from the signature and checking against the
207-
// expected puklic key.
208-
secp256k1_ecdsa_recoverable_signature recoverable_sig;
209-
assert_true(
210-
secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &recoverable_sig, sig, recid));
211-
212-
secp256k1_pubkey recovered_pubkey;
213-
assert_true(secp256k1_ecdsa_recover(ctx, &recovered_pubkey, &recoverable_sig, msg));
214-
215-
assert_true(_pubkeys_equal(ctx, &recovered_pubkey, &expected_pubkey));
216-
}
217-
}
218148
static void _expect_encrypt_and_store_seed(void)
219149
{
220150
will_return(__wrap_memory_is_initialized, false);
@@ -394,7 +324,6 @@ int main(void)
394324

395325
const struct CMUnitTest tests[] = {
396326
cmocka_unit_test(_test_keystore_secp256k1_nonce_commit),
397-
cmocka_unit_test(_test_keystore_secp256k1_sign),
398327
cmocka_unit_test(_test_keystore_lock),
399328
cmocka_unit_test(_test_keystore_create_and_store_seed),
400329
cmocka_unit_test(_test_secp256k1_schnorr_sign),

0 commit comments

Comments
 (0)