Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sim: Add hw rollback protection tests #1710

Merged
merged 4 commits into from
Jun 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/sim.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ jobs:
- "sig-rsa validate-primary-slot direct-xip"
- "sig-rsa validate-primary-slot ram-load multiimage"
- "sig-rsa validate-primary-slot direct-xip multiimage"
- "sig-ecdsa hw-rollback-protection multiimage"
runs-on: ubuntu-latest
env:
MULTI_FEATURES: ${{ matrix.features }}
Expand Down
1 change: 1 addition & 0 deletions boot/bootutil/include/bootutil/caps.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ uint32_t bootutil_get_caps(void);
#define BOOTUTIL_CAP_AES256 (1<<14)
#define BOOTUTIL_CAP_RAM_LOAD (1<<15)
#define BOOTUTIL_CAP_DIRECT_XIP (1<<16)
#define BOOTUTIL_CAP_HW_ROLLBACK_PROT (1<<17)

/*
* Query the number of images this bootloader is configured for. This
Expand Down
3 changes: 3 additions & 0 deletions boot/bootutil/src/caps.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ uint32_t bootutil_get_caps(void)
#if defined(MCUBOOT_DIRECT_XIP)
res |= BOOTUTIL_CAP_DIRECT_XIP;
#endif
#if defined(MCUBOOT_HW_ROLLBACK_PROT)
res |= BOOTUTIL_CAP_HW_ROLLBACK_PROT;
#endif

return res;
}
Expand Down
2 changes: 1 addition & 1 deletion boot/bootutil/src/image_validate.c
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,7 @@ bootutil_img_validate(struct enc_key_data *enc_state, int image_index,
* stored security counter value.
*/
fih_rc = fih_ret_encode_zero_equality(img_security_cnt <
fih_int_decode(security_cnt));
(uint32_t)fih_int_decode(security_cnt));
if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) {
FIH_SET(fih_rc, FIH_FAILURE);
goto out;
Expand Down
1 change: 1 addition & 0 deletions sim/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ ram-load = ["mcuboot-sys/ram-load"]
direct-xip = ["mcuboot-sys/direct-xip"]
downgrade-prevention = ["mcuboot-sys/downgrade-prevention"]
max-align-32 = ["mcuboot-sys/max-align-32"]
hw-rollback-protection = ["mcuboot-sys/hw-rollback-protection"]

[dependencies]
byteorder = "1.4"
Expand Down
3 changes: 3 additions & 0 deletions sim/mcuboot-sys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ downgrade-prevention = []
# 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"

Expand Down
6 changes: 6 additions & 0 deletions sim/mcuboot-sys/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ fn main() {
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);
Expand Down Expand Up @@ -75,6 +76,11 @@ fn main() {
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 {
Expand Down
43 changes: 43 additions & 0 deletions sim/mcuboot-sys/csupport/security_cnt.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* Copyright (c) 2023 Arm Limited
*/

Roolli marked this conversation as resolved.
Show resolved Hide resolved
#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);
davidvincze marked this conversation as resolved.
Show resolved Hide resolved
}

#endif /* MCUBOOT_HW_ROLLBACK_PROT */
59 changes: 59 additions & 0 deletions sim/mcuboot-sys/src/api.rs
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -131,10 +132,32 @@ pub struct BootsimRamInfo {
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.
Expand Down Expand Up @@ -317,3 +340,39 @@ pub extern fn sim_log_enabled(level: libc::c_int) -> libc::c_int {
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;
}
12 changes: 11 additions & 1 deletion sim/mcuboot-sys/src/c.rs
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -150,6 +150,16 @@ pub fn kw_encrypt(kek: &[u8], seckey: &[u8], keylen: u32) -> Result<Vec<u8>, &'s
}
}

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};
Expand Down
1 change: 1 addition & 0 deletions sim/src/caps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pub enum Caps {
Aes256 = (1 << 14),
RamLoad = (1 << 15),
DirectXip = (1 << 16),
HwRollbackProtection = (1 << 17),
}

impl Caps {
Expand Down
73 changes: 63 additions & 10 deletions sim/src/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,11 +221,11 @@ impl ImagesBuilder {
Box::new(BoringDep::new(image_num, deps))
};
let primaries = install_image(&mut flash, &slots[0],
maximal(42784), &ram, &*dep, false);
maximal(42784), &ram, &*dep, false, Some(0));
davidvincze marked this conversation as resolved.
Show resolved Hide resolved
let upgrades = match deps.depends[image_num] {
DepType::NoUpgrade => install_no_image(),
_ => install_image(&mut flash, &slots[1],
maximal(46928), &ram, &*dep, false)
maximal(46928), &ram, &*dep, false, Some(0))
};
OneImage {
slots,
Expand Down Expand Up @@ -274,9 +274,9 @@ impl ImagesBuilder {
let images = self.slots.into_iter().enumerate().map(|(image_num, slots)| {
let dep = BoringDep::new(image_num, &NO_DEPS);
let primaries = install_image(&mut bad_flash, &slots[0],
maximal(32784), &ram, &dep, false);
maximal(32784), &ram, &dep, false, Some(0));
let upgrades = install_image(&mut bad_flash, &slots[1],
maximal(41928), &ram, &dep, true);
maximal(41928), &ram, &dep, true, Some(0));
OneImage {
slots,
primaries,
Expand All @@ -297,9 +297,9 @@ impl ImagesBuilder {
let images = self.slots.into_iter().enumerate().map(|(image_num, slots)| {
let dep = BoringDep::new(image_num, &NO_DEPS);
let primaries = install_image(&mut bad_flash, &slots[0],
maximal(32784), &ram, &dep, false);
maximal(32784), &ram, &dep, false, Some(0));
let upgrades = install_image(&mut bad_flash, &slots[1],
ImageSize::Oversized, &ram, &dep, false);
ImageSize::Oversized, &ram, &dep, false, Some(0));
OneImage {
slots,
primaries,
Expand All @@ -320,7 +320,7 @@ impl ImagesBuilder {
let images = self.slots.into_iter().enumerate().map(|(image_num, slots)| {
let dep = BoringDep::new(image_num, &NO_DEPS);
let primaries = install_image(&mut flash, &slots[0],
maximal(32784), &ram, &dep, false);
maximal(32784), &ram, &dep, false, Some(0));
let upgrades = install_no_image();
OneImage {
slots,
Expand All @@ -343,7 +343,7 @@ impl ImagesBuilder {
let dep = BoringDep::new(image_num, &NO_DEPS);
let primaries = install_no_image();
let upgrades = install_image(&mut flash, &slots[1],
maximal(32784), &ram, &dep, false);
maximal(32784), &ram, &dep, false, Some(0));
OneImage {
slots,
primaries,
Expand All @@ -365,7 +365,31 @@ impl ImagesBuilder {
let dep = BoringDep::new(image_num, &NO_DEPS);
let primaries = install_no_image();
let upgrades = install_image(&mut flash, &slots[1],
ImageSize::Oversized, &ram, &dep, false);
ImageSize::Oversized, &ram, &dep, false, Some(0));
OneImage {
slots,
primaries,
upgrades,
}}).collect();
Images {
flash,
areadesc: self.areadesc,
images,
total_count: None,
ram: self.ram,
}
}

/// If security_cnt is None then do not add a security counter TLV, otherwise add the specified value.
pub fn make_image_with_security_counter(self, security_cnt: Option<u32>) -> Images {
let mut flash = self.flash;
let ram = self.ram.clone(); // TODO: Avoid this clone.
let images = self.slots.into_iter().enumerate().map(|(image_num, slots)| {
let dep = BoringDep::new(image_num, &NO_DEPS);
let primaries = install_image(&mut flash, &slots[0],
maximal(32784), &ram, &dep, false, security_cnt);
let upgrades = install_image(&mut flash, &slots[1],
maximal(41928), &ram, &dep, false, security_cnt.map(|v| v + 1));
OneImage {
slots,
primaries,
Expand Down Expand Up @@ -1269,6 +1293,31 @@ impl Images {
return false;
}

pub fn run_hw_rollback_prot(&self) -> bool {
if !Caps::HwRollbackProtection.present() {
return false;
}

let mut flash = self.flash.clone();

// set the "stored" security counter to a fixed value.
c::set_security_counter(0, 30);

let result = c::boot_go(&mut flash, &self.areadesc, None, None, true);

if result.success() {
warn!("Successful boot when it did not suppose to happen!");
return true;
}
let counter_val = c::get_security_counter(0);
if counter_val != 30 {
warn!("Counter was changed when it did not suppose to!");
return true;
}

false
}

/// Adds a new flash area that fails statistically
fn mark_bad_status_with_rate(&self, flash: &mut SimMultiFlash, slot: usize,
rate: f32) {
Expand Down Expand Up @@ -1650,14 +1699,16 @@ fn image_largest_trailer(dev: &dyn Flash) -> usize {
/// fields used by the given code. Returns a copy of the image that was written.
fn install_image(flash: &mut SimMultiFlash, slot: &SlotInfo, len: ImageSize,
ram: &RamData,
deps: &dyn Depender, bad_sig: bool) -> ImageData {
deps: &dyn Depender, bad_sig: bool, security_counter:Option<u32>) -> ImageData {
let offset = slot.base_off;
let slot_len = slot.len;
let dev_id = slot.dev_id;
let dev = flash.get_mut(&dev_id).unwrap();

let mut tlv: Box<dyn ManifestGen> = Box::new(make_tlv());

tlv.set_security_counter(security_counter);

// Add the dependencies early to the tlv.
for dep in deps.my_deps(offset, slot.index) {
tlv.add_dependency(deps.other_id(), &dep);
Expand Down Expand Up @@ -1891,6 +1942,8 @@ fn make_tlv() -> TlvGen {
TlvGen::new_ecdsa()
} else if Caps::Ed25519.present() {
TlvGen::new_ed25519()
} else if Caps::HwRollbackProtection.present() {
TlvGen::new_sec_cnt()
} else {
TlvGen::new_hash_only()
}
Expand Down
Loading
Loading