diff --git a/src/constants.rs b/src/constants.rs index 5eba22d..264b282 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -5,6 +5,8 @@ use hex_literal::hex; +use crate::state::ManagementAlgorithm; + pub const RID_LENGTH: usize = 5; // top nibble of first byte is "category", here "A" = International @@ -269,6 +271,8 @@ pub const YUBICO_DEFAULT_MANAGEMENT_KEY: &[u8; 24] = &hex!( " ); +pub const YUBICO_DEFAULT_MANAGEMENT_KEY_ALG: ManagementAlgorithm = ManagementAlgorithm::Tdes; + // stolen from le yubico pub const DISCOVERY_OBJECT: &[u8; 20] = b"~\x12O\x0b\xa0\x00\x00\x03\x08\x00\x00\x10\x00\x01\x00_/\x02@\x00"; diff --git a/src/container.rs b/src/container.rs index d727d2b..8f43553 100644 --- a/src/container.rs +++ b/src/container.rs @@ -60,6 +60,8 @@ macro_rules! enum_subset { } } +pub(crate) use enum_subset; + pub struct Tag<'a>(&'a [u8]); impl<'a> Tag<'a> { pub fn new(slice: &'a [u8]) -> Self { diff --git a/src/lib.rs b/src/lib.rs index 5e6c6b0..9f305d0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,6 +30,7 @@ pub mod vpicc; use core::convert::TryInto; use flexiber::EncodableHeapless; +use heapless_bytes::Bytes; use iso7816::{Data, Status}; use trussed::client; use trussed::{syscall, try_syscall}; @@ -37,7 +38,9 @@ use trussed::{syscall, try_syscall}; use constants::*; pub type Result = iso7816::Result<()>; -use state::{LoadedState, State}; +use state::{CommandCache, LoadedState, State}; + +use crate::piv_types::DynamicAuthenticationTemplate; /// PIV authenticator Trussed app. /// @@ -496,11 +499,33 @@ impl<'a, T: trussed::Client + trussed::client::Ed255> LoadedAuthenticator<'a, T> pub fn request_for_challenge( &mut self, _auth: GeneralAuthenticate, - _data: derp::Input<'_>, - _reply: &mut Data, + data: derp::Input<'_>, + reply: &mut Data, ) -> Result { - info!("Request for challenge"); - todo!() + if data.len() != 0 { + warn!("Request for challenge with non empty data"); + return Err(Status::IncorrectDataParameter); + } + info!("Request for challenge "); + let challenge = syscall!(self.trussed.random_bytes( + self.state + .persistent + .keys + .management_key + .alg + .challenge_length() + )) + .bytes; + self.state.runtime.command_cache = Some(CommandCache::AuthenticateChallenge( + Bytes::from_slice(&challenge).unwrap(), + )); + let resp = DynamicAuthenticationTemplate::with_challenge(&challenge); + resp.encode_to_heapless_vec(reply) + .map_err(|_err| { + error!("Failed to encode challenge: {_err:?}"); + Status::UnspecifiedNonpersistentExecutionError + }) + .map(drop) } pub fn request_for_witness( diff --git a/src/state.rs b/src/state.rs index 62d890b..f3a41f3 100644 --- a/src/state.rs +++ b/src/state.rs @@ -13,6 +13,7 @@ use trussed::{ }; use crate::constants::*; +use crate::piv_types::Algorithms; use crate::{Pin, Puk}; @@ -133,13 +134,36 @@ impl SlotName { } } +crate::container::enum_subset! { + #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] + pub enum ManagementAlgorithm: Algorithms { + Tdes, + Aes256 + } +} + +impl ManagementAlgorithm { + pub fn challenge_length(self) -> usize { + match self { + Self::Tdes => 8, + Self::Aes256 => 16, + } + } +} + +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct ManagementKey { + pub id: KeyId, + pub alg: ManagementAlgorithm, +} + #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct Keys { // 9a "PIV Authentication Key" (YK: PIV Authentication) #[serde(skip_serializing_if = "Option::is_none")] pub authentication_key: Option, // 9b "PIV Card Application Administration Key" (YK: PIV Management) - pub management_key: KeyId, + pub management_key: ManagementKey, // 9c "Digital Signature Key" (YK: Digital Signature) #[serde(skip_serializing_if = "Option::is_none")] pub signature_key: Option, @@ -295,17 +319,12 @@ pub struct AppSecurityStatus { #[derive(Clone, Debug, Eq, PartialEq)] pub enum CommandCache { GetData(GetData), - AuthenticateManagement(AuthenticateManagement), + AuthenticateChallenge(Bytes<16>), } #[derive(Clone, Debug, Eq, PartialEq)] pub struct GetData {} -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct AuthenticateManagement { - pub challenge: [u8; 8], -} - impl Persistent { pub const PIN_RETRIES_DEFAULT: u8 = 3; // hmm...! @@ -424,19 +443,22 @@ impl Persistent { syscall!(client .unsafe_inject_shared_key(management_key, trussed::types::Location::Internal,)) .key; - let old_management_key = self.keys.management_key; - self.keys.management_key = new_management_key; + let old_management_key = self.keys.management_key.id; + self.keys.management_key.id = new_management_key; self.save(client); syscall!(client.delete(old_management_key)); } pub fn initialize(client: &mut impl trussed::Client) -> Self { info!("initializing PIV state"); - let management_key = syscall!(client.unsafe_inject_shared_key( - YUBICO_DEFAULT_MANAGEMENT_KEY, - trussed::types::Location::Internal, - )) - .key; + let management_key = ManagementKey { + id: syscall!(client.unsafe_inject_shared_key( + YUBICO_DEFAULT_MANAGEMENT_KEY, + trussed::types::Location::Internal, + )) + .key, + alg: YUBICO_DEFAULT_MANAGEMENT_KEY_ALG, + }; let mut guid: [u8; 16] = syscall!(client.random_bytes(16)) .bytes