pass rust secp256k1 context to keystore_secp256k1_sign
What changed, and why it matters
This commit is a routine internal cleanup: it changes a cryptographic signing helper so that callers pass in the secp256k1 library context explicitly, instead of the function fetching that context itself from an internal helper. There is no direct security fix here and no evidence of an exploitable bug being patched. It is a step toward removing a dependency on an old wrapper function (`wally_get_secp_context()`).
No immediate action required. Treat as normal maintenance. Continue monitoring the series of commits removing `wally_get_secp_context()` to ensure no context-lifetime or initialization-order issues are introduced.
Security signals we found
Refactoring of cryptographic context plumbing
Removal of internal `wally_get_secp_context()` usage in signing path
No change to signature algorithm, key material, or nonce handling
Evidence from the diff
The patch refactors keystore_secp256k1_sign() and its Rust FFI wrapper to accept a secp256k1_context* / &Secp256k1<All> parameter rather than calling wally_get_secp_context() internally. All call sites in Bitcoin/Ethereum signing paths are updated to pass SECP256K1, and the unit test now creates and destroys its own context. The change reduces hidden global state but does not alter signature semantics, key handling, or the Anti-Klepto protocol logic.
Changed components
src/keystore.csrc/keystore.hsrc/rust/bitbox02/src/keystore.rsBitcoin sign-message APIBitcoin sign-transaction APIEthereum sign APIEthereum sign-message APIEthereum typed-data sign APIkeystore Anti-Klepto unit testInspect captured patch +23 / −26
diff --git a/src/keystore.c b/src/keystore.c
index dee1e0d..350f052 100644
--- a/src/keystore.c
+++ b/src/keystore.c
@@ -516,13 +516,13 @@ bool keystore_secp256k1_nonce_commit(
}
bool keystore_secp256k1_sign(
+ const secp256k1_context* ctx,
const uint8_t* private_key,
const uint8_t* msg32,
const uint8_t* host_nonce32,
uint8_t* sig_compact_out,
int* recid_out)
{
- const secp256k1_context* ctx = wally_get_secp_context();
secp256k1_ecdsa_signature secp256k1_sig = {0};
if (!secp256k1_anti_exfil_sign(
ctx, &secp256k1_sig, msg32, private_key, host_nonce32, recid_out)) {
diff --git a/src/keystore.h b/src/keystore.h
index ebbfbbe..b038897 100644
--- a/src/keystore.h
+++ b/src/keystore.h
@@ -163,6 +163,7 @@ USE_RESULT bool keystore_secp256k1_nonce_commit(
* This is part of the ECSDA Anti-Klepto protocol, preventing this function to leak any secrets via
* the signatures (see the ecdsa-s2c module in secp256k1-zpk for more details).
*
+ * @param[in] ctx secp256k1 context
* @param[in] private_key 32 byte private key
* @param[in] msg32 32 byte message to sign
* @param[in] host_nonce32 32 byte nonce contribution. Cannot be NULL.
@@ -174,6 +175,7 @@ USE_RESULT bool keystore_secp256k1_nonce_commit(
*/
// clang-format on
USE_RESULT bool keystore_secp256k1_sign(
+ const secp256k1_context* ctx,
const uint8_t* private_key,
const uint8_t* msg32,
const uint8_t* host_nonce32,
diff --git a/src/rust/bitbox02-rust/src/hww/api/bitcoin/signmsg.rs b/src/rust/bitbox02-rust/src/hww/api/bitcoin/signmsg.rs
index 4239f2f..7806e09 100644
--- a/src/rust/bitbox02-rust/src/hww/api/bitcoin/signmsg.rs
+++ b/src/rust/bitbox02-rust/src/hww/api/bitcoin/signmsg.rs
@@ -120,6 +120,7 @@ pub async fn process(
};
let sign_result = bitbox02::keystore::secp256k1_sign(
+ SECP256K1,
crate::keystore::secp256k1_get_private_key(keypath)?
.as_slice()
.try_into()
diff --git a/src/rust/bitbox02-rust/src/hww/api/bitcoin/signtx.rs b/src/rust/bitbox02-rust/src/hww/api/bitcoin/signtx.rs
index a28edba..919e212 100644
--- a/src/rust/bitbox02-rust/src/hww/api/bitcoin/signtx.rs
+++ b/src/rust/bitbox02-rust/src/hww/api/bitcoin/signtx.rs
@@ -1232,6 +1232,7 @@ async fn _process(
};
let sign_result = bitbox02::keystore::secp256k1_sign(
+ SECP256K1,
private_key.as_slice().try_into().unwrap(),
&sighash,
&host_nonce,
diff --git a/src/rust/bitbox02-rust/src/hww/api/ethereum/sign.rs b/src/rust/bitbox02-rust/src/hww/api/ethereum/sign.rs
index 433e791..90673d2 100644
--- a/src/rust/bitbox02-rust/src/hww/api/ethereum/sign.rs
+++ b/src/rust/bitbox02-rust/src/hww/api/ethereum/sign.rs
@@ -410,6 +410,7 @@ pub async fn _process(
None => [0; 32],
};
let sign_result = keystore::secp256k1_sign(
+ SECP256K1,
&crate::keystore::secp256k1_get_private_key(request.keypath())?
.as_slice()
.try_into()
diff --git a/src/rust/bitbox02-rust/src/hww/api/ethereum/sign_typed_msg.rs b/src/rust/bitbox02-rust/src/hww/api/ethereum/sign_typed_msg.rs
index a45a5cd..bf1818d 100644
--- a/src/rust/bitbox02-rust/src/hww/api/ethereum/sign_typed_msg.rs
+++ b/src/rust/bitbox02-rust/src/hww/api/ethereum/sign_typed_msg.rs
@@ -584,6 +584,7 @@ pub async fn process(
};
let sign_result = bitbox02::keystore::secp256k1_sign(
+ SECP256K1,
crate::keystore::secp256k1_get_private_key(&request.keypath)?
.as_slice()
.try_into()
diff --git a/src/rust/bitbox02-rust/src/hww/api/ethereum/signmsg.rs b/src/rust/bitbox02-rust/src/hww/api/ethereum/signmsg.rs
index d11efdd..9d47d83 100644
--- a/src/rust/bitbox02-rust/src/hww/api/ethereum/signmsg.rs
+++ b/src/rust/bitbox02-rust/src/hww/api/ethereum/signmsg.rs
@@ -89,6 +89,7 @@ pub async fn process(
};
let sign_result = bitbox02::keystore::secp256k1_sign(
+ SECP256K1,
crate::keystore::secp256k1_get_private_key(&request.keypath)?
.as_slice()
.try_into()
diff --git a/src/rust/bitbox02/src/keystore.rs b/src/rust/bitbox02/src/keystore.rs
index 1bb086c..861edd0 100644
--- a/src/rust/bitbox02/src/keystore.rs
+++ b/src/rust/bitbox02/src/keystore.rs
@@ -151,6 +151,7 @@ pub struct SignResult {
}
pub fn secp256k1_sign(
+ secp: &Secp256k1<All>,
private_key: &[u8; 32],
msg: &[u8; 32],
host_nonce: &[u8; 32],
@@ -159,6 +160,7 @@ pub fn secp256k1_sign(
let mut recid: core::ffi::c_int = 0;
match unsafe {
bitbox02_sys::keystore_secp256k1_sign(
+ secp.ctx().as_ptr().cast(),
private_key.as_ptr(),
msg.as_ptr(),
host_nonce.as_ptr(),
@@ -278,11 +280,11 @@ mod tests {
let msg = [0x88u8; 32];
let host_nonce = [0x56u8; 32];
+ let secp = secp256k1::Secp256k1::new();
let sign_result =
- secp256k1_sign(&private_key.try_into().unwrap(), &msg, &host_nonce).unwrap();
+ secp256k1_sign(&secp, &private_key.try_into().unwrap(), &msg, &host_nonce).unwrap();
// Verify signature against expected pubkey.
- let secp = secp256k1::Secp256k1::new();
let expected_pubkey = {
let pubkey =
hex::decode("023ffb4a4e41444d40e4e1e4c6cc329bcba2be50d0ef380aea19d490c373be58fb")
diff --git a/test/unit-test/test_keystore_antiklepto.c b/test/unit-test/test_keystore_antiklepto.c
index 50fa6f7..2d2cb48 100644
--- a/test/unit-test/test_keystore_antiklepto.c
+++ b/test/unit-test/test_keystore_antiklepto.c
@@ -21,7 +21,6 @@
#include <secp256k1_ecdsa_s2c.h>
#include <wally_bip32.h>
-#include <wally_crypto.h>
#include <stdbool.h>
#include <stddef.h>
@@ -66,6 +65,8 @@ static void _test_keystore_antiklepto(void** state)
uint8_t sig[64];
int recid;
+ secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
+
for (int i = 0; i < 3; i++) {
keypath[4] = i;
msg[0] = i;
@@ -88,43 +89,30 @@ static void _test_keystore_antiklepto(void** state)
// Anti-Klepto Protocol".
// Protocol step 1.
- assert_true(secp256k1_ecdsa_anti_exfil_host_commit(
- wally_get_secp_context(), host_nonce_commitment, host_nonce));
+ assert_true(secp256k1_ecdsa_anti_exfil_host_commit(ctx, host_nonce_commitment, host_nonce));
// Commit - protocol step 2.
assert_true(keystore_secp256k1_nonce_commit(
- wally_get_secp_context(),
- xprv_derived.priv_key + 1,
- msg,
- host_nonce_commitment,
- signer_commitment));
+ ctx, xprv_derived.priv_key + 1, msg, host_nonce_commitment, signer_commitment));
// Protocol step 3: host_nonce sent from host to signer to be used in step 4
// Sign - protocol step 4.
assert_true(
- keystore_secp256k1_sign(xprv_derived.priv_key + 1, msg, host_nonce, sig, &recid));
+ keystore_secp256k1_sign(ctx, xprv_derived.priv_key + 1, msg, host_nonce, sig, &recid));
// Protocol step 5: host verification.
secp256k1_ecdsa_signature parsed_signature;
- assert_true(secp256k1_ecdsa_signature_parse_compact(
- wally_get_secp_context(), &parsed_signature, sig));
+ assert_true(secp256k1_ecdsa_signature_parse_compact(ctx, &parsed_signature, sig));
secp256k1_pubkey parsed_pubkey;
assert_true(secp256k1_ec_pubkey_parse(
- wally_get_secp_context(),
- &parsed_pubkey,
- xprv_derived.pub_key,
- sizeof(xprv_derived.pub_key)));
+ ctx, &parsed_pubkey, xprv_derived.pub_key, sizeof(xprv_derived.pub_key)));
secp256k1_ecdsa_s2c_opening opening;
- assert_true(secp256k1_ecdsa_s2c_opening_parse(
- wally_get_secp_context(), &opening, signer_commitment));
+ assert_true(secp256k1_ecdsa_s2c_opening_parse(ctx, &opening, signer_commitment));
assert_true(secp256k1_anti_exfil_host_verify(
- wally_get_secp_context(),
- &parsed_signature,
- msg,
- &parsed_pubkey,
- host_nonce,
- &opening));
+ ctx, &parsed_signature, msg, &parsed_pubkey, host_nonce, &opening));
}
+
+ secp256k1_context_destroy(ctx);
}
int main(void)
Why this scored 18/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.