diff --git a/libs/bao1x-hal/src/hardening.rs b/libs/bao1x-hal/src/hardening.rs index 0741cdb8a..889a8c6bf 100644 --- a/libs/bao1x-hal/src/hardening.rs +++ b/libs/bao1x-hal/src/hardening.rs @@ -458,7 +458,7 @@ pub fn compare_refkeys( csprng: &mut Csprng, pubkey_ptr: *const bao1x_api::signatures::SignatureInFlash, fail_counter: usize, -) { +) -> HardenedBool { let reference_keys = [bao1x_api::BAO1_PUBKEY, bao1x_api::BAO2_PUBKEY, bao1x_api::BETA_PUBKEY, bao1x_api::DEV_PUBKEY]; // check that the pub keys match those burned into the indelible key area @@ -472,7 +472,22 @@ pub fn compare_refkeys( good_compare = HardenedBool::FALSE; } } - + csprng.random_delay(); + // The IFR (indelible) copy is weird, because the highest byte doesn't match (it's actually a flag that + // indicates the region has to be write protected). Thus, the IFR keys are a set of four, disjointed + // 31-byte memory areas, plus a collection of 4 bytes that correspond to the missing MSB. + let ifr_keys = [ + unsafe { core::slice::from_raw_parts(0x6040_01A0 as *const u8, 31) }, + unsafe { core::slice::from_raw_parts(0x6040_01C0 as *const u8, 31) }, + unsafe { core::slice::from_raw_parts(0x6040_01E0 as *const u8, 31) }, + unsafe { core::slice::from_raw_parts(0x6040_0200 as *const u8, 31) }, + ]; + let ifr_msb = unsafe { core::slice::from_raw_parts(0x6040_0240 as *const u8, 4) }; + for (i, (boot0_key, ref_key)) in pk_src.sealed_data.pubkeys.iter().zip(ifr_keys).enumerate() { + if ref_key != &boot0_key.pk[..31] || ifr_msb[i] != boot0_key.pk[31] { + good_compare = HardenedBool::FALSE; + } + } csprng.random_delay(); match good_compare.is_true() { Some(false) => { @@ -493,4 +508,5 @@ pub fn compare_refkeys( None => die(), } bollard!(die, 4); + good_compare } diff --git a/libs/bao1x-hal/src/sigcheck.rs b/libs/bao1x-hal/src/sigcheck.rs index bf71525fc..410236c13 100644 --- a/libs/bao1x-hal/src/sigcheck.rs +++ b/libs/bao1x-hal/src/sigcheck.rs @@ -276,6 +276,41 @@ pub fn erase_secrets(_csprng: &mut Option<&mut Csprng>) -> Result<(), String> { ); } +#[cfg(not(feature = "std"))] +pub fn erase_collateral(csprng: &mut Option<&mut Csprng>) -> Result<(), String> { + let slot_mgr = SlotManager::new(); + let mut rram = crate::rram::Reram::new(); + + let slot = &bao1x_api::offsets::COLLATERAL; + for data_index in slot.try_into_data_iter().unwrap() { + bollard!(die_no_std, 4); + csprng.as_deref_mut().map(|rng| rng.random_delay()); + // only clear ACL if it isn't already cleared + if slot_mgr.get_acl(slot).unwrap().raw_u32() != 0 { + // clear the ACL so we can operate on the data + slot_mgr + .set_acl(&mut rram, slot, &AccessSettings::Data(DataSlotAccess::new_with_raw_value(0))) + .expect("couldn't reset ACL"); + } + let bytes = unsafe { slot_mgr.read_data_slot(data_index) }; + // only erase if the key hasn't already been erased, to avoid stressing the RRAM array + // erase_secrets() may be called on every boot in some modes. + bollard!(die_no_std, 4); + if !bytes.iter().all(|&b| b == ERASE_VALUE) { + let mut eraser = alloc::vec::Vec::with_capacity(slot.len() * SLOT_ELEMENT_LEN_BYTES); + eraser.resize(slot.len() * SLOT_ELEMENT_LEN_BYTES, ERASE_VALUE); + + slot_mgr.write(&mut rram, slot, &eraser).ok(); + } + let check = unsafe { slot_mgr.read_data_slot(data_index) }; + if !check.iter().all(|&b| b == ERASE_VALUE) { + crate::println!("Failed to erase key at {}: {:x?}", data_index, check); + } + bollard!(die_no_std, 4); + } + Ok(()) +} + #[cfg(not(feature = "std"))] pub fn erase_secrets(csprng: &mut Option<&mut Csprng>) -> Result<(), String> { // ensure coreuser settings, as we could enter from a variety of loader stages