What changed, and why it matters
This commit tightens Rust type signatures for functions that talk to the secure chip (Optiga). Instead of accepting arbitrary-length byte slices and then checking lengths at runtime, the functions now require fixed-size arrays. This is a defensive hardening change: it moves some length checks from runtime to compile time, reducing the chance of a length mismatch bug being exploited. There is no direct evidence in the commit that an exploitable vulnerability existed before this change.
Treat as a hardening/refactoring commit. Review that all callers now pass correctly sized arrays and that no remaining slice-based callers bypass the new fixed-size constraints. No urgent security response is indicated by the diff alone.
Security signals we found
Type narrowing from slices to fixed-size arrays in cryptographic/secure-chip interfaces
Removal of runtime length-validation branches
Compile-time enforcement of buffer sizes for HMAC, CMAC, and RNG outputs
Evidence from the diff
The patch changes several async and sync secure-chip helper functions in src/rust/bitbox-securechip/src/optiga/ops.rs, ops_fake.rs, and call sites in optiga.rs from slice types (&[u8], &mut [u8]) to fixed-size array references (&[u8; N]). It removes runtime length checks and the OPTIGA_CRYPT_ERROR_MEMORY_INSUFFICIENT constant from the allowlist and fake implementation. The change is purely type-level tightening; no logic bugs or memory-safety issues are demonstrated in the diff. It reduces the attack surface by making invalid lengths unrepresentable at compile time.
Changed components
bitbox-securechip Rust crateOptiga secure chip abstraction layercrypt_symmetric_encryptcrypt_generate_auth_codecrypt_hmac_verifycrypt_hmacops_fake test/mock implementationsInspect captured patch +29 / −67
diff --git a/src/rust/bitbox-securechip-sys/build.rs b/src/rust/bitbox-securechip-sys/build.rs
index f639233..317a570 100644
--- a/src/rust/bitbox-securechip-sys/build.rs
+++ b/src/rust/bitbox-securechip-sys/build.rs
@@ -70,7 +70,6 @@ const ALLOWLIST_VARS: &[&str] = &[
"OPTIGA_CRYPT_SUCCESS",
"OPTIGA_CRYPT_ERROR",
"OPTIGA_CRYPT_ERROR_INVALID_INPUT",
- "OPTIGA_CRYPT_ERROR_MEMORY_INSUFFICIENT",
"OPTIGA_LIB_BUSY",
"OPTIGA_LIB_SUCCESS",
"OPTIGA_UTIL_ERROR",
diff --git a/src/rust/bitbox-securechip/src/optiga.rs b/src/rust/bitbox-securechip/src/optiga.rs
index c2fb146..438b2a1 100644
--- a/src/rust/bitbox-securechip/src/optiga.rs
+++ b/src/rust/bitbox-securechip/src/optiga.rs
@@ -53,17 +53,11 @@ fn key_id_from_oid(oid: u16) -> bitbox_securechip_sys::optiga_key_id_t {
async fn authorize(oid_auth: u16, auth_secret: &[u8; KDF_LEN]) -> Result<(), Error> {
let mut random_data = zeroed_secret::<KDF_LEN>();
- ops::crypt_generate_auth_code(OPTIGA_RNG_TYPE_TRNG, random_data.as_mut_slice()).await?;
+ ops::crypt_generate_auth_code(OPTIGA_RNG_TYPE_TRNG, &mut random_data).await?;
let mut hmac = zeroed_secret::<KDF_LEN>();
hmac_sha256(auth_secret, random_data.as_slice(), &mut hmac);
- ops::crypt_hmac_verify(
- OPTIGA_HMAC_SHA_256,
- oid_auth,
- random_data.as_slice(),
- hmac.as_slice(),
- )
- .await
+ ops::crypt_hmac_verify(OPTIGA_HMAC_SHA_256, oid_auth, &random_data, &hmac).await
}
async fn reset_counter(oid: u16, limit: u32) -> Result<(), Error> {
@@ -92,7 +86,7 @@ async fn kdf_internal(msg: &[u8; KDF_LEN], kdf_out: &mut [u8; KDF_LEN]) -> Resul
OPTIGA_SYMMETRIC_CMAC,
key_id_from_oid(OID_AES_SYMKEY),
msg,
- mac_out.as_mut_slice(),
+ &mut mac_out,
)
.await?;
diff --git a/src/rust/bitbox-securechip/src/optiga/ops.rs b/src/rust/bitbox-securechip/src/optiga/ops.rs
index 935e6a8..de885f9 100644
--- a/src/rust/bitbox-securechip/src/optiga/ops.rs
+++ b/src/rust/bitbox-securechip/src/optiga/ops.rs
@@ -384,8 +384,8 @@ pub(super) async fn util_write_data(
pub(super) async fn crypt_symmetric_encrypt(
encryption_mode: bitbox_securechip_sys::optiga_symmetric_encryption_mode_t,
symmetric_key_oid: bitbox_securechip_sys::optiga_key_id_t,
- plain_data: &[u8],
- encrypted_data: &mut [u8],
+ plain_data: &[u8; super::KDF_LEN],
+ encrypted_data: &mut [u8; 16],
) -> Result<(), Error> {
// 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.
@@ -393,18 +393,13 @@ pub(super) async fn crypt_symmetric_encrypt(
static OUTPUT: StaticBytes<16> = StaticBytes::const_init();
static OUTPUT_LEN: GroundedCell<u32> = GroundedCell::const_init();
- if plain_data.len() > super::KDF_LEN || encrypted_data.len() > 16 {
- return Err(Error::SecureChip(SecureChipError::SC_ERR_INVALID_ARGS));
- }
-
let crypt = unsafe { bitbox_securechip_sys::optiga_crypt_instance() };
- let input_len: u32 = plain_data.len().try_into().unwrap();
- let requested_output_len = encrypted_data.len();
+ let input_len = super::KDF_LEN as u32;
INPUT.copy_from_slice(plain_data);
OUTPUT.clear();
unsafe {
- OUTPUT_LEN.get().write(requested_output_len as u32);
+ OUTPUT_LEN.get().write(16);
}
let result = run_async_op(|| unsafe {
bitbox_securechip_sys::optiga_crypt_symmetric_encrypt(
@@ -429,7 +424,7 @@ pub(super) async fn crypt_symmetric_encrypt(
return Err(err);
}
- if unsafe { OUTPUT_LEN.get().read() as usize } != requested_output_len {
+ if unsafe { OUTPUT_LEN.get().read() } != 16 {
INPUT.zeroize();
OUTPUT.zeroize();
return Err(Error::SecureChip(
@@ -444,18 +439,13 @@ pub(super) async fn crypt_symmetric_encrypt(
pub(super) async fn crypt_generate_auth_code(
rng_type: bitbox_securechip_sys::optiga_rng_type_t,
- random_data: &mut [u8],
+ random_data: &mut [u8; 32],
) -> Result<(), Error> {
// Static because the Optiga library keeps a raw pointer to the output buffer until the async
// callback completes, and the Rust future may be dropped before that happens.
- static RANDOM: StaticBytes<{ super::KDF_LEN }> = StaticBytes::const_init();
-
- if random_data.len() > super::KDF_LEN {
- return Err(Error::SecureChip(SecureChipError::SC_ERR_INVALID_ARGS));
- }
+ static RANDOM: StaticBytes<32> = StaticBytes::const_init();
let crypt = unsafe { bitbox_securechip_sys::optiga_crypt_instance() };
- let random_data_len: u16 = random_data.len().try_into().unwrap();
RANDOM.clear();
let result = run_async_op(|| unsafe {
@@ -465,7 +455,7 @@ pub(super) async fn crypt_generate_auth_code(
core::ptr::null(),
0,
RANDOM.as_mut_ptr(),
- random_data_len,
+ 32,
)
})
.await
@@ -483,21 +473,15 @@ pub(super) async fn crypt_generate_auth_code(
pub(super) async fn crypt_hmac_verify(
hmac_type: bitbox_securechip_sys::optiga_hmac_type_t,
secret: u16,
- input_data: &[u8],
- hmac: &[u8],
+ input_data: &[u8; super::KDF_LEN],
+ hmac: &[u8; super::KDF_LEN],
) -> Result<(), Error> {
// Static because the Optiga library keeps raw pointers to the input buffers until the async
// callback completes, and the Rust future may be dropped before that happens.
static INPUT: StaticBytes<{ super::KDF_LEN }> = StaticBytes::const_init();
static HMAC: StaticBytes<{ super::KDF_LEN }> = StaticBytes::const_init();
- if input_data.len() > super::KDF_LEN || hmac.len() > super::KDF_LEN {
- return Err(Error::SecureChip(SecureChipError::SC_ERR_INVALID_ARGS));
- }
-
let crypt = unsafe { bitbox_securechip_sys::optiga_crypt_instance() };
- let input_data_len: u32 = input_data.len().try_into().unwrap();
- let hmac_len: u32 = hmac.len().try_into().unwrap();
INPUT.copy_from_slice(input_data);
HMAC.copy_from_slice(hmac);
@@ -507,9 +491,9 @@ pub(super) async fn crypt_hmac_verify(
hmac_type,
secret,
INPUT.as_mut_ptr(),
- input_data_len,
+ super::KDF_LEN as u32,
HMAC.as_mut_ptr(),
- hmac_len,
+ super::KDF_LEN as u32,
)
})
.await
diff --git a/src/rust/bitbox-securechip/src/optiga/ops_fake.rs b/src/rust/bitbox-securechip/src/optiga/ops_fake.rs
index e94dcc0..74e996c 100644
--- a/src/rust/bitbox-securechip/src/optiga/ops_fake.rs
+++ b/src/rust/bitbox-securechip/src/optiga/ops_fake.rs
@@ -14,8 +14,6 @@ const AUTH_CODE_RANDOM_FIXED: [u8; super::KDF_LEN] = [0x77; super::KDF_LEN];
const OPTIGA_CRYPT_ERROR: i32 = bitbox_securechip_sys::OPTIGA_CRYPT_ERROR as i32;
const OPTIGA_CRYPT_ERROR_INVALID_INPUT: i32 =
bitbox_securechip_sys::OPTIGA_CRYPT_ERROR_INVALID_INPUT as i32;
-const OPTIGA_CRYPT_ERROR_MEMORY_INSUFFICIENT: i32 =
- bitbox_securechip_sys::OPTIGA_CRYPT_ERROR_MEMORY_INSUFFICIENT as i32;
const OPTIGA_UTIL_ERROR: i32 = bitbox_securechip_sys::OPTIGA_UTIL_ERROR as i32;
const OPTIGA_UTIL_ERROR_INVALID_INPUT: i32 =
bitbox_securechip_sys::OPTIGA_UTIL_ERROR_INVALID_INPUT as i32;
@@ -157,7 +155,7 @@ pub(super) async fn util_read_data(oid: u16, offset: u16, out: &mut [u8]) -> Res
pub(super) async fn crypt_hmac(
hmac_type: bitbox_securechip_sys::optiga_hmac_type_t,
secret: u16,
- msg: &[u8],
+ msg: &[u8; super::KDF_LEN],
mac_out: &mut [u8; super::KDF_LEN],
) -> Result<(), Error> {
crypt_hmac_sync(hmac_type, secret, msg, mac_out)
@@ -175,8 +173,8 @@ pub(super) async fn util_write_data(
pub(super) async fn crypt_symmetric_encrypt(
encryption_mode: bitbox_securechip_sys::optiga_symmetric_encryption_mode_t,
symmetric_key_oid: bitbox_securechip_sys::optiga_key_id_t,
- plain_data: &[u8],
- encrypted_data: &mut [u8],
+ plain_data: &[u8; super::KDF_LEN],
+ encrypted_data: &mut [u8; 16],
) -> Result<(), Error> {
crypt_symmetric_encrypt_sync(
encryption_mode,
@@ -188,7 +186,7 @@ pub(super) async fn crypt_symmetric_encrypt(
pub(super) async fn crypt_generate_auth_code(
rng_type: bitbox_securechip_sys::optiga_rng_type_t,
- random_data: &mut [u8],
+ random_data: &mut [u8; 32],
) -> Result<(), Error> {
crypt_generate_auth_code_sync(rng_type, random_data)
}
@@ -196,8 +194,8 @@ pub(super) async fn crypt_generate_auth_code(
pub(super) async fn crypt_hmac_verify(
hmac_type: bitbox_securechip_sys::optiga_hmac_type_t,
secret: u16,
- input_data: &[u8],
- hmac: &[u8],
+ input_data: &[u8; super::KDF_LEN],
+ hmac: &[u8; super::KDF_LEN],
) -> Result<(), Error> {
crypt_hmac_verify_sync(hmac_type, secret, input_data, hmac)
}
@@ -291,16 +289,13 @@ pub(super) fn util_write_data_sync(
pub(super) fn crypt_hmac_sync(
hmac_type: bitbox_securechip_sys::optiga_hmac_type_t,
secret: u16,
- input_data: &[u8],
- mac_out: &mut [u8],
+ input_data: &[u8; super::KDF_LEN],
+ mac_out: &mut [u8; super::KDF_LEN],
) -> Result<(), Error> {
// Use hmac_sha256 with a different fixed key and msg as the value.
if hmac_type != super::OPTIGA_HMAC_SHA_256 {
return Err(Error::from_status(OPTIGA_CRYPT_ERROR_INVALID_INPUT));
}
- if mac_out.len() != super::KDF_LEN {
- return Err(Error::from_status(OPTIGA_CRYPT_ERROR_MEMORY_INSUFFICIENT));
- }
let mut state = lock_state();
let key = match secret {
@@ -327,8 +322,8 @@ pub(super) fn crypt_hmac_sync(
pub(super) fn crypt_symmetric_encrypt_sync(
encryption_mode: bitbox_securechip_sys::optiga_symmetric_encryption_mode_t,
symmetric_key_oid: bitbox_securechip_sys::optiga_key_id_t,
- plain_data: &[u8],
- encrypted_data: &mut [u8],
+ plain_data: &[u8; super::KDF_LEN],
+ encrypted_data: &mut [u8; 16],
) -> Result<(), Error> {
// Use hmac_sha256 with a fixed key and msg as the value, truncated to 16 bytes.
if encryption_mode != super::OPTIGA_SYMMETRIC_CMAC
@@ -336,10 +331,6 @@ pub(super) fn crypt_symmetric_encrypt_sync(
{
return Err(Error::from_status(OPTIGA_CRYPT_ERROR_INVALID_INPUT));
}
- if encrypted_data.len() != 16 {
- return Err(Error::from_status(OPTIGA_CRYPT_ERROR_MEMORY_INSUFFICIENT));
- }
-
let out = compute_hmac(&KDF_CMAC_KEY_FIXED, plain_data);
encrypted_data.copy_from_slice(&out[..16]);
Ok(())
@@ -360,14 +351,11 @@ pub(super) fn crypt_symmetric_generate_key_sync(
pub(super) fn crypt_generate_auth_code_sync(
rng_type: bitbox_securechip_sys::optiga_rng_type_t,
- random_data: &mut [u8],
+ random_data: &mut [u8; 32],
) -> Result<(), Error> {
if rng_type != super::OPTIGA_RNG_TYPE_TRNG {
return Err(Error::from_status(OPTIGA_CRYPT_ERROR_INVALID_INPUT));
}
- if random_data.len() != super::KDF_LEN {
- return Err(Error::from_status(OPTIGA_CRYPT_ERROR_INVALID_INPUT));
- }
random_data.copy_from_slice(&AUTH_CODE_RANDOM_FIXED);
Ok(())
}
@@ -375,15 +363,12 @@ pub(super) fn crypt_generate_auth_code_sync(
pub(super) fn crypt_hmac_verify_sync(
hmac_type: bitbox_securechip_sys::optiga_hmac_type_t,
secret: u16,
- input_data: &[u8],
- hmac: &[u8],
+ input_data: &[u8; super::KDF_LEN],
+ hmac: &[u8; super::KDF_LEN],
) -> Result<(), Error> {
if hmac_type != super::OPTIGA_HMAC_SHA_256 {
return Err(Error::from_status(OPTIGA_CRYPT_ERROR_INVALID_INPUT));
}
- if input_data.len() != super::KDF_LEN || hmac.len() != super::KDF_LEN {
- return Err(Error::from_status(OPTIGA_CRYPT_ERROR_INVALID_INPUT));
- }
let mut state = lock_state();
let key = match secret {
@@ -408,7 +393,7 @@ pub(super) fn crypt_hmac_verify_sync(
};
let computed = compute_hmac(key, input_data);
- if computed != hmac {
+ if computed.as_slice() != hmac.as_slice() {
return Err(Error::from_status(super::OPTIGA_HMAC_VERIFY_FAIL));
}
Why this scored 29/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.