diff --git a/src/container.rs b/src/container.rs index 76b197c..65997cd 100644 --- a/src/container.rs +++ b/src/container.rs @@ -16,6 +16,7 @@ macro_rules! enum_subset { ) => { $(#[$outer])* #[repr(u8)] + #[derive(Clone, Copy)] $vis enum $name { $( $var, @@ -46,9 +47,9 @@ macro_rules! enum_subset { } } - impl PartialEq<$sup> for $name { - fn eq(&self, other: &$sup) -> bool { - match (self,other) { + impl> PartialEq for $name { + fn eq(&self, other: &T) -> bool { + match (self,(*other).into()) { $( | ($name::$var, $sup::$var) )* => true, @@ -57,6 +58,8 @@ macro_rules! enum_subset { } } + impl Eq for $name {} + impl TryFrom for $name { type Error = ::iso7816::Status; fn try_from(tag: u8) -> ::core::result::Result { @@ -84,7 +87,7 @@ pub enum SecurityCondition { pub struct RetiredIndex(u8); crate::enum_u8! { - #[derive(Clone, Copy, Debug, Eq, PartialEq)] + #[derive(Debug)] pub enum KeyReference { GlobalPin = 0x00, SecureMessaging = 0x04, @@ -148,14 +151,14 @@ macro_rules! impl_use_security_condition { } enum_subset! { - #[derive(Clone, Copy, Debug, Eq, PartialEq)] + #[derive(Debug)] pub enum AttestKeyReference: KeyReference { PivAuthentication, } } enum_subset! { - #[derive(Clone, Copy, Debug, Eq, PartialEq)] + #[derive(Debug)] pub enum AsymmetricKeyReference: KeyReference { // SecureMessaging, PivAuthentication, @@ -187,7 +190,7 @@ enum_subset! { } enum_subset! { - #[derive(Clone, Copy, Debug, Eq, PartialEq)] + #[derive(Debug)] pub enum GenerateKeyReference: AsymmetricKeyReference { // SecureMessaging, PivAuthentication, @@ -198,7 +201,7 @@ enum_subset! { } enum_subset! { - #[derive(Clone, Copy, Debug, Eq, PartialEq)] + #[derive(Debug)] pub enum ChangeReferenceKeyReference: KeyReference { GlobalPin, ApplicationPin, @@ -207,7 +210,7 @@ enum_subset! { } enum_subset! { - #[derive(Clone, Copy, Debug, Eq, PartialEq)] + #[derive(Debug)] pub enum VerifyKeyReference: KeyReference { GlobalPin, ApplicationPin, @@ -220,7 +223,7 @@ enum_subset! { enum_subset! { - #[derive(Clone, Copy, Debug, Eq, PartialEq)] + #[derive(Debug)] pub enum AuthenticateKeyReference: KeyReference { SecureMessaging, PivAuthentication, diff --git a/src/lib.rs b/src/lib.rs index 991b793..c85ad45 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,7 +11,7 @@ extern crate log; delog::generate_macros!(); pub mod commands; -use commands::piv_types::Algorithms; +use commands::piv_types::{Algorithms, RsaAlgorithms}; pub use commands::{Command, YubicoPivExtension}; use commands::{GeneralAuthenticate, PutData, ResetRetryCounter}; pub mod constants; @@ -592,7 +592,7 @@ impl<'a, T: trussed::Client + trussed::client::Ed255> LoadedAuthenticator<'a, T> self.admin_challenge(auth.algorithm, data, reply) } SecureMessaging => Err(Status::FunctionNotSupported), - _ => self.sign_challenge( + PivAuthentication | CardAuthentication | DigitalSignature => self.sign_challenge( auth.algorithm, auth.key_reference.try_into().map_err(|_| { if cfg!(debug_assertions) { @@ -600,7 +600,24 @@ impl<'a, T: trussed::Client + trussed::client::Ed255> LoadedAuthenticator<'a, T> panic!("Failed to convert key reference: {:?}", auth.key_reference); } else { error!("Failed to convert key reference: {:?}", auth.key_reference); - Status::UnspecifiedNonpersistentExecutionError + Status::UnspecifiedPersistentExecutionError + } + })?, + data, + reply, + ), + KeyManagement | Retired01 | Retired02 | Retired03 | Retired04 | Retired05 + | Retired06 | Retired07 | Retired08 | Retired09 | Retired10 | Retired11 + | Retired12 | Retired13 | Retired14 | Retired15 | Retired16 | Retired17 + | Retired18 | Retired19 | Retired20 => self.agreement_challenge( + auth.algorithm, + auth.key_reference.try_into().map_err(|_| { + if cfg!(debug_assertions) { + // To find errors more easily in tests and fuzzing but not crash in production + panic!("Failed to convert key reference: {:?}", auth.key_reference); + } else { + error!("Failed to convert key reference: {:?}", auth.key_reference); + Status::UnspecifiedPersistentExecutionError } })?, data, @@ -610,6 +627,50 @@ impl<'a, T: trussed::Client + trussed::client::Ed255> LoadedAuthenticator<'a, T> } } + pub fn agreement_challenge( + &mut self, + requested_alg: Algorithms, + key_ref: AsymmetricKeyReference, + data: derp::Input<'_>, + mut reply: Reply<'_, R>, + ) -> Result { + let Some(KeyWithAlg { alg, id }) = self.state.persistent.keys.asymetric_for_reference(key_ref) else { + warn!("Attempt to use unset key"); + return Err(Status::ConditionsOfUseNotSatisfied); + }; + + if alg != requested_alg { + warn!("Bad algorithm: {:?}", requested_alg); + return Err(Status::IncorrectP1OrP2Parameter); + } + let rsa_alg: RsaAlgorithms = alg.try_into().map_err(|_| { + warn!("Tried to perform agreement on a challenge with a non-rsa algorithm"); + Status::ConditionsOfUseNotSatisfied + })?; + + let response = try_syscall!(self.trussed.decrypt( + rsa_alg.mechanism(), + id, + data.as_slice_less_safe(), + &[], + &[], + &[] + )) + .map_err(|_err| { + warn!("Failed to decrypt challenge: {:?}", _err); + Status::IncorrectDataParameter + })? + .plaintext + .ok_or_else(|| { + warn!("Failed to decrypt challenge, no plaintext"); + Status::IncorrectDataParameter + })?; + reply.expand(&[0x82])?; + reply.append_len(response.len())?; + reply.expand(&response)?; + Ok(()) + } + pub fn sign_challenge( &mut self, requested_alg: Algorithms, diff --git a/src/piv_types.rs b/src/piv_types.rs index 36cd39a..2a1ae1f 100644 --- a/src/piv_types.rs +++ b/src/piv_types.rs @@ -19,6 +19,7 @@ macro_rules! enum_u8 { ) => { $(#[$outer])* #[repr(u8)] + #[derive(Clone, Copy)] $vis enum $name { $( $var = $num, @@ -42,6 +43,17 @@ macro_rules! enum_u8 { *self as u8 == *other } } + + impl + Copy> PartialEq for $name { + fn eq(&self, other: &T) -> bool { + let other: $name = (*other).into(); + matches!((self,other), $( + | ($name::$var, $name::$var) + )*) + } + } + + impl Eq for $name {} } } @@ -70,7 +82,7 @@ impl TryFrom<&[u8]> for Puk { } enum_u8! { - #[derive(Clone, Copy, Eq, PartialEq, Debug,Deserialize,Serialize)] + #[derive(Debug,Deserialize,Serialize)] // As additional reference, see: // https://globalplatform.org/wp-content/uploads/2014/03/GPC_ISO_Framework_v1.0.pdf#page=15 // @@ -109,7 +121,7 @@ enum_u8! { } crate::container::enum_subset! { - #[derive(Clone, Copy, Eq, PartialEq, Debug,Deserialize,Serialize)] + #[derive(Debug,Deserialize,Serialize)] pub enum AsymmetricAlgorithms: Algorithms { Rsa2048, Rsa4096, @@ -165,6 +177,43 @@ impl AsymmetricAlgorithms { } } +macro_rules! impl_use_try_into { + ($sup:ident => {$(($from:ident, $into:ident)),*}) => { + $( + impl TryFrom<$from> for $into { + type Error = iso7816::Status; + fn try_from(v: $from) -> core::result::Result<$into, iso7816::Status> { + let sup: $sup = v.into(); + sup.try_into() + } + } + )* + }; +} + +crate::container::enum_subset! { + #[derive(Debug,Deserialize,Serialize)] + pub enum RsaAlgorithms: Algorithms { + Rsa2048, + Rsa4096, + } +} + +impl RsaAlgorithms { + pub fn mechanism(self) -> Mechanism { + match self { + Self::Rsa2048 => Mechanism::Rsa2048Pkcs, + Self::Rsa4096 => Mechanism::Rsa4096Pkcs, + } + } +} + +impl_use_try_into!( + Algorithms => { + (AsymmetricAlgorithms, RsaAlgorithms) + } +); + /// TODO: #[derive(Clone, Copy, Default, Eq, PartialEq)] pub struct CryptographicAlgorithmTemplate<'a> { diff --git a/src/state.rs b/src/state.rs index 22591ec..7da22a4 100644 --- a/src/state.rs +++ b/src/state.rs @@ -38,7 +38,7 @@ pub enum TouchPolicy { } crate::container::enum_subset! { - #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] + #[derive(Debug, serde::Deserialize, serde::Serialize)] pub enum AdministrationAlgorithm: Algorithms { Tdes, Aes256