upgrade boot1 path to implement signature hardening

multi-pass signature checking and jump target hardening
per boot0 standards implemented here
This commit is contained in:
bunnie 2025-12-05 23:15:15 +08:00
parent 006eab2084
commit 8be0f1004b
No known key found for this signature in database

View file

@ -1,14 +1,6 @@
use bao1x_api::{bollard, signatures::FunctionCode};
use bao1x_api::{PARANOID_MODE, PARANOID_MODE_DUPE, bollard, pubkeys::BOOT1_TO_LOADER_OR_BAREMETAL};
use bao1x_hal::hardening::Csprng;
pub const ALLOWED_FUNCTIONS: [u32; 5] = [
FunctionCode::Baremetal as u32,
FunctionCode::UpdatedBaremetal as u32,
FunctionCode::Loader as u32,
FunctionCode::UpdatedLoader as u32,
FunctionCode::Developer as u32,
];
#[inline(always)]
fn seal_boot1_keys() {
// This is a security-critical initialization. Failure to do this correctly totally breaks
@ -25,25 +17,26 @@ fn seal_boot1_keys() {
}
pub fn try_boot(or_die: bool, csprng: &mut Csprng) {
let one_way = bao1x_hal::acram::OneWayCounter::new();
bollard!(bao1x_hal::sigcheck::die_no_std, 4);
csprng.random_delay();
let (paranoid1, paranoid2) = one_way.hardened_get2(PARANOID_MODE, PARANOID_MODE_DUPE).unwrap();
csprng.random_delay();
// loader is at the same offset as baremetal. Accept either as valid boot.
// This diverges if the signature check is successful
match bao1x_hal::sigcheck::validate_image(
bao1x_api::LOADER_START as *const u32,
bao1x_api::BOOT1_START as *const u32,
bao1x_api::BOOT1_REVOCATION_OFFSET,
&ALLOWED_FUNCTIONS,
false,
None,
Some(csprng),
) {
Ok((k, k2, tag)) => {
crate::println!(
"Booting with key {}/{}({})",
k,
k2,
core::str::from_utf8(&tag).unwrap_or("invalid tag")
);
if k != k2 {
match bao1x_hal::sigcheck::validate_image(BOOT1_TO_LOADER_OR_BAREMETAL, None, Some(csprng)) {
Ok((key, key_inv, tag, target)) => {
if paranoid1 == 0 && paranoid2 == 0 {
// only emit prints if not in paranoid mode
crate::println!(
"Booting with key {}/{}({})",
key,
!key_inv,
core::str::from_utf8(&tag).unwrap_or("invalid tag")
);
}
if key != !key_inv {
bao1x_hal::sigcheck::die_no_std();
}
@ -53,20 +46,35 @@ pub fn try_boot(or_die: bool, csprng: &mut Csprng) {
// the tag is from signed, trusted data
// k is just a nominal slot number. If either match, assume we are dealing with a
// developer image.
bao1x_hal::sigcheck::hardened_erase_policy(paranoid1, paranoid2, key, key_inv, tag, csprng)
.inspect_err(|e| crate::println!("{}", e))
.ok(); // "ok" because the expected error is a check on logic/configuration bugs, not attacks
// this print message is not hardened, and it's actually retrospective of the policy
// implementation
if tag == *bao1x_api::pubkeys::KEYSLOT_INITIAL_TAGS[bao1x_api::pubkeys::DEVELOPER_KEY_SLOT]
|| k == bao1x_api::pubkeys::DEVELOPER_KEY_SLOT
|| key == bao1x_api::pubkeys::DEVELOPER_KEY_SLOT
|| !key_inv == bao1x_api::pubkeys::DEVELOPER_KEY_SLOT
{
crate::println!("Developer key detected, ensuring secrets are erased");
bao1x_hal::sigcheck::erase_secrets(&mut Some(csprng));
}
// double up the security check if in paranoid mode
csprng.random_delay();
if paranoid1 != 0 || paranoid2 != 0 {
bollard!(bao1x_hal::sigcheck::die_no_std, 4);
bao1x_hal::sigcheck::validate_image(BOOT1_TO_LOADER_OR_BAREMETAL, None, Some(csprng))
.unwrap_or_else(|_| bao1x_hal::hardening::die());
}
csprng.random_delay();
bollard!(bao1x_hal::sigcheck::die_no_std, 4);
// this has to be called *after* erase_secrets, because we can't erase the secrets
// once the mappings have been sealed off. This is why we can't use the auto-jump method
// like we do in boot0.
csprng.random_delay();
bollard!(bao1x_hal::sigcheck::die_no_std, 4);
seal_boot1_keys();
bollard!(bao1x_hal::sigcheck::die_no_std, 4);
bao1x_hal::sigcheck::jump_to(bao1x_api::LOADER_START);
bao1x_hal::sigcheck::jump_to((target ^ u32::from_le_bytes(tag)) as usize);
}
Err(e) => crate::println!("Image did not validate: {:?}", e),
}