From 48604d165bda5ea0f5cf1d1a6327d887bca06768 Mon Sep 17 00:00:00 2001 From: shimun Date: Wed, 6 May 2020 23:47:33 +0200 Subject: [PATCH] response enum --- src/protocol/cbor.rs | 127 ++++++++++++++++++++++++++++++++++++++----- src/transport/mod.rs | 9 +++ 2 files changed, 122 insertions(+), 14 deletions(-) create mode 100644 src/transport/mod.rs diff --git a/src/protocol/cbor.rs b/src/protocol/cbor.rs index b314c0a..64aef35 100644 --- a/src/protocol/cbor.rs +++ b/src/protocol/cbor.rs @@ -2,7 +2,7 @@ use failure::_core::marker::PhantomData; use num_traits::{FromPrimitive, ToPrimitive}; use serde::de::{Error, MapAccess, Visitor}; -use serde::ser::{SerializeMap, SerializeStruct}; +use serde::ser::{SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant}; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use serde_cbor::de::IoRead; use serde_cbor::ser::IoWrite; @@ -28,13 +28,14 @@ pub trait CborDeserializable<'a>: Deserialize<'a> { impl<'a, T: Deserialize<'a>> CborDeserializable<'a> for T {} +#[allow(clippy::large_enum_variant)] #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] -pub enum CborRequests<'a> { +pub enum CborRequest<'a> { __Bump, // 0x01 - MakeCredential, + MakeCredential(MakeCredentialRequest<'a>), // 0x02 - GetAssertion, + GetAssertion(GetAssertionRequest<'a>), // 0x03 ___Unused, // 0x04 @@ -44,7 +45,7 @@ pub enum CborRequests<'a> { // 0x06 ClientPin(GetClientPinRequest<'a>), // 0x07 - Reset, + Reset(Reset), // 0x08 GetNextAssertion, // 0x09 @@ -57,21 +58,106 @@ pub enum CborRequests<'a> { Config, } -pub type GetInfoRequest = (); +macro_rules! map_response { + ($name:ident, $from:ty, $( $request:path, $response_struct:ty => $response:ident),* ) => { + #[derive(Clone, Debug, PartialEq)] + pub enum $name<'a> { + $( + $response($response_struct), + )* + } + + impl<'a> $name<'a> { + + /// Parse the response if one is to be expected + pub fn parse_response(request: &$from, source: &mut impl Read) -> Option> { + match request { + $( + $request{..} => Some(<$response_struct>::deserialize_cbor(source).map(Self::$response)), + )* + _ => None + } + } + } + + $( + impl<'a> std::convert::From<$response_struct> for $name<'a> { + fn from(resp: $response_struct) -> Self { + Self::$response(resp) + } + } + )* +}} + +map_response! { + CborResponse,CborRequest<'a>, + CborRequest::MakeCredential, MakeCredentialResponse<'a> => MakeCredential, + CborRequest::GetAssertion, GetAssertionResponse<'a> => GetAssertion, + CborRequest::GetInfo, GetInfoResponse<'a> => GetInfo, + CborRequest::ClientPin, GetClientPinResponse<'a> => ClientPin +} + +#[derive(Debug)] +pub struct CborCommand(pub T); + +/*impl CborCommand { + pub fn parse_response<'a, R: Deserialize<'a> + Debug + Sized>(message: &mut dyn Read) -> Result where Self: CborCommandResponse<'a, R> { + R::deserialize_cbor(message) + } +} + +pub trait CborCommandResponse<'a,R: Deserialize<'a> + Debug> {}*/ +pub trait CborCommandValue: Serialize + Debug { + fn value() -> u8; +} + +impl Serialize for CborCommand { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_newtype_variant("", T::value() as u32, "", &self.0) + } +} + +macro_rules! command_value { + ($request_:ty,$lifetime:tt,$x:expr) => { + impl<$lifetime> CborCommandValue for $request_ { + fn value() -> u8 { + $x + } + } + }; + ($request_:ty,$x:expr) => { + impl CborCommandValue for $request_ { + fn value() -> u8 { + $x + } + } + }; +} + +command_value!(MakeCredentialRequest<'a>,'a, 0x01); +command_value!(GetAssertionRequest<'a>,'a, 0x02); +command_value!(GetInfoRequest, 0x04); +command_value!(GetClientPinRequest<'a>, 'a, 0x06); +command_value!(Reset, 0x07); + +#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct GetInfoRequest; #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] -pub struct GetInfoResponse { +pub struct GetInfoResponse<'a> { /// Not used in protocol but required to bump the packed index to 0x01 // 0x00 #[serde(default)] __bump: (), // 0x01 - versions: Vec, + versions: Cow<'a, [Cow<'a, str>]>, // 0x02 - extensions: Vec, + extensions: Cow<'a, [Cow<'a, str>]>, // 0x03 - #[serde(with = "serde_bytes")] - aaguid: Vec, + aaguid: Cow<'a, serde_bytes::Bytes>, // 0x04 options: GetInfoOptions, // FIDO2.1 from here on therefore optional @@ -80,7 +166,7 @@ pub struct GetInfoResponse { max_msg_size: u64, // 0x06 #[serde(rename = "pinUvAuthProtocols")] - pin_auth_protocols: Vec, + pin_auth_protocols: Cow<'a, [u64]>, // 0x07 #[serde(rename = "maxCredentialCountInList", default)] max_credential_count: u64, @@ -89,10 +175,10 @@ pub struct GetInfoResponse { max_credential_id_len: u64, // 0x09 #[serde(default)] - transports: Vec, + transports: Cow<'a, [Cow<'a, str>]>, // 0x0A #[serde(default)] - algorithms: Vec, + algorithms: Cow<'a, [Cow<'a, PublicKeyCredentialParameters>]>, // 0x0B #[serde(rename = "maxAuthenticatorConfigLength", default)] max_config_len: u64, @@ -272,6 +358,7 @@ pub struct GetClientPinRequest<'a> { #[serde(rename = "pinHashEnc", default)] pin_hash_enc: Option>, } + #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] pub enum GetClientPinSubommand { /// Not used in protocol but required to bump the packed index to 0x01 @@ -790,6 +877,9 @@ pub struct GetAssertionResponse<'a> { credential_selected: Option, } +#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct Reset; + #[cfg(test)] mod test { @@ -826,6 +916,15 @@ mod test { assert_ok(GetInfoResponse::deserialize_cbor(&mut resp)); } + //#[test] + fn encode_command() { + let cmd = CborCommand(GetInfoRequest); + let mut buf = Vec::new(); + cmd.serialize_cbor(&mut buf).unwrap(); + dbg!(serde_cbor::value::Value::deserialize_cbor(&mut (&buf[..])).unwrap()); + panic!() + } + #[test] fn response_yubikey() { test_response(&[ diff --git a/src/transport/mod.rs b/src/transport/mod.rs new file mode 100644 index 0000000..7a709ec --- /dev/null +++ b/src/transport/mod.rs @@ -0,0 +1,9 @@ +use crate::protocol::{CborRequest, CborResponse}; + +pub trait CtapTransport { + type Error; + + fn cbor<'a>(&mut self, command: &CborRequest<'a>) -> Result; +} + +pub enum CtapCommand {}