From 9737a006e7e768c8c0b75ca1cc8887e6824861e6 Mon Sep 17 00:00:00 2001 From: shimun Date: Sat, 25 Apr 2020 21:05:30 +0200 Subject: [PATCH 1/8] working cbor deserialize --- Cargo.toml | 4 + src/protocol/cbor.rs | 615 +++++++++++++++++++++++++++++++++++++++++++ src/protocol/mod.rs | 3 + 3 files changed, 622 insertions(+) create mode 100644 src/protocol/cbor.rs create mode 100644 src/protocol/mod.rs diff --git a/Cargo.toml b/Cargo.toml index ce7a1c2..59d60af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,10 @@ rust-crypto = "0.2" csv-core = "0.1.6" derive_builder = "0.9.0" crossbeam = { version = "0.7.3", optional = true } +serde_derive = "1.0.106" +serde = "1.0.106" +serde_cbor = "0.11.1" +serde_bytes = "0.11.3" [dev-dependencies] crossbeam = "0.7.3" hex = "0.4.0" diff --git a/src/protocol/cbor.rs b/src/protocol/cbor.rs new file mode 100644 index 0000000..1b5c2da --- /dev/null +++ b/src/protocol/cbor.rs @@ -0,0 +1,615 @@ +use num_traits::{FromPrimitive, ToPrimitive}; +use serde::de::{Error, MapAccess, Visitor}; +use serde::ser::{SerializeMap, SerializeStruct}; +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; +use serde_cbor::de::IoRead; +use serde_cbor::ser::IoWrite; +use std::collections::{BTreeMap, HashMap}; +use std::fmt; +use std::fmt::Debug; +use std::io::{Read, Write}; + +#[deny(missing_debug_implementations)] + +pub trait CborSerializable: Serialize { + fn serialize_cbor(&self, sink: &mut impl Write) -> Result<(), serde_cbor::error::Error> { + self.serialize(&mut serde_cbor::Serializer::new(IoWrite::new(sink)).packed_format()) + } +} + +impl CborSerializable for T {} + +pub trait CborDeserializable<'a>: Deserialize<'a> { + fn deserialize_cbor(source: &mut impl Read) -> Result { + Self::deserialize(&mut serde_cbor::Deserializer::from_reader(source)) + } +} + +impl<'a, T: Deserialize<'a>> CborDeserializable<'a> for T {} + +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +pub enum CborRequests { + __Bump, + // 0x01 + MakeCredential, + // 0x02 + GetAssertion, + // 0x03 + ___Unused, + // 0x04 + GetInfo(GetInfoRequest), + // 0x05 + ____Unused, + // 0x06 + ClientPin, + // 0x07 + Reset, + // 0x08 + GetNextAssertion, + // 0x09 + BioEnrolment, + // 0x0A + CredentialManagement, + // 0x0B + Selection, + // 0x0C + Config, +} + +pub type GetInfoRequest = (); + +#[derive(Debug, Clone, Serialize, Deserialize,Eq, PartialEq )] +pub struct GetInfoResponse { + /// Not used in protocol but required to bump the packed index to 0x01 + // 0x00 + #[serde(default)] + __bump: (), + // 0x01 + versions: Vec, + // 0x02 + extensions: Vec, + // 0x03 + #[serde(with = "serde_bytes")] + aaguid: Vec, + // 0x04 + options: GetInfoOptions, + // FIDO2.1 from here on therefore optional + // 0x05 + #[serde(rename = "maxMsgSize", default)] + max_msg_size: u64, + // 0x06 + #[serde(rename = "pinUvAuthProtocols")] + pin_auth_protocols: Vec, + // 0x07 + #[serde(rename = "maxCredentialCountInList", default)] + max_credential_count: u64, + // 0x08 + #[serde(rename = "maxCredentialIdLength", default)] + max_credential_id_len: u64, + // 0x09 + #[serde(default)] + transports: Vec, + // 0x0A + #[serde(default)] + algorithms: Vec, + // 0x0B + #[serde(rename = "maxAuthenticatorConfigLength", default)] + max_config_len: u64, + // 0x0C + #[serde(rename = "defaultCredProtect", default)] + default_cred_protect: CredProtect, +} + +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +pub struct GetInfoOptions { + #[serde(default)] + plat: bool, + #[serde(default)] + rk: bool, + #[serde(default, rename = "clientPin")] + client_pin: Option, + #[serde(default)] + up: bool, + #[serde(default)] + uv: bool, + #[serde(default, rename = "uvToken")] + uv_token: bool, + #[serde(default)] + config: bool, +} + +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +pub struct PublicKeyCredentialParameters { + /// Not used in protocol but required to bump the packed index to 0x01 + // 0x00 + #[serde(default)] + __bump: (), + // 0x01 + #[serde(default, rename = "type")] + type_: PublicKeyCredentialType, + // 0x02 + alg: PublicKeyCredentialAlgorithm, +} + +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +pub enum PublicKeyCredentialType { + #[serde(rename = "public-key")] + PublicKey = 0, +} + +impl Default for PublicKeyCredentialType { + fn default() -> Self { + PublicKeyCredentialType::PublicKey + } +} + +#[derive(Debug, Clone, FromPrimitive, ToPrimitive, Eq, PartialEq)] +pub enum PublicKeyCredentialAlgorithm { + ES256 = -7, + // Unsupported + EdDSA = -8, + // Unsupported + ES384 = -35, + // Unsupported + ES512 = -36, + // Unsupported + PS256 = -37, + // Unsupported + PS384 = -38, + // Unsupported + PS512 = -39, + // Unsupported + RS256 = -257, + // Unsupported + //#[serde(other)] + Unknown = std::i32::MIN as isize, +} + +impl Default for PublicKeyCredentialAlgorithm { + fn default() -> Self { + PublicKeyCredentialAlgorithm::Unknown + } +} + +impl Serialize for PublicKeyCredentialAlgorithm { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_i32(self.to_i32().unwrap()) + } +} + +impl<'de> Deserialize<'de> for PublicKeyCredentialAlgorithm { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct AlgorithmVisitor; + impl<'de> Visitor<'de> for AlgorithmVisitor { + type Value = i32; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("an integer between -2^31 and 2^31") + } + + fn visit_i64(self, value: i64) -> Result + where + E: de::Error, + { + Ok(value as i32) + } + + fn visit_u64(self, value: u64) -> Result + where + E: de::Error, + { + Ok(value as i32) + } + } + Ok(PublicKeyCredentialAlgorithm::from_i32(dbg!( + deserializer.deserialize_i32(AlgorithmVisitor)? + )) + .unwrap_or_default()) //Not ideal + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +pub enum CredProtect { + VerificationOptional, + VerificationOptionalWithCredentialIDList, + VerificationRequired, +} + +impl Default for CredProtect { + fn default() -> Self { + CredProtect::VerificationOptional + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +pub struct GetClientPinRequest { + /// Not used in protocol but required to bump the packed index to 0x01 + // 0x00 + #[serde(default)] + __bump: (), + // 0x01 + #[serde(rename = "pinUvAuthProtocol")] + pin_auth_protocols: u64, + // 0x02 + #[serde(rename = "subCommand")] + sub_command: GetClientPinSubommand, + // 0x03 + #[serde(rename = "keyAgreement", default)] + key_agreement: Option, + // 0x04 + #[serde(rename = "pinUvAuthParam", with = "serde_bytes", default)] + pin_auth_param: Vec, + // 0x05 + #[serde(rename = "newPinEnc", with = "serde_bytes", default)] + new_pin_enc: Vec, + // 0x06 + #[serde(rename = "pinHashEnc", with = "serde_bytes", default)] + pin_hash_enc: Vec, +} +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +pub enum GetClientPinSubommand { + /// Not used in protocol but required to bump the packed index to 0x01 + // 0x00 + __bump, + // 0x01 + #[serde(rename = "getPINRetries")] + GetPINRetries, + // 0x02 + #[serde(rename = "getKeyAgreement")] + GetKeyAgreement, + // 0x03 + #[serde(rename = "setPIN")] + SetPIN, + // 0x04 + #[serde(rename = "changePIN")] + ChangePIN, + // 0x05 + #[serde(rename = "getPinUvAuthTokenUsingPin")] + GetPinUvAuthTokenUsingPin, + // 0x06 + #[serde(rename = "getPinUvAuthTokenUsingUv")] + GetPinUvAuthTokenUsingUv, + // 0x07 + #[serde(rename = "getUVRetries")] + GetUVRetries, +} + +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +pub struct GetClientPinResponse { + /// Not used in protocol but required to bump the packed index to 0x01 + // 0x00 + #[serde(default)] + __bump: (), + // 0x01 + #[serde(rename = "keyAgreement", default)] + key_agreement: Option, + // 0x02 + /// Encrypted pinUvAuthToken using sharedSecret used in subsequent authenticatorMakeCredential authenticatorGetAssertion operations + #[serde(rename = "pinUvAuthToken", with = "serde_bytes", default)] + pin_auth_token: Vec, + // 0x03 + /// Number of PIN attempts remaining before lockout. + #[serde(rename = "pinRetries", default)] + pin_retries: u8, + // 0x04 + /// Present and true if the authenticator requires a power cycle + /// before any future PIN operation, false if no power cycle + /// needed. If the field is omitted, no information is given about + /// whether a power cycle is needed or not. + #[serde(rename = "powerCycleState", default)] + requires_power_cycle: bool, + // 0x05 + /// Number of uv attempts remaining before lockout. + #[serde(rename = "uvRetries", default)] + uv_retries: u8, +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct CoseKey { + /// Not used in protocol but required to bump the packed index to 0x01 + // 0x00 + // #[serde(default)] + //__bump: (), + // 0x01 + //#[serde(rename = "type", default)] + type_: PublicKeyCredentialType, + // 0x02 + //#[serde(with = "serde_bytes", default)] + id: Vec, + // 0x03 + alg: PublicKeyCredentialAlgorithm, + parameters: BTreeMap, +} + +#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] +pub struct CoseKeyDescriptor { + /// Not used in protocol but required to bump the packed index to 0x01 + // 0x00 + #[serde(default)] + __bump: (), + #[serde(rename = "type")] + type_: PublicKeyCredentialType, + #[serde(with = "serde_bytes", default)] + id: Vec, + alg: PublicKeyCredentialAlgorithm, + parameters: BTreeMap, +} + +impl Serialize for CoseKey { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut s = serializer.serialize_map(Some(self.parameters.len() + 2 + (!self.id.is_empty()) as usize))?; + s.serialize_entry(&0x01, &self.type_); + if !self.id.is_empty() { + s.serialize_entry(&0x02, &self.id); + } + s.serialize_entry(&0x03, &self.alg); + for (k, v) in &self.parameters { + s.serialize_entry(k, v)?; + } + s.end() + } +} + +impl<'de> Deserialize<'de> for CoseKey { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + + enum Field { + Type, Alg, Id, Other(i32) + } + + impl<'de> Deserialize<'de> for Field { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct FieldVisitor; + + impl<'de> Visitor<'de> for FieldVisitor { + type Value = Field; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("type, alg, id or other") + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + match value { + "alg" => Ok(Field::Alg), + "type" => Ok(Field::Type), + "id" => Ok(Field::Id), + _ => Err(de::Error::unknown_field(value, &["type", "alg", "id"])), + } + } + fn visit_i64(self, value: i64) -> Result + where + E: de::Error, + { + match value { + 0x01 => Ok(Field::Type), + 0x02 => Ok(Field::Id), + 0x03=> Ok(Field::Alg), + other => Ok(Field::Other(other as i32)), + } + } + + fn visit_u64(self, value: u64) -> Result + where + E: de::Error, + { + let signed: i64 = value as i64; + self.visit_i64(signed) + } + } + + deserializer.deserialize_identifier(FieldVisitor) + } + } + + struct CoseKeyVisitor; + + impl<'de> Visitor<'de> for CoseKeyVisitor { + type Value = CoseKey; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("struct CoseKey") + } + + fn visit_map(self, mut map: V) -> Result + where + V: MapAccess<'de>, + { + let mut type_: Option = None; + let mut alg: Option = None; + let mut id: Option = None; + let mut parameters: BTreeMap = BTreeMap::new(); + while let Some(k) = map.next_key()? { + match k { + Field::Type => type_ = Some(map.next_value()?), + Field::Id => id = Some(map.next_value()?), + Field::Alg => alg = Some(map.next_value()?), + Field::Other(i) => {parameters.insert(i, map.next_value()?);} + }; + } + let type_ = type_.ok_or_else(|| serde::de::Error::missing_field("type"))?; + let id = id.map(|id| id.into_vec()).unwrap_or_else(|| vec![]); + let alg = alg.ok_or_else(|| serde::de::Error::missing_field("alg"))?; + Ok(CoseKey{ + type_, + id, + alg, + parameters, + }) + } + } + + deserializer.deserialize_map(CoseKeyVisitor) + } +} + +#[cfg(test)] +mod test { + + use super::*; + + fn assert_ok(res: Result) { + assert!(dbg!(res).is_ok()) + } + + mod info { + use super::*; + + fn test_response(mut resp: &[u8]) { + assert_ok(GetInfoResponse::deserialize_cbor(&mut resp)); + } + + #[test] + #[ignore] + fn response_yubikey() { + test_response(&[ + 170, 1, 131, 102, 85, 50, 70, 95, 86, 50, 104, 70, 73, 68, 79, 95, 50, 95, 48, 108, + 70, 73, 68, 79, 95, 50, 95, 49, 95, 80, 82, 69, 2, 130, 107, 99, 114, 101, 100, 80, + 114, 111, 116, 101, 99, 116, 107, 104, 109, 97, 99, 45, 115, 101, 99, 114, 101, + 116, 3, 80, 185, 44, 63, 154, 192, 20, 64, 86, 136, 127, 20, 10, 37, 1, 22, 59, 4, + 165, 98, 114, 107, 245, 98, 117, 112, 245, 100, 112, 108, 97, 116, 244, 105, 99, + 108, 105, 101, 110, 116, 80, 105, 110, 244, 117, 99, 114, 101, 100, 101, 110, 116, + 105, 97, 108, 77, 103, 109, 116, 80, 114, 101, 118, 105, 101, 119, 245, 5, 25, 4, + 176, 6, 129, 1, 7, 8, 8, 24, 128, 9, 129, 99, 117, 115, 98, 10, 130, 162, 99, 97, + 108, 103, 38, 100, 116, 121, 112, 101, 106, 112, 117, 98, 108, 105, 99, 45, 107, + 101, 121, 162, 99, 97, 108, 103, 39, 100, 116, 121, 112, 101, 106, 112, 117, 98, + 108, 105, 99, 45, 107, 101, 121, + ]); + } + + #[test] + #[ignore] + fn response_solokey() { + test_response(&[ + 168, 1, 131, 102, 85, 50, 70, 95, 86, 50, 104, 70, 73, 68, 79, 95, 50, 95, 48, 108, + 70, 73, 68, 79, 95, 50, 95, 49, 95, 80, 82, 69, 2, 130, 107, 104, 109, 97, 99, 45, + 115, 101, 99, 114, 101, 116, 107, 99, 114, 101, 100, 80, 114, 111, 116, 101, 99, + 116, 3, 80, 136, 118, 99, 27, 212, 160, 66, 127, 87, 115, 14, 199, 28, 158, 2, 121, + 4, 165, 98, 114, 107, 245, 98, 117, 112, 245, 100, 112, 108, 97, 116, 244, 104, 99, + 114, 101, 100, 77, 103, 109, 116, 245, 105, 99, 108, 105, 101, 110, 116, 80, 105, + 110, 244, 5, 25, 4, 176, 6, 129, 1, 7, 20, 8, 24, 128, + ]); + } + } + + mod client_pin { + use super::*; + + use crate::protocol::GetClientPinRequest; + + fn test_request(mut req: &[u8]) { + assert_ok(GetClientPinRequest::deserialize_cbor(&mut req)); + } + + fn test_response(mut resp: &[u8]) { + let res = GetClientPinResponse::deserialize_cbor(&mut resp).unwrap(); + let cose = res.key_agreement.unwrap(); + dbg!(cose.alg); + //assert_ne!(cose.alg, PublicKeyCredentialAlgorithm::Unknown); + /*let mut buf = Vec::new(); + cose.serialize_cbor(&mut buf).unwrap(); + let mut buf_slice = &buf[..]; + assert_eq!(cose, CoseKey::deserialize_cbor(&mut buf_slice).unwrap())*/ + } + + #[test] + fn req_key_agreement() { + test_request(&[162, 1, 1, 2, 2]) + } + + #[test] + fn cose_key_round() { + let key = CoseKey{ + type_: PublicKeyCredentialType::PublicKey, + id: vec![1u8, 3 ,4, 5], + alg: PublicKeyCredentialAlgorithm::PS512, + parameters: vec![(-3, serde_cbor::value::Value::Text("test".into()))].into_iter().collect() + }; + let mut buf = Vec::new(); + key.serialize_cbor(&mut buf).unwrap(); + let mut buf_slice = &buf[..]; + let key2 = CoseKey::deserialize_cbor(&mut buf_slice).unwrap(); + assert_eq!(key, key2); + } + + #[test] + #[ignore] + fn resp_key_agreement_yubikey() { + test_response(&[ + 161, 1, 165, 1, 2, 3, 56, 24, 32, 1, 33, 88, 32, 225, 230, 133, 231, 92, 119, 250, + 236, 61, 49, 83, 182, 138, 31, 226, 185, 149, 235, 126, 130, 32, 69, 185, 206, 169, + 0, 247, 169, 221, 63, 98, 253, 34, 88, 32, 61, 125, 120, 237, 105, 178, 59, 168, + 222, 89, 214, 218, 225, 237, 207, 166, 25, 51, 4, 85, 200, 172, 177, 121, 208, 241, + 75, 16, 68, 234, 86, 56, + ]); + } + + // SoloKey v1 fw 4.0.0 + #[test] + #[ignore] + fn resp_key_agreement_solokey() { + test_response(&[ + 161, 1, 165, 1, 2, 3, 56, 24, 32, 1, 33, 88, 32, 9, 161, 111, 235, 155, 109, 166, + 62, 78, 140, 241, 254, 59, 243, 234, 160, 36, 11, 234, 22, 156, 39, 195, 18, 114, + 187, 7, 51, 117, 33, 163, 39, 34, 88, 32, 122, 88, 3, 24, 219, 125, 161, 38, 156, + 117, 45, 246, 233, 246, 32, 124, 155, 33, 236, 78, 216, 17, 50, 134, 30, 80, 78, + 82, 247, 155, 238, 60, + ]) + } + + // Trezor Model T fw 2.3.0 + #[test] + #[ignore] + fn resp_key_agreement_trezor() { + test_response(&[ + 161, 1, 165, 1, 2, 3, 56, 24, 32, 1, 33, 88, 32, 164, 211, 77, 255, 139, 183, 45, + 143, 125, 177, 191, 110, 155, 139, 204, 44, 59, 191, 143, 107, 179, 129, 182, 9, + 254, 209, 183, 7, 135, 188, 110, 111, 34, 88, 32, 255, 220, 244, 80, 159, 87, 162, + 4, 52, 93, 145, 18, 189, 11, 246, 139, 99, 180, 132, 151, 79, 3, 220, 96, 108, 107, + 45, 138, 23, 64, 241, 27, + ]); + } + } + + //#[test] + fn vec_layout() { + #[derive(Debug, Clone, Serialize, Deserialize)] + struct VecLayout { + len: u8, + item1: String, + item2: String, + } + let vec = vec!["hello".to_string(), "world".into()]; + let mut native_bytes = Vec::new(); + vec.serialize(&mut serde_cbor::Serializer::new(&mut native_bytes).packed_format()) + .unwrap(); + let layout = VecLayout { + len: 2, + item1: "hello".into(), + item2: "world".into(), + }; + let mut layout_bytes = Vec::new(); + layout + .serialize(&mut serde_cbor::Serializer::new(&mut layout_bytes).packed_format()) + .unwrap(); + layout_bytes.push(0); + assert_eq!(dbg!(native_bytes), layout_bytes); + } +} diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs new file mode 100644 index 0000000..6baad76 --- /dev/null +++ b/src/protocol/mod.rs @@ -0,0 +1,3 @@ +mod cbor; + +pub use self::cbor::*; -- 2.49.0 From 33229a0b3c57dccdef77617cb7baf0d5b7b6b2db Mon Sep 17 00:00:00 2001 From: shimun Date: Sun, 26 Apr 2020 14:24:39 +0200 Subject: [PATCH 2/8] impl make credential --- src/protocol/cbor.rs | 460 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 395 insertions(+), 65 deletions(-) diff --git a/src/protocol/cbor.rs b/src/protocol/cbor.rs index 1b5c2da..d41203d 100644 --- a/src/protocol/cbor.rs +++ b/src/protocol/cbor.rs @@ -8,6 +8,8 @@ use std::collections::{BTreeMap, HashMap}; use std::fmt; use std::fmt::Debug; use std::io::{Read, Write}; +use std::borrow::Cow; +use failure::_core::marker::PhantomData; #[deny(missing_debug_implementations)] @@ -41,7 +43,7 @@ pub enum CborRequests { // 0x05 ____Unused, // 0x06 - ClientPin, + ClientPin(GetClientPinRequest), // 0x07 Reset, // 0x08 @@ -58,7 +60,7 @@ pub enum CborRequests { pub type GetInfoRequest = (); -#[derive(Debug, Clone, Serialize, Deserialize,Eq, PartialEq )] +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] pub struct GetInfoResponse { /// Not used in protocol but required to bump the packed index to 0x01 // 0x00 @@ -100,7 +102,7 @@ pub struct GetInfoResponse { default_cred_protect: CredProtect, } -#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] pub struct GetInfoOptions { #[serde(default)] plat: bool, @@ -118,7 +120,7 @@ pub struct GetInfoOptions { config: bool, } -#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] pub struct PublicKeyCredentialParameters { /// Not used in protocol but required to bump the packed index to 0x01 // 0x00 @@ -131,10 +133,13 @@ pub struct PublicKeyCredentialParameters { alg: PublicKeyCredentialAlgorithm, } -#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] pub enum PublicKeyCredentialType { #[serde(rename = "public-key")] PublicKey = 0, + NoTwo = 2, + #[serde(other)] + Other, } impl Default for PublicKeyCredentialType { @@ -143,11 +148,13 @@ impl Default for PublicKeyCredentialType { } } -#[derive(Debug, Clone, FromPrimitive, ToPrimitive, Eq, PartialEq)] +#[derive(Debug, Clone, FromPrimitive, ToPrimitive, Eq, PartialEq)] pub enum PublicKeyCredentialAlgorithm { ES256 = -7, // Unsupported EdDSA = -8, + // Compatibility reasons + ECDH_ES_HKDF_256 = -25, // Unsupported ES384 = -35, // Unsupported @@ -214,7 +221,7 @@ impl<'de> Deserialize<'de> for PublicKeyCredentialAlgorithm { } } -#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] pub enum CredProtect { VerificationOptional, VerificationOptionalWithCredentialIDList, @@ -227,7 +234,7 @@ impl Default for CredProtect { } } -#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] pub struct GetClientPinRequest { /// Not used in protocol but required to bump the packed index to 0x01 // 0x00 @@ -252,7 +259,7 @@ pub struct GetClientPinRequest { #[serde(rename = "pinHashEnc", with = "serde_bytes", default)] pin_hash_enc: Vec, } -#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] pub enum GetClientPinSubommand { /// Not used in protocol but required to bump the packed index to 0x01 // 0x00 @@ -280,7 +287,7 @@ pub enum GetClientPinSubommand { GetUVRetries, } -#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] pub struct GetClientPinResponse { /// Not used in protocol but required to bump the packed index to 0x01 // 0x00 @@ -312,32 +319,13 @@ pub struct GetClientPinResponse { #[derive(Debug, Clone, Eq, PartialEq)] pub struct CoseKey { - /// Not used in protocol but required to bump the packed index to 0x01 - // 0x00 - // #[serde(default)] - //__bump: (), // 0x01 - //#[serde(rename = "type", default)] type_: PublicKeyCredentialType, // 0x02 - //#[serde(with = "serde_bytes", default)] id: Vec, // 0x03 alg: PublicKeyCredentialAlgorithm, - parameters: BTreeMap, -} - -#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] -pub struct CoseKeyDescriptor { - /// Not used in protocol but required to bump the packed index to 0x01 - // 0x00 - #[serde(default)] - __bump: (), - #[serde(rename = "type")] - type_: PublicKeyCredentialType, - #[serde(with = "serde_bytes", default)] - id: Vec, - alg: PublicKeyCredentialAlgorithm, + // remainder parameters: BTreeMap, } @@ -346,7 +334,9 @@ impl Serialize for CoseKey { where S: Serializer, { - let mut s = serializer.serialize_map(Some(self.parameters.len() + 2 + (!self.id.is_empty()) as usize))?; + let mut s = serializer.serialize_map(Some( + self.parameters.len() + 2 + (!self.id.is_empty()) as usize, + ))?; s.serialize_entry(&0x01, &self.type_); if !self.id.is_empty() { s.serialize_entry(&0x02, &self.id); @@ -364,15 +354,17 @@ impl<'de> Deserialize<'de> for CoseKey { where D: Deserializer<'de>, { - enum Field { - Type, Alg, Id, Other(i32) + Type, + Alg, + Id, + Other(i32), } impl<'de> Deserialize<'de> for Field { fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, + where + D: Deserializer<'de>, { struct FieldVisitor; @@ -384,8 +376,8 @@ impl<'de> Deserialize<'de> for CoseKey { } fn visit_str(self, value: &str) -> Result - where - E: de::Error, + where + E: de::Error, { match value { "alg" => Ok(Field::Alg), @@ -394,24 +386,38 @@ impl<'de> Deserialize<'de> for CoseKey { _ => Err(de::Error::unknown_field(value, &["type", "alg", "id"])), } } + fn visit_bytes(self, value: &[u8]) -> Result + where + E: de::Error, + { + match value { + b"alg" => Ok(Field::Alg), + b"type" => Ok(Field::Type), + b"id" => Ok(Field::Id), + _ => Err(de::Error::unknown_field( + &format!("{:?}", value), + &["type", "alg", "id"], + )), + } + } fn visit_i64(self, value: i64) -> Result - where - E: de::Error, + where + E: de::Error, { match value { 0x01 => Ok(Field::Type), 0x02 => Ok(Field::Id), - 0x03=> Ok(Field::Alg), + 0x03 => Ok(Field::Alg), other => Ok(Field::Other(other as i32)), } } fn visit_u64(self, value: u64) -> Result - where - E: de::Error, + where + E: de::Error, { let signed: i64 = value as i64; - self.visit_i64(signed) + self.visit_i64(signed) } } @@ -438,16 +444,18 @@ impl<'de> Deserialize<'de> for CoseKey { let mut parameters: BTreeMap = BTreeMap::new(); while let Some(k) = map.next_key()? { match k { - Field::Type => type_ = Some(map.next_value()?), + Field::Type => type_ = Some(map.next_value()?), Field::Id => id = Some(map.next_value()?), Field::Alg => alg = Some(map.next_value()?), - Field::Other(i) => {parameters.insert(i, map.next_value()?);} + Field::Other(i) => { + parameters.insert(i, map.next_value()?); + } }; } let type_ = type_.ok_or_else(|| serde::de::Error::missing_field("type"))?; let id = id.map(|id| id.into_vec()).unwrap_or_else(|| vec![]); let alg = alg.ok_or_else(|| serde::de::Error::missing_field("alg"))?; - Ok(CoseKey{ + Ok(CoseKey { type_, id, alg, @@ -460,13 +468,246 @@ impl<'de> Deserialize<'de> for CoseKey { } } +#[derive(Debug, Default, Clone,Serialize, Deserialize, PartialEq, Eq)] +pub struct MakeCredentialRequest<'a> { + /// Not used in protocol but required to bump the packed index to 0x01 + // 0x00 + #[serde(default)] + __bump: (), + // 0x01 + #[serde(rename = "clientDataHash")] + client_data_hash: Cow<'a, serde_bytes::Bytes>, + // 0x02 + rp: PublicKeyCredentialRpEntity<'a>, + // 0x03 + user: PublicKeyCredentialUserEntity<'a>, + // 0x04 + #[serde(rename = "pubKeyCredParams")] + pubkey_cred_params: Cow<'a, [PublicKeyCredentialParameters]>, + // 0x05 + #[serde(rename = "excludeList", default)] + exclude_list: Cow<'a, [PublicKeyCredentialDescriptor<'a>]>, + // 0x06 + #[serde(default)] + extensions: BTreeMap, Cow<'a ,serde_cbor::value::Value>>, + #[serde(default)] + options: AuthenticatorOptions<'a>, + #[serde(rename = "pinUvAuthParam", default)] + pin_auth_param: Option>, + #[serde(rename = "pinUvAuthProtocol", default)] + pin_auth_protocol: Option, +} + +#[derive(Debug, Default, Clone, Deserialize, PartialEq, Eq)] +pub struct PublicKeyCredentialRpEntity<'a> { + id: Cow<'a ,str>, + #[serde(default)] + name: Option>, + #[serde(default)] + icon: Option>, +} + +// Ensure keys aren't packed but instead serialized as string +impl<'a> Serialize for PublicKeyCredentialRpEntity<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut s = serializer.serialize_map(Some(1 + self.name.is_some() as usize + self.icon.is_some() as usize))?; + for (k, v) in &[("id", Some(&self.id)), ("name", self.name.as_ref()), ("icon", self.icon.as_ref())] { + s.serialize_entry(k, v)?; + } + s.end() + } +} + +#[derive(Debug, Default, Clone, Deserialize, PartialEq, Eq)] +pub struct PublicKeyCredentialUserEntity<'a> { + id: Cow<'a ,[u8]>, + #[serde(default)] + name: Option>, + #[serde(default)] + icon: Option>, +} + +// Ensure keys aren't packed but instead serialized as string +impl<'a> Serialize for PublicKeyCredentialUserEntity<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut s = serializer.serialize_map(Some(1 + self.name.is_some() as usize + self.icon.is_some() as usize))?; + s.serialize_entry("id", &self.id)?; + for (k, v) in &[("name", self.name.as_ref()), ("icon", self.icon.as_ref())] { + s.serialize_entry(k, v)?; + } + s.end() + } +} + +#[derive(Debug, Default, Clone, Deserialize, PartialEq, Eq)] +pub struct PublicKeyCredentialDescriptor<'a> { + #[serde(rename = "type")] + type_: PublicKeyCredentialType, + id: Cow<'a ,[u8]>, +} + +// Ensure keys aren't packed but instead serialized as string +impl<'a> Serialize for PublicKeyCredentialDescriptor<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut s = serializer.serialize_map(Some(2))?; + s.serialize_entry("type", &self.type_)?; + s.serialize_entry("id", &self.id)?; + s.end() + } +} + +#[derive(Debug, Default, Clone, PartialEq, Eq)] +pub struct AuthenticatorOptions<'a> { + rk: bool, + uv: bool, + other: BTreeMap, bool>, +} + +impl<'a> Serialize for AuthenticatorOptions<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut s = serializer.serialize_map(Some( + self.other.len() + [self.rk, self.uv].iter().filter(|o| **o).count(), + ))?; + for (k, v) in &[("rk", self.rk), ("uv", self.uv)] { + if *v { + s.serialize_entry(k, v)?; + } + } + for (k, v) in &self.other { + s.serialize_entry(k, v)?; + } + s.end() + } +} + +impl<'a, 'de> Deserialize<'de> for AuthenticatorOptions<'a> { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + enum Field { + RK, + UV, + Other(String), + } + + impl<'de> Deserialize<'de> for Field { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct FieldVisitor; + + impl<'de> Visitor<'de> for FieldVisitor { + type Value = Field; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("type, alg, id or other") + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + match value { + "rk" => Ok(Field::RK), + "uv" => Ok(Field::UV), + other => Ok(Field::Other(other.into())), + } + } + fn visit_bytes(self, value: &[u8]) -> Result + where + E: de::Error, + { + match value { + b"rk" => Ok(Field::RK), + b"uv" => Ok(Field::UV), + _ => Err(de::Error::unknown_field( + &format!("{:?}", value), + &["rk", "uv"], + )), + } + } + } + + deserializer.deserialize_identifier(FieldVisitor) + } + } + + struct AuthenticatorOptionsVisitor; + + impl<'de> Visitor<'de> for AuthenticatorOptionsVisitor { + type Value = AuthenticatorOptions<'static>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("struct AuthenticatorOptions") + } + + fn visit_map(self, mut map: V) -> Result + where + V: MapAccess<'de>, + { + let mut options = AuthenticatorOptions::default(); + while let Some(k) = map.next_key()? { + match k { + Field::RK => options.rk = map.next_value()?, + Field::UV => options.uv = map.next_value()?, + Field::Other(i) => { + options.other.insert(Cow::Owned(i), map.next_value()?); + } + }; + } + Ok(options) + } + } + + deserializer.deserialize_map(AuthenticatorOptionsVisitor) + } +} + +#[derive(Debug, Default, Clone,Serialize, Deserialize, PartialEq, Eq)] +pub struct MakeCredentialResponse { + /// Not used in protocol but required to bump the packed index to 0x01 + // 0x00 + #[serde(default)] + __bump: (), + // 0x01 + fmt: String, + // 0x02 + #[serde(rename = "authData", with = "serde_bytes")] + auth_data: Vec, + // 0x03 + #[serde(rename = "attStmt")] + attestation_statement: BTreeMap, +} + #[cfg(test)] mod test { use super::*; fn assert_ok(res: Result) { - assert!(dbg!(res).is_ok()) + assert!(dbg!(res).is_ok()); + } + + fn encode_round<'a, T: Serialize + Deserialize<'a> + Debug + Eq>(val: T) { + let mut buf = Vec::new(); + val.serialize_cbor(&mut buf).unwrap(); + let mut buf_slice = &buf[..]; + let val2 = T::deserialize_cbor(&mut buf_slice).unwrap(); + assert_eq!(val, val2); } mod info { @@ -477,7 +718,6 @@ mod test { } #[test] - #[ignore] fn response_yubikey() { test_response(&[ 170, 1, 131, 102, 85, 50, 70, 95, 86, 50, 104, 70, 73, 68, 79, 95, 50, 95, 48, 108, @@ -495,7 +735,6 @@ mod test { } #[test] - #[ignore] fn response_solokey() { test_response(&[ 168, 1, 131, 102, 85, 50, 70, 95, 86, 50, 104, 70, 73, 68, 79, 95, 50, 95, 48, 108, @@ -520,8 +759,8 @@ mod test { fn test_response(mut resp: &[u8]) { let res = GetClientPinResponse::deserialize_cbor(&mut resp).unwrap(); - let cose = res.key_agreement.unwrap(); - dbg!(cose.alg); + //let cose = res.key_agreement; + //dbg!(cose.alg); //assert_ne!(cose.alg, PublicKeyCredentialAlgorithm::Unknown); /*let mut buf = Vec::new(); cose.serialize_cbor(&mut buf).unwrap(); @@ -534,23 +773,41 @@ mod test { test_request(&[162, 1, 1, 2, 2]) } - #[test] - fn cose_key_round() { - let key = CoseKey{ + fn cose_key() -> CoseKey { + CoseKey { type_: PublicKeyCredentialType::PublicKey, - id: vec![1u8, 3 ,4, 5], + id: vec![8, 3, 4, 5], alg: PublicKeyCredentialAlgorithm::PS512, - parameters: vec![(-3, serde_cbor::value::Value::Text("test".into()))].into_iter().collect() - }; - let mut buf = Vec::new(); - key.serialize_cbor(&mut buf).unwrap(); - let mut buf_slice = &buf[..]; - let key2 = CoseKey::deserialize_cbor(&mut buf_slice).unwrap(); - assert_eq!(key, key2); + parameters: vec![ + (-3, serde_cbor::value::Value::Text("test".into())), + ( + -42, + serde_cbor::value::Value::Bytes("test".as_bytes().to_vec()), + ), + ] + .into_iter() + .collect(), + } + } + + #[test] + fn cose_key_round() { + encode_round(cose_key()); + } + + #[test] + fn client_pin_resp_round() { + encode_round(GetClientPinResponse { + __bump: (), + key_agreement: Some(cose_key()), + pin_auth_token: vec![1, 2, 3, 4], + pin_retries: 3, + requires_power_cycle: true, + uv_retries: 1, + }); } #[test] - #[ignore] fn resp_key_agreement_yubikey() { test_response(&[ 161, 1, 165, 1, 2, 3, 56, 24, 32, 1, 33, 88, 32, 225, 230, 133, 231, 92, 119, 250, @@ -563,7 +820,6 @@ mod test { // SoloKey v1 fw 4.0.0 #[test] - #[ignore] fn resp_key_agreement_solokey() { test_response(&[ 161, 1, 165, 1, 2, 3, 56, 24, 32, 1, 33, 88, 32, 9, 161, 111, 235, 155, 109, 166, @@ -576,7 +832,6 @@ mod test { // Trezor Model T fw 2.3.0 #[test] - #[ignore] fn resp_key_agreement_trezor() { test_response(&[ 161, 1, 165, 1, 2, 3, 56, 24, 32, 1, 33, 88, 32, 164, 211, 77, 255, 139, 183, 45, @@ -587,6 +842,81 @@ mod test { ]); } } + + mod make_credential { + use super::*; + + fn rp_entity<'a>() -> PublicKeyCredentialRpEntity<'a> { + PublicKeyCredentialRpEntity { + id: Cow::Borrowed("test"), + name: Some("Test".into()), + icon: None + } + } + + fn user_entity<'a>() -> PublicKeyCredentialUserEntity<'a> { + PublicKeyCredentialUserEntity { + id: Cow::Borrowed(&[0u8]), + name: Some("Tester".into()), + icon: None + } + } + + fn pubkey_cred_desc<'a>() -> PublicKeyCredentialDescriptor<'a> { + PublicKeyCredentialDescriptor{ + type_: Default::default(), + id: Cow::Owned(vec![1, 2, 3, 4]) + } + } + + fn authenticator_options<'a>() -> AuthenticatorOptions<'a> { + AuthenticatorOptions{ + rk: false, + uv: true, + other: vec![(Cow::Borrowed("whatever the future may bring"), true)].into_iter().collect() + } + } + + fn make_credential_request<'a>() -> MakeCredentialRequest<'a> { + MakeCredentialRequest{ + __bump: (), + client_data_hash: Cow::Owned(serde_bytes::ByteBuf::from(vec![0u8; 32].into_boxed_slice())), + rp: rp_entity(), + user: user_entity(), + pubkey_cred_params: Cow::Owned(vec![PublicKeyCredentialParameters{ + __bump: (), + type_: Default::default(), + alg: PublicKeyCredentialAlgorithm::EdDSA + }]) , + exclude_list: Cow::Borrowed(&[]), + extensions: Default::default(), + options: authenticator_options(), + pin_auth_param: None, + pin_auth_protocol: None + } + } + + #[test] + fn rp_entity_round() { + encode_round(rp_entity()); + } + + #[test] + fn user_entity_round() { + encode_round(user_entity()); + } + + #[test] + fn pubkey_cred_desc_round() { + encode_round(pubkey_cred_desc()); + } + + #[test] + fn authenticator_options_round() { + encode_round(authenticator_options()); + } + + } //#[test] fn vec_layout() { -- 2.49.0 From 317f5ebdb4974ceb99f056804dce399175154c0d Mon Sep 17 00:00:00 2001 From: shimun Date: Sun, 26 Apr 2020 17:52:40 +0200 Subject: [PATCH 3/8] impl get assertion --- src/protocol/cbor.rs | 521 +++++++++++++++++++++++++++++++------------ 1 file changed, 382 insertions(+), 139 deletions(-) diff --git a/src/protocol/cbor.rs b/src/protocol/cbor.rs index d41203d..b314c0a 100644 --- a/src/protocol/cbor.rs +++ b/src/protocol/cbor.rs @@ -1,17 +1,16 @@ +#[deny(missing_debug_implementations, unused_results)] +use failure::_core::marker::PhantomData; use num_traits::{FromPrimitive, ToPrimitive}; use serde::de::{Error, MapAccess, Visitor}; use serde::ser::{SerializeMap, SerializeStruct}; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use serde_cbor::de::IoRead; use serde_cbor::ser::IoWrite; +use std::borrow::Cow; use std::collections::{BTreeMap, HashMap}; use std::fmt; use std::fmt::Debug; use std::io::{Read, Write}; -use std::borrow::Cow; -use failure::_core::marker::PhantomData; - -#[deny(missing_debug_implementations)] pub trait CborSerializable: Serialize { fn serialize_cbor(&self, sink: &mut impl Write) -> Result<(), serde_cbor::error::Error> { @@ -30,7 +29,7 @@ pub trait CborDeserializable<'a>: Deserialize<'a> { impl<'a, T: Deserialize<'a>> CborDeserializable<'a> for T {} #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] -pub enum CborRequests { +pub enum CborRequests<'a> { __Bump, // 0x01 MakeCredential, @@ -43,7 +42,7 @@ pub enum CborRequests { // 0x05 ____Unused, // 0x06 - ClientPin(GetClientPinRequest), + ClientPin(GetClientPinRequest<'a>), // 0x07 Reset, // 0x08 @@ -120,7 +119,7 @@ pub struct GetInfoOptions { config: bool, } -#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +#[derive(Debug, Clone, Deserialize, Eq, PartialEq)] pub struct PublicKeyCredentialParameters { /// Not used in protocol but required to bump the packed index to 0x01 // 0x00 @@ -133,6 +132,20 @@ pub struct PublicKeyCredentialParameters { alg: PublicKeyCredentialAlgorithm, } +// Ensure keys aren't packed but instead serialized as string +impl Serialize for PublicKeyCredentialParameters { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut s = serializer.serialize_map(Some(2))?; + s.serialize_entry("type", &self.type_)?; + s.serialize_entry("alg", &self.alg)?; + + s.end() + } +} + #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] pub enum PublicKeyCredentialType { #[serde(rename = "public-key")] @@ -235,7 +248,7 @@ impl Default for CredProtect { } #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] -pub struct GetClientPinRequest { +pub struct GetClientPinRequest<'a> { /// Not used in protocol but required to bump the packed index to 0x01 // 0x00 #[serde(default)] @@ -250,14 +263,14 @@ pub struct GetClientPinRequest { #[serde(rename = "keyAgreement", default)] key_agreement: Option, // 0x04 - #[serde(rename = "pinUvAuthParam", with = "serde_bytes", default)] - pin_auth_param: Vec, + #[serde(rename = "pinUvAuthParam", default)] + pin_auth_param: Option>, // 0x05 - #[serde(rename = "newPinEnc", with = "serde_bytes", default)] - new_pin_enc: Vec, + #[serde(rename = "newPinEnc", default)] + new_pin_enc: Option>, // 0x06 - #[serde(rename = "pinHashEnc", with = "serde_bytes", default)] - pin_hash_enc: Vec, + #[serde(rename = "pinHashEnc", default)] + pin_hash_enc: Option>, } #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] pub enum GetClientPinSubommand { @@ -288,18 +301,18 @@ pub enum GetClientPinSubommand { } #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] -pub struct GetClientPinResponse { +pub struct GetClientPinResponse<'a> { /// Not used in protocol but required to bump the packed index to 0x01 // 0x00 #[serde(default)] __bump: (), // 0x01 #[serde(rename = "keyAgreement", default)] - key_agreement: Option, + key_agreement: Option>, // 0x02 /// Encrypted pinUvAuthToken using sharedSecret used in subsequent authenticatorMakeCredential authenticatorGetAssertion operations - #[serde(rename = "pinUvAuthToken", with = "serde_bytes", default)] - pin_auth_token: Vec, + #[serde(rename = "pinUvAuthToken", default)] + pin_auth_token: Option>, // 0x03 /// Number of PIN attempts remaining before lockout. #[serde(rename = "pinRetries", default)] @@ -318,18 +331,18 @@ pub struct GetClientPinResponse { } #[derive(Debug, Clone, Eq, PartialEq)] -pub struct CoseKey { +pub struct CoseKey<'a> { // 0x01 type_: PublicKeyCredentialType, // 0x02 - id: Vec, + id: Cow<'a, serde_bytes::Bytes>, // 0x03 alg: PublicKeyCredentialAlgorithm, // remainder parameters: BTreeMap, } -impl Serialize for CoseKey { +impl<'a> Serialize for CoseKey<'a> { fn serialize(&self, serializer: S) -> Result where S: Serializer, @@ -337,11 +350,11 @@ impl Serialize for CoseKey { let mut s = serializer.serialize_map(Some( self.parameters.len() + 2 + (!self.id.is_empty()) as usize, ))?; - s.serialize_entry(&0x01, &self.type_); + s.serialize_entry(&0x01, &self.type_)?; if !self.id.is_empty() { - s.serialize_entry(&0x02, &self.id); + s.serialize_entry(&0x02, &self.id)?; } - s.serialize_entry(&0x03, &self.alg); + s.serialize_entry(&0x03, &self.alg)?; for (k, v) in &self.parameters { s.serialize_entry(k, v)?; } @@ -349,7 +362,7 @@ impl Serialize for CoseKey { } } -impl<'de> Deserialize<'de> for CoseKey { +impl<'a, 'de> Deserialize<'de> for CoseKey<'a> { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, @@ -425,10 +438,10 @@ impl<'de> Deserialize<'de> for CoseKey { } } - struct CoseKeyVisitor; + struct CoseKeyVisitor<'a>(PhantomData<&'a ()>); - impl<'de> Visitor<'de> for CoseKeyVisitor { - type Value = CoseKey; + impl<'a, 'de> Visitor<'de> for CoseKeyVisitor<'a> { + type Value = CoseKey<'a>; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("struct CoseKey") @@ -440,7 +453,7 @@ impl<'de> Deserialize<'de> for CoseKey { { let mut type_: Option = None; let mut alg: Option = None; - let mut id: Option = None; + let mut id: Option> = None; let mut parameters: BTreeMap = BTreeMap::new(); while let Some(k) = map.next_key()? { match k { @@ -453,7 +466,8 @@ impl<'de> Deserialize<'de> for CoseKey { }; } let type_ = type_.ok_or_else(|| serde::de::Error::missing_field("type"))?; - let id = id.map(|id| id.into_vec()).unwrap_or_else(|| vec![]); + let id = id.unwrap_or(Cow::Borrowed(serde_bytes::Bytes::new(&[]))); + //let id = id.map(|id| id.into_vec()).unwrap_or_else(|| vec![]); let alg = alg.ok_or_else(|| serde::de::Error::missing_field("alg"))?; Ok(CoseKey { type_, @@ -464,11 +478,11 @@ impl<'de> Deserialize<'de> for CoseKey { } } - deserializer.deserialize_map(CoseKeyVisitor) + deserializer.deserialize_map(CoseKeyVisitor(PhantomData)) } } -#[derive(Debug, Default, Clone,Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct MakeCredentialRequest<'a> { /// Not used in protocol but required to bump the packed index to 0x01 // 0x00 @@ -489,32 +503,38 @@ pub struct MakeCredentialRequest<'a> { exclude_list: Cow<'a, [PublicKeyCredentialDescriptor<'a>]>, // 0x06 #[serde(default)] - extensions: BTreeMap, Cow<'a ,serde_cbor::value::Value>>, + extensions: BTreeMap, Cow<'a, serde_cbor::value::Value>>, #[serde(default)] - options: AuthenticatorOptions<'a>, + options: Option>, #[serde(rename = "pinUvAuthParam", default)] - pin_auth_param: Option>, + pin_auth_param: Option>, #[serde(rename = "pinUvAuthProtocol", default)] pin_auth_protocol: Option, } #[derive(Debug, Default, Clone, Deserialize, PartialEq, Eq)] pub struct PublicKeyCredentialRpEntity<'a> { - id: Cow<'a ,str>, + id: Cow<'a, str>, #[serde(default)] - name: Option>, + name: Option>, #[serde(default)] - icon: Option>, + icon: Option>, } // Ensure keys aren't packed but instead serialized as string impl<'a> Serialize for PublicKeyCredentialRpEntity<'a> { fn serialize(&self, serializer: S) -> Result - where - S: Serializer, + where + S: Serializer, { - let mut s = serializer.serialize_map(Some(1 + self.name.is_some() as usize + self.icon.is_some() as usize))?; - for (k, v) in &[("id", Some(&self.id)), ("name", self.name.as_ref()), ("icon", self.icon.as_ref())] { + let mut s = serializer.serialize_map(Some( + 1 + self.name.is_some() as usize + self.icon.is_some() as usize, + ))?; + for (k, v) in &[ + ("id", Some(&self.id)), + ("name", self.name.as_ref()), + ("icon", self.icon.as_ref()), + ] { s.serialize_entry(k, v)?; } s.end() @@ -523,20 +543,22 @@ impl<'a> Serialize for PublicKeyCredentialRpEntity<'a> { #[derive(Debug, Default, Clone, Deserialize, PartialEq, Eq)] pub struct PublicKeyCredentialUserEntity<'a> { - id: Cow<'a ,[u8]>, + id: Cow<'a, serde_bytes::Bytes>, #[serde(default)] - name: Option>, + name: Option>, #[serde(default)] - icon: Option>, + icon: Option>, } // Ensure keys aren't packed but instead serialized as string impl<'a> Serialize for PublicKeyCredentialUserEntity<'a> { fn serialize(&self, serializer: S) -> Result - where - S: Serializer, + where + S: Serializer, { - let mut s = serializer.serialize_map(Some(1 + self.name.is_some() as usize + self.icon.is_some() as usize))?; + let mut s = serializer.serialize_map(Some( + 1 + self.name.is_some() as usize + self.icon.is_some() as usize, + ))?; s.serialize_entry("id", &self.id)?; for (k, v) in &[("name", self.name.as_ref()), ("icon", self.icon.as_ref())] { s.serialize_entry(k, v)?; @@ -549,14 +571,14 @@ impl<'a> Serialize for PublicKeyCredentialUserEntity<'a> { pub struct PublicKeyCredentialDescriptor<'a> { #[serde(rename = "type")] type_: PublicKeyCredentialType, - id: Cow<'a ,[u8]>, + id: Cow<'a, serde_bytes::Bytes>, } // Ensure keys aren't packed but instead serialized as string impl<'a> Serialize for PublicKeyCredentialDescriptor<'a> { fn serialize(&self, serializer: S) -> Result - where - S: Serializer, + where + S: Serializer, { let mut s = serializer.serialize_map(Some(2))?; s.serialize_entry("type", &self.type_)?; @@ -565,11 +587,56 @@ impl<'a> Serialize for PublicKeyCredentialDescriptor<'a> { } } -#[derive(Debug, Default, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct AuthenticatorOptions<'a> { - rk: bool, - uv: bool, - other: BTreeMap, bool>, + options: BTreeMap, bool>, +} + +impl AuthenticatorOptions<'_> { + pub fn credential(rk: bool, uv: bool) -> Self { + AuthenticatorOptions { + options: [("rk", rk), ("uv", uv)] + .to_vec() + .into_iter() + .filter(|(_, val)| *val) + .map(|(k, v)| (Cow::Borrowed(k), v)) + .collect(), + } + } + + pub fn assertion(up: bool, uv: bool) -> Self { + AuthenticatorOptions { + options: [("up", up), ("uv", uv)] + .to_vec() + .into_iter() + .filter(|(_, val)| *val) + .map(|(k, v)| (Cow::Borrowed(k), v)) + .collect(), + } + } + + /// Resident key + pub fn rk(&self) -> bool { + *self.options.get("rk").unwrap_or(&false) + } + + /// User verification + pub fn uv(&self) -> bool { + *self.options.get("uv").unwrap_or(&false) + } + + /// User presence + pub fn up(&self) -> bool { + *self.options.get("up").unwrap_or(&false) + } + + pub fn is_silent_auth(&self) -> bool { + !self.up() && !self.uv() + } + + pub fn is_physical_up(&self) -> bool { + self.up() && self.uv() + } } impl<'a> Serialize for AuthenticatorOptions<'a> { @@ -577,17 +644,13 @@ impl<'a> Serialize for AuthenticatorOptions<'a> { where S: Serializer, { - let mut s = serializer.serialize_map(Some( - self.other.len() + [self.rk, self.uv].iter().filter(|o| **o).count(), - ))?; - for (k, v) in &[("rk", self.rk), ("uv", self.uv)] { + let mut s = + serializer.serialize_map(Some(self.options.iter().filter(|(_, val)| **val).count()))?; + for (k, v) in &self.options { if *v { s.serialize_entry(k, v)?; } } - for (k, v) in &self.other { - s.serialize_entry(k, v)?; - } s.end() } } @@ -598,8 +661,6 @@ impl<'a, 'de> Deserialize<'de> for AuthenticatorOptions<'a> { D: Deserializer<'de>, { enum Field { - RK, - UV, Other(String), } @@ -614,7 +675,7 @@ impl<'a, 'de> Deserialize<'de> for AuthenticatorOptions<'a> { type Value = Field; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("type, alg, id or other") + formatter.write_str("enum AuthenticatorOptions") } fn visit_str(self, value: &str) -> Result @@ -622,34 +683,19 @@ impl<'a, 'de> Deserialize<'de> for AuthenticatorOptions<'a> { E: de::Error, { match value { - "rk" => Ok(Field::RK), - "uv" => Ok(Field::UV), other => Ok(Field::Other(other.into())), } } - fn visit_bytes(self, value: &[u8]) -> Result - where - E: de::Error, - { - match value { - b"rk" => Ok(Field::RK), - b"uv" => Ok(Field::UV), - _ => Err(de::Error::unknown_field( - &format!("{:?}", value), - &["rk", "uv"], - )), - } - } } deserializer.deserialize_identifier(FieldVisitor) } } - struct AuthenticatorOptionsVisitor; + struct AuthenticatorOptionsVisitor<'a>(PhantomData<&'a ()>); - impl<'de> Visitor<'de> for AuthenticatorOptionsVisitor { - type Value = AuthenticatorOptions<'static>; + impl<'a, 'de> Visitor<'de> for AuthenticatorOptionsVisitor<'a> { + type Value = AuthenticatorOptions<'a>; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("struct AuthenticatorOptions") @@ -659,40 +705,91 @@ impl<'a, 'de> Deserialize<'de> for AuthenticatorOptions<'a> { where V: MapAccess<'de>, { - let mut options = AuthenticatorOptions::default(); + let mut options = BTreeMap::, bool>::new(); while let Some(k) = map.next_key()? { match k { - Field::RK => options.rk = map.next_value()?, - Field::UV => options.uv = map.next_value()?, Field::Other(i) => { - options.other.insert(Cow::Owned(i), map.next_value()?); + options.insert(Cow::Owned(i), map.next_value()?); } }; } - Ok(options) + Ok(AuthenticatorOptions { options }) } } - deserializer.deserialize_map(AuthenticatorOptionsVisitor) + deserializer.deserialize_map(AuthenticatorOptionsVisitor::<'a>(PhantomData)) } } -#[derive(Debug, Default, Clone,Serialize, Deserialize, PartialEq, Eq)] -pub struct MakeCredentialResponse { +#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct MakeCredentialResponse<'a> { /// Not used in protocol but required to bump the packed index to 0x01 // 0x00 #[serde(default)] __bump: (), // 0x01 - fmt: String, + fmt: Cow<'a, str>, // 0x02 - #[serde(rename = "authData", with = "serde_bytes")] - auth_data: Vec, + #[serde(rename = "authData")] + auth_data: Cow<'a, serde_bytes::Bytes>, // 0x03 #[serde(rename = "attStmt")] attestation_statement: BTreeMap, } +#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct GetAssertionRequest<'a> { + /// Not used in protocol but required to bump the packed index to 0x01 + // 0x00 + #[serde(default)] + __bump: (), + // 0x01 + #[serde(rename = "rpId")] + rp_id: Cow<'a, str>, + // 0x02 + #[serde(rename = "clientDataHash")] + client_data_hash: Cow<'a, serde_bytes::Bytes>, + // 0x03 + #[serde(rename = "allowList", default)] + allow_list: Cow<'a, [PublicKeyCredentialDescriptor<'a>]>, + // 0x04 + #[serde(default)] + extensions: BTreeMap, Cow<'a, serde_cbor::value::Value>>, + // 0x05 + #[serde(default)] + options: Option>, + // 0x06 + #[serde(rename = "pinUvAuthParam", default)] + pin_auth_param: Option>, + // 0x07 + #[serde(rename = "pinUvAuthProtocol", default)] + pin_auth_protocol: Option, +} + +#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct GetAssertionResponse<'a> { + /// Not used in protocol but required to bump the packed index to 0x01 + // 0x00 + #[serde(default)] + __bump: (), + // 0x01 + credential: PublicKeyCredentialDescriptor<'a>, + // 0x02 + #[serde(rename = "authData")] + auth_data: Cow<'a, serde_bytes::Bytes>, + // 0x03 + signature: Cow<'a, serde_bytes::Bytes>, + // 0x04 + /// User identifiable information (name, DisplayName, icon) MUST not be returned if user verification is not done by the authenticator. + #[serde(default)] + user: Option>, + // 0x05 + #[serde(rename = "numberOfCredentials", default)] + credential_count: Option, + #[serde(rename = "userSelected", default)] + credential_selected: Option, +} + #[cfg(test)] mod test { @@ -702,11 +799,23 @@ mod test { assert!(dbg!(res).is_ok()); } + fn assert_decodes<'a, T: Debug + Deserialize<'a>>(mut bytes: &[u8]) -> T { + let mut cbor = bytes; + match T::deserialize_cbor(&mut bytes) { + e @ Err(_) => { + dbg!(serde_cbor::value::Value::deserialize_cbor(&mut cbor)); + dbg!(e).unwrap(); + unreachable!() + } + Ok(ok) => return ok, + } + } + fn encode_round<'a, T: Serialize + Deserialize<'a> + Debug + Eq>(val: T) { let mut buf = Vec::new(); val.serialize_cbor(&mut buf).unwrap(); let mut buf_slice = &buf[..]; - let val2 = T::deserialize_cbor(&mut buf_slice).unwrap(); + let val2 = assert_decodes::(&mut buf_slice); assert_eq!(val, val2); } @@ -773,10 +882,10 @@ mod test { test_request(&[162, 1, 1, 2, 2]) } - fn cose_key() -> CoseKey { + fn cose_key<'a>() -> CoseKey<'a> { CoseKey { type_: PublicKeyCredentialType::PublicKey, - id: vec![8, 3, 4, 5], + id: Cow::Owned(serde_bytes::ByteBuf::from(vec![8, 3, 4, 5])), alg: PublicKeyCredentialAlgorithm::PS512, parameters: vec![ (-3, serde_cbor::value::Value::Text("test".into())), @@ -800,7 +909,7 @@ mod test { encode_round(GetClientPinResponse { __bump: (), key_agreement: Some(cose_key()), - pin_auth_token: vec![1, 2, 3, 4], + pin_auth_token: Some(Cow::Owned(serde_bytes::ByteBuf::from(vec![8, 3, 4, 5]))), pin_retries: 3, requires_power_cycle: true, uv_retries: 1, @@ -842,7 +951,7 @@ mod test { ]); } } - + mod make_credential { use super::*; @@ -850,49 +959,48 @@ mod test { PublicKeyCredentialRpEntity { id: Cow::Borrowed("test"), name: Some("Test".into()), - icon: None + icon: None, } } fn user_entity<'a>() -> PublicKeyCredentialUserEntity<'a> { PublicKeyCredentialUserEntity { - id: Cow::Borrowed(&[0u8]), + id: Cow::Borrowed(serde_bytes::Bytes::new(&[0u8])), name: Some("Tester".into()), - icon: None + icon: None, } } fn pubkey_cred_desc<'a>() -> PublicKeyCredentialDescriptor<'a> { - PublicKeyCredentialDescriptor{ + PublicKeyCredentialDescriptor { type_: Default::default(), - id: Cow::Owned(vec![1, 2, 3, 4]) + id: Cow::Owned(serde_bytes::ByteBuf::from(vec![1, 2, 3, 4])), } } fn authenticator_options<'a>() -> AuthenticatorOptions<'a> { - AuthenticatorOptions{ - rk: false, - uv: true, - other: vec![(Cow::Borrowed("whatever the future may bring"), true)].into_iter().collect() - } + AuthenticatorOptions::credential(false, true) } fn make_credential_request<'a>() -> MakeCredentialRequest<'a> { - MakeCredentialRequest{ + MakeCredentialRequest { __bump: (), - client_data_hash: Cow::Owned(serde_bytes::ByteBuf::from(vec![0u8; 32].into_boxed_slice())), + client_data_hash: Cow::Owned(serde_bytes::ByteBuf::from( + vec![0u8; 32].into_boxed_slice(), + )), + //client_data_hash: Cow::Owned(vec![0u8; 32]), rp: rp_entity(), user: user_entity(), - pubkey_cred_params: Cow::Owned(vec![PublicKeyCredentialParameters{ + pubkey_cred_params: Cow::Owned(vec![PublicKeyCredentialParameters { __bump: (), type_: Default::default(), - alg: PublicKeyCredentialAlgorithm::EdDSA - }]) , + alg: PublicKeyCredentialAlgorithm::EdDSA, + }]), exclude_list: Cow::Borrowed(&[]), extensions: Default::default(), - options: authenticator_options(), + options: Some(authenticator_options()), pin_auth_param: None, - pin_auth_protocol: None + pin_auth_protocol: None, } } @@ -915,31 +1023,166 @@ mod test { fn authenticator_options_round() { encode_round(authenticator_options()); } - + + #[test] + fn make_credential_request_round() { + encode_round(make_credential_request()); + } + + #[test] + fn test_make_credential_request() { + assert_decodes::>(&[ + 166, 1, 88, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 162, 98, 105, 100, 105, 99, 116, 97, 112, 95, 100, + 101, 109, 111, 100, 110, 97, 109, 101, 111, 99, 116, 97, 112, 95, 104, 109, 97, 99, + 32, 99, 114, 97, 116, 101, 3, 162, 98, 105, 100, 65, 0, 100, 110, 97, 109, 101, + 103, 101, 120, 97, 109, 112, 108, 101, 4, 129, 162, 99, 97, 108, 103, 38, 100, 116, + 121, 112, 101, 106, 112, 117, 98, 108, 105, 99, 45, 107, 101, 121, 6, 161, 107, + 104, 109, 97, 99, 45, 115, 101, 99, 114, 101, 116, 245, 7, 161, 98, 114, 107, 245, + ]); + } + + #[test] + fn test_make_credential_response_yubikey() { + assert_decodes::>(&[ + 163, 1, 102, 112, 97, 99, 107, 101, 100, 2, 88, 162, 171, 147, 255, 145, 49, 113, + 135, 145, 80, 61, 246, 246, 160, 31, 189, 65, 0, 129, 154, 207, 25, 28, 121, 3, + 211, 79, 224, 82, 111, 17, 229, 163, 193, 0, 0, 0, 1, 185, 44, 63, 154, 192, 20, + 64, 86, 136, 127, 20, 10, 37, 1, 22, 59, 0, 16, 56, 155, 5, 107, 163, 252, 17, 198, + 217, 36, 74, 82, 82, 163, 168, 238, 165, 1, 2, 3, 38, 32, 1, 33, 88, 32, 246, 69, + 134, 25, 161, 218, 253, 105, 245, 206, 238, 87, 169, 160, 244, 100, 85, 68, 186, + 94, 20, 17, 74, 164, 37, 197, 153, 114, 27, 77, 12, 71, 34, 88, 32, 147, 225, 251, + 119, 11, 173, 253, 130, 178, 46, 109, 204, 252, 135, 33, 39, 43, 40, 108, 72, 208, + 254, 126, 40, 22, 146, 46, 193, 33, 148, 202, 33, 161, 107, 104, 109, 97, 99, 45, + 115, 101, 99, 114, 101, 116, 245, 3, 163, 99, 97, 108, 103, 38, 99, 115, 105, 103, + 88, 70, 48, 68, 2, 32, 84, 4, 232, 22, 111, 249, 130, 31, 242, 175, 108, 239, 65, + 144, 88, 177, 197, 158, 176, 10, 62, 240, 108, 24, 70, 236, 240, 32, 84, 142, 84, + 148, 2, 32, 48, 47, 250, 108, 99, 157, 93, 204, 51, 124, 180, 62, 0, 114, 44, 247, + 62, 255, 173, 153, 226, 228, 246, 153, 106, 212, 125, 151, 216, 119, 6, 51, 99, + 120, 53, 99, 129, 89, 2, 194, 48, 130, 2, 190, 48, 130, 1, 166, 160, 3, 2, 1, 2, 2, + 4, 64, 2, 121, 168, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 11, 5, 0, 48, + 46, 49, 44, 48, 42, 6, 3, 85, 4, 3, 19, 35, 89, 117, 98, 105, 99, 111, 32, 85, 50, + 70, 32, 82, 111, 111, 116, 32, 67, 65, 32, 83, 101, 114, 105, 97, 108, 32, 52, 53, + 55, 50, 48, 48, 54, 51, 49, 48, 32, 23, 13, 49, 52, 48, 56, 48, 49, 48, 48, 48, 48, + 48, 48, 90, 24, 15, 50, 48, 53, 48, 48, 57, 48, 52, 48, 48, 48, 48, 48, 48, 90, 48, + 111, 49, 11, 48, 9, 6, 3, 85, 4, 6, 19, 2, 83, 69, 49, 18, 48, 16, 6, 3, 85, 4, 10, + 12, 9, 89, 117, 98, 105, 99, 111, 32, 65, 66, 49, 34, 48, 32, 6, 3, 85, 4, 11, 12, + 25, 65, 117, 116, 104, 101, 110, 116, 105, 99, 97, 116, 111, 114, 32, 65, 116, 116, + 101, 115, 116, 97, 116, 105, 111, 110, 49, 40, 48, 38, 6, 3, 85, 4, 3, 12, 31, 89, + 117, 98, 105, 99, 111, 32, 85, 50, 70, 32, 69, 69, 32, 83, 101, 114, 105, 97, 108, + 32, 49, 48, 55, 51, 57, 48, 52, 48, 52, 48, 48, 89, 48, 19, 6, 7, 42, 134, 72, 206, + 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 3, 66, 0, 4, 92, 183, 14, 166, 108, + 19, 242, 191, 224, 255, 147, 132, 208, 179, 67, 32, 72, 205, 160, 182, 251, 135, + 80, 27, 114, 155, 230, 205, 77, 104, 6, 173, 224, 82, 102, 118, 173, 253, 212, 111, + 46, 81, 100, 79, 18, 119, 101, 79, 42, 240, 208, 143, 150, 204, 85, 59, 181, 241, + 69, 21, 191, 190, 157, 208, 163, 108, 48, 106, 48, 34, 6, 9, 43, 6, 1, 4, 1, 130, + 196, 10, 2, 4, 21, 49, 46, 51, 46, 54, 46, 49, 46, 52, 46, 49, 46, 52, 49, 52, 56, + 50, 46, 49, 46, 49, 48, 19, 6, 11, 43, 6, 1, 4, 1, 130, 229, 28, 2, 1, 1, 4, 4, 3, + 2, 5, 32, 48, 33, 6, 11, 43, 6, 1, 4, 1, 130, 229, 28, 1, 1, 4, 4, 18, 4, 16, 185, + 44, 63, 154, 192, 20, 64, 86, 136, 127, 20, 10, 37, 1, 22, 59, 48, 12, 6, 3, 85, + 29, 19, 1, 1, 255, 4, 2, 48, 0, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 11, + 5, 0, 3, 130, 1, 1, 0, 178, 135, 228, 70, 6, 168, 222, 188, 192, 19, 50, 245, 198, + 202, 144, 206, 49, 48, 38, 11, 143, 119, 70, 32, 95, 26, 212, 142, 183, 131, 163, + 27, 203, 145, 6, 133, 148, 202, 215, 181, 164, 219, 166, 56, 19, 126, 137, 211, 62, + 42, 127, 182, 177, 112, 53, 114, 251, 8, 40, 44, 156, 108, 20, 26, 216, 239, 65, + 215, 196, 174, 225, 140, 84, 245, 139, 253, 241, 199, 255, 223, 122, 146, 16, 202, + 58, 191, 10, 86, 249, 39, 227, 85, 188, 164, 114, 179, 123, 51, 166, 237, 90, 251, + 177, 194, 198, 16, 48, 142, 90, 179, 172, 38, 187, 61, 67, 206, 255, 19, 62, 224, + 23, 181, 128, 70, 157, 53, 230, 151, 80, 242, 72, 66, 230, 96, 23, 121, 13, 115, + 110, 4, 235, 191, 239, 228, 182, 36, 77, 28, 113, 29, 179, 14, 249, 48, 148, 234, + 149, 229, 125, 159, 77, 99, 29, 84, 158, 243, 239, 9, 238, 91, 25, 196, 23, 194, + 78, 205, 63, 185, 139, 80, 252, 42, 181, 125, 218, 103, 220, 157, 38, 122, 65, 173, + 118, 102, 181, 9, 90, 248, 34, 84, 4, 210, 153, 44, 123, 218, 167, 60, 78, 214, 10, + 198, 118, 78, 12, 99, 149, 216, 51, 133, 4, 255, 149, 205, 33, 212, 170, 0, 51, 73, + 130, 174, 184, 115, 38, 198, 190, 19, 50, 241, 59, 18, 174, 164, 223, 232, 233, + 214, 248, 221, 184, 56, 3, 149, 249, 136, + ]); + } + + //#[test] + fn test_make_credential_requestt() { + let mut buf = Vec::new(); + make_credential_request().serialize_cbor(&mut buf); + dbg!(serde_cbor::value::Value::deserialize_cbor(&mut (&buf[..]))); + panic!() + } } - //#[test] - fn vec_layout() { - #[derive(Debug, Clone, Serialize, Deserialize)] - struct VecLayout { - len: u8, - item1: String, - item2: String, + mod get_assertion { + use super::*; + + fn get_assertion_request<'a>() -> GetAssertionRequest<'a> { + GetAssertionRequest { + __bump: (), + rp_id: Cow::from("test"), + client_data_hash: Cow::Borrowed(serde_bytes::Bytes::new(&[0u8; 32])), + allow_list: Default::default(), + extensions: Default::default(), + options: Some(AuthenticatorOptions::assertion(true, true)), + pin_auth_param: None, + pin_auth_protocol: None, + } + } + + #[test] + fn get_assertion_request_round() { + encode_round(get_assertion_request()); + } + + #[test] + fn test_get_assertion_request() { + assert_decodes::>(&[ + 164, 1, 105, 99, 116, 97, 112, 95, 100, 101, 109, 111, 2, 88, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, + 129, 162, 98, 105, 100, 80, 188, 116, 69, 227, 50, 124, 140, 13, 30, 132, 51, 122, + 137, 240, 91, 67, 100, 116, 121, 112, 101, 106, 112, 117, 98, 108, 105, 99, 45, + 107, 101, 121, 4, 161, 107, 104, 109, 97, 99, 45, 115, 101, 99, 114, 101, 116, 163, + 1, 164, 32, 1, 33, 88, 32, 119, 92, 235, 161, 245, 203, 239, 97, 4, 180, 198, 43, + 156, 66, 208, 88, 40, 254, 162, 90, 152, 46, 153, 212, 147, 117, 76, 46, 193, 231, + 4, 129, 34, 88, 32, 227, 183, 102, 71, 152, 54, 189, 245, 231, 140, 17, 239, 248, + 139, 194, 225, 94, 86, 39, 203, 242, 183, 41, 143, 108, 65, 243, 121, 181, 253, 40, + 226, 1, 2, 2, 88, 32, 242, 81, 144, 103, 36, 192, 242, 17, 225, 70, 80, 167, 100, + 158, 79, 232, 146, 127, 211, 190, 229, 47, 211, 8, 189, 170, 216, 129, 226, 255, + 216, 76, 3, 80, 153, 134, 155, 103, 213, 108, 13, 108, 122, 96, 197, 205, 186, 193, + 20, 37, + ]); + } + + #[test] + fn test_get_assertion_response_yubikey() { + assert_decodes::>(&[ + 164, 1, 162, 98, 105, 100, 80, 14, 2, 90, 14, 176, 127, 23, 26, 112, 189, 7, 253, + 218, 142, 157, 153, 100, 116, 121, 112, 101, 106, 112, 117, 98, 108, 105, 99, 45, + 107, 101, 121, 2, 88, 84, 171, 147, 255, 145, 49, 113, 135, 145, 80, 61, 246, 246, + 160, 31, 189, 65, 0, 129, 154, 207, 25, 28, 121, 3, 211, 79, 224, 82, 111, 17, 229, + 163, 129, 0, 0, 0, 7, 161, 107, 104, 109, 97, 99, 45, 115, 101, 99, 114, 101, 116, + 88, 32, 223, 41, 90, 109, 42, 82, 208, 6, 206, 171, 212, 226, 224, 237, 130, 9, 52, + 59, 28, 158, 133, 142, 189, 70, 175, 16, 189, 130, 15, 251, 139, 75, 3, 88, 71, 48, + 69, 2, 33, 0, 176, 239, 65, 249, 233, 130, 83, 116, 67, 49, 5, 215, 3, 172, 65, + 176, 179, 91, 198, 165, 224, 84, 82, 229, 230, 24, 215, 98, 219, 48, 28, 240, 2, + 32, 60, 46, 255, 221, 206, 172, 31, 249, 85, 244, 133, 71, 125, 94, 0, 91, 50, 224, + 43, 30, 113, 145, 254, 193, 198, 82, 240, 172, 32, 253, 156, 90, 4, 161, 98, 105, + 100, 65, 0, + ]); + } + #[test] + fn test_get_assertion_response_solokey() { + assert_decodes::>(&[ + 163, 1, 162, 98, 105, 100, 88, 70, 178, 60, 31, 172, 224, 56, 238, 158, 64, 8, 252, + 234, 231, 234, 32, 67, 42, 251, 118, 141, 152, 72, 220, 19, 42, 24, 149, 131, 4, + 78, 50, 46, 88, 99, 171, 147, 255, 145, 49, 113, 135, 145, 80, 61, 246, 246, 160, + 31, 189, 65, 0, 129, 154, 207, 25, 28, 121, 3, 211, 79, 224, 82, 111, 17, 229, 163, + 241, 64, 0, 0, 100, 116, 121, 112, 101, 106, 112, 117, 98, 108, 105, 99, 45, 107, + 101, 121, 2, 88, 84, 171, 147, 255, 145, 49, 113, 135, 145, 80, 61, 246, 246, 160, + 31, 189, 65, 0, 129, 154, 207, 25, 28, 121, 3, 211, 79, 224, 82, 111, 17, 229, 163, + 129, 0, 0, 64, 254, 161, 107, 104, 109, 97, 99, 45, 115, 101, 99, 114, 101, 116, + 88, 32, 108, 143, 197, 14, 100, 162, 70, 237, 198, 31, 36, 164, 199, 214, 252, 153, + 25, 20, 137, 18, 158, 15, 158, 132, 30, 104, 148, 80, 11, 15, 104, 36, 3, 88, 70, + 48, 68, 2, 33, 0, 169, 159, 135, 26, 0, 124, 121, 110, 127, 51, 238, 77, 27, 168, + 200, 106, 177, 163, 145, 8, 242, 81, 20, 22, 1, 110, 133, 249, 228, 173, 14, 55, 2, + 31, 77, 250, 13, 108, 46, 220, 50, 62, 166, 232, 158, 158, 36, 157, 115, 46, 209, + 141, 201, 105, 120, 98, 160, 127, 55, 19, 69, 102, 13, 76, 95, + ]); } - let vec = vec!["hello".to_string(), "world".into()]; - let mut native_bytes = Vec::new(); - vec.serialize(&mut serde_cbor::Serializer::new(&mut native_bytes).packed_format()) - .unwrap(); - let layout = VecLayout { - len: 2, - item1: "hello".into(), - item2: "world".into(), - }; - let mut layout_bytes = Vec::new(); - layout - .serialize(&mut serde_cbor::Serializer::new(&mut layout_bytes).packed_format()) - .unwrap(); - layout_bytes.push(0); - assert_eq!(dbg!(native_bytes), layout_bytes); } } -- 2.49.0 From 48604d165bda5ea0f5cf1d1a6327d887bca06768 Mon Sep 17 00:00:00 2001 From: shimun Date: Wed, 6 May 2020 23:47:33 +0200 Subject: [PATCH 4/8] 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 {} -- 2.49.0 From 86649b56aa197c98fdffc03408173d13793d5f9d Mon Sep 17 00:00:00 2001 From: shimun Date: Thu, 7 May 2020 01:56:14 +0200 Subject: [PATCH 5/8] cleanup --- src/protocol/cbor.rs | 44 +++++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/src/protocol/cbor.rs b/src/protocol/cbor.rs index 64aef35..0c47b90 100644 --- a/src/protocol/cbor.rs +++ b/src/protocol/cbor.rs @@ -1,13 +1,12 @@ -#[deny(missing_debug_implementations, unused_results)] +#![deny(missing_debug_implementations, unused_results)] use failure::_core::marker::PhantomData; use num_traits::{FromPrimitive, ToPrimitive}; -use serde::de::{Error, MapAccess, Visitor}; -use serde::ser::{SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant}; +use serde::de::{MapAccess, Visitor}; +use serde::ser::SerializeMap; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; -use serde_cbor::de::IoRead; use serde_cbor::ser::IoWrite; use std::borrow::Cow; -use std::collections::{BTreeMap, HashMap}; +use std::collections::BTreeMap; use std::fmt; use std::fmt::Debug; use std::io::{Read, Write}; @@ -548,7 +547,7 @@ impl<'a, 'de> Deserialize<'de> for CoseKey<'a> { Field::Id => id = Some(map.next_value()?), Field::Alg => alg = Some(map.next_value()?), Field::Other(i) => { - parameters.insert(i, map.next_value()?); + let _ = parameters.insert(i, map.next_value()?); } }; } @@ -769,9 +768,7 @@ impl<'a, 'de> Deserialize<'de> for AuthenticatorOptions<'a> { where E: de::Error, { - match value { - other => Ok(Field::Other(other.into())), - } + Ok(Field::Other(value.into())) } } @@ -796,7 +793,7 @@ impl<'a, 'de> Deserialize<'de> for AuthenticatorOptions<'a> { while let Some(k) = map.next_key()? { match k { Field::Other(i) => { - options.insert(Cow::Owned(i), map.next_value()?); + let _ = options.insert(Cow::Owned(i), map.next_value()?); } }; } @@ -893,8 +890,8 @@ mod test { let mut cbor = bytes; match T::deserialize_cbor(&mut bytes) { e @ Err(_) => { - dbg!(serde_cbor::value::Value::deserialize_cbor(&mut cbor)); - dbg!(e).unwrap(); + dbg!(serde_cbor::value::Value::deserialize_cbor(&mut cbor).unwrap()); + let _ = dbg!(e); unreachable!() } Ok(ok) => return ok, @@ -966,14 +963,7 @@ mod test { } fn test_response(mut resp: &[u8]) { - let res = GetClientPinResponse::deserialize_cbor(&mut resp).unwrap(); - //let cose = res.key_agreement; - //dbg!(cose.alg); - //assert_ne!(cose.alg, PublicKeyCredentialAlgorithm::Unknown); - /*let mut buf = Vec::new(); - cose.serialize_cbor(&mut buf).unwrap(); - let mut buf_slice = &buf[..]; - assert_eq!(cose, CoseKey::deserialize_cbor(&mut buf_slice).unwrap())*/ + let _ = GetClientPinResponse::deserialize_cbor(&mut resp).unwrap(); } #[test] @@ -1130,7 +1120,7 @@ mod test { #[test] fn test_make_credential_request() { - assert_decodes::>(&[ + let _ = assert_decodes::>(&[ 166, 1, 88, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 162, 98, 105, 100, 105, 99, 116, 97, 112, 95, 100, 101, 109, 111, 100, 110, 97, 109, 101, 111, 99, 116, 97, 112, 95, 104, 109, 97, 99, @@ -1143,7 +1133,7 @@ mod test { #[test] fn test_make_credential_response_yubikey() { - assert_decodes::>(&[ + let _ = assert_decodes::>(&[ 163, 1, 102, 112, 97, 99, 107, 101, 100, 2, 88, 162, 171, 147, 255, 145, 49, 113, 135, 145, 80, 61, 246, 246, 160, 31, 189, 65, 0, 129, 154, 207, 25, 28, 121, 3, 211, 79, 224, 82, 111, 17, 229, 163, 193, 0, 0, 0, 1, 185, 44, 63, 154, 192, 20, @@ -1201,8 +1191,8 @@ mod test { //#[test] fn test_make_credential_requestt() { let mut buf = Vec::new(); - make_credential_request().serialize_cbor(&mut buf); - dbg!(serde_cbor::value::Value::deserialize_cbor(&mut (&buf[..]))); + let _ = make_credential_request().serialize_cbor(&mut buf); + let _ = dbg!(serde_cbor::value::Value::deserialize_cbor(&mut (&buf[..]))); panic!() } } @@ -1230,7 +1220,7 @@ mod test { #[test] fn test_get_assertion_request() { - assert_decodes::>(&[ + let _ = assert_decodes::>(&[ 164, 1, 105, 99, 116, 97, 112, 95, 100, 101, 109, 111, 2, 88, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 129, 162, 98, 105, 100, 80, 188, 116, 69, 227, 50, 124, 140, 13, 30, 132, 51, 122, @@ -1249,7 +1239,7 @@ mod test { #[test] fn test_get_assertion_response_yubikey() { - assert_decodes::>(&[ + let _ = assert_decodes::>(&[ 164, 1, 162, 98, 105, 100, 80, 14, 2, 90, 14, 176, 127, 23, 26, 112, 189, 7, 253, 218, 142, 157, 153, 100, 116, 121, 112, 101, 106, 112, 117, 98, 108, 105, 99, 45, 107, 101, 121, 2, 88, 84, 171, 147, 255, 145, 49, 113, 135, 145, 80, 61, 246, 246, @@ -1266,7 +1256,7 @@ mod test { } #[test] fn test_get_assertion_response_solokey() { - assert_decodes::>(&[ + let _ = assert_decodes::>(&[ 163, 1, 162, 98, 105, 100, 88, 70, 178, 60, 31, 172, 224, 56, 238, 158, 64, 8, 252, 234, 231, 234, 32, 67, 42, 251, 118, 141, 152, 72, 220, 19, 42, 24, 149, 131, 4, 78, 50, 46, 88, 99, 171, 147, 255, 145, 49, 113, 135, 145, 80, 61, 246, 246, 160, -- 2.49.0 From c358202a3a0210d4e0c63285635648b4e5b10d37 Mon Sep 17 00:00:00 2001 From: shimun Date: Tue, 23 Jun 2020 19:01:12 +0200 Subject: [PATCH 6/8] drone --- .drone.yml | 24 ++++++++++++++++++++++++ src/protocol/error.rs | 0 2 files changed, 24 insertions(+) create mode 100644 .drone.yml create mode 100644 src/protocol/error.rs diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..324d666 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,24 @@ +kind: pipeline +name: default + +steps: + - name: fmt + image: rust:1.43.0 + commands: + - rustup component add rustfmt + - cargo fmt --all -- --check + - name: test + image: rust:1.43.0 + commands: + - cargo test + - name: publish + image: rust:1.43.0 + environment: + CARGO_REGISTRY_TOKEN: + from_secret: cargo_tkn + commands: + - grep -E 'version ?= ?"${DRONE_TAG}"' -i Cargo.toml || (printf "incorrect crate/tag version" && exit 1) + - cargo package + - cargo publish + when: + event: tag diff --git a/src/protocol/error.rs b/src/protocol/error.rs new file mode 100644 index 0000000..e69de29 -- 2.49.0 From 0a2a0542338baf4edd20e6f077924062121437ae Mon Sep 17 00:00:00 2001 From: shimun Date: Tue, 23 Jun 2020 19:10:40 +0200 Subject: [PATCH 7/8] fmt --- src/protocol/cbor.rs | 2 ++ src/protocol/error.rs | 7 +++++++ src/protocol/mod.rs | 3 ++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/protocol/cbor.rs b/src/protocol/cbor.rs index 0c47b90..1a82060 100644 --- a/src/protocol/cbor.rs +++ b/src/protocol/cbor.rs @@ -922,6 +922,8 @@ mod test { panic!() } + //TODO: test Trezor response since tr isn't running fido 2.1 yet + #[test] fn response_yubikey() { test_response(&[ diff --git a/src/protocol/error.rs b/src/protocol/error.rs index e69de29..74f162e 100644 --- a/src/protocol/error.rs +++ b/src/protocol/error.rs @@ -0,0 +1,7 @@ +use thiserror::Error; + +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum ProtocolError { + CborEncode, + CborDecode { data: Vec }, +} diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index 6baad76..baa1819 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -1,3 +1,4 @@ mod cbor; - +mod error; pub use self::cbor::*; +pub use self::error::*; -- 2.49.0 From 5bf210dc735cf738622034fe3b852b11e1563587 Mon Sep 17 00:00:00 2001 From: shimun Date: Tue, 23 Jun 2020 19:15:51 +0200 Subject: [PATCH 8/8] test all features --- .drone.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.drone.yml b/.drone.yml index 324d666..1555d63 100644 --- a/.drone.yml +++ b/.drone.yml @@ -10,7 +10,7 @@ steps: - name: test image: rust:1.43.0 commands: - - cargo test + - cargo test --all-features - name: publish image: rust:1.43.0 environment: @@ -18,7 +18,7 @@ steps: from_secret: cargo_tkn commands: - grep -E 'version ?= ?"${DRONE_TAG}"' -i Cargo.toml || (printf "incorrect crate/tag version" && exit 1) - - cargo package - - cargo publish + - cargo package --all-features + - cargo publish --all-features when: event: tag -- 2.49.0