mirror of
https://github.com/saymrwulf/xous-core.git
synced 2026-11-15 08:30:02 +00:00
Remove key slot implementation; migrate keys to data slots
The -A1 stepping is going to tie the IFR access control bits for data and key slots together. This is due to an RTL bug where the key slot maps were typo'd and they ended up mapping to the same IFR ACL. We "could" continue to use key slots with a rule that key and data slots would have to coordinate with each other and not use the same slot number. However, because of this linkage, and because there are other bugs suspected in the keyslot implementation, an executive decision was made to disregard the key slot feature in the chip. The main downsides of this are: - fewer slots (2048 vs 4096) - no key access linkage to HMAC auth on SCE I *think* that's it. Fewer slots - 2048 slots is still a lot of slots, about half are used now, I think this is plenty. Key access linkage to HMAC auth on SCE - I have reasons to suspect this feature is broken anyways for subtle reasons related to malleability of the HMAC round constants. Even without that brokenness, it's hard to use HMAC auth to manage the system because it requires per-device unique keys: so, we're using a PKI scheme and no symmetric secrets to handle access controls. Removing the key slots also greatly simplifies the code base analytically. This culling removes the key slot abstraction the "simple" way, but without the weirdness of the keyslots (in particular the key ID chaining which makes them different from data slots) I could remove even more abstractions and simplify the code. One upside is now all elements can enjoy the write mode behavior which can force the slot to be only 0->1 writeable, which could be useful for other things down the road. This was tested to not interfere with Dabao-Alpha 0 bootloaders.
This commit is contained in:
parent
6af2001810
commit
a7477ec53b
5 changed files with 69 additions and 297 deletions
|
|
@ -162,10 +162,6 @@ fn check_and_fix_acls(rram: &mut Reram, slot_mgr: &mut SlotManager, slot_list: &
|
|||
let mut is_consistent = true;
|
||||
let mut acl = match slot_mgr.get_acl(&slot_element) {
|
||||
Ok(settings) => settings,
|
||||
Err(bao1x_api::AccessError::KeyAclInconsistency(prototype)) => {
|
||||
is_consistent = false;
|
||||
AccessSettings::Key(prototype)
|
||||
}
|
||||
Err(bao1x_api::AccessError::DataAclInconsistency(prototype)) => {
|
||||
is_consistent = false;
|
||||
AccessSettings::Data(prototype)
|
||||
|
|
@ -175,12 +171,6 @@ fn check_and_fix_acls(rram: &mut Reram, slot_mgr: &mut SlotManager, slot_list: &
|
|||
let (pa, rw) = slot_element.get_access_spec();
|
||||
let is_correct = match acl {
|
||||
AccessSettings::Data(sa) => sa.get_partition_access() == pa && sa.get_rw_permissions() == rw,
|
||||
AccessSettings::Key(sa) => {
|
||||
// crate::println!("sa.pa {:x?} pa {:x?}", sa.get_partition_access(), pa);
|
||||
// crate::println!("sa.rw {:x?} rw {:x?}", sa.get_rw_permissions(), rw);
|
||||
// crate::println!("sa.akeyid: {:x}", sa.akey_id());
|
||||
sa.get_partition_access() == pa && sa.get_rw_permissions() == rw && sa.akey_id() == 0xFF
|
||||
}
|
||||
};
|
||||
if !is_correct || !is_consistent {
|
||||
crate::println!(
|
||||
|
|
@ -195,14 +185,14 @@ fn check_and_fix_acls(rram: &mut Reram, slot_mgr: &mut SlotManager, slot_list: &
|
|||
sa.set_partition_access(&pa);
|
||||
sa.set_rw_permissions(rw);
|
||||
}
|
||||
AccessSettings::Key(sa) => {
|
||||
sa.set_partition_access(&pa);
|
||||
sa.set_rw_permissions(rw);
|
||||
sa.set_akey_id(0xFF); // 0xff disables key chaining to access the key
|
||||
}
|
||||
crate::println!("Writing ACL raw value: {:x?}", acl);
|
||||
match slot_mgr.set_acl(rram, slot_element, &acl) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
crate::println!("Couldn't set ACL for {:?} ({:?})", slot_element, e);
|
||||
}
|
||||
}
|
||||
crate::println!("Fixed ACL raw value: {:x?}", acl);
|
||||
slot_mgr.set_acl(rram, slot_element, &acl).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -236,16 +226,6 @@ fn print_slots(slot_mgr: &SlotManager, slot_list: &[SlotIndex]) {
|
|||
bytes
|
||||
);
|
||||
},
|
||||
SlotType::Key => unsafe {
|
||||
let bytes = slot_mgr.read_key_slot(data_index);
|
||||
let acl = slot_mgr.get_key_acl(acl_index);
|
||||
crate::println!(
|
||||
" Key {} ({:x?}): {:x?}",
|
||||
data_index / SLOT_ELEMENT_LEN_BYTES,
|
||||
acl,
|
||||
bytes
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,9 +7,6 @@ use core::ops::Range;
|
|||
use arbitrary_int::{Number, u4};
|
||||
use bitbybit::bitfield;
|
||||
|
||||
pub const KEY_SLOT_START: usize = 0x603F_0000;
|
||||
pub const KEY_SLOT_LEN: usize = 0x1_0000;
|
||||
pub const MAX_KEY_SLOTS: usize = KEY_SLOT_LEN / SLOT_ELEMENT_LEN_BYTES;
|
||||
pub const DATA_SLOT_START: usize = 0x603E_0000;
|
||||
pub const DATA_SLOT_LEN: usize = 0x1_0000;
|
||||
pub const MAX_DATA_SLOTS: usize = DATA_SLOT_LEN / SLOT_ELEMENT_LEN_BYTES;
|
||||
|
|
@ -17,10 +14,6 @@ pub const SLOT_ELEMENT_LEN_BYTES: usize = 256 / 8;
|
|||
|
||||
pub const ACRAM_DATASLOT_START: usize = 0x603D_C000;
|
||||
pub const ACRAM_DATASLOT_LEN: usize = 0x2000;
|
||||
pub const ACRAM_KEYSLOT_START: usize = 0x603D_E000;
|
||||
pub const ACRAM_KEYSLOT_LEN: usize = 0x2000;
|
||||
// pub const ACRAM_GKEYSLOT_START: usize = 0x603D_E400; // This is mentioned in the docs but I don't see it in
|
||||
// the code?
|
||||
pub const IFR_BASE: usize = 0x6040_0000;
|
||||
pub const IFR_LEN: usize = 0x400;
|
||||
/// This number is special: it only appears during certain portions of the chip life cycle in
|
||||
|
|
@ -101,79 +94,6 @@ impl DataSlotAccess {
|
|||
}
|
||||
}
|
||||
|
||||
#[bitfield(u32)]
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
pub struct KeySlotAccess {
|
||||
#[bits(24..=31, rw)]
|
||||
akey_id: u8,
|
||||
#[bit(23, rw)]
|
||||
fw1: bool,
|
||||
#[bit(22, rw)]
|
||||
fw0: bool,
|
||||
#[bit(21, rw)]
|
||||
boot1: bool,
|
||||
#[bit(20, rw)]
|
||||
boot0: bool,
|
||||
#[bits(8..=15, rw)]
|
||||
seg_id: u8,
|
||||
#[bit(3, rw)]
|
||||
sce_wr_dis: bool,
|
||||
#[bit(2, rw)]
|
||||
sce_rd_dis: bool,
|
||||
#[bit(1, rw)]
|
||||
core_wr_dis: bool,
|
||||
#[bit(0, rw)]
|
||||
core_rd_dis: bool,
|
||||
}
|
||||
|
||||
impl KeySlotAccess {
|
||||
// This method is only valid in no-std currently. Not sure if there is even meaning for us
|
||||
// to access this in the Xous environment, as this is primarily a secure boot construct
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub fn get_entry(slot: usize) -> Self {
|
||||
let slot_array =
|
||||
unsafe { core::slice::from_raw_parts(ACRAM_KEYSLOT_START as *const KeySlotAccess, 2048) };
|
||||
slot_array[slot]
|
||||
}
|
||||
|
||||
pub fn get_partition_access(&self) -> PartitionAccess { PartitionAccess::from_raw_u32(self.raw_value()) }
|
||||
|
||||
pub fn set_partition_access(&mut self, pa: &PartitionAccess) {
|
||||
*self = Self::new_with_raw_value((self.raw_value() & !(0xf << 20)) | (pa.to_raw_u4().as_u32() << 20));
|
||||
}
|
||||
|
||||
pub fn get_rw_permissions(&self) -> RwPerms {
|
||||
match [self.core_rd_dis(), self.core_wr_dis()] {
|
||||
[true, true] => RwPerms::Denied,
|
||||
[false, true] => RwPerms::ReadOnly,
|
||||
[true, false] => RwPerms::WriteOnly,
|
||||
[false, false] => RwPerms::ReadWrite,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_rw_permissions(&mut self, spec: RwPerms) {
|
||||
match spec {
|
||||
RwPerms::Denied => {
|
||||
self.set_core_rd_dis(true);
|
||||
self.set_core_wr_dis(true);
|
||||
}
|
||||
RwPerms::ReadOnly => {
|
||||
self.set_core_rd_dis(false);
|
||||
self.set_core_wr_dis(true);
|
||||
}
|
||||
RwPerms::WriteOnly => {
|
||||
self.set_core_rd_dis(true);
|
||||
self.set_core_wr_dis(false);
|
||||
}
|
||||
RwPerms::ReadWrite => {
|
||||
self.set_core_rd_dis(false);
|
||||
self.set_core_wr_dis(false);
|
||||
}
|
||||
RwPerms::Unspecified => panic!("RwPerms must be specified"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub enum AccessError {
|
||||
/// Access is anticipated to be denied by the hardware. Users can attempt to still
|
||||
|
|
@ -196,7 +116,6 @@ pub enum AccessError {
|
|||
/// Returned when a set of slots that should have the same ACL don't. The value
|
||||
/// inside is the value of the first ACL in the set.
|
||||
DataAclInconsistency(DataSlotAccess),
|
||||
KeyAclInconsistency(KeySlotAccess),
|
||||
}
|
||||
|
||||
/// Enum to specify read/write permissions to a given slot.
|
||||
|
|
@ -279,28 +198,22 @@ impl PartitionAccess {
|
|||
pub enum SlotIndex {
|
||||
Data(usize, PartitionAccess, RwPerms),
|
||||
DataRange(Range<usize>, PartitionAccess, RwPerms),
|
||||
Key(usize, PartitionAccess, RwPerms),
|
||||
KeyRange(Range<usize>, PartitionAccess, RwPerms),
|
||||
}
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub enum SlotType {
|
||||
Data,
|
||||
Key,
|
||||
}
|
||||
impl SlotIndex {
|
||||
pub fn get_access_spec(&self) -> (PartitionAccess, RwPerms) {
|
||||
match self {
|
||||
Self::Data(_, spec, rw) => (*spec, *rw),
|
||||
Self::DataRange(_, spec, rw) => (*spec, *rw),
|
||||
Self::Key(_, spec, rw) => (*spec, *rw),
|
||||
Self::KeyRange(_, spec, rw) => (*spec, *rw),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_type(&self) -> SlotType {
|
||||
match self {
|
||||
Self::Data(_, _, _) | Self::DataRange(_, _, _) => SlotType::Data,
|
||||
Self::Key(_, _, _) | Self::KeyRange(_, _, _) => SlotType::Key,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -308,8 +221,6 @@ impl SlotIndex {
|
|||
match self {
|
||||
Self::Data(base, _, _) => *base,
|
||||
Self::DataRange(range, _, _) => range.start,
|
||||
Self::Key(base, _, _) => *base,
|
||||
Self::KeyRange(range, _, _) => range.start,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -317,8 +228,6 @@ impl SlotIndex {
|
|||
match self {
|
||||
Self::Data(_, _, _) => 1,
|
||||
Self::DataRange(range, _, _) => range.len(),
|
||||
Self::Key(_, _, _) => 1,
|
||||
Self::KeyRange(range, _, _) => range.len(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -333,13 +242,6 @@ impl SlotIndex {
|
|||
Err(AccessError::OutOfBounds)
|
||||
}
|
||||
}
|
||||
Self::Key(index, _, _) => {
|
||||
if *index < MAX_KEY_SLOTS {
|
||||
Ok(*index * SLOT_ELEMENT_LEN_BYTES)
|
||||
} else {
|
||||
Err(AccessError::OutOfBounds)
|
||||
}
|
||||
}
|
||||
Self::DataRange(range, _, _) => {
|
||||
let index = range.start;
|
||||
if range.end <= MAX_DATA_SLOTS {
|
||||
|
|
@ -348,14 +250,6 @@ impl SlotIndex {
|
|||
Err(AccessError::OutOfBounds)
|
||||
}
|
||||
}
|
||||
Self::KeyRange(range, _, _) => {
|
||||
let index = range.start;
|
||||
if range.end <= MAX_KEY_SLOTS {
|
||||
Ok(index * SLOT_ELEMENT_LEN_BYTES)
|
||||
} else {
|
||||
Err(AccessError::OutOfBounds)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -374,19 +268,6 @@ impl SlotIndex {
|
|||
}
|
||||
Ok(SlotOffsetIter::Range(range.clone().map(|idx| idx * SLOT_ELEMENT_LEN_BYTES)))
|
||||
}
|
||||
Self::Key(index, _, _) => {
|
||||
if *index < MAX_KEY_SLOTS {
|
||||
Ok(SlotOffsetIter::Single(core::iter::once(*index * SLOT_ELEMENT_LEN_BYTES)))
|
||||
} else {
|
||||
Err(AccessError::OutOfBounds)
|
||||
}
|
||||
}
|
||||
Self::KeyRange(range, _, _) => {
|
||||
if range.end > MAX_KEY_SLOTS {
|
||||
return Err(AccessError::OutOfBounds);
|
||||
}
|
||||
Ok(SlotOffsetIter::Range(range.clone().map(|idx| idx * SLOT_ELEMENT_LEN_BYTES)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -399,13 +280,6 @@ impl SlotIndex {
|
|||
Err(AccessError::OutOfBounds)
|
||||
}
|
||||
}
|
||||
Self::Key(index, _, _) => {
|
||||
if *index < MAX_KEY_SLOTS {
|
||||
Ok(*index * size_of::<u32>())
|
||||
} else {
|
||||
Err(AccessError::OutOfBounds)
|
||||
}
|
||||
}
|
||||
Self::DataRange(range, _, _) => {
|
||||
if range.end <= MAX_DATA_SLOTS {
|
||||
Ok(range.start * size_of::<u32>())
|
||||
|
|
@ -413,13 +287,6 @@ impl SlotIndex {
|
|||
Err(AccessError::OutOfBounds)
|
||||
}
|
||||
}
|
||||
Self::KeyRange(range, _, _) => {
|
||||
if range.end <= MAX_KEY_SLOTS {
|
||||
Ok(range.start * size_of::<u32>())
|
||||
} else {
|
||||
Err(AccessError::OutOfBounds)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -440,19 +307,6 @@ impl SlotIndex {
|
|||
}
|
||||
Ok(SlotOffsetIter::Range(range.clone().map(|idx| idx * ACL_SIZE)))
|
||||
}
|
||||
Self::Key(index, _, _) => {
|
||||
if *index < MAX_KEY_SLOTS {
|
||||
Ok(SlotOffsetIter::Single(core::iter::once(*index * ACL_SIZE)))
|
||||
} else {
|
||||
Err(AccessError::OutOfBounds)
|
||||
}
|
||||
}
|
||||
Self::KeyRange(range, _, _) => {
|
||||
if range.end > MAX_KEY_SLOTS {
|
||||
return Err(AccessError::OutOfBounds);
|
||||
}
|
||||
Ok(SlotOffsetIter::Range(range.clone().map(|idx| idx * ACL_SIZE)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,21 +44,21 @@ pub const DEFAULT_FCLK_FREQUENCY: u32 = 700_000_000;
|
|||
/// are derived from. The seed may be blended with other bits of data scattered about
|
||||
/// the RRAM array, other device identifiers and hardware measurements to create the final
|
||||
/// device secret key.
|
||||
pub const ROOT_SEED: SlotIndex = SlotIndex::Key(256, PartitionAccess::Fw0, RwPerms::ReadWrite);
|
||||
pub const ROOT_SEED: SlotIndex = SlotIndex::Data(256, PartitionAccess::Fw0, RwPerms::ReadWrite);
|
||||
|
||||
/// The `RMA_KEY` is a secret parameter that is unique per-device secret which is recorded
|
||||
/// at manufacturing time. Its purpose is to facilitate the creation of a signed RMA
|
||||
/// authorization certificate which upon receipt would blank the device and unlock various
|
||||
/// features for debugging failed hardware.
|
||||
pub const RMA_KEY: SlotIndex = SlotIndex::Key(257, PartitionAccess::Fw0, RwPerms::ReadWrite);
|
||||
pub const RMA_KEY: SlotIndex = SlotIndex::Data(257, PartitionAccess::Fw0, RwPerms::ReadWrite);
|
||||
|
||||
/// Reserved for use as a CP to FT tracking cookie. This is used to help track inventory
|
||||
/// between CP and FT, if such a feature is desired in the supply chain. Blanked on entry
|
||||
/// to developer mode.
|
||||
pub const CP_COOKIE: SlotIndex = SlotIndex::Key(258, PartitionAccess::Fw0, RwPerms::ReadWrite);
|
||||
pub const CP_COOKIE: SlotIndex = SlotIndex::Data(258, PartitionAccess::Fw0, RwPerms::ReadWrite);
|
||||
|
||||
/// The swap encryption key. Used to protect swap images beyond the signing key, if we so desire.
|
||||
pub const SWAP_KEY: SlotIndex = SlotIndex::Key(259, PartitionAccess::Fw0, RwPerms::ReadWrite);
|
||||
pub const SWAP_KEY: SlotIndex = SlotIndex::Data(259, PartitionAccess::Fw0, RwPerms::ReadWrite);
|
||||
|
||||
/// `NUISANCE_KEYS` are hashed together with `ROOT_SEED` to derive the core secret.
|
||||
/// Their primary purpose is to annoy microscopists trying to read the secret key by
|
||||
|
|
@ -85,9 +85,9 @@ pub const SWAP_KEY: SlotIndex = SlotIndex::Key(259, PartitionAccess::Fw0, RwPerm
|
|||
/// numbered data slots. Some of the data slots in the first 8 slots will be read-only at CP time, and
|
||||
/// thus they can't be initialized with random data and be used as a nuisance key. This is a minor degradation
|
||||
/// in security margin.
|
||||
pub const NUISANCE_KEYS_0: SlotIndex = SlotIndex::KeyRange(8..128, PartitionAccess::Fw0, RwPerms::ReadWrite);
|
||||
pub const NUISANCE_KEYS_0: SlotIndex = SlotIndex::DataRange(8..128, PartitionAccess::Fw0, RwPerms::ReadWrite);
|
||||
pub const NUISANCE_KEYS_1: SlotIndex =
|
||||
SlotIndex::KeyRange(1920..2048, PartitionAccess::Fw0, RwPerms::ReadWrite);
|
||||
SlotIndex::DataRange(1920..2048, PartitionAccess::Fw0, RwPerms::ReadWrite);
|
||||
pub const NUISANCE_KEYS: [SlotIndex; 2] = [NUISANCE_KEYS_0, NUISANCE_KEYS_1];
|
||||
|
||||
/// `CHAFF_KEYS` are a bank of keys that are hashed into the key array, but instead of
|
||||
|
|
@ -109,7 +109,7 @@ pub const NUISANCE_KEYS: [SlotIndex; 2] = [NUISANCE_KEYS_0, NUISANCE_KEYS_1];
|
|||
/// the chaff is cleared, we also don't care as much about side channels since we're now operating
|
||||
/// in a fundamentally insecure regime (e.g. developer mode - you can just read out the data by
|
||||
/// running your own code on the device).
|
||||
pub const CHAFF_KEYS: SlotIndex = SlotIndex::KeyRange(128..256, PartitionAccess::Fw0, RwPerms::ReadWrite);
|
||||
pub const CHAFF_KEYS: SlotIndex = SlotIndex::DataRange(128..256, PartitionAccess::Fw0, RwPerms::ReadWrite);
|
||||
|
||||
/// All the slots of concern located in a single iterator. The idea is that everything is
|
||||
/// condensed here and used to check for access integrity using the array below.
|
||||
|
|
@ -127,4 +127,4 @@ pub const DATA_SLOTS: [SlotIndex; 8] = [
|
|||
/// In addition to these KEY_SLOTS, the DEVELOPER_MODE one way counter is a security-important parameter
|
||||
/// that should be included as domain separation in any KDF.
|
||||
pub const KEY_SLOTS: [SlotIndex; 6] =
|
||||
[ROOT_SEED, RMA_KEY, CP_COOKIE, NUISANCE_KEYS_0, NUISANCE_KEYS_1, CHAFF_KEYS];
|
||||
[CP_COOKIE, RMA_KEY, ROOT_SEED, NUISANCE_KEYS_0, NUISANCE_KEYS_1, CHAFF_KEYS];
|
||||
|
|
|
|||
|
|
@ -137,27 +137,23 @@ impl OneWayCounter {
|
|||
#[derive(PartialEq, Eq, Debug)]
|
||||
pub enum AccessSettings {
|
||||
Data(DataSlotAccess),
|
||||
Key(KeySlotAccess),
|
||||
}
|
||||
impl AccessSettings {
|
||||
pub fn raw_u32(&self) -> u32 {
|
||||
match self {
|
||||
Self::Data(d) => d.raw_value(),
|
||||
Self::Key(d) => d.raw_value(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn allows_cpu_read(&self) -> bool {
|
||||
match self {
|
||||
Self::Data(d) => !d.core_rd_dis(),
|
||||
Self::Key(d) => !d.core_rd_dis(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn allows_cpu_write(&self) -> bool {
|
||||
match self {
|
||||
Self::Data(d) => !d.core_wr_dis(),
|
||||
Self::Key(d) => !d.core_wr_dis(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -165,7 +161,6 @@ impl AccessSettings {
|
|||
pub fn is_set_only(&self) -> bool {
|
||||
match self {
|
||||
Self::Data(d) => d.write_mode(),
|
||||
Self::Key(_d) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -181,9 +176,11 @@ pub enum AccessType {
|
|||
pub struct SlotManager {
|
||||
data_range: xous::MemoryRange,
|
||||
data_acl_range: xous::MemoryRange,
|
||||
key_range: xous::MemoryRange,
|
||||
key_acl_range: xous::MemoryRange,
|
||||
user_id: CoreuserId,
|
||||
#[cfg(feature = "std")]
|
||||
data_write_offset: xous::MemoryRange,
|
||||
#[cfg(feature = "std")]
|
||||
data_acl_write_offset: xous::MemoryRange,
|
||||
}
|
||||
|
||||
impl SlotManager {
|
||||
|
|
@ -206,21 +203,13 @@ impl SlotManager {
|
|||
)
|
||||
.expect("Couldn't map ACL range");
|
||||
#[cfg(feature = "std")]
|
||||
let key_range = xous::map_memory(
|
||||
xous::MemoryAddress::new(KEY_SLOT_START),
|
||||
None,
|
||||
KEY_SLOT_LEN,
|
||||
xous::MemoryFlags::R | xous::MemoryFlags::W,
|
||||
)
|
||||
.expect("Couldn't map slot range");
|
||||
// safety: these ranges are valid and pre-aligned
|
||||
let data_write_offset = unsafe { xous::MemoryRange::new(DATA_SLOT_START, DATA_SLOT_LEN).unwrap() };
|
||||
#[cfg(feature = "std")]
|
||||
let key_acl_range = xous::map_memory(
|
||||
xous::MemoryAddress::new(ACRAM_KEYSLOT_START),
|
||||
None,
|
||||
ACRAM_KEYSLOT_LEN,
|
||||
xous::MemoryFlags::R | xous::MemoryFlags::W,
|
||||
)
|
||||
.expect("Couldn't map ACL range");
|
||||
// safety: these ranges are valid and pre-aligned
|
||||
let data_acl_write_offset =
|
||||
unsafe { xous::MemoryRange::new(ACRAM_DATASLOT_START, ACRAM_DATASLOT_LEN).unwrap() };
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
// safety: these ranges are valid and pre-aligned
|
||||
let data_range = unsafe { xous::MemoryRange::new(DATA_SLOT_START, DATA_SLOT_LEN).unwrap() };
|
||||
|
|
@ -228,13 +217,6 @@ impl SlotManager {
|
|||
// safety: these ranges are valid and pre-aligned
|
||||
let data_acl_range =
|
||||
unsafe { xous::MemoryRange::new(ACRAM_DATASLOT_START, ACRAM_DATASLOT_LEN).unwrap() };
|
||||
#[cfg(not(feature = "std"))]
|
||||
// safety: these ranges are valid and pre-aligned
|
||||
let key_range = unsafe { xous::MemoryRange::new(KEY_SLOT_START, KEY_SLOT_LEN).unwrap() };
|
||||
#[cfg(not(feature = "std"))]
|
||||
// safety: these ranges are valid and pre-aligned
|
||||
let key_acl_range =
|
||||
unsafe { xous::MemoryRange::new(ACRAM_KEYSLOT_START, ACRAM_KEYSLOT_LEN).unwrap() };
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
let user_id = if xous::process::id() == crate::coreuser::TRUSTED_PID as u32 {
|
||||
|
|
@ -246,15 +228,17 @@ impl SlotManager {
|
|||
#[cfg(not(feature = "std"))]
|
||||
let user_id = crate::coreuser::TRUSTED_USER;
|
||||
|
||||
Self { data_range, data_acl_range, key_range, key_acl_range, user_id }
|
||||
#[cfg(not(feature = "std"))]
|
||||
let ret = Self { data_range, data_acl_range, user_id };
|
||||
#[cfg(feature = "std")]
|
||||
let ret = Self { data_range, data_acl_range, user_id, data_write_offset, data_acl_write_offset };
|
||||
ret
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub fn register_mapping(&self, rram: &mut Reram) {
|
||||
rram.add_range(DATA_SLOT_START - utralib::HW_RERAM_MEM, self.data_range);
|
||||
rram.add_range(ACRAM_DATASLOT_START - utralib::HW_RERAM_MEM, self.data_acl_range);
|
||||
rram.add_range(KEY_SLOT_START - utralib::HW_RERAM_MEM, self.key_range);
|
||||
rram.add_range(ACRAM_KEYSLOT_START - utralib::HW_RERAM_MEM, self.key_acl_range);
|
||||
}
|
||||
|
||||
pub fn read(&self, slot: &SlotIndex) -> Result<&[u8], AccessError> {
|
||||
|
|
@ -269,15 +253,9 @@ impl SlotManager {
|
|||
SlotIndex::Data(_, _, _) => unsafe {
|
||||
&self.data_range.as_slice()[offset..offset + SLOT_ELEMENT_LEN_BYTES]
|
||||
},
|
||||
SlotIndex::Key(_, _, _) => unsafe {
|
||||
&self.key_range.as_slice()[offset..offset + SLOT_ELEMENT_LEN_BYTES]
|
||||
},
|
||||
SlotIndex::DataRange(range, _, _) => unsafe {
|
||||
&self.data_range.as_slice()[offset..offset + SLOT_ELEMENT_LEN_BYTES * range.len()]
|
||||
},
|
||||
SlotIndex::KeyRange(range, _, _) => unsafe {
|
||||
&self.key_range.as_slice()[offset..offset + SLOT_ELEMENT_LEN_BYTES * range.len()]
|
||||
},
|
||||
})
|
||||
} else {
|
||||
Err(AccessError::AccessDenied)
|
||||
|
|
@ -288,26 +266,22 @@ impl SlotManager {
|
|||
/// There is access or type checking done. This is a "raw read" primitive mostly used for debugging.
|
||||
pub unsafe fn read_data_slot(&self, absolute_offset: usize) -> &[u8] {
|
||||
let ptr: *const u8 = &self.data_range.as_slice::<u8>()[absolute_offset] as *const u8;
|
||||
crate::println!("data loc: {:x}", ptr as usize);
|
||||
crate::println!("data loc: {:x}, offset: {:x}", ptr as usize, absolute_offset);
|
||||
&self.data_range.as_slice()[absolute_offset..absolute_offset + SLOT_ELEMENT_LEN_BYTES]
|
||||
}
|
||||
|
||||
/// Safety: the caller must resolve the index into either the data or the key array correctly.
|
||||
/// There is access or type checking done. This is a "raw read" primitive mostly used for debugging.
|
||||
pub unsafe fn read_key_slot(&self, absolute_offset: usize) -> &[u8] {
|
||||
let ptr: *const u8 = &self.key_range.as_slice::<u8>()[absolute_offset] as *const u8;
|
||||
crate::println!("key loc: {:x}", ptr as usize);
|
||||
&self.key_range.as_slice()[absolute_offset..absolute_offset + SLOT_ELEMENT_LEN_BYTES]
|
||||
}
|
||||
|
||||
pub fn write(&self, writer: &mut Reram, slot: &SlotIndex, value: &[u8]) -> Result<(), AccessError> {
|
||||
let acl = self.get_acl(slot)?;
|
||||
if self.user_id.is_accessible(&acl, &AccessType::Write) {
|
||||
let offset = slot.try_into_data_offset()?;
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
let range = match slot {
|
||||
SlotIndex::Data(_, _, _) | SlotIndex::DataRange(_, _, _) => &self.data_range,
|
||||
SlotIndex::Key(_, _, _) | SlotIndex::KeyRange(_, _, _) => &self.key_range,
|
||||
};
|
||||
#[cfg(feature = "std")]
|
||||
let range = match slot {
|
||||
SlotIndex::Data(_, _, _) | SlotIndex::DataRange(_, _, _) => &self.data_write_offset,
|
||||
};
|
||||
|
||||
if value.len() != slot.len() * SLOT_ELEMENT_LEN_BYTES {
|
||||
|
|
@ -322,6 +296,7 @@ impl SlotManager {
|
|||
if self.user_id.is_accessible(&acl, &AccessType::Read) {
|
||||
// read-verify only if we have read access
|
||||
let readback = self.read(slot)?;
|
||||
crate::println!("rbk: {:x?}", &readback[..8]);
|
||||
if readback != value { Err(AccessError::WriteError) } else { Ok(()) }
|
||||
} else {
|
||||
Ok(())
|
||||
|
|
@ -338,12 +313,6 @@ impl SlotManager {
|
|||
self.data_acl_range.as_slice()[offset..offset + size_of::<u32>()].try_into().unwrap()
|
||||
})))
|
||||
}
|
||||
SlotIndex::Key(_, _, _) => {
|
||||
let offset = slot.try_into_acl_offset()?;
|
||||
AccessSettings::Key(KeySlotAccess::new_with_raw_value(u32::from_le_bytes(unsafe {
|
||||
self.key_acl_range.as_slice()[offset..offset + size_of::<u32>()].try_into().unwrap()
|
||||
})))
|
||||
}
|
||||
SlotIndex::DataRange(_, _, _) => {
|
||||
let mut range = slot.try_into_acl_iter()?;
|
||||
let offset = range.next().unwrap();
|
||||
|
|
@ -360,22 +329,6 @@ impl SlotManager {
|
|||
}
|
||||
AccessSettings::Data(prototype)
|
||||
}
|
||||
SlotIndex::KeyRange(_, _, _) => {
|
||||
let mut range = slot.try_into_acl_iter()?;
|
||||
let offset = range.next().unwrap();
|
||||
let prototype = KeySlotAccess::new_with_raw_value(u32::from_le_bytes(unsafe {
|
||||
self.key_acl_range.as_slice()[offset..offset + size_of::<u32>()].try_into().unwrap()
|
||||
}));
|
||||
for offset in range {
|
||||
let successor = KeySlotAccess::new_with_raw_value(u32::from_le_bytes(unsafe {
|
||||
self.key_acl_range.as_slice()[offset..offset + size_of::<u32>()].try_into().unwrap()
|
||||
}));
|
||||
if prototype != successor {
|
||||
return Err(AccessError::KeyAclInconsistency(prototype));
|
||||
}
|
||||
}
|
||||
AccessSettings::Key(prototype)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -389,22 +342,22 @@ impl SlotManager {
|
|||
}))
|
||||
}
|
||||
|
||||
/// Safety: the caller must resolve the index into either the data or the key array correctly.
|
||||
/// There is access or type checking done. This is a "raw read" primitive mostly used for debugging.
|
||||
pub unsafe fn get_key_acl(&self, absolute_offset: usize) -> KeySlotAccess {
|
||||
KeySlotAccess::new_with_raw_value(u32::from_le_bytes(unsafe {
|
||||
self.key_acl_range.as_slice()[absolute_offset..absolute_offset + size_of::<u32>()]
|
||||
.try_into()
|
||||
.unwrap()
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn set_acl(
|
||||
&self,
|
||||
writer: &mut Reram,
|
||||
slot: &SlotIndex,
|
||||
setting: &AccessSettings,
|
||||
) -> Result<(), AccessError> {
|
||||
#[cfg(not(feature = "std"))]
|
||||
let ptr = match slot {
|
||||
SlotIndex::Data(_, _, _) | SlotIndex::DataRange(_, _, _) => self.data_acl_range.as_ptr() as usize,
|
||||
};
|
||||
#[cfg(feature = "std")]
|
||||
let ptr = match slot {
|
||||
SlotIndex::Data(_, _, _) | SlotIndex::DataRange(_, _, _) => {
|
||||
self.data_acl_write_offset.as_ptr() as usize
|
||||
}
|
||||
};
|
||||
// safety: the unsafe blocks here are concerned that the data types of the resulting
|
||||
// slice are all representable. In this case, all bits are valid in the final representation.
|
||||
match slot {
|
||||
|
|
@ -412,45 +365,27 @@ impl SlotManager {
|
|||
let offset = slot.try_into_acl_offset()?;
|
||||
writer
|
||||
.protected_write_slice(
|
||||
self.data_acl_range.as_ptr() as usize - utralib::HW_RERAM_MEM + offset,
|
||||
&setting.raw_u32().to_le_bytes(),
|
||||
)
|
||||
.map(|_| ())
|
||||
.map_err(|_| AccessError::WriteError)
|
||||
}
|
||||
SlotIndex::Key(_, _, _) => {
|
||||
let offset = slot.try_into_acl_offset()?;
|
||||
writer
|
||||
.protected_write_slice(
|
||||
self.key_acl_range.as_ptr() as usize - utralib::HW_RERAM_MEM + offset,
|
||||
ptr - utralib::HW_RERAM_MEM + offset,
|
||||
&setting.raw_u32().to_le_bytes(),
|
||||
)
|
||||
.map(|_| ())
|
||||
.map_err(|_| AccessError::WriteError)
|
||||
}
|
||||
SlotIndex::DataRange(_, _, _) => {
|
||||
let mut success = true;
|
||||
for offset in slot.try_into_acl_iter()? {
|
||||
writer
|
||||
.protected_write_slice(
|
||||
self.data_acl_range.as_ptr() as usize - utralib::HW_RERAM_MEM + offset,
|
||||
&setting.raw_u32().to_le_bytes(),
|
||||
)
|
||||
.map(|_| ())
|
||||
.map_err(|_| AccessError::WriteError)?
|
||||
match writer.protected_write_slice(
|
||||
ptr - utralib::HW_RERAM_MEM + offset,
|
||||
&setting.raw_u32().to_le_bytes(),
|
||||
) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
success = false;
|
||||
crate::println!("Can't write to ACL offset {:x}: {:?}", offset, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
SlotIndex::KeyRange(_, _, _) => {
|
||||
for offset in slot.try_into_acl_iter()? {
|
||||
writer
|
||||
.protected_write_slice(
|
||||
self.key_acl_range.as_ptr() as usize - utralib::HW_RERAM_MEM + offset,
|
||||
&setting.raw_u32().to_le_bytes(),
|
||||
)
|
||||
.map(|_| ())
|
||||
.map_err(|_| AccessError::WriteError)?
|
||||
}
|
||||
Ok(())
|
||||
if success { Ok(()) } else { Err(AccessError::WriteError) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use alloc::vec::Vec;
|
|||
|
||||
use bao1x_api::signatures::*;
|
||||
#[cfg(not(feature = "std"))]
|
||||
use bao1x_api::{DEVELOPER_MODE, KeySlotAccess, RwPerms, SLOT_ELEMENT_LEN_BYTES, SlotType};
|
||||
use bao1x_api::{DEVELOPER_MODE, DataSlotAccess, RwPerms, SLOT_ELEMENT_LEN_BYTES, SlotType};
|
||||
use digest::Digest;
|
||||
use sha2_bao1x::{Sha256, Sha512};
|
||||
use xous::arch::PAGE_SIZE;
|
||||
|
|
@ -226,7 +226,7 @@ pub fn erase_secrets() {
|
|||
// have access permissions, and panic instead of allowing a boot.
|
||||
const ZERO_ERR_THRESH: usize = 2;
|
||||
for slot in crate::board::KEY_SLOTS.iter() {
|
||||
if slot.get_type() == SlotType::Key {
|
||||
if slot.get_type() == SlotType::Data {
|
||||
let (_pa, rw_perms) = slot.get_access_spec();
|
||||
for data_index in slot.try_into_data_iter().unwrap() {
|
||||
match rw_perms {
|
||||
|
|
@ -238,11 +238,11 @@ pub fn erase_secrets() {
|
|||
.set_acl(
|
||||
&mut rram,
|
||||
slot,
|
||||
&AccessSettings::Key(KeySlotAccess::new_with_raw_value(0)),
|
||||
&AccessSettings::Data(DataSlotAccess::new_with_raw_value(0)),
|
||||
)
|
||||
.expect("couldn't reset ACL");
|
||||
}
|
||||
let bytes = unsafe { slot_mgr.read_key_slot(data_index) };
|
||||
let bytes = unsafe { slot_mgr.read_data_slot(data_index) };
|
||||
if bytes.iter().all(|&b| b == 0) {
|
||||
zero_key_count += 1;
|
||||
}
|
||||
|
|
@ -253,12 +253,15 @@ pub fn erase_secrets() {
|
|||
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).expect("couldn't erase key");
|
||||
slot_mgr.write(&mut rram, slot, &eraser).ok();
|
||||
}
|
||||
let check = unsafe { slot_mgr.read_key_slot(data_index) };
|
||||
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);
|
||||
panic!("Key erasure did not succeed, refusing to boot!");
|
||||
// reboot on failure to erase
|
||||
let mut rcurst =
|
||||
utralib::CSR::new(utralib::utra::sysctrl::HW_SYSCTRL_BASE as *mut u32);
|
||||
rcurst.wo(utralib::utra::sysctrl::SFR_RCURST0, 0x55AA);
|
||||
} else {
|
||||
crate::println!("Key range at {} confirmed erased", slot.get_base());
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue