add an emulated keystore for hosted mode

this is a dummy keystore so we can test other UX operations.
the full keystore will be a lot harder to write.
This commit is contained in:
bunnie 2025-05-06 01:45:53 +08:00
parent 1ee1239494
commit b2f32b7b6f
6 changed files with 192 additions and 5 deletions

View file

@ -60,6 +60,7 @@ aes = { path = "../aes" }
byteorder = "1.4.3" # used by keywrap
# hardware acceleration adaptations are inserted into a fork of the main branch.
hex = { version = "0.4.3", default-features = false, features = [] }
hkdf = "0.12.4"
#[dependencies.curve25519-dalek]
#version = "=4.1.2" # note this is patched to our fork in ./Cargo.toml
@ -77,13 +78,24 @@ cramium-soc = ["utralib/cramium-soc"]
board-baosec = [
"utralib/cramium-soc",
"ux-api/board-baosec",
"keystore-api/gen2",
"modals/no-gam",
] # USB form factor token
loader-baosec = ["ux-api/loader-baosec"]
test-baosec = ["ux-api/loader-baosec"]
hosted-baosec = [] # emulated hardware on x86 platform
board-baosor = ["utralib/cramium-soc"] # Precursor form factor
hosted-baosec = [
"keystore-api/gen2",
"modals/no-gam",
] # emulated hardware on x86 platform
board-baosor = [
"utralib/cramium-soc",
"keystore-api/gen2",
] # Precursor form factor
loader-baosor = []
board-dabao = ["utralib/cramium-soc"] # Dev board form factor
board-dabao = [
"utralib/cramium-soc",
"keystore-api/gen2",
] # Dev board form factor
loader-dabao = []
policy-menu = [

View file

@ -46,7 +46,7 @@ impl Keystore {
Message::new_blocking_scalar(Opcode::GetDna.to_usize().unwrap(), 0, 0, 0, 0),
)
.unwrap();
if let xous::Result::Scalar2(val1, val2) = response {
if let xous::Result::Scalar5(val1, val2, _, _, _) = response {
(val1 as u64) | ((val2 as u64) << 32)
} else {
panic!("get_dna() failed with internal error");

View file

@ -1,5 +1,7 @@
use keystore_api::*;
mod platform;
fn main() -> ! {
log_server::init_wait().unwrap();
log::set_max_level(log::LevelFilter::Info);
@ -9,5 +11,5 @@ fn main() -> ! {
// TODO: limit connections to this server?? once we know all that will connect
let keys_sid = xns.register_name(SERVER_NAME_KEYS, None).expect("can't register server");
loop {}
platform::keystore(keys_sid);
}

View file

@ -0,0 +1,9 @@
#[cfg(feature = "board-baosec")]
mod baosec;
#[cfg(feature = "board-baosec")]
pub use baosec::*;
#[cfg(feature = "hosted-baosec")]
mod hosted;
#[cfg(feature = "hosted-baosec")]
pub use hosted::*;

View file

@ -0,0 +1,3 @@
use xous::SID;
pub fn keystore(sid: &SID) -> ! { loop {} }

View file

@ -0,0 +1,161 @@
use std::convert::TryInto;
use aes::Aes256;
use aes::cipher::{BlockDecrypt, BlockEncrypt, KeyInit, generic_array::GenericArray};
use hkdf::Hkdf;
use keystore_api::*;
use sha2::Sha256;
use xous::SID;
use xous_ipc::Buffer;
/// Any old key for the hosted mode testing. 0 is as good a number as any other.
const WELL_KNOWN_KEY: &'static str = "0000000000000000000000000000000000000000000000000000000000000000";
pub fn keystore(sid: SID) -> ! {
let mut msg_opt = None;
loop {
xous::reply_and_receive_next(sid, &mut msg_opt).unwrap();
let msg = msg_opt.as_mut().unwrap();
let opcode = num_traits::FromPrimitive::from_usize(msg.body.id()).unwrap_or(Opcode::InvalidCall);
log::debug!("{:?}", opcode);
match opcode {
Opcode::AesOracle => {
let mut buffer =
unsafe { Buffer::from_memory_message_mut(msg.body.memory_message_mut().unwrap()) };
// as_flat saves a copy step, but we have to deserialize some enums manually
let mut aes_op = buffer.to_original::<AesOp, _>().unwrap();
let op = match aes_op.aes_op {
// seems stupid, but we have to do this because we want to have zeroize on the AesOp
// record, and it means we can't have Copy on this.
AesOpType::Decrypt => AesOpType::Decrypt,
AesOpType::Encrypt => AesOpType::Encrypt,
};
let mut ikm = [0u8; 32];
hex::encode_to_slice(WELL_KNOWN_KEY, &mut ikm).unwrap();
let hk = Hkdf::<Sha256>::new(None, &ikm);
let mut okm = [0u8; 32];
hk.expand(&aes_op.domain.as_bytes(), &mut okm)
.expect("32 is a valid length for Sha256 to output");
let cipher = Aes256::new(GenericArray::from_slice(&okm));
// deserialize the specifier
match aes_op.block {
AesBlockType::SingleBlock(b) => {
match op {
AesOpType::Decrypt => cipher.decrypt_block(&mut b.try_into().unwrap()),
AesOpType::Encrypt => cipher.encrypt_block(&mut b.try_into().unwrap()),
}
aes_op.block = AesBlockType::SingleBlock(b);
}
AesBlockType::ParBlock(mut pb) => {
match op {
AesOpType::Decrypt => {
for block in pb.iter_mut() {
cipher.decrypt_block(block.try_into().unwrap());
}
}
AesOpType::Encrypt => {
for block in pb.iter_mut() {
cipher.encrypt_block(block.try_into().unwrap());
}
}
}
aes_op.block = AesBlockType::ParBlock(pb);
}
};
buffer.replace(aes_op).unwrap();
}
Opcode::AesKwp => {
let mut ikm = [0u8; 32];
hex::encode_to_slice(WELL_KNOWN_KEY, &mut ikm).unwrap();
let mut buffer =
unsafe { Buffer::from_memory_message_mut(msg.body.memory_message_mut().unwrap()) };
let mut kwp = buffer.to_original::<KeyWrapper, _>().unwrap();
use aes_kw::Kek;
use aes_kw::KekAes256;
let keywrapper: KekAes256 = Kek::from(ikm);
match kwp.op {
KeyWrapOp::Wrap => {
match keywrapper.wrap_with_padding_vec(&kwp.data[..kwp.len as usize]) {
Ok(wrapped) => {
for (&src, dst) in wrapped.iter().zip(kwp.data.iter_mut()) {
*dst = src;
}
kwp.len = wrapped.len() as u32;
kwp.result = None;
// this is an un-used field but...why not?
kwp.expected_len = wrapped.len() as u32;
}
Err(e) => {
kwp.result = Some(match e {
aes_kw::Error::IntegrityCheckFailed => KeywrapError::IntegrityCheckFailed,
aes_kw::Error::InvalidDataSize => KeywrapError::InvalidDataSize,
aes_kw::Error::InvalidKekSize { size } => {
log::info!("invalid size {}", size); // weird. can't name this _size
KeywrapError::InvalidKekSize
}
aes_kw::Error::InvalidOutputSize { expected } => {
log::info!("invalid output size {}", expected);
KeywrapError::InvalidOutputSize
}
});
}
}
}
KeyWrapOp::Unwrap => {
match keywrapper.unwrap_with_padding_vec(&kwp.data[..kwp.len as usize]) {
Ok(wrapped) => {
for (&src, dst) in wrapped.iter().zip(kwp.data.iter_mut()) {
*dst = src;
}
kwp.len = wrapped.len() as u32;
kwp.result = None;
// this is an un-used field but...why not?
kwp.expected_len = wrapped.len() as u32;
}
Err(e) => {
kwp.result = Some(match e {
aes_kw::Error::IntegrityCheckFailed => KeywrapError::IntegrityCheckFailed,
aes_kw::Error::InvalidDataSize => KeywrapError::InvalidDataSize,
aes_kw::Error::InvalidKekSize { size } => {
log::info!("invalid size {}", size); // weird. can't name this _size
KeywrapError::InvalidKekSize
}
aes_kw::Error::InvalidOutputSize { expected } => {
log::info!("invalid output size {}", expected);
KeywrapError::InvalidOutputSize
}
});
}
}
}
}
buffer.replace(kwp).unwrap();
}
Opcode::ClearPasswordCacheEntry => {
if let Some(scalar) = msg.body.scalar_message_mut() {
// no passwords, nothing to clear: always return success
scalar.arg1 = 1;
}
}
Opcode::EnsurePassword => {
if let Some(scalar) = msg.body.scalar_message_mut() {
let pws: PasswordState = PasswordState::Correct;
(scalar.arg1, scalar.arg2, scalar.arg3) = pws.into();
log::info!("Password is always ensured in hosted mode");
}
}
Opcode::GetDna => {
// dummy DNA value
if let Some(scalar) = msg.body.scalar_message_mut() {
scalar.arg1 = 0x1234_5678;
scalar.arg2 = 0xface_cafe;
}
}
Opcode::InvalidCall => {
log::error!("Invalid call in keystore: {:?}", opcode);
}
}
}
}