What changed, and why it matters
This commit changes how the BitBox02 factory-setup program checks digital signatures. It switches from a dynamically created crypto context to a built-in, read-only verification context, which makes the factory-setup firmware about 35 KB smaller. The commit also adds an explicit self-test because the new static context skips the automatic self-test that the old path performed. The change is a size optimization, not a fix for an active security flaw, but it touches cryptographic verification code used during device manufacturing.
Review that the explicit self-test is always executed before any ECDSA verification in factory setup, and verify that the static context is appropriate for all code paths that may call `rust_secp256k1_verify`. Consider whether the same optimization should be applied consistently elsewhere or kept scoped to factory setup to avoid accidental signing-context misuse.
Security signals we found
Cryptographic context change in verification path
Removal of dynamic secp256k1 context creation in factory setup
Explicit addition of secp256k1_selftest() to compensate for skipped implicit self-test
Image size reduction of ~35 KB by excluding signing precomputation table
No change to runtime signature verification semantics if self-test passes
Evidence from the diff
The patch replaces secp256k1_context_preallocated_create()-based ECDSA verification in the factory-setup image with libsecp256k1’s static secp256k1_context_no_precomp context. The Rust wrapper rust_secp256k1_verify now calls ffi::secp256k1_ecdsa_verify directly against the static context, avoiding linkage of the signing precomputation table. Because the static context skips the implicit secp256k1_selftest() run by the allocation path, a new rust_secp256k1_selftest() C-callable wrapper is added and invoked once at factory-setup startup. The stated benefit is a 35,728-byte reduction in factory-setup image size.
Changed components
src/factorysetup.csrc/rust/bitbox02-rust-c/src/secp256k1.rslibsecp256k1 ECDSA verification path used during factory setupInspect captured patch +26 / −4
### src/factorysetup.c
@@ -1156,6 +1156,7 @@ int main(void)
screen_init(oled_set_pixel, oled_mirror, oled_clear_buffer);
screen_splash();
common_main();
+ rust_secp256k1_selftest();
{
// We set auto_enter = true, as new devices are shipped without firmware, and a new device
### src/rust/bitbox02-rust-c/src/secp256k1.rs
@@ -1,6 +1,19 @@
// SPDX-License-Identifier: Apache-2.0
-use bitcoin::secp256k1::{Message, PublicKey};
+use bitcoin::secp256k1::{
+ Message, PublicKey,
+ ffi::{self, CPtr},
+};
+
+unsafe extern "C" {
+ fn secp256k1_selftest();
+}
+
+#[unsafe(no_mangle)]
+pub extern "C" fn rust_secp256k1_selftest() {
+ // SAFETY: This function takes no arguments and either returns successfully or aborts.
+ unsafe { secp256k1_selftest() }
+}
#[unsafe(no_mangle)]
pub extern "C" fn rust_secp256k1_verify(
@@ -19,9 +32,17 @@ pub extern "C" fn rust_secp256k1_verify(
let Ok(public_key) = PublicKey::from_slice(pubkey.as_ref()) else {
return false;
};
- bitbox02_rust::secp256k1::SECP256K1
- .verify_ecdsa(&message, &signature, &public_key)
- .is_ok()
+ // Signature verification does not need a dynamically allocated signing context. Using the
+ // static context also avoids linking the signing precomputation table into factory setup,
+ // saving roughly 35 kB in the image.
+ unsafe {
+ ffi::secp256k1_ecdsa_verify(
+ ffi::secp256k1_context_no_precomp,
+ signature.as_c_ptr(),
+ message.as_c_ptr(),
+ public_key.as_c_ptr(),
+ ) == 1
+ }
}
#[cfg(test)]Why this scored 21/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.