sim: Add hw-rollback-protection feature
This commit adds simulator support to test the
hw-rollback-protection feature which is using
nv-counters. In the simulator they are stored in Rust
to prevent any race conditions from happening due to
the parallel execution of the tests.
Signed-off-by: Roland Mikhel <roland.mikhel@arm.com>
Change-Id: I445fc50615ed1f0c06e5933b16811c24d9d302fc
diff --git a/sim/mcuboot-sys/Cargo.toml b/sim/mcuboot-sys/Cargo.toml
index 6d21bf5..c140341 100644
--- a/sim/mcuboot-sys/Cargo.toml
+++ b/sim/mcuboot-sys/Cargo.toml
@@ -83,6 +83,9 @@
# Support images with 32-byte maximum write alignment value.
max-align-32 = []
+# Enable hardware rollback protection
+hw-rollback-protection = []
+
[build-dependencies]
cc = "1.0.25"
diff --git a/sim/mcuboot-sys/build.rs b/sim/mcuboot-sys/build.rs
index 27da2e3..a01844e 100644
--- a/sim/mcuboot-sys/build.rs
+++ b/sim/mcuboot-sys/build.rs
@@ -34,6 +34,7 @@
let ram_load = env::var("CARGO_FEATURE_RAM_LOAD").is_ok();
let direct_xip = env::var("CARGO_FEATURE_DIRECT_XIP").is_ok();
let max_align_32 = env::var("CARGO_FEATURE_MAX_ALIGN_32").is_ok();
+ let hw_rollback_protection = env::var("CARGO_FEATURE_HW_ROLLBACK_PROTECTION").is_ok();
let mut conf = CachedBuild::new();
conf.conf.define("__BOOTSIM__", None);
@@ -75,6 +76,11 @@
conf.conf.define("MCUBOOT_DIRECT_XIP", None);
}
+ if hw_rollback_protection {
+ conf.conf.define("MCUBOOT_HW_ROLLBACK_PROT", None);
+ conf.file("csupport/security_cnt.c");
+ }
+
// Currently no more than one sig type can be used simultaneously.
if vec![sig_rsa, sig_rsa3072, sig_ecdsa, sig_ed25519].iter()
.fold(0, |sum, &v| sum + v as i32) > 1 {
diff --git a/sim/mcuboot-sys/csupport/security_cnt.c b/sim/mcuboot-sys/csupport/security_cnt.c
new file mode 100644
index 0000000..0f79d8d
--- /dev/null
+++ b/sim/mcuboot-sys/csupport/security_cnt.c
@@ -0,0 +1,43 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Copyright (c) 2023 Arm Limited
+ */
+
+#include "bootutil/security_cnt.h"
+#include "mcuboot_config/mcuboot_logging.h"
+#include "bootutil/fault_injection_hardening.h"
+
+/*
+ * Since the simulator is executing unit tests in parallel,
+ * the storage area where the security counter values reside
+ * has to be managed per thread from Rust's side.
+ */
+#ifdef MCUBOOT_HW_ROLLBACK_PROT
+
+int sim_set_nv_counter_for_image(uint32_t image_index, uint32_t security_counter_value);
+
+int sim_get_nv_counter_for_image(uint32_t image_index, uint32_t* data);
+
+fih_ret boot_nv_security_counter_init(void) {
+ return FIH_SUCCESS;
+}
+
+fih_ret boot_nv_security_counter_get(uint32_t image_id, fih_int *security_cnt) {
+ uint32_t counter = 0;
+ FIH_DECLARE(fih_rc, FIH_FAILURE);
+ fih_rc = fih_ret_encode_zero_equality(sim_get_nv_counter_for_image(image_id, &counter));
+
+ MCUBOOT_LOG_INF("Read security counter value (%d) for image: %d\n", counter, image_id);
+ *security_cnt = fih_int_encode(counter);
+
+ FIH_RET(fih_rc);
+}
+
+int32_t boot_nv_security_counter_update(uint32_t image_id, uint32_t img_security_cnt) {
+ MCUBOOT_LOG_INF("Writing security counter value (%d) for image: %d\n", img_security_cnt, image_id);
+
+ return sim_set_nv_counter_for_image(image_id, img_security_cnt);
+}
+
+#endif /* MCUBOOT_HW_ROLLBACK_PROT */
diff --git a/sim/mcuboot-sys/src/api.rs b/sim/mcuboot-sys/src/api.rs
index 624b3e9..db8564d 100644
--- a/sim/mcuboot-sys/src/api.rs
+++ b/sim/mcuboot-sys/src/api.rs
@@ -1,5 +1,6 @@
// Copyright (c) 2017-2021 Linaro LTD
// Copyright (c) 2018-2019 JUUL Labs
+// Copyright (c) 2023 Arm Limited
//
// SPDX-License-Identifier: Apache-2.0
@@ -131,10 +132,32 @@
pub base: usize,
}
+/// This struct stores the non-volatile security counter per image. It will be stored per test thread,
+/// and the C code will set / get the values here.
+#[repr(C)]
+#[derive(Debug, Default)]
+pub struct NvCounterStorage {
+ pub storage: Vec<u32>,
+}
+
+impl NvCounterStorage {
+ pub fn new() -> Self {
+ let count = if cfg!(feature = "multiimage") {
+ 2
+ } else {
+ 1
+ };
+ Self {
+ storage: vec![0; count]
+ }
+ }
+}
+
thread_local! {
pub static THREAD_CTX: RefCell<FlashContext> = RefCell::new(FlashContext::new());
pub static SIM_CTX: RefCell<CSimContextPtr> = RefCell::new(CSimContextPtr::new());
pub static RAM_CTX: RefCell<BootsimRamInfo> = RefCell::new(BootsimRamInfo::default());
+ pub static NV_COUNTER_CTX: RefCell<NvCounterStorage> = RefCell::new(NvCounterStorage::new());
}
/// Set the flash device to be used by the simulation. The pointer is unsafely stashed away.
@@ -317,3 +340,39 @@
0
}
}
+
+#[no_mangle]
+pub extern "C" fn sim_set_nv_counter_for_image(image_index: u32, security_counter_value: u32) -> libc::c_int {
+ let mut rc = 0;
+ NV_COUNTER_CTX.with(|ctx| {
+ let mut counter_storage = ctx.borrow_mut();
+ if image_index as usize >= counter_storage.storage.len() {
+ rc = -1;
+ return;
+ }
+ if counter_storage.storage[image_index as usize] > security_counter_value {
+ rc = -2;
+ warn!("Failed to set security counter value ({}) for image index {}", security_counter_value, image_index);
+ return;
+ }
+
+ counter_storage.storage[image_index as usize] = security_counter_value;
+ });
+
+ return rc;
+}
+
+#[no_mangle]
+pub extern "C" fn sim_get_nv_counter_for_image(image_index: u32, security_counter_value: *mut u32) -> libc::c_int {
+ let mut rc = 0;
+ NV_COUNTER_CTX.with(|ctx| {
+ let counter_storage = ctx.borrow();
+ if image_index as usize >= counter_storage.storage.len() {
+ rc = -1;
+ return;
+ }
+ unsafe { *security_counter_value = counter_storage.storage[image_index as usize] };
+
+ });
+ return rc;
+}
diff --git a/sim/mcuboot-sys/src/c.rs b/sim/mcuboot-sys/src/c.rs
index e9bac0a..5d2c8ca 100644
--- a/sim/mcuboot-sys/src/c.rs
+++ b/sim/mcuboot-sys/src/c.rs
@@ -1,6 +1,6 @@
// Copyright (c) 2017-2021 Linaro LTD
// Copyright (c) 2017-2019 JUUL Labs
-// Copyright (c) 2019-2021 Arm Limited
+// Copyright (c) 2019-2023 Arm Limited
//
// SPDX-License-Identifier: Apache-2.0
@@ -150,6 +150,16 @@
}
}
+pub fn set_security_counter(image_index: u32, security_counter_value: u32) {
+ api::sim_set_nv_counter_for_image(image_index, security_counter_value);
+}
+
+pub fn get_security_counter(image_index: u32) -> u32 {
+ let mut counter_val: u32 = 0;
+ api::sim_get_nv_counter_for_image(image_index, &mut counter_val as *mut u32);
+ return counter_val;
+}
+
mod raw {
use crate::area::CAreaDesc;
use crate::api::{BootRsp, CSimContext};