mirror of
https://github.com/saymrwulf/xous-core.git
synced 2026-11-04 04:27:34 +00:00
This is going to require users to update their devices before doing a key initialization. The problem is that we can't trust the length of data presented to us to self-sign, so we sign the whole area. In v0.9.2 of the loader, a check was introduced to check the putative length of the signed area against the actual length of the signed area, by encoding the length of the signed area within the signed region. This would require the rootkeys algorithm to also encode that new length at the end of the signed area, but this was never done. However, this bug was never found during testing probably because I failed to completely erase the original key provisioning (I was more fixated on checking that existing keys were not damaged by firmware updates at the time), and thus did not trigger a full rootkey provisioning cycle when I did the test. I had only just caught it now while porting the PDDB from simulation to real hardware.
166 lines
No EOL
4.7 KiB
Rust
166 lines
No EOL
4.7 KiB
Rust
pub(crate) const SERVER_NAME_KEYS: &str = "_Root key server and update manager_";
|
|
#[allow(dead_code)]
|
|
pub(crate) const ROOTKEY_MODAL_NAME: &'static str = "rootkeys modal";
|
|
#[allow(dead_code)]
|
|
pub(crate) const ROOTKEY_MENU_NAME: &'static str = "rootkeys menu";
|
|
#[allow(dead_code)]
|
|
pub(crate) const SIG_VERSION: u32 = 1;
|
|
|
|
#[allow(dead_code)]
|
|
#[derive(num_derive::FromPrimitive, num_derive::ToPrimitive, Debug)]
|
|
pub(crate) enum Opcode {
|
|
/// use to check if we've been initialized
|
|
KeysInitialized,
|
|
/// check that the digital signature on the gateware
|
|
CheckGatewareSignature,
|
|
/// check if the efuse has been locked down
|
|
IsEfuseSecured,
|
|
/// quick check to see if the JTAG can read its IDCODE
|
|
IsJtagWorking,
|
|
/// initiate an AES oracle operation
|
|
AesOracle,
|
|
/// create new FPGA keys; provisioning requires a slave device to be connected that can run the JTAG sequence
|
|
BbramProvision,
|
|
|
|
TestUx,
|
|
|
|
/// attempt to initialize keys on a brand new system. Does nothing if the keys are already provisioned.
|
|
UxTryInitKeys,
|
|
UxConfirmInitKeys,
|
|
UxConfirmation,
|
|
UxInitRequestPassword,
|
|
UxInitPasswordReturn,
|
|
|
|
/// provision a gateware update with our secret data
|
|
UxUpdateGateware,
|
|
UxUpdateGwCheckSig,
|
|
UxUpdateGwShowInfo,
|
|
UxUpdateGwShowLog,
|
|
UxUpdateGwShowStatus,
|
|
UxUpdateGwConfirm,
|
|
UxUpdateGwDecidePassword,
|
|
UxUpdateGwPasswordPolicy,
|
|
UxUpdateGwRun,
|
|
|
|
/// self-sign kernel/loader
|
|
UxSelfSignXous,
|
|
UxSignXousPasswordPolicy,
|
|
UxSignXousRun,
|
|
|
|
/// Ux AES calls
|
|
UxAesEnsurePassword,
|
|
UxAesPasswordPolicy,
|
|
UxAesEnsureReturn,
|
|
|
|
/// Ux BBRAM flow
|
|
UxBbramCheckHelper,
|
|
UxBbramCheckReturn,
|
|
UxBbramPasswordReturn,
|
|
UxBbramRun,
|
|
|
|
// General Ux calls
|
|
UxGutter, // NOP for UX calls that require a destination
|
|
UxGetPolicy,
|
|
UxPolicyReturn,
|
|
UxTryReboot,
|
|
UxDoReboot,
|
|
|
|
/// UX opcodes
|
|
MenuRedraw,
|
|
MenuKeys,
|
|
MenuDrop,
|
|
ModalRedraw,
|
|
ModalKeys,
|
|
ModalDrop,
|
|
|
|
/// Suspend/resume callback
|
|
SuspendResume,
|
|
|
|
Quit
|
|
}
|
|
|
|
#[derive(Debug, num_derive::FromPrimitive, num_derive::ToPrimitive, PartialEq, Eq)]
|
|
pub enum PasswordRetentionPolicy {
|
|
AlwaysKeep,
|
|
EraseOnSuspend,
|
|
AlwaysPurge,
|
|
}
|
|
|
|
/// Enumerate the possible password types dealt with by this manager.
|
|
/// Note that the discriminant of the enum is used to every-so-slightly change the salt going into bcrypt
|
|
/// I don't think it hurts; more importantly, it also prevents an off-the-shelf "hashcat" run from
|
|
/// being used to brute force both passwords in a single go, as the salt has to be (slightly)
|
|
/// recomputed for each type of password.
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
|
pub enum PasswordType {
|
|
Boot = 1,
|
|
Update = 2,
|
|
}
|
|
#[cfg_attr(not(any(target_os = "none", target_os = "xous")), allow(dead_code))]
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
|
pub enum RootkeyResult {
|
|
AlignmentError,
|
|
KeyError,
|
|
IntegrityError,
|
|
FlashError,
|
|
}
|
|
|
|
/// AES operation definitions
|
|
pub use cipher::{BlockCipher, consts::U16};
|
|
use zeroize::Zeroize;
|
|
|
|
/// 128-bit AES block
|
|
#[allow(dead_code)]
|
|
pub type Block = cipher::generic_array::GenericArray<u8, cipher::consts::U16>;
|
|
/// 16 x 128-bit AES blocks to be processed in bulk
|
|
#[allow(dead_code)]
|
|
pub type ParBlocks = cipher::generic_array::GenericArray<Block, cipher::consts::U16>;
|
|
|
|
pub const PAR_BLOCKS: usize = 16;
|
|
/// Selects which key to use for the decryption/encryption oracle.
|
|
/// currently only one type is available, the User key, but dozens more
|
|
/// could be accommodated.
|
|
#[derive(Debug, num_derive::FromPrimitive, num_derive::ToPrimitive, PartialEq, Eq, Copy, Clone)]
|
|
pub enum AesRootkeyType {
|
|
User0 = 0x28,
|
|
NoneSpecified = 0xff,
|
|
}
|
|
|
|
#[derive(Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Zeroize)]
|
|
#[zeroize(drop)]
|
|
pub enum AesBlockType {
|
|
SingleBlock([u8; 16]),
|
|
ParBlock([[u8; 16]; PAR_BLOCKS]),
|
|
}
|
|
#[derive(Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Zeroize)]
|
|
#[zeroize(drop)]
|
|
pub enum AesOpType {
|
|
Encrypt,
|
|
Decrypt,
|
|
}
|
|
#[derive(Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Zeroize)]
|
|
#[zeroize(drop)]
|
|
pub struct AesOp {
|
|
/// the caller can try to request "any" index, but it's checked inside the oracle first.
|
|
pub key_index: u8,
|
|
pub block: AesBlockType,
|
|
pub aes_op: AesOpType,
|
|
}
|
|
impl AesOp {
|
|
pub fn clear(&mut self) {
|
|
match self.block {
|
|
AesBlockType::SingleBlock(mut blk) => {
|
|
for b in blk.iter_mut() {
|
|
*b = 0;
|
|
}
|
|
}
|
|
AesBlockType::ParBlock(mut blks) => {
|
|
for blk in blks.iter_mut() {
|
|
for b in blk.iter_mut() {
|
|
*b = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |