rust: use new global secp256k1 ctx in streaming-silent-payments
What changed, and why it matters
This commit is a small internal cleanup in the BitBox02 firmware's silent-payment code. It replaces a locally-created secp256k1 cryptographic context with a shared global one. There is no direct evidence in the commit that this fixes an exploitable security bug; it appears to be a code-quality and consistency change.
Treat as routine maintenance. Review the global `SECP256K1` context initialization for thread-safety and randomization settings, but no immediate security response is indicated by this commit alone.
Security signals we found
Refactoring of cryptographic context lifetime management
No change to secret key derivation, signing equations, or input validation
No vendor security disclosure or CVE reference present in commit or supplied materials
Evidence from the diff
The patch changes SilentPayment to hold a borrowed reference to a global Secp256k1 context (SECP256K1) instead of owning its own Secp256k1::new() instance. Callers in signtx.rs now pass the global context, and unit/integration tests are updated accordingly. The get_secp() accessor is removed. This reduces context duplication and aligns the module with the project’s existing global context pattern. The diff does not show any corrected cryptographic computation, bounds check, or secret-handling change.
Changed components
src/rust/streaming-silent-payments/src/lib.rssrc/rust/bitbox02-rust/src/hww/api/bitcoin/signtx.rssrc/rust/streaming-silent-payments/tests/table_test.rsInspect captured patch +22 / −24
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 a7cf908..8a7a2ab 100644
--- a/src/rust/bitbox02-rust/src/hww/api/bitcoin/signtx.rs
+++ b/src/rust/bitbox02-rust/src/hww/api/bitcoin/signtx.rs
@@ -23,6 +23,7 @@ use super::script_configs::{ValidatedScriptConfig, ValidatedScriptConfigWithKeyp
use super::{bip143, bip341, common, keypath};
use crate::hal::Ui;
+use crate::secp256k1::SECP256K1;
use crate::workflow::{confirm, transaction};
use crate::xpubcache::{Bip32XpubCache, Compute};
@@ -716,7 +717,7 @@ async fn _process(
let taproot_only = validated_script_configs.iter().all(is_taproot);
let mut silent_payment = if request.contains_silent_payment_outputs {
- Some(SilentPayment::new(coin.try_into()?))
+ Some(SilentPayment::new(SECP256K1, coin.try_into()?))
} else {
None
};
@@ -783,7 +784,7 @@ async fn _process(
if let Some(ref mut silent_payment) = silent_payment {
let keypair = bitcoin::key::UntweakedKeypair::from_seckey_slice(
- silent_payment.get_secp(),
+ SECP256K1,
&crate::keystore::secp256k1_get_private_key(&tx_input.keypath)?,
)
.unwrap();
@@ -791,10 +792,7 @@ async fn _process(
// provide the key path spend private key, which means the internal key plus the tap
// tweak.
let private_key = if is_taproot(script_config_account) {
- keypair
- .tap_tweak(silent_payment.get_secp(), None)
- .to_inner()
- .secret_key()
+ keypair.tap_tweak(SECP256K1, None).to_inner().secret_key()
} else {
keypair.secret_key()
};
diff --git a/src/rust/streaming-silent-payments/src/lib.rs b/src/rust/streaming-silent-payments/src/lib.rs
index 3ac3b30..9242d3d 100644
--- a/src/rust/streaming-silent-payments/src/lib.rs
+++ b/src/rust/streaming-silent-payments/src/lib.rs
@@ -50,8 +50,8 @@ impl Network {
}
}
-pub struct SilentPayment {
- secp: Secp256k1<secp256k1::All>,
+pub struct SilentPayment<'a> {
+ secp: &'a Secp256k1<secp256k1::All>,
network: Network,
smallest_outpoint: Option<bitcoin::OutPoint>,
a_sum: Option<SecretKey>,
@@ -156,10 +156,10 @@ fn create_dleq_proof(
Ok(result.try_into().unwrap())
}
-impl SilentPayment {
- pub fn new(network: Network) -> Self {
+impl<'a> SilentPayment<'a> {
+ pub fn new(secp: &'a Secp256k1<secp256k1::All>, network: Network) -> Self {
SilentPayment {
- secp: Secp256k1::new(),
+ secp,
network,
smallest_outpoint: None,
a_sum: None,
@@ -168,10 +168,6 @@ impl SilentPayment {
}
}
- pub fn get_secp(&self) -> &Secp256k1<secp256k1::All> {
- &self.secp
- }
-
/// This must be called for *every* input of the transaction.
///
/// Important: if the input type cannot be represented by `InputType`, the transaction must be
@@ -195,7 +191,7 @@ impl SilentPayment {
}
}
- let (_, parity) = input_key.x_only_public_key(&self.secp);
+ let (_, parity) = input_key.x_only_public_key(self.secp);
let negated_key: SecretKey = if input_type.is_taproot() && parity == secp256k1::Parity::Odd
{
input_key.negate()
@@ -230,7 +226,7 @@ impl SilentPayment {
} = decode_address(silent_payment_address, self.network.sp_hrp())?;
let a_sum = self.a_sum.as_ref().unwrap();
- let a_sum_pubkey = a_sum.public_key(&self.secp);
+ let a_sum_pubkey = a_sum.public_key(self.secp);
let inputs_hash =
hash::calculate_input_hash(self.smallest_outpoint.as_ref().ok_or(())?, a_sum_pubkey);
@@ -238,7 +234,7 @@ impl SilentPayment {
let partial_secret = a_sum.mul_tweak(&inputs_hash).map_err(|_| ())?;
let ecdh_shared_secret: PublicKey = scan_pubkey
- .mul_tweak(&self.secp, &partial_secret.into())
+ .mul_tweak(self.secp, &partial_secret.into())
.map_err(|_| ())?;
// If we want to support more than one silent payment output, we need to get this value from
@@ -250,13 +246,13 @@ impl SilentPayment {
let t_k = calculate_t_k(&ecdh_shared_secret, silent_payment_k).map_err(|_| ())?;
- let res = t_k.public_key(&self.secp);
+ let res = t_k.public_key(self.secp);
let reskey = res.combine(&spend_pubkey).map_err(|_| ())?;
let (reskey_xonly, _) = reskey.x_only_public_key();
Ok(TransactionOutput {
pubkey: reskey_xonly,
- dleq_proof: create_dleq_proof(&self.secp, a_sum, &a_sum_pubkey, &scan_pubkey)?,
+ dleq_proof: create_dleq_proof(self.secp, a_sum, &a_sum_pubkey, &scan_pubkey)?,
})
}
}
@@ -269,7 +265,8 @@ mod tests {
#[test]
fn test_basic() {
- let mut v = SilentPayment::new(Network::Btc);
+ let secp = Secp256k1::new();
+ let mut v = SilentPayment::new(&secp, Network::Btc);
v.add_input(
InputType::P2wpkh,
&SecretKey::from_str(
@@ -319,7 +316,8 @@ mod tests {
#[test]
fn test_only_one_output() {
- let mut v = SilentPayment::new(Network::Btc);
+ let secp = Secp256k1::new();
+ let mut v = SilentPayment::new(&secp, Network::Btc);
v.add_input(
InputType::P2wpkh,
&SecretKey::from_str(
@@ -357,7 +355,8 @@ mod tests {
#[test]
fn test_no_input_after_output() {
- let mut v = SilentPayment::new(Network::Btc);
+ let secp = Secp256k1::new();
+ let mut v = SilentPayment::new(&secp, Network::Btc);
v.add_input(
InputType::P2wpkh,
&SecretKey::from_str(
diff --git a/src/rust/streaming-silent-payments/tests/table_test.rs b/src/rust/streaming-silent-payments/tests/table_test.rs
index 7e6b8fd..90bbcec 100644
--- a/src/rust/streaming-silent-payments/tests/table_test.rs
+++ b/src/rust/streaming-silent-payments/tests/table_test.rs
@@ -116,6 +116,7 @@ pub struct OutputWithSignature {
#[test]
fn test_sending() {
+ let secp = bitcoin::secp256k1::Secp256k1::new();
let reader =
BufReader::new(File::open("./tests/testdata/send_and_receive_test_vectors.json").unwrap());
let tests: Vec<TestData> = serde_json::from_reader(reader).unwrap();
@@ -149,7 +150,7 @@ fn test_sending() {
assert_eq!(sending_data.given.recipients.len(), 1);
let sp_address = sending_data.given.recipients[0].as_str();
- let mut v = SilentPayment::new(Network::Btc);
+ let mut v = SilentPayment::new(&secp, Network::Btc);
for inp in sending_data.given.vin.iter() {
let pk_script_hex = inp.prevout.scriptPubKey.hex.as_str();
let pk_script_bytes = hex::decode(pk_script_hex).unwrap();
Why this scored 17/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.