diff --git a/services/keystore/Cargo.toml b/services/keystore/Cargo.toml index 33c9df1b4..94ca42144 100644 --- a/services/keystore/Cargo.toml +++ b/services/keystore/Cargo.toml @@ -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 = [ diff --git a/services/keystore/src/lib.rs b/services/keystore/src/lib.rs index 2f3313812..d64b9c2cc 100644 --- a/services/keystore/src/lib.rs +++ b/services/keystore/src/lib.rs @@ -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"); diff --git a/services/keystore/src/main.rs b/services/keystore/src/main.rs index a3a02cd0c..ddb31a40c 100644 --- a/services/keystore/src/main.rs +++ b/services/keystore/src/main.rs @@ -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); } diff --git a/services/keystore/src/platform.rs b/services/keystore/src/platform.rs new file mode 100644 index 000000000..1cecdb77e --- /dev/null +++ b/services/keystore/src/platform.rs @@ -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::*; diff --git a/services/keystore/src/platform/baosec.rs b/services/keystore/src/platform/baosec.rs new file mode 100644 index 000000000..6ab351f65 --- /dev/null +++ b/services/keystore/src/platform/baosec.rs @@ -0,0 +1,3 @@ +use xous::SID; + +pub fn keystore(sid: &SID) -> ! { loop {} } diff --git a/services/keystore/src/platform/hosted.rs b/services/keystore/src/platform/hosted.rs new file mode 100644 index 000000000..a586fa537 --- /dev/null +++ b/services/keystore/src/platform/hosted.rs @@ -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::().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::::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::().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); + } + } + } +}