What changed, and why it matters
This commit is a code refactoring that moves the device attestation signing function from C code into Rust code. It converts the function to be asynchronous (async/await) and rewrites the parsing of the secure chip's DER-encoded signature in Rust. There is no direct evidence in the commit that this fixes a security vulnerability; it appears to be a normal portability and maintainability improvement. The change does add safety checks for signature length and zeroizes sensitive buffers after use, which are good defensive practices.
Treat as a routine refactoring with minor defensive hardening. Reviewers should verify that the new async state machine correctly preserves the previous synchronous ordering guarantees, that the static buffers used for the Optiga callback are not accessed concurrently, and that the DER parser correctly rejects malformed or oversized signatures. No urgent security response is indicated by the available evidence.
Security signals we found
Removal of C synchronous secure chip signing wrapper
Addition of async Rust secure chip ECDSA signing operation
Relocation of DER signature parsing from C-API Rust crate into securechip crate
Added signature length bounds check (`signature_len > ECDSA_SIGNATURE_MAX_LEN`)
Explicit zeroization of digest and signature static buffers on error paths
No mention of CVE, security bug, or vulnerability fix in commit message or diff
Evidence from the diff
The commit ports optiga_attestation_sign and the related ATECC function from C to Rust. The C implementation in src/optiga/optiga.c and the synchronous wrapper optiga_ops_crypt_ecdsa_sign_sync are removed. A new Rust async function attestation_sign is added in bitbox-securechip/src/optiga.rs and bitbox-securechip/src/atecc.rs, which calls a new async crypt_ecdsa_sign operation and then parses the resulting DER signature using a new Rust module der.rs. The DER parsing logic is moved from bitbox02-rust-c/src/der.rs (deleted) into bitbox-securechip/src/optiga/der.rs. The call chain through the HAL and attestation API is updated to be async. The new Rust code includes bounds checks on the parsed integer lengths and the returned signature length, and explicitly zeroizes the static digest and signature buffers on error paths.
Changed components
src/optiga/optiga.csrc/optiga/optiga_ops.csrc/rust/bitbox-securechip/src/optiga.rssrc/rust/bitbox-securechip/src/optiga/ops.rssrc/rust/bitbox-securechip/src/optiga/der.rssrc/rust/bitbox-securechip/src/atecc.rssrc/rust/bitbox02-rust/src/attestation.rssrc/rust/bitbox02-rust/src/hww.rssrc/rust/bitbox02/src/securechip/imp.rssrc/rust/bitbox02/src/hal/securechip.rsInspect captured patch +180 / −145
diff --git a/src/optiga/optiga.c b/src/optiga/optiga.c
index f0055dd..b6cbf73 100644
--- a/src/optiga/optiga.c
+++ b/src/optiga/optiga.c
@@ -1295,23 +1295,6 @@ bool optiga_gen_attestation_key(uint8_t* pubkey_out)
return true;
}
-bool optiga_attestation_sign(const uint8_t* challenge, uint8_t* signature_out)
-{
- uint8_t sig_der[70] = {0};
- uint16_t sig_der_size = sizeof(sig_der);
- optiga_lib_status_t res = optiga_ops_crypt_ecdsa_sign_sync(
- _crypt, challenge, 32, OPTIGA_KEY_ID_E0F1, sig_der, &sig_der_size);
- if (res != OPTIGA_CRYPT_SUCCESS) {
- util_log("sign failed: %x", res);
- return false;
- }
- // Parse signature, see Solution Reference Manual 6.2.2,
- // example for ECC NIST-P256 signature.
- // The R/S components are
- return rust_der_parse_optiga_signature(
- rust_util_bytes(sig_der, sig_der_size), rust_util_bytes_mut(signature_out, 64));
-}
-
optiga_util_t* optiga_util_instance(void)
{
return _util;
diff --git a/src/optiga/optiga.h b/src/optiga/optiga.h
index 18c346a..39d71cb 100644
--- a/src/optiga/optiga.h
+++ b/src/optiga/optiga.h
@@ -92,7 +92,6 @@ typedef struct optiga_crypt optiga_crypt_t;
USE_RESULT int optiga_setup(const securechip_interface_functions_t* ifs);
USE_RESULT bool optiga_gen_attestation_key(uint8_t* pubkey_out);
-USE_RESULT bool optiga_attestation_sign(const uint8_t* challenge, uint8_t* signature_out);
USE_RESULT optiga_util_t* optiga_util_instance(void);
USE_RESULT optiga_crypt_t* optiga_crypt_instance(void);
#if APP_U2F == 1 || FACTORYSETUP == 1
diff --git a/src/optiga/optiga_ops.c b/src/optiga/optiga_ops.c
index e593d07..d675b29 100644
--- a/src/optiga/optiga_ops.c
+++ b/src/optiga/optiga_ops.c
@@ -150,21 +150,6 @@ optiga_lib_status_t optiga_ops_crypt_ecc_generate_keypair_sync(
return res;
}
-optiga_lib_status_t optiga_ops_crypt_ecdsa_sign_sync(
- optiga_crypt_t* me,
- const uint8_t* digest,
- uint8_t digest_length,
- optiga_key_id_t private_key,
- uint8_t* signature,
- uint16_t* signature_length)
-{
- _optiga_lib_status = OPTIGA_LIB_BUSY;
- optiga_lib_status_t res = optiga_crypt_ecdsa_sign(
- me, digest, digest_length, private_key, signature, signature_length);
- _WAIT(res, _optiga_lib_status);
- return res;
-}
-
optiga_lib_status_t optiga_ops_crypt_random_sync(
optiga_crypt_t* me,
optiga_rng_type_t rng_type,
diff --git a/src/optiga/optiga_ops.h b/src/optiga/optiga_ops.h
index 2d87708..7c830bf 100644
--- a/src/optiga/optiga_ops.h
+++ b/src/optiga/optiga_ops.h
@@ -55,14 +55,6 @@ optiga_lib_status_t optiga_ops_crypt_ecc_generate_keypair_sync(
uint8_t* public_key,
uint16_t* public_key_length);
-optiga_lib_status_t optiga_ops_crypt_ecdsa_sign_sync(
- optiga_crypt_t* me,
- const uint8_t* digest,
- uint8_t digest_length,
- optiga_key_id_t private_key,
- uint8_t* signature,
- uint16_t* signature_length);
-
optiga_lib_status_t optiga_ops_crypt_random_sync(
optiga_crypt_t* me,
optiga_rng_type_t rng_type,
diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock
index c2955ec..25f6187 100644
--- a/src/rust/Cargo.lock
+++ b/src/rust/Cargo.lock
@@ -236,6 +236,7 @@ dependencies = [
"bitbox-platform-host",
"bitbox-securechip-sys",
"critical-section",
+ "der",
"grounded",
"hex_lit",
"util",
@@ -349,7 +350,6 @@ dependencies = [
"bitbox02-rust",
"bitcoin",
"cortex-m",
- "der",
"digest",
"grounded",
"hex",
diff --git a/src/rust/bitbox-hal/src/securechip.rs b/src/rust/bitbox-hal/src/securechip.rs
index 75a1b60..d23aa30 100644
--- a/src/rust/bitbox-hal/src/securechip.rs
+++ b/src/rust/bitbox-hal/src/securechip.rs
@@ -89,7 +89,7 @@ pub trait SecureChip {
/// Signs a 32-byte attestation challenge and writes the raw 64-byte P-256 signature to
/// `signature`.
- fn attestation_sign(
+ async fn attestation_sign(
&mut self,
challenge: &[u8; 32],
signature: &mut [u8; 64],
diff --git a/src/rust/bitbox-platform-host/src/securechip.rs b/src/rust/bitbox-platform-host/src/securechip.rs
index 4ed707d..f8fac3d 100644
--- a/src/rust/bitbox-platform-host/src/securechip.rs
+++ b/src/rust/bitbox-platform-host/src/securechip.rs
@@ -145,7 +145,7 @@ impl bitbox_hal::SecureChip for FakeSecureChip {
)))
}
- fn attestation_sign(
+ async fn attestation_sign(
&mut self,
challenge: &[u8; 32],
signature: &mut [u8; 64],
diff --git a/src/rust/bitbox-securechip-sys/build.rs b/src/rust/bitbox-securechip-sys/build.rs
index aa6e067..0931c4d 100644
--- a/src/rust/bitbox-securechip-sys/build.rs
+++ b/src/rust/bitbox-securechip-sys/build.rs
@@ -35,8 +35,8 @@ const ALLOWLIST_FNS: &[&str] = &[
"atecc_stretch_password",
"atecc_u2f_counter_inc",
"atecc_u2f_counter_set",
- "optiga_attestation_sign",
"optiga_crypt_clear_auto_state",
+ "optiga_crypt_ecdsa_sign",
"optiga_crypt_generate_auth_code",
"optiga_crypt_hmac",
"optiga_crypt_hmac_verify",
diff --git a/src/rust/bitbox-securechip/Cargo.toml b/src/rust/bitbox-securechip/Cargo.toml
index b420a56..efba376 100644
--- a/src/rust/bitbox-securechip/Cargo.toml
+++ b/src/rust/bitbox-securechip/Cargo.toml
@@ -15,6 +15,7 @@ bitbox-securechip-sys = { path = "../bitbox-securechip-sys" }
critical-section = { workspace = true }
grounded = { workspace = true }
util = { path = "../util", features = ["sha2"] }
+der = { version = "0.7.9", default-features = false }
zeroize = { workspace = true }
[features]
diff --git a/src/rust/bitbox-securechip/src/atecc.rs b/src/rust/bitbox-securechip/src/atecc.rs
index a9ae41a..2b7e069 100644
--- a/src/rust/bitbox-securechip/src/atecc.rs
+++ b/src/rust/bitbox-securechip/src/atecc.rs
@@ -5,7 +5,7 @@ use alloc::boxed::Box;
use bitbox_hal::Memory;
use zeroize::Zeroizing;
-pub fn attestation_sign(challenge: &[u8; 32], signature: &mut [u8; 64]) -> Result<(), ()> {
+pub async fn attestation_sign(challenge: &[u8; 32], signature: &mut [u8; 64]) -> Result<(), ()> {
match unsafe {
bitbox_securechip_sys::atecc_attestation_sign(challenge.as_ptr(), signature.as_mut_ptr())
} {
diff --git a/src/rust/bitbox-securechip/src/optiga.rs b/src/rust/bitbox-securechip/src/optiga.rs
index e159b67..9c6ccd5 100644
--- a/src/rust/bitbox-securechip/src/optiga.rs
+++ b/src/rust/bitbox-securechip/src/optiga.rs
@@ -6,6 +6,8 @@ use bitbox_hal::{Memory, Random};
use util::sha2::{hmac_sha256, hmac_sha256_overwrite, sha256};
use zeroize::Zeroizing;
+mod der;
+
#[cfg(not(test))]
#[path = "optiga/ops.rs"]
mod ops;
@@ -340,13 +342,14 @@ async fn stretch_password_v1(
)
}
-pub fn attestation_sign(challenge: &[u8; 32], signature: &mut [u8; 64]) -> Result<(), ()> {
- match unsafe {
- bitbox_securechip_sys::optiga_attestation_sign(challenge.as_ptr(), signature.as_mut_ptr())
- } {
- true => Ok(()),
- false => Err(()),
- }
+pub async fn attestation_sign(challenge: &[u8; 32], signature: &mut [u8; 64]) -> Result<(), ()> {
+ let sig_der = ops::crypt_ecdsa_sign(
+ challenge,
+ bitbox_securechip_sys::optiga_key_id::OPTIGA_KEY_ID_E0F1,
+ )
+ .await
+ .map_err(|_| ())?;
+ der::parse_optiga_signature(sig_der.as_slice(), signature)
}
pub async fn random() -> Result<Box<Zeroizing<[u8; 32]>>, Error> {
@@ -516,6 +519,21 @@ mod tests {
(guard, memory)
}
+ #[async_test::test]
+ #[allow(clippy::await_holding_lock)]
+ async fn test_attestation_sign() {
+ let (_guard, _memory) = setup_test();
+ let mut signature = [0u8; 64];
+ attestation_sign(&[0x42; 32], &mut signature).await.unwrap();
+ assert_eq!(
+ signature,
+ hex!(
+ "0000000000000000000000000000000000000000000000000000000000001234\
+ 000000000000000000000000000000000000000000000000000000000000abcd"
+ ),
+ );
+ }
+
// Expected stretched_out for password "pw" for the V0 algorithm given the deterministic fake
// constants in ops_fake.rs.
//
diff --git a/src/rust/bitbox-securechip/src/optiga/der.rs b/src/rust/bitbox-securechip/src/optiga/der.rs
new file mode 100644
index 0000000..3799125
--- /dev/null
+++ b/src/rust/bitbox-securechip/src/optiga/der.rs
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: Apache-2.0
+
+use der::asn1::UintRef;
+use der::{Decode, SliceReader};
+
+fn parse_int256(decoder: &mut SliceReader) -> Result<[u8; 32], ()> {
+ let int = UintRef::decode(decoder).map_err(|_| ())?;
+ let int_bytes = int.as_bytes();
+ if int_bytes.len() > 32 {
+ return Err(());
+ }
+ let mut array = [0u8; 32];
+ let start_index = 32 - int_bytes.len();
+ array[start_index..].copy_from_slice(int_bytes);
+ Ok(array)
+}
+
+fn parse_two_int256s(data: &[u8]) -> Result<([u8; 32], [u8; 32]), ()> {
+ let mut decoder = SliceReader::new(data).map_err(|_| ())?;
+ let first = parse_int256(&mut decoder)?;
+ let second = parse_int256(&mut decoder)?;
+ Ok((first, second))
+}
+
+/// Parse an ECC signature as returned by the Optiga Trust M.
+/// See Solution Reference Manual 6.2.2, example for ECC NIST-P256 signature.
+/// The input is the DER encoding of the signature R/S values encoded as two DER "INTEGER"s.
+/// It is the same encoding as a regular DER signature, but without the `0x30` sequence header.
+/// `sig_compact_out` will contain the 32-byte R and 32-byte S values.
+pub(super) fn parse_optiga_signature(
+ sig_der: &[u8],
+ sig_compact_out: &mut [u8; 64],
+) -> Result<(), ()> {
+ let (first, second) = parse_two_int256s(sig_der)?;
+ sig_compact_out[..32].copy_from_slice(&first);
+ sig_compact_out[32..].copy_from_slice(&second);
+ Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use hex_lit::hex;
+
+ #[test]
+ fn test_parse_optiga_signature() {
+ let sig_der = hex!("02021234020300abcd");
+ let mut sig_compact = [0u8; 64];
+ parse_optiga_signature(&sig_der, &mut sig_compact).unwrap();
+ assert_eq!(
+ sig_compact,
+ hex!(
+ "0000000000000000000000000000000000000000000000000000000000001234\
+ 000000000000000000000000000000000000000000000000000000000000abcd"
+ ),
+ );
+ }
+}
diff --git a/src/rust/bitbox-securechip/src/optiga/ops.rs b/src/rust/bitbox-securechip/src/optiga/ops.rs
index c27fa70..28ee8b0 100644
--- a/src/rust/bitbox-securechip/src/optiga/ops.rs
+++ b/src/rust/bitbox-securechip/src/optiga/ops.rs
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::{Error, SecureChipError};
-use alloc::boxed::Box;
+use alloc::{boxed::Box, vec::Vec};
use core::cell::UnsafeCell;
use core::future::poll_fn;
use core::task::{Poll, Waker};
@@ -456,6 +456,59 @@ pub(super) async fn crypt_generate_auth_code(
Ok(())
}
+pub(super) async fn crypt_ecdsa_sign(
+ digest: &[u8; super::KDF_LEN],
+ private_key: bitbox_securechip_sys::optiga_key_id_t,
+) -> Result<Vec<u8>, Error> {
+ const ECDSA_SIGNATURE_MAX_LEN: usize = 70;
+
+ // Static because the Optiga library keeps raw pointers to the input, output and length until
+ // the async callback completes, and the Rust future may be dropped before that happens.
+ static DIGEST: StaticBytes<{ super::KDF_LEN }> = StaticBytes::const_init();
+ static SIGNATURE: StaticBytes<ECDSA_SIGNATURE_MAX_LEN> = StaticBytes::const_init();
+ static SIGNATURE_LEN: GroundedCell<u16> = GroundedCell::const_init();
+
+ let crypt = unsafe { bitbox_securechip_sys::optiga_crypt_instance() };
+
+ DIGEST.copy_from_slice(digest);
+ SIGNATURE.clear();
+ unsafe {
+ SIGNATURE_LEN.get().write(ECDSA_SIGNATURE_MAX_LEN as u16);
+ }
+ let result = run_async_op(|| unsafe {
+ bitbox_securechip_sys::optiga_crypt_ecdsa_sign(
+ crypt,
+ DIGEST.as_mut_ptr(),
+ super::KDF_LEN as u8,
+ private_key,
+ SIGNATURE.as_mut_ptr(),
+ SIGNATURE_LEN.get(),
+ )
+ })
+ .await
+ .map_err(|status| Error::from_status(status as i32));
+ if let Err(err) = result {
+ DIGEST.zeroize();
+ SIGNATURE.zeroize();
+ return Err(err);
+ }
+
+ let signature_len = unsafe { SIGNATURE_LEN.get().read() as usize };
+ if signature_len > ECDSA_SIGNATURE_MAX_LEN {
+ DIGEST.zeroize();
+ SIGNATURE.zeroize();
+ return Err(Error::SecureChip(
+ SecureChipError::SC_OPTIGA_ERR_UNEXPECTED_LEN,
+ ));
+ }
+
+ let mut signature = alloc::vec![0; signature_len];
+ SIGNATURE.copy_to_slice(signature.as_mut_slice());
+ DIGEST.zeroize();
+ SIGNATURE.zeroize();
+ Ok(signature)
+}
+
pub(super) async fn crypt_hmac_verify(
hmac_type: bitbox_securechip_sys::optiga_hmac_type_t,
secret: u16,
diff --git a/src/rust/bitbox-securechip/src/optiga/ops_fake.rs b/src/rust/bitbox-securechip/src/optiga/ops_fake.rs
index 6055609..53dbdbd 100644
--- a/src/rust/bitbox-securechip/src/optiga/ops_fake.rs
+++ b/src/rust/bitbox-securechip/src/optiga/ops_fake.rs
@@ -1,7 +1,8 @@
// SPDX-License-Identifier: Apache-2.0
use crate::Error;
-use alloc::boxed::Box;
+use alloc::{boxed::Box, vec::Vec};
+use hex_lit::hex;
use std::sync::{LazyLock, Mutex, MutexGuard};
use zeroize::Zeroizing;
@@ -193,6 +194,14 @@ pub(super) async fn crypt_generate_auth_code(
crypt_generate_auth_code_sync(rng_type, random_data)
}
+pub(super) async fn crypt_ecdsa_sign(
+ _digest: &[u8; super::KDF_LEN],
+ _private_key: bitbox_securechip_sys::optiga_key_id_t,
+) -> Result<Vec<u8>, Error> {
+ const SIG_DER: [u8; 9] = hex!("02021234020300abcd");
+ Ok(SIG_DER.to_vec())
+}
+
pub(super) async fn crypt_hmac_verify(
hmac_type: bitbox_securechip_sys::optiga_hmac_type_t,
secret: u16,
diff --git a/src/rust/bitbox02-rust-c/Cargo.toml b/src/rust/bitbox02-rust-c/Cargo.toml
index 3a4ff57..1ce843c 100644
--- a/src/rust/bitbox02-rust-c/Cargo.toml
+++ b/src/rust/bitbox02-rust-c/Cargo.toml
@@ -20,7 +20,6 @@ bitbox-noise = { path = "../bitbox-noise", optional = true }
cortex-m = { workspace = true }
util = { path = "../util" }
bitbox-framed-serial-link = { path = "../bitbox-framed-serial-link" }
-der = { version = "0.7.9", default-features = false, optional = true }
hex = { workspace = true }
grounded = { workspace = true }
sha2 = { workspace = true, optional = true }
@@ -75,7 +74,6 @@ firmware = [
"bitbox-noise",
"util/sha2",
"util/firmware",
- "der",
"bitbox-aes",
]
diff --git a/src/rust/bitbox02-rust-c/src/der.rs b/src/rust/bitbox02-rust-c/src/der.rs
deleted file mode 100644
index 049bdd8..0000000
--- a/src/rust/bitbox02-rust-c/src/der.rs
+++ /dev/null
@@ -1,67 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-use der::asn1::UintRef;
-use der::{Decode, SliceReader};
-
-fn parse_int256(decoder: &mut SliceReader) -> Result<[u8; 32], ()> {
- let int = UintRef::decode(decoder).map_err(|_| ())?;
- let int_bytes = int.as_bytes();
- if int_bytes.len() > 32 {
- return Err(());
- }
- let mut array = [0u8; 32];
- let start_index = 32 - int_bytes.len();
- array[start_index..].copy_from_slice(int_bytes);
- Ok(array)
-}
-
-fn parse_two_int256s(data: &[u8]) -> Result<([u8; 32], [u8; 32]), ()> {
- let mut decoder = SliceReader::new(data).map_err(|_| ())?;
-
- let first = parse_int256(&mut decoder)?;
- let second = parse_int256(&mut decoder)?;
-
- Ok((first, second))
-}
-
-/// Parse a ECC signature as returned by the Optiga Trust M.
-/// See Solution Reference Manual 6.2.2, example for ECC NIST-P256 signature.
-/// https://github.com/Infineon/optiga-trust-m-overview/blob/98b2b9c178f0391b1ab26b52082899704dab688a/docs/pdf/OPTIGA_Trust_M_Datasheet_v3.70.pdf
-/// The input is the DER encoding of the signature R/S values encoded as two DER "INGEGER".
-/// It's the same encoding as a regular DER-signature, but without the 0x30 sequence header.
-/// sig_compact_out must be 64 bytes and will contain the R/S values (each 32 bytes).
-#[unsafe(no_mangle)]
-pub extern "C" fn rust_der_parse_optiga_signature(
- sig_der: util::bytes::Bytes,
- mut sig_compact_out: util::bytes::BytesMut,
-) -> bool {
- match parse_two_int256s(sig_der.as_ref()) {
- Ok((first, second)) => {
- sig_compact_out.as_mut()[..32].copy_from_slice(&first);
- sig_compact_out.as_mut()[32..].copy_from_slice(&second);
- true
- }
- Err(_) => false,
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_rust_der_parse_optiga_signature() {
- let sig_der = b"\x02\x02\x12\x34\x02\x03\x00\xab\xcd";
- let mut sig_compact = [0u8; 64];
- assert!(rust_der_parse_optiga_signature(
- unsafe { util::bytes::rust_util_bytes(sig_der.as_ptr(), sig_der.len()) },
- unsafe {
- util::bytes::rust_util_bytes_mut(sig_compact.as_mut_ptr(), sig_compact.len())
- },
- ));
- assert_eq!(
- hex::encode(sig_compact),
- "0000000000000000000000000000000000000000000000000000000000001234000000000000000000000000000000000000000000000000000000000000abcd",
- );
- }
-}
diff --git a/src/rust/bitbox02-rust-c/src/lib.rs b/src/rust/bitbox02-rust-c/src/lib.rs
index 3525176..88aebbd 100644
--- a/src/rust/bitbox02-rust-c/src/lib.rs
+++ b/src/rust/bitbox02-rust-c/src/lib.rs
@@ -18,8 +18,6 @@ pub mod async_usb;
))]
mod communication_mode;
#[cfg(feature = "firmware")]
-mod der;
-#[cfg(feature = "firmware")]
mod firmware_c_api;
#[cfg(feature = "factory-setup")]
mod secp256k1;
diff --git a/src/rust/bitbox02-rust/src/attestation.rs b/src/rust/bitbox02-rust/src/attestation.rs
index 124851e..b2bad61 100644
--- a/src/rust/bitbox02-rust/src/attestation.rs
+++ b/src/rust/bitbox02-rust/src/attestation.rs
@@ -11,7 +11,7 @@ pub struct Data {
pub challenge_signature: [u8; 64],
}
-pub fn perform(hal: &mut impl crate::hal::Hal, host_challenge: [u8; 32]) -> Result<Data, ()> {
+pub async fn perform(hal: &mut impl crate::hal::Hal, host_challenge: [u8; 32]) -> Result<Data, ()> {
let mut result = Data {
bootloader_hash: [0; 32],
device_pubkey: [0; 64],
@@ -27,7 +27,8 @@ pub fn perform(hal: &mut impl crate::hal::Hal, host_challenge: [u8; 32]) -> Resu
result.bootloader_hash = hal.memory().get_attestation_bootloader_hash();
let hash: [u8; 32] = Sha256::digest(host_challenge).into();
hal.securechip()
- .attestation_sign(&hash, &mut result.challenge_signature)?;
+ .attestation_sign(&hash, &mut result.challenge_signature)
+ .await?;
Ok(result)
}
@@ -37,8 +38,8 @@ mod tests {
use crate::hal::testing::TestingHal;
use sha2::{Digest, Sha256};
- #[test]
- fn test_perform_success() {
+ #[async_test::test]
+ async fn test_perform_success() {
let mut hal = TestingHal::new();
let expected_pubkey = [0x55u8; 64];
@@ -59,7 +60,7 @@ mod tests {
let host_challenge = [0x42u8; 32];
- let data = perform(&mut hal, host_challenge).unwrap();
+ let data = perform(&mut hal, host_challenge).await.unwrap();
assert_eq!(data.device_pubkey, expected_pubkey);
assert_eq!(data.certificate, expected_certificate);
@@ -74,14 +75,14 @@ mod tests {
);
}
- #[test]
- fn test_perform_attestation_not_set() {
+ #[async_test::test]
+ async fn test_perform_attestation_not_set() {
let mut hal = TestingHal::new();
let host_challenge = [0u8; 32];
// No attestation data configured on hal.memory(),
// so get_attestation_pubkey_and_certificate should fail
// and perform() should propagate Err(()).
- assert!(perform(&mut hal, host_challenge).is_err());
+ assert!(perform(&mut hal, host_challenge).await.is_err());
}
}
diff --git a/src/rust/bitbox02-rust/src/hww.rs b/src/rust/bitbox02-rust/src/hww.rs
index 5ba7de1..68dd298 100644
--- a/src/rust/bitbox02-rust/src/hww.rs
+++ b/src/rust/bitbox02-rust/src/hww.rs
@@ -77,7 +77,7 @@ async fn api_unlock(hal: &mut impl crate::hal::Hal) -> Vec<u8> {
///
/// On success, returns < 0 | bootloader_hash 32 | device_pubkey 64 |
/// certificate 64 | root_pubkey_identifier 32 | challenge_signature 64>
-fn api_attestation(hal: &mut impl crate::hal::Hal, usb_in: &[u8]) -> Vec<u8> {
+async fn api_attestation(hal: &mut impl crate::hal::Hal, usb_in: &[u8]) -> Vec<u8> {
use core::convert::TryInto;
let usb_in: [u8; 32] = match usb_in.try_into() {
@@ -85,7 +85,7 @@ fn api_attestation(hal: &mut impl crate::hal::Hal, usb_in: &[u8]) -> Vec<u8> {
Err(_) => return [OP_STATUS_FAILURE].to_vec(),
};
- let result = match crate::attestation::perform(hal, usb_in) {
+ let result = match crate::attestation::perform(hal, usb_in).await {
Ok(result) => result,
Err(()) => return [OP_STATUS_FAILURE].to_vec(),
};
@@ -113,7 +113,7 @@ pub async fn process_packet(hal: &mut impl crate::hal::Hal, usb_in: Vec<u8>) ->
match usb_in.split_first() {
Some((&OP_UNLOCK, b"")) => return api_unlock(hal).await,
- Some((&OP_ATTESTATION, rest)) => return api_attestation(hal, rest),
+ Some((&OP_ATTESTATION, rest)) => return api_attestation(hal, rest).await,
_ => (),
}
diff --git a/src/rust/bitbox02/src/hal/securechip.rs b/src/rust/bitbox02/src/hal/securechip.rs
index 20d9435..a2d30a3 100644
--- a/src/rust/bitbox02/src/hal/securechip.rs
+++ b/src/rust/bitbox02/src/hal/securechip.rs
@@ -117,12 +117,12 @@ impl SecureChip for BitBox02SecureChip {
crate::securechip::kdf(msg).await.map_err(to_hal_error)
}
- fn attestation_sign(
+ async fn attestation_sign(
&mut self,
challenge: &[u8; 32],
signature: &mut [u8; 64],
) -> Result<(), ()> {
- crate::securechip::attestation_sign(challenge, signature)
+ crate::securechip::attestation_sign(challenge, signature).await
}
async fn monotonic_increments_remaining(&mut self) -> Result<u32, ()> {
diff --git a/src/rust/bitbox02/src/securechip/imp.rs b/src/rust/bitbox02/src/securechip/imp.rs
index 5fbe708..507db6a 100644
--- a/src/rust/bitbox02/src/securechip/imp.rs
+++ b/src/rust/bitbox02/src/securechip/imp.rs
@@ -19,10 +19,10 @@ fn backend() -> Backend {
BACKEND.read().unwrap()
}
-pub fn attestation_sign(challenge: &[u8; 32], signature: &mut [u8; 64]) -> Result<(), ()> {
+pub async fn attestation_sign(challenge: &[u8; 32], signature: &mut [u8; 64]) -> Result<(), ()> {
match backend() {
- Backend::Atecc => atecc::attestation_sign(challenge, signature),
- Backend::Optiga => optiga::attestation_sign(challenge, signature),
+ Backend::Atecc => atecc::attestation_sign(challenge, signature).await,
+ Backend::Optiga => optiga::attestation_sign(challenge, signature).await,
}
}
diff --git a/src/rust/bitbox02/src/securechip/imp_fake.rs b/src/rust/bitbox02/src/securechip/imp_fake.rs
index 9d693fa..110ca1a 100644
--- a/src/rust/bitbox02/src/securechip/imp_fake.rs
+++ b/src/rust/bitbox02/src/securechip/imp_fake.rs
@@ -24,7 +24,7 @@ fn hmac_sha256(key: &[u8], data: &[u8]) -> [u8; 32] {
out
}
-pub fn attestation_sign(_challenge: &[u8; 32], _signature: &mut [u8; 64]) -> Result<(), ()> {
+pub async fn attestation_sign(_challenge: &[u8; 32], _signature: &mut [u8; 64]) -> Result<(), ()> {
Err(())
}
diff --git a/src/rust/bitbox03/src/securechip.rs b/src/rust/bitbox03/src/securechip.rs
index 80146b5..0f49fe8 100644
--- a/src/rust/bitbox03/src/securechip.rs
+++ b/src/rust/bitbox03/src/securechip.rs
@@ -39,7 +39,7 @@ impl hal::securechip::SecureChip for BitBox03SecureChip {
todo!()
}
- fn attestation_sign(
+ async fn attestation_sign(
&mut self,
_challenge: &[u8; 32],
_signature: &mut [u8; 64],
diff --git a/test/simulator-graphical-bb03/Cargo.lock b/test/simulator-graphical-bb03/Cargo.lock
index 49dbdee..1f0765f 100644
--- a/test/simulator-graphical-bb03/Cargo.lock
+++ b/test/simulator-graphical-bb03/Cargo.lock
@@ -436,6 +436,7 @@ dependencies = [
"bitbox-hal",
"bitbox-securechip-sys",
"critical-section",
+ "der",
"grounded",
"util",
"zeroize",
@@ -1065,6 +1066,12 @@ dependencies = [
"syn",
]
+[[package]]
+name = "der"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
+
[[package]]
name = "deranged"
version = "0.5.5"
diff --git a/test/simulator-graphical/Cargo.lock b/test/simulator-graphical/Cargo.lock
index ceebcde..4bdfa92 100644
--- a/test/simulator-graphical/Cargo.lock
+++ b/test/simulator-graphical/Cargo.lock
@@ -380,6 +380,7 @@ dependencies = [
"bitbox-hal",
"bitbox-securechip-sys",
"critical-section",
+ "der",
"grounded",
"util",
"zeroize",
@@ -486,7 +487,6 @@ dependencies = [
"bitbox02",
"bitbox02-rust",
"cortex-m",
- "der",
"grounded",
"hex",
"util",
Why this scored 20/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.