port test_memory_functional C tests to Rust
What changed, and why it matters
This commit is a routine refactoring that rewrites existing C unit tests in Rust. It does not change the actual firmware behavior or fix any security issue. One redundant test was removed, and several internal test-only helpers were added so the Rust tests can call the same underlying C memory functions.
No security action needed. Treat as normal test refactoring.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change ports test_memory_functional.c from cmocka to Rust. It deletes the C test file and removes it from CMakeLists.txt, then adds equivalent Rust tests in src/rust/bitbox02/src/memory.rs. To make the tests compile, build.rs exposes additional C constants/types/functions to Rust, and memory.rs adds #[cfg(test)] wrappers around memory functions. testing.rs is updated to call the new memory_setup wrapper. random.rs changes its conditional compilation from target_arch=”arm” to feature=”testing” for the mcu_32_bytes implementation. No production code paths are modified.
Changed components
src/rust/bitbox02-sys/build.rssrc/rust/bitbox02/src/memory.rssrc/rust/bitbox02/src/random.rssrc/rust/bitbox02/src/testing.rstest/unit-test/CMakeLists.txttest/unit-test/test_memory_functional.cInspect captured patch +338 / −291
diff --git a/src/rust/bitbox02-sys/build.rs b/src/rust/bitbox02-sys/build.rs
index 728ba9f..1f08359 100644
--- a/src/rust/bitbox02-sys/build.rs
+++ b/src/rust/bitbox02-sys/build.rs
@@ -32,6 +32,7 @@ const ALLOWLIST_VARS: &[&str] = &[
"MAX_UNLOCK_ATTEMPTS",
"MAX_VARINT_SIZE",
"MEMORY_DEVICE_NAME_MAX_LEN",
+ "MEMORY_MULTISIG_NUM_ENTRIES",
"MEMORY_MULTISIG_NAME_MAX_LEN",
"MEMORY_PLATFORM_BITBOX02_PLUS",
"MEMORY_PLATFORM_BITBOX02",
@@ -42,12 +43,14 @@ const ALLOWLIST_VARS: &[&str] = &[
"MEMORY_SPI_BLE_FIRMWARE_MAX_SIZE",
"SCREEN_HEIGHT",
"SCREEN_WIDTH",
+ "secfalse_u8",
"SD_MAX_FILE_SIZE",
"SLIDER_POSITION_TWO_THIRD",
"XPUB_ENCODED_LEN",
];
const ALLOWLIST_TYPES: &[&str] = &[
+ "auto_enter_t",
"buffer_t",
"component_t",
"confirm_params_t",
@@ -60,6 +63,7 @@ const ALLOWLIST_TYPES: &[&str] = &[
"securechip_error_t",
"trinary_input_string_params_t",
"UG_COLOR",
+ "upside_down_t",
];
const ALLOWLIST_FNS: &[&str] = &[
@@ -96,12 +100,16 @@ const ALLOWLIST_FNS: &[&str] = &[
"memory_ble_enable",
"memory_ble_enabled",
"memory_bootloader_hash",
+ "memory_bootloader_set_flags",
"memory_check_noise_remote_static_pubkey",
"memory_get_attestation_bootloader_hash",
"memory_get_attestation_pubkey_and_certificate",
+ "memory_get_authorization_key",
"memory_get_ble_metadata",
"memory_get_device_name",
"memory_get_encrypted_seed_and_hmac",
+ "memory_get_encryption_key",
+ "memory_get_io_protection_key",
"memory_get_noise_static_private_key",
"memory_get_platform",
"memory_get_salt_root",
@@ -114,11 +122,15 @@ const ALLOWLIST_FNS: &[&str] = &[
"memory_multisig_set_by_hash",
"memory_reset_hww",
"memory_set_ble_metadata",
+ "memory_set_attestation_bootloader_hash",
+ "memory_set_attestation_certificate",
+ "memory_set_attestation_device_pubkey",
"memory_set_device_name",
"memory_set_encrypted_seed_and_hmac",
"memory_set_initialized",
"memory_set_mnemonic_passphrase_enabled",
"memory_set_salt_root",
+ "memory_set_bootloader_hash_fake",
"memory_set_seed_birthdate",
"memory_setup",
"memory_spi_get_active_ble_firmware_version",
diff --git a/src/rust/bitbox02/src/memory.rs b/src/rust/bitbox02/src/memory.rs
index c9d2e78..21074b7 100644
--- a/src/rust/bitbox02/src/memory.rs
+++ b/src/rust/bitbox02/src/memory.rs
@@ -298,25 +298,104 @@ pub fn set_salt_root(salt_root: &[u8; 32]) -> Result<(), ()> {
}
#[cfg(test)]
-mod tests {
- use super::*;
+fn fake_memory_factoryreset() {
+ unsafe { bitbox02_sys::fake_memory_factoryreset() }
+}
- use hex_lit::hex;
+#[cfg(test)]
+fn memory_bootloader_hash() -> [u8; 32] {
+ let mut out = [0u8; 32];
+ unsafe { bitbox02_sys::memory_bootloader_hash(out.as_mut_ptr()) };
+ out
+}
- #[test]
- fn test_get_attestation_bootloader_hash() {
- let expected: [u8; 32] =
- hex!("713df0d58c717d4031787cdc8fa35b902582be6ab6a22e09de4477d30e2230fc");
- assert_eq!(get_attestation_bootloader_hash(), expected);
+#[cfg(test)]
+fn set_bootloader_hash_fake(hash: &[u8; 32]) {
+ unsafe { bitbox02_sys::memory_set_bootloader_hash_fake(hash.as_ptr()) }
+}
+
+#[cfg(any(feature = "testing", feature = "simulator-graphical"))]
+pub(crate) fn memory_setup(random_fn: unsafe extern "C" fn(*mut u8)) -> bool {
+ unsafe {
+ static mut MEMORY_IFS: bitbox02_sys::memory_interface_functions_t =
+ bitbox02_sys::memory_interface_functions_t {
+ random_32_bytes: None,
+ };
+ MEMORY_IFS.random_32_bytes = Some(random_fn);
+ bitbox02_sys::memory_setup(core::ptr::addr_of!(MEMORY_IFS))
+ }
+}
+
+#[cfg(test)]
+fn set_attestation_device_pubkey(pubkey: &[u8; 64]) -> bool {
+ unsafe { bitbox02_sys::memory_set_attestation_device_pubkey(pubkey.as_ptr()) }
+}
+
+#[cfg(test)]
+fn set_attestation_certificate(
+ pubkey: &[u8; 64],
+ certificate: &[u8; 64],
+ root_identifier: &[u8; 32],
+) -> bool {
+ unsafe {
+ bitbox02_sys::memory_set_attestation_certificate(
+ pubkey.as_ptr(),
+ certificate.as_ptr(),
+ root_identifier.as_ptr(),
+ )
}
+}
+
+#[cfg(test)]
+fn get_io_protection_key(out: &mut [u8; 32]) {
+ unsafe { bitbox02_sys::memory_get_io_protection_key(out.as_mut_ptr()) }
+}
+
+#[cfg(test)]
+fn get_authorization_key(out: &mut [u8; 32]) {
+ unsafe { bitbox02_sys::memory_get_authorization_key(out.as_mut_ptr()) }
+}
+
+#[cfg(test)]
+fn get_encryption_key(out: &mut [u8; 32]) {
+ unsafe { bitbox02_sys::memory_get_encryption_key(out.as_mut_ptr()) }
+}
+
+#[cfg(test)]
+fn bootloader_set_flags(auto_enter: u8, upside_down: bool) -> bool {
+ unsafe {
+ bitbox02_sys::memory_bootloader_set_flags(
+ bitbox02_sys::auto_enter_t { value: auto_enter },
+ bitbox02_sys::upside_down_t { value: upside_down },
+ )
+ }
+}
+
+#[cfg(test)]
+fn set_attestation_bootloader_hash(hash: &[u8; 32]) -> bool {
+ unsafe { bitbox02_sys::memory_set_attestation_bootloader_hash(hash.as_ptr()) }
+}
+
+#[cfg(all(test, feature = "testing"))]
+mod tests {
+ use super::*;
+ use crate::random;
+ use crate::testing::mock_memory;
+ use alloc::{format, string::String, vec, vec::Vec};
+ use core::{
+ ptr, slice,
+ sync::atomic::{AtomicUsize, Ordering},
+ };
+ use hex_lit::hex;
#[test]
fn test_get_salt_root_roundtrip() {
+ mock_memory();
let original = get_salt_root().unwrap();
let expected = hex!("00112233445566778899aabbccddeefffeeddccbbaa998877665544332211000");
- set_salt_root(expected.as_slice().try_into().unwrap()).unwrap();
+ set_salt_root(&expected).unwrap();
let salt_root = get_salt_root().unwrap();
assert_eq!(salt_root.as_slice(), &expected);
@@ -326,4 +405,239 @@ mod tests {
set_salt_root(original.as_slice().try_into().unwrap()).unwrap();
}
+
+ static RAND_FIXTURES: [[u8; 32]; 7] = [
+ // salt root
+ hex!("bdb9ca4975e59e1b61d9141c5e79688cba7b3989b52b782de2e7e49b07ec8fae"),
+ // io_protection_key
+ hex!("28309e5a2e3bcf4aac94c0e59010fa3492e10839efb5b66192ad18f66a80510b"),
+ // io_protection_key_split
+ hex!("ae5be44d8b71a6041a7e9733e55f8c88b79dd552107624e0a916c10d8755e04e"),
+ // authorization_key
+ hex!("62c741d9ce7832e856ec06f6351cefcd9e7c5ca607938abb709770a5f2dbebcb"),
+ // authorization_key_split
+ hex!("20742d5a582f1f25b6e9d1c1e8b1effb40cfac855667ea7f49968af7f7eb5c19"),
+ // encryption_key
+ hex!("ed183784cbd297f9c2c241d0dd7cd16d62366c44b833ddf2c012fb4b49e1e8f3"),
+ // encryption_key_split
+ hex!("19f60ee825e752150d308817348c0fa6b3fe4f604c85c17e2eb97ada604a476f"),
+ ];
+ static RAND_FIXTURE_INDEX: AtomicUsize = AtomicUsize::new(0);
+
+ unsafe extern "C" fn memory_setup_rand_mock_test_functional(buf_out: *mut u8) {
+ let index = RAND_FIXTURE_INDEX.fetch_add(1, Ordering::SeqCst);
+ let fixture = RAND_FIXTURES.get(index).expect("unexpected RNG request");
+ unsafe { ptr::copy_nonoverlapping(fixture.as_ptr(), buf_out, fixture.len()) };
+ }
+
+ unsafe extern "C" fn mcu_random_adapter(buf_out: *mut u8) {
+ let slice = unsafe { slice::from_raw_parts_mut(buf_out, 32) };
+ let array: &mut [u8; 32] = slice.try_into().unwrap();
+ random::mcu_32_bytes(array);
+ }
+
+ #[test]
+ fn test_memory_multisig() {
+ mock_memory();
+
+ let hashes: [[u8; 32]; 6] = [
+ *b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ *b"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+ *b"cccccccccccccccccccccccccccccccc",
+ *b"dddddddddddddddddddddddddddddddd",
+ *b"eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
+ *b"ffffffffffffffffffffffffffffffff",
+ ];
+ let names = ["name1", "name2", "name3", "name4", "name5", "name6"];
+
+ assert!(multisig_get_by_hash(&hashes[0]).is_none());
+
+ // set
+ assert!(multisig_set_by_hash(&hashes[0], names[0]).is_ok());
+ assert!(multisig_set_by_hash(&hashes[1], names[1]).is_ok());
+ // overwrite with the same is possible
+ assert!(multisig_set_by_hash(&hashes[1], names[1]).is_ok());
+
+ // get
+ assert!(multisig_get_by_hash(&hashes[0]).is_some());
+ assert_eq!(multisig_get_by_hash(&hashes[0]).as_deref(), Some(names[0]));
+ assert_eq!(multisig_get_by_hash(&hashes[1]).as_deref(), Some(names[1]));
+
+ // rename
+ let name0_renamed = "name 1 renamed";
+ assert!(multisig_set_by_hash(&hashes[0], name0_renamed).is_ok());
+ assert_eq!(
+ multisig_get_by_hash(&hashes[0]).as_deref(),
+ Some(name0_renamed)
+ );
+
+ // rename to a name which already exists fails (duplicate name).
+ let err = multisig_set_by_hash(&hashes[0], names[1]).unwrap_err();
+ assert_eq!(err, MemoryError::MEMORY_ERR_DUPLICATE_NAME);
+ // was in fact not renamed
+ assert_eq!(
+ multisig_get_by_hash(&hashes[0]).as_deref(),
+ Some(name0_renamed)
+ );
+ }
+
+ #[test]
+ fn test_memory_multisig_invalid() {
+ mock_memory();
+
+ // invalid hash
+ let invalid_hash = [0xFFu8; 32];
+ let err = multisig_set_by_hash(&invalid_hash, "foo").unwrap_err();
+ assert_eq!(err, MemoryError::MEMORY_ERR_INVALID_INPUT);
+
+ // invalid name
+ let valid_hash = *b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
+ let err = multisig_set_by_hash(&valid_hash, "").unwrap_err();
+ assert_eq!(err, MemoryError::MEMORY_ERR_INVALID_INPUT);
+ }
+
+ #[test]
+ fn test_memory_multisig_full() {
+ mock_memory();
+
+ // Only 25 slots available.
+ let limit = bitbox02_sys::MEMORY_MULTISIG_NUM_ENTRIES as usize;
+ let mut hashes = vec![[0u8; 32]; limit + 1];
+ let mut names: Vec<String> = Vec::with_capacity(limit + 1);
+ for (i, hash) in hashes.iter_mut().enumerate() {
+ hash.fill((i + i) as u8);
+ names.push(format!("name{i}"));
+ }
+
+ for i in 0..limit {
+ assert!(multisig_set_by_hash(&hashes[i], &names[i]).is_ok());
+ }
+
+ let err = multisig_set_by_hash(&hashes[limit], &names[limit]).unwrap_err();
+ assert_eq!(err, MemoryError::MEMORY_ERR_FULL);
+ }
+
+ #[test]
+ fn test_memory_attestation() {
+ mock_memory();
+
+ let expected_pubkey = [0x55u8; 64];
+ let expected_certificate = [0x66u8; 64];
+ let expected_root_pubkey_identifier = [0x77u8; 32];
+ let mut pubkey = [0u8; 64];
+ let mut certificate = [0u8; 64];
+ let mut root_identifier = [0u8; 32];
+
+ // Setup not done yet.
+ assert!(
+ get_attestation_pubkey_and_certificate(
+ &mut pubkey,
+ &mut certificate,
+ &mut root_identifier
+ )
+ .is_err()
+ );
+
+ assert!(set_attestation_device_pubkey(&expected_pubkey));
+
+ // Setup not done yet.
+ assert!(
+ get_attestation_pubkey_and_certificate(
+ &mut pubkey,
+ &mut certificate,
+ &mut root_identifier
+ )
+ .is_err()
+ );
+
+ let wrong_pubkey = [0x11u8; 64];
+ // Pubkey has to match the previously stored pubkey.
+ assert!(!set_attestation_certificate(
+ &wrong_pubkey,
+ &expected_certificate,
+ &expected_root_pubkey_identifier,
+ ));
+
+ assert!(set_attestation_certificate(
+ &expected_pubkey,
+ &expected_certificate,
+ &expected_root_pubkey_identifier,
+ ));
+
+ // Setup done.
+ get_attestation_pubkey_and_certificate(&mut pubkey, &mut certificate, &mut root_identifier)
+ .unwrap();
+ assert_eq!(pubkey, expected_pubkey);
+ assert_eq!(certificate, expected_certificate);
+ assert_eq!(root_identifier, expected_root_pubkey_identifier);
+ }
+
+ // Test a series of write/read operations.
+ #[test]
+ fn test_memory_setup_functional() {
+ fake_memory_factoryreset();
+ RAND_FIXTURE_INDEX.store(0, Ordering::SeqCst);
+ assert!(memory_setup(memory_setup_rand_mock_test_functional));
+
+ let mut io_protection_key = [0u8; 32];
+ let mut authorization_key = [0u8; 32];
+ let mut encryption_key = [0u8; 32];
+
+ get_io_protection_key(&mut io_protection_key);
+ get_authorization_key(&mut authorization_key);
+ get_encryption_key(&mut encryption_key);
+
+ let expected_io_protection_key =
+ hex!("866b7a17a54a694eb6ea57d6754f76bc257cdd6bffc392813bbbd9fbedd5b145");
+ let expected_authorization_key =
+ hex!("42b36c8396572dcde005d737ddad0036deb3f02351f460c43901fa520530b7d2");
+ let expected_encryption_key =
+ hex!("f4ee396cee35c5eccff2c9c7e9f0decbd1c82324f4b61c8ceeab819129abaf9c");
+
+ assert_eq!(io_protection_key, expected_io_protection_key);
+ assert_eq!(authorization_key, expected_authorization_key);
+ assert_eq!(encryption_key, expected_encryption_key);
+
+ assert!(memory_setup(memory_setup_rand_mock_test_functional));
+
+ // Run again, shouldn't do anything. Other operations modifying the same memory chunk
+ // shouldn't change the secure chip keys.
+ assert!(bootloader_set_flags(bitbox02_sys::secfalse_u8 as u8, true));
+
+ get_io_protection_key(&mut io_protection_key);
+ get_authorization_key(&mut authorization_key);
+ get_encryption_key(&mut encryption_key);
+
+ assert_eq!(io_protection_key, expected_io_protection_key);
+ assert_eq!(authorization_key, expected_authorization_key);
+ assert_eq!(encryption_key, expected_encryption_key);
+ }
+
+ #[test]
+ fn test_attestation_bootloader_hash() {
+ fake_memory_factoryreset();
+ assert!(memory_setup(mcu_random_adapter));
+
+ let mock1 = hex!("0322b3191aab5bc415c5bafac5333445175be2faa8333ac3abee4cd17e49082a");
+ let mock2 = hex!("6cad6abc3fd447a58d7a262d7606a040e49e82b00648623625883e9fc0faa8ad");
+
+ set_bootloader_hash_fake(&mock1);
+
+ let hash = memory_bootloader_hash();
+ assert_eq!(hash, mock1);
+ assert_eq!(get_attestation_bootloader_hash(), mock1);
+
+ assert!(set_attestation_bootloader_hash(&mock1));
+ assert_eq!(get_attestation_bootloader_hash(), mock1);
+
+ set_bootloader_hash_fake(&mock2);
+ let hash = memory_bootloader_hash();
+ assert_eq!(hash, mock2);
+
+ assert_eq!(get_attestation_bootloader_hash(), mock1);
+
+ reset_hww().unwrap();
+
+ assert_eq!(get_attestation_bootloader_hash(), mock1);
+ }
}
diff --git a/src/rust/bitbox02/src/random.rs b/src/rust/bitbox02/src/random.rs
index 2219648..f215c44 100644
--- a/src/rust/bitbox02/src/random.rs
+++ b/src/rust/bitbox02/src/random.rs
@@ -12,12 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#[cfg(target_arch = "arm")]
+#[cfg(not(feature = "testing"))]
pub fn mcu_32_bytes(out: &mut [u8; 32]) {
unsafe { bitbox02_sys::random_32_bytes_mcu(out.as_mut_ptr()) }
}
-#[cfg(not(target_arch = "arm"))]
+#[cfg(feature = "testing")]
pub fn mcu_32_bytes(out: &mut [u8; 32]) {
unsafe extern "C" {
fn rand() -> core::ffi::c_int;
diff --git a/src/rust/bitbox02/src/testing.rs b/src/rust/bitbox02/src/testing.rs
index 2f449db..576c7bf 100644
--- a/src/rust/bitbox02/src/testing.rs
+++ b/src/rust/bitbox02/src/testing.rs
@@ -19,18 +19,13 @@ unsafe extern "C" fn c_mock_random_32_bytes(buf_out: *mut u8) {
s.copy_from_slice(b"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
}
-static MEMORY_IFS: bitbox02_sys::memory_interface_functions_t =
- bitbox02_sys::memory_interface_functions_t {
- random_32_bytes: Some(c_mock_random_32_bytes),
- };
-
/// This sets up memory in RAM for use in unit tests. As there is only one RAM volume, access only serially.
/// The memory is initialized to be like after factory setup, i.e. 0xFF everywhere followed by `memory_setup()`.
pub fn mock_memory() {
unsafe {
bitbox02_sys::fake_memory_factoryreset();
- assert!(bitbox02_sys::memory_setup(&MEMORY_IFS));
+ assert!(crate::memory::memory_setup(c_mock_random_32_bytes));
if bitbox02_sys::smarteeprom_is_enabled() {
bitbox02_sys::smarteeprom_disable();
diff --git a/test/unit-test/CMakeLists.txt b/test/unit-test/CMakeLists.txt
index d878970..463dea3 100644
--- a/test/unit-test/CMakeLists.txt
+++ b/test/unit-test/CMakeLists.txt
@@ -55,8 +55,6 @@ else()
""
memory
"-Wl,--wrap=memory_read_chunk_fake,--wrap=memory_write_chunk_fake,--wrap=rust_noise_generate_static_private_key,--wrap=memory_read_shared_bootdata_fake,--wrap=memory_write_to_address_fake,--wrap=random_32_bytes_mcu"
- memory_functional
- ""
util
""
ugui
diff --git a/test/unit-test/test_memory_functional.c b/test/unit-test/test_memory_functional.c
deleted file mode 100644
index ffe4919..0000000
--- a/test/unit-test/test_memory_functional.c
+++ /dev/null
@@ -1,272 +0,0 @@
-// Copyright 2019 Shift Cryptosecurity AG
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <setjmp.h>
-#include <stdarg.h>
-#include <stddef.h>
-#include <cmocka.h>
-
-#include <fake_memory.h>
-#include <memory/memory.h>
-#include <random.h>
-
-static void _test_memory_multisig(void** state)
-{
- fake_memory_factoryreset();
-
- const uint8_t hashes[][32] = {
- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
- "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
- "cccccccccccccccccccccccccccccccc",
- "dddddddddddddddddddddddddddddddd",
- "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
- "ffffffffffffffffffffffffffffffff",
- };
- const char* names[] = {
- "name1",
- "name2",
- "name3",
- "name4",
- "name5",
- "name6",
- };
-
- char name[31] = {0};
- assert_false(memory_multisig_get_by_hash(hashes[0], name));
-
- // set
- assert_int_equal(MEMORY_OK, memory_multisig_set_by_hash(hashes[0], names[0]));
- assert_int_equal(MEMORY_OK, memory_multisig_set_by_hash(hashes[1], names[1]));
- // overwrite with the same is possible
- assert_int_equal(MEMORY_OK, memory_multisig_set_by_hash(hashes[1], names[1]));
-
- // get
- assert_true(memory_multisig_get_by_hash(hashes[0], NULL));
- assert_true(memory_multisig_get_by_hash(hashes[0], name));
- assert_string_equal(name, names[0]);
- assert_true(memory_multisig_get_by_hash(hashes[1], name));
- assert_string_equal(name, names[1]);
- // rename
- const char* name0_renamed = "name 1 renamed";
- assert_int_equal(MEMORY_OK, memory_multisig_set_by_hash(hashes[0], name0_renamed));
- assert_true(memory_multisig_get_by_hash(hashes[0], name));
- assert_string_equal(name, name0_renamed);
-
- // rename to a name which already exists fails (duplicate name).
- assert_int_equal(MEMORY_ERR_DUPLICATE_NAME, memory_multisig_set_by_hash(hashes[0], names[1]));
- // was in fact not renamed
- assert_true(memory_multisig_get_by_hash(hashes[0], name));
- assert_string_equal(name, name0_renamed);
-}
-
-static void _test_memory_multisig_invalid(void** state)
-{
- // invalid hash
- uint8_t empty[32];
- memset(empty, 0xFF, sizeof(empty));
- assert_int_equal(MEMORY_ERR_INVALID_INPUT, memory_multisig_set_by_hash(empty, "foo"));
-
- // invalid name
- uint8_t hash[32] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
- assert_int_equal(MEMORY_ERR_INVALID_INPUT, memory_multisig_set_by_hash(hash, ""));
-}
-
-static void _test_memory_multisig_full(void** state)
-{
- fake_memory_factoryreset();
- // Only 25 slots available.
- const size_t limit = 25;
- uint8_t hashes[limit + 1][32];
- char names[limit + 1][10];
- for (size_t i = 0; i < limit + 1; i++) {
- memset(hashes[i], (int)(i + i), 32);
- snprintf(names[i], sizeof(names[i]), "name%lu", i);
- }
-
- for (size_t i = 0; i < limit; i++) {
- assert_int_equal(MEMORY_OK, memory_multisig_set_by_hash(hashes[i], names[i]));
- }
- assert_int_equal(MEMORY_ERR_FULL, memory_multisig_set_by_hash(hashes[limit], names[limit]));
-}
-
-static void _test_memory_attestation(void** state)
-{
- fake_memory_factoryreset();
-
- uint8_t expected_pubkey[64];
- memset(expected_pubkey, 0x55, sizeof(expected_pubkey));
- uint8_t expected_certificate[64];
- memset(expected_certificate, 0x66, sizeof(expected_certificate));
- uint8_t expected_root_pubkey_identifier[32];
- memset(expected_root_pubkey_identifier, 0x77, sizeof(expected_root_pubkey_identifier));
-
- uint8_t pubkey[64];
- uint8_t certificate[64];
- uint8_t root_pubkey_identifier[32];
- // Setup not done yet.
- assert_false(
- memory_get_attestation_pubkey_and_certificate(pubkey, certificate, root_pubkey_identifier));
-
- assert_true(memory_set_attestation_device_pubkey(expected_pubkey));
-
- // Setup not done yet.
- assert_false(
- memory_get_attestation_pubkey_and_certificate(pubkey, certificate, root_pubkey_identifier));
-
- uint8_t wrong_pubkey[64];
- memset(wrong_pubkey, 0x11, sizeof(wrong_pubkey));
- // Pubkey has to match the previously stored pubkey.
- assert_false(memory_set_attestation_certificate(
- wrong_pubkey, expected_certificate, expected_root_pubkey_identifier));
-
- assert_true(memory_set_attestation_certificate(
- expected_pubkey, expected_certificate, expected_root_pubkey_identifier));
-
- // Setup done.
- assert_true(
- memory_get_attestation_pubkey_and_certificate(pubkey, certificate, root_pubkey_identifier));
- assert_memory_equal(pubkey, expected_pubkey, sizeof(pubkey));
- assert_memory_equal(certificate, expected_certificate, sizeof(certificate));
- assert_memory_equal(
- root_pubkey_identifier, expected_root_pubkey_identifier, sizeof(root_pubkey_identifier));
-}
-
-void _memory_setup_rand_mock_test_functional(uint8_t* buf_out)
-{
- static uint8_t ctr = 0;
- static uint8_t fixtures[][32] = {
- // salt root
- "\xbd\xb9\xca\x49\x75\xe5\x9e\x1b\x61\xd9\x14\x1c\x5e\x79\x68\x8c\xba\x7b\x39\x89\xb5\x2b"
- "\x78\x2d\xe2\xe7\xe4\x9b\x07\xec\x8f\xae",
- // io_protection_key
- "\x28\x30\x9e\x5a\x2e\x3b\xcf\x4a\xac\x94\xc0\xe5\x90\x10\xfa\x34\x92\xe1\x08\x39\xef\xb5"
- "\xb6\x61\x92\xad\x18\xf6\x6a\x80\x51\x0b",
- // io_protection_key_split
- "\xae\x5b\xe4\x4d\x8b\x71\xa6\x04\x1a\x7e\x97\x33\xe5\x5f\x8c\x88\xb7\x9d\xd5\x52\x10\x76"
- "\x24\xe0\xa9\x16\xc1\x0d\x87\x55\xe0\x4e",
- // authorization_key
- "\x62\xc7\x41\xd9\xce\x78\x32\xe8\x56\xec\x06\xf6\x35\x1c\xef\xcd\x9e\x7c\x5c\xa6\x07\x93"
- "\x8a\xbb\x70\x97\x70\xa5\xf2\xdb\xeb\xcb",
- // authorization_key_split
- "\x20\x74\x2d\x5a\x58\x2f\x1f\x25\xb6\xe9\xd1\xc1\xe8\xb1\xef\xfb\x40\xcf\xac\x85\x56\x67"
- "\xea\x7f\x49\x96\x8a\xf7\xf7\xeb\x5c\x19",
- // encryption_key
- "\xed\x18\x37\x84\xcb\xd2\x97\xf9\xc2\xc2\x41\xd0\xdd\x7c\xd1\x6d\x62\x36\x6c\x44\xb8\x33"
- "\xdd\xf2\xc0\x12\xfb\x4b\x49\xe1\xe8\xf3",
- // encryption_key_split
- "\x19\xf6\x0e\xe8\x25\xe7\x52\x15\x0d\x30\x88\x17\x34\x8c\x0f\xa6\xb3\xfe\x4f\x60\x4c\x85"
- "\xc1\x7e\x2e\xb9\x7a\xda\x60\x4a\x47\x6f",
- };
- memcpy(buf_out, fixtures[ctr], 32);
- ctr++;
-}
-
-// Test a series of write/read operations
-static void _test_functional(void** state)
-{
- fake_memory_factoryreset();
-
- memory_interface_functions_t ifs = {
- .random_32_bytes = _memory_setup_rand_mock_test_functional,
- };
- assert_true(memory_setup(&ifs));
-
- uint8_t io_protection_key[32];
- const uint8_t expected_io_protection_key[32] =
- "\x86\x6b\x7a\x17\xa5\x4a\x69\x4e\xb6\xea\x57\xd6\x75\x4f\x76\xbc\x25\x7c\xdd\x6b\xff\xc3"
- "\x92\x81\x3b\xbb\xd9\xfb\xed\xd5\xb1\x45";
- memory_get_io_protection_key(io_protection_key);
- assert_memory_equal(io_protection_key, expected_io_protection_key, sizeof(io_protection_key));
-
- uint8_t authorization_key[32];
- const uint8_t expected_authorization_key[32] =
- "\x42\xb3\x6c\x83\x96\x57\x2d\xcd\xe0\x05\xd7\x37\xdd\xad\x00\x36\xde\xb3\xf0\x23\x51\xf4"
- "\x60\xc4\x39\x01\xfa\x52\x05\x30\xb7\xd2";
- memory_get_authorization_key(authorization_key);
- assert_memory_equal(authorization_key, expected_authorization_key, sizeof(authorization_key));
-
- uint8_t encryption_key[32];
- const uint8_t expected_encryption_key[32] =
- "\xf4\xee\x39\x6c\xee\x35\xc5\xec\xcf\xf2\xc9\xc7\xe9\xf0\xde\xcb\xd1\xc8\x23\x24\xf4\xb6"
- "\x1c\x8c\xee\xab\x81\x91\x29\xab\xaf\x9c";
- memory_get_encryption_key(encryption_key);
- assert_memory_equal(encryption_key, expected_encryption_key, sizeof(encryption_key));
-
- // Run again, shouldn't do anything. Secure chip keys unchanged.
- assert_true(memory_setup(&ifs));
- // Other operations modifying the same memory chunk shouldn't change the secure chip keys.
- auto_enter_t autoenter = {.value = secfalse_u8};
- upside_down_t upside_down = {.value = true};
- assert_true(memory_bootloader_set_flags(autoenter, upside_down));
-
- assert_memory_equal(io_protection_key, expected_io_protection_key, sizeof(io_protection_key));
- assert_memory_equal(authorization_key, expected_authorization_key, sizeof(authorization_key));
- assert_memory_equal(encryption_key, expected_encryption_key, sizeof(encryption_key));
-}
-
-static void _test_attestation_bootloader_hash(void** state)
-{
- fake_memory_factoryreset();
-
- memory_interface_functions_t ifs = {
- .random_32_bytes = random_32_bytes_mcu,
- };
- assert_true(memory_setup(&ifs));
-
- const uint8_t mock1[32] =
- "\x03\x22\xb3\x19\x1a\xab\x5b\xc4\x15\xc5\xba\xfa\xc5\x33\x34\x45\x17\x5b\xe2\xfa\xa8\x33"
- "\x3a\xc3\xab\xee\x4c\xd1\x7e\x49\x08\x2a";
- memory_set_bootloader_hash_fake(mock1);
- uint8_t hash[32];
- memory_bootloader_hash(hash);
- memory_get_attestation_bootloader_hash(hash);
- assert_memory_equal(hash, mock1, sizeof(hash));
-
- assert_true(memory_set_attestation_bootloader_hash(hash));
- memset(hash, 0x00, sizeof(hash));
- memory_get_attestation_bootloader_hash(hash);
- assert_memory_equal(hash, mock1, sizeof(hash));
-
- const uint8_t mock2[32] =
- "\x6c\xad\x6a\xbc\x3f\xd4\x47\xa5\x8d\x7a\x26\x2d\x76\x06\xa0\x40\xe4\x9e\x82\xb0\x06\x48"
- "\x62\x36\x25\x88\x3e\x9f\xc0\xfa\xa8\xad";
- memory_set_bootloader_hash_fake(mock2);
-
- memset(hash, 0x00, sizeof(hash));
- memory_bootloader_hash(hash);
- assert_memory_equal(hash, mock2, sizeof(hash));
-
- memset(hash, 0x00, sizeof(hash));
- memory_get_attestation_bootloader_hash(hash);
- assert_memory_equal(hash, mock1, sizeof(hash));
-
- assert_true(memory_reset_hww());
-
- memset(hash, 0x00, sizeof(hash));
- memory_get_attestation_bootloader_hash(hash);
- assert_memory_equal(hash, mock1, sizeof(hash));
-}
-
-int main(void)
-{
- const struct CMUnitTest tests[] = {
- cmocka_unit_test(_test_memory_multisig),
- cmocka_unit_test(_test_memory_multisig_invalid),
- cmocka_unit_test(_test_memory_multisig_full),
- cmocka_unit_test(_test_memory_attestation),
- cmocka_unit_test(_test_functional),
- cmocka_unit_test(_test_attestation_bootloader_hash),
- };
- return cmocka_run_group_tests(tests, NULL, NULL);
-}
Why this scored 15/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.