From 22f4669ec76d628826892fbb99aa8bdf5f3b23a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Fri, 2 Dec 2022 18:04:14 +0100 Subject: [PATCH] Reuse generic container approach from Opcard --- src/container.rs | 62 ++++++++++++------- src/lib.rs | 155 +++++++++++++++++++++++------------------------ src/state.rs | 75 +++++++++++++++++++++++ 3 files changed, 192 insertions(+), 100 deletions(-) diff --git a/src/container.rs b/src/container.rs index db5faa0..f3e3cdb 100644 --- a/src/container.rs +++ b/src/container.rs @@ -242,8 +242,26 @@ pub enum Container { PrintedInformation, DiscoveryObject, KeyHistoryObject, - RetiredX509Certificate(RetiredIndex), - + RetiredCert01, + RetiredCert02, + RetiredCert03, + RetiredCert04, + RetiredCert05, + RetiredCert06, + RetiredCert07, + RetiredCert08, + RetiredCert09, + RetiredCert10, + RetiredCert11, + RetiredCert12, + RetiredCert13, + RetiredCert14, + RetiredCert15, + RetiredCert16, + RetiredCert17, + RetiredCert18, + RetiredCert19, + RetiredCert20, CardholderIrisImages, BiometricInformationTemplatesGroupTemplate, SecureMessagingCertificateSigner, @@ -309,26 +327,26 @@ impl TryFrom<&[u8]> for Container { hex!("5FC109") => PrintedInformation, hex!("7E") => DiscoveryObject, - hex!("5FC10D") => RetiredX509Certificate(RetiredIndex(1)), - hex!("5FC10E") => RetiredX509Certificate(RetiredIndex(2)), - hex!("5FC10F") => RetiredX509Certificate(RetiredIndex(3)), - hex!("5FC110") => RetiredX509Certificate(RetiredIndex(4)), - hex!("5FC111") => RetiredX509Certificate(RetiredIndex(5)), - hex!("5FC112") => RetiredX509Certificate(RetiredIndex(6)), - hex!("5FC113") => RetiredX509Certificate(RetiredIndex(7)), - hex!("5FC114") => RetiredX509Certificate(RetiredIndex(8)), - hex!("5FC115") => RetiredX509Certificate(RetiredIndex(9)), - hex!("5FC116") => RetiredX509Certificate(RetiredIndex(10)), - hex!("5FC117") => RetiredX509Certificate(RetiredIndex(11)), - hex!("5FC118") => RetiredX509Certificate(RetiredIndex(12)), - hex!("5FC119") => RetiredX509Certificate(RetiredIndex(13)), - hex!("5FC11A") => RetiredX509Certificate(RetiredIndex(14)), - hex!("5FC11B") => RetiredX509Certificate(RetiredIndex(15)), - hex!("5FC11C") => RetiredX509Certificate(RetiredIndex(16)), - hex!("5FC11D") => RetiredX509Certificate(RetiredIndex(17)), - hex!("5FC11E") => RetiredX509Certificate(RetiredIndex(18)), - hex!("5FC11F") => RetiredX509Certificate(RetiredIndex(19)), - hex!("5FC120") => RetiredX509Certificate(RetiredIndex(20)), + hex!("5FC10D") => RetiredCert01, + hex!("5FC10E") => RetiredCert02, + hex!("5FC10F") => RetiredCert03, + hex!("5FC110") => RetiredCert04, + hex!("5FC111") => RetiredCert05, + hex!("5FC112") => RetiredCert06, + hex!("5FC113") => RetiredCert07, + hex!("5FC114") => RetiredCert08, + hex!("5FC115") => RetiredCert09, + hex!("5FC116") => RetiredCert10, + hex!("5FC117") => RetiredCert11, + hex!("5FC118") => RetiredCert12, + hex!("5FC119") => RetiredCert13, + hex!("5FC11A") => RetiredCert14, + hex!("5FC11B") => RetiredCert15, + hex!("5FC11C") => RetiredCert16, + hex!("5FC11D") => RetiredCert17, + hex!("5FC11E") => RetiredCert18, + hex!("5FC11F") => RetiredCert19, + hex!("5FC120") => RetiredCert20, hex!("5FC121") => CardholderIrisImages, hex!("7F61") => BiometricInformationTemplatesGroupTemplate, diff --git a/src/lib.rs b/src/lib.rs index 61c4b2c..5b78516 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -123,7 +123,7 @@ where Command::ChangeReference(change_reference) => { self.load()?.change_reference(change_reference) } - Command::GetData(container) => self.get_data(container, reply), + Command::GetData(container) => self.load()?.get_data(container, reply), Command::Select(_aid) => self.select(reply), Command::GeneralAuthenticate(authenticate) => { self.load()? @@ -140,83 +140,6 @@ where } } - fn get_data( - &mut self, - container: container::Container, - mut reply: Reply<'_, R>, - ) -> Result { - // TODO: check security status, else return Status::SecurityStatusNotSatisfied - - // Table 3, Part 1, SP 800-73-4 - // https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-73-4.pdf#page=30 - use crate::container::Container; - match container { - Container::DiscoveryObject => { - // Err(Status::InstructionNotSupportedOrInvalid) - reply.extend_from_slice(DISCOVERY_OBJECT).ok(); - // todo!("discovery object"), - } - - Container::BiometricInformationTemplatesGroupTemplate => { - return Err(Status::InstructionNotSupportedOrInvalid); - // todo!("biometric information template"), - } - - // '5FC1 07' (351B) - Container::CardCapabilityContainer => { - piv_types::CardCapabilityContainer::default() - .encode_to_heapless_vec(*reply) - .unwrap(); - info!("returning CCC {:02X?}", reply); - } - - // '5FC1 02' (351B) - Container::CardHolderUniqueIdentifier => { - let guid = self.state.persistent(&mut self.trussed)?.guid(); - piv_types::CardHolderUniqueIdentifier::default() - .with_guid(guid) - .encode_to_heapless_vec(*reply) - .unwrap(); - info!("returning CHUID {:02X?}", reply); - } - - // // '5FC1 05' (351B) - // Container::X509CertificateForPivAuthentication => { - // // return Err(Status::NotFound); - - // // info!("loading 9a cert"); - // // it seems like fetching this certificate is the way Filo's agent decides - // // whether the key is "already setup": - // // https://github.com/FiloSottile/yubikey-agent/blob/8781bc0082db5d35712a2244e3ab3086f415dd59/setup.go#L69-L70 - // let data = try_syscall!(self.trussed.read_file( - // trussed::types::Location::Internal, - // trussed::types::PathBuf::from(b"authentication-key.x5c"), - // )).map_err(|_| { - // // info!("error loading: {:?}", &e); - // Status::NotFound - // } )?.data; - - // // todo: cleanup - // let tag = flexiber::Tag::application(0x13); // 0x53 - // flexiber::TaggedSlice::from(tag, &data) - // .unwrap() - // .encode_to_heapless_vec(reply) - // .unwrap(); - // } - - // // '5F FF01' (754B) - // YubicoObjects::AttestationCertificate => { - // let data = Data::from_slice(YUBICO_ATTESTATION_CERTIFICATE).unwrap(); - // reply.extend_from_slice(&data).ok(); - // } - _ => { - warn!("Unimplemented GET DATA object: {container:?}"); - return Err(Status::FunctionNotSupported); - } - } - Ok(()) - } - pub fn yubico_piv_extension( &mut self, data: &[u8], @@ -959,6 +882,82 @@ impl<'a, T: trussed::Client + trussed::client::Ed255> LoadedAuthenticator<'a, T> Err(Status::IncorrectDataParameter) } + fn get_data( + &mut self, + container: container::Container, + mut reply: Reply<'_, R>, + ) -> Result { + // TODO: check security status, else return Status::SecurityStatusNotSatisfied + + // Table 3, Part 1, SP 800-73-4 + // https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-73-4.pdf#page=30 + use crate::container::Container; + match container { + Container::DiscoveryObject => { + // Err(Status::InstructionNotSupportedOrInvalid) + reply.extend_from_slice(DISCOVERY_OBJECT).ok(); + // todo!("discovery object"), + } + + Container::BiometricInformationTemplatesGroupTemplate => { + return Err(Status::InstructionNotSupportedOrInvalid); + // todo!("biometric information template"), + } + + // '5FC1 07' (351B) + Container::CardCapabilityContainer => { + piv_types::CardCapabilityContainer::default() + .encode_to_heapless_vec(*reply) + .unwrap(); + info!("returning CCC {:02X?}", reply); + } + + // '5FC1 02' (351B) + Container::CardHolderUniqueIdentifier => { + let guid = self.state.persistent.guid(); + piv_types::CardHolderUniqueIdentifier::default() + .with_guid(guid) + .encode_to_heapless_vec(*reply) + .unwrap(); + info!("returning CHUID {:02X?}", reply); + } + + // // '5FC1 05' (351B) + // Container::X509CertificateForPivAuthentication => { + // // return Err(Status::NotFound); + + // // info!("loading 9a cert"); + // // it seems like fetching this certificate is the way Filo's agent decides + // // whether the key is "already setup": + // // https://github.com/FiloSottile/yubikey-agent/blob/8781bc0082db5d35712a2244e3ab3086f415dd59/setup.go#L69-L70 + // let data = try_syscall!(self.trussed.read_file( + // trussed::types::Location::Internal, + // trussed::types::PathBuf::from(b"authentication-key.x5c"), + // )).map_err(|_| { + // // info!("error loading: {:?}", &e); + // Status::NotFound + // } )?.data; + + // // todo: cleanup + // let tag = flexiber::Tag::application(0x13); // 0x53 + // flexiber::TaggedSlice::from(tag, &data) + // .unwrap() + // .encode_to_heapless_vec(reply) + // .unwrap(); + // } + + // // '5F FF01' (754B) + // YubicoObjects::AttestationCertificate => { + // let data = Data::from_slice(YUBICO_ATTESTATION_CERTIFICATE).unwrap(); + // reply.extend_from_slice(&data).ok(); + // } + _ => { + warn!("Unimplemented GET DATA object: {container:?}"); + return Err(Status::FunctionNotSupported); + } + } + Ok(()) + } // match container { // containers::Container::CardHolderUniqueIdentifier => // piv_types::CardHolderUniqueIdentifier::default() diff --git a/src/state.rs b/src/state.rs index 8a1a247..c6304f7 100644 --- a/src/state.rs +++ b/src/state.rs @@ -13,6 +13,7 @@ use trussed::{ types::{KeyId, KeySerialization, Location, Mechanism, PathBuf, StorageAttributes}, }; +use crate::container::Container; use crate::{constants::*, piv_types::AsymmetricAlgorithms}; use crate::{ container::{AsymmetricKeyReference, SecurityCondition}, @@ -493,3 +494,77 @@ fn load_if_exists( }, } } + +#[derive(Clone, Copy, Debug)] +pub struct ContainerStorage(Container); + +impl ContainerStorage { + fn path(self) -> PathBuf { + PathBuf::from(match self.0 { + Container::CardCapabilityContainer => "CardCapabilityContainer", + Container::CardHolderUniqueIdentifier => "CardHolderUniqueIdentifier", + Container::X509CertificateFor9A => "X509CertificateFor9A", + Container::CardholderFingerprints => "CardholderFingerprints", + Container::SecurityObject => "SecurityObject", + Container::CardholderFacialImage => "CardholderFacialImage", + Container::X509CertificateFor9E => "X509CertificateFor9E", + Container::X509CertificateFor9C => "X509CertificateFor9C", + Container::X509CertificateFor9D => "X509CertificateFor9D", + Container::PrintedInformation => "PrintedInformation", + Container::DiscoveryObject => "DiscoveryObject", + Container::KeyHistoryObject => "KeyHistoryObject", + Container::RetiredCert01 => "RetiredCert01", + Container::RetiredCert02 => "RetiredCert02", + Container::RetiredCert03 => "RetiredCert03", + Container::RetiredCert04 => "RetiredCert04", + Container::RetiredCert05 => "RetiredCert05", + Container::RetiredCert06 => "RetiredCert06", + Container::RetiredCert07 => "RetiredCert07", + Container::RetiredCert08 => "RetiredCert08", + Container::RetiredCert09 => "RetiredCert09", + Container::RetiredCert10 => "RetiredCert10", + Container::RetiredCert11 => "RetiredCert11", + Container::RetiredCert12 => "RetiredCert12", + Container::RetiredCert13 => "RetiredCert13", + Container::RetiredCert14 => "RetiredCert14", + Container::RetiredCert15 => "RetiredCert15", + Container::RetiredCert16 => "RetiredCert16", + Container::RetiredCert17 => "RetiredCert17", + Container::RetiredCert18 => "RetiredCert18", + Container::RetiredCert19 => "RetiredCert19", + Container::RetiredCert20 => "RetiredCert20", + Container::CardholderIrisImages => "CardholderIrisImages", + Container::BiometricInformationTemplatesGroupTemplate => { + "BiometricInformationTemplatesGroupTemplate" + } + Container::SecureMessagingCertificateSigner => "SecureMessagingCertificateSigner", + Container::PairingCodeReferenceDataContainer => "PairingCodeReferenceDataContainer", + }) + } + + fn default(self) -> &'static [u8] { + todo!() + } + + pub fn load( + self, + client: &mut impl trussed::Client, + ) -> Result, Status> { + load_if_exists(client, Location::Internal, &self.path()) + .map(|data| data.unwrap_or_else(|| Bytes::from_slice(self.default()).unwrap())) + } + + pub fn save(self, client: &mut impl trussed::Client, bytes: &[u8]) -> Result<(), Status> { + let msg = Bytes::from(heapless::Vec::try_from(bytes).map_err(|_| { + error!("Buffer full"); + Status::IncorrectDataParameter + })?); + try_syscall!(client.write_file(Location::Internal, self.path(), msg, None)).map_err( + |_err| { + error!("Failed to store data: {_err:?}"); + Status::UnspecifiedNonpersistentExecutionError + }, + )?; + Ok(()) + } +}