1 // Copyright 2023 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //! # CTAP Protocol 16 //! 17 //! This crate represents CTAP messages and turns them into a binary representation to be sent to a 18 //! remote device. 19 20 use anyhow::anyhow; 21 22 /// The Rust representation of CTAP values (or identifiers). 23 #[derive(Debug, PartialEq)] 24 pub enum Value { 25 AuthenticatorMakeCredential = 0x01, 26 AuthenticatorGetAssertion = 0x02, 27 AuthenticatorGetInfo = 0x04, 28 AuthenticatorClientPIN = 0x06, 29 AuthenticatorReset = 0x07, 30 AuthenticatorGetNextAssertion = 0x08, 31 } 32 33 /// The Rust representation of CTAP parameters. 34 #[derive(Debug, PartialEq)] 35 pub enum Parameters { 36 AuthenticatorMakeCredential, 37 AuthenticatorGetAssertion, 38 AuthenticatorClientPIN, 39 } 40 41 impl From<Parameters> for Vec<u8> { from(message: Parameters) -> Vec<u8>42 fn from(message: Parameters) -> Vec<u8> { 43 // TODO: serialize parameters correctly 44 match message { 45 Parameters::AuthenticatorMakeCredential => vec![], 46 Parameters::AuthenticatorGetAssertion => vec![], 47 Parameters::AuthenticatorClientPIN => vec![], 48 } 49 } 50 } 51 52 pub struct Command { 53 pub value: Value, 54 pub parameters: Option<Parameters>, 55 } 56 57 impl From<Command> for Vec<u8> { 58 /// Converts the given CTAP command into its binary representation. from(message: Command) -> Vec<u8>59 fn from(message: Command) -> Vec<u8> { 60 let mut result = vec![message.value as u8]; 61 if let Some(p) = message.parameters { 62 result.append(&mut p.into()); 63 } 64 result 65 } 66 } 67 68 impl TryFrom<Vec<u8>> for Command { 69 type Error = anyhow::Error; 70 /// Convert a binary message to its Rust representation. try_from(bytes: Vec<u8>) -> anyhow::Result<Self>71 fn try_from(bytes: Vec<u8>) -> anyhow::Result<Self> { 72 if bytes.is_empty() { 73 Err(anyhow!("Binary message was empty.")) 74 } else { 75 match bytes[0] { 76 _x if _x == Value::AuthenticatorMakeCredential as u8 => Ok(Self { 77 value: Value::AuthenticatorMakeCredential, 78 parameters: Some(Parameters::AuthenticatorMakeCredential), 79 }), 80 _x if _x == Value::AuthenticatorGetAssertion as u8 => Ok(Self { 81 value: Value::AuthenticatorGetAssertion, 82 parameters: Some(Parameters::AuthenticatorGetAssertion), 83 }), 84 _x if _x == Value::AuthenticatorGetInfo as u8 => Ok(Self { 85 value: Value::AuthenticatorGetInfo, 86 parameters: None, 87 }), 88 _x if _x == Value::AuthenticatorClientPIN as u8 => Ok(Self { 89 value: Value::AuthenticatorClientPIN, 90 parameters: Some(Parameters::AuthenticatorClientPIN), 91 }), 92 _x if _x == Value::AuthenticatorReset as u8 => Ok(Self { 93 value: Value::AuthenticatorReset, 94 parameters: None, 95 }), 96 _x if _x == Value::AuthenticatorGetNextAssertion as u8 => Ok(Self { 97 value: Value::AuthenticatorGetNextAssertion, 98 parameters: None, 99 }), 100 _ => Err(anyhow!("Unknown message type.")), 101 } 102 } 103 } 104 } 105 106 #[cfg(test)] 107 mod tests { 108 use super::*; 109 use anyhow::bail; 110 111 #[test] translate_message_to_bytes()112 fn translate_message_to_bytes() { 113 let mut message = Command { 114 value: Value::AuthenticatorReset, 115 parameters: None, 116 }; 117 let mut bytes: Vec<u8> = message.into(); 118 119 assert_eq!(bytes, vec![Value::AuthenticatorReset as u8]); 120 121 message = Command { 122 value: Value::AuthenticatorMakeCredential, 123 parameters: None, 124 }; 125 bytes = message.into(); 126 127 assert_eq!(bytes, vec![Value::AuthenticatorMakeCredential as u8]); 128 129 message = Command { 130 value: Value::AuthenticatorGetAssertion, 131 parameters: None, 132 }; 133 bytes = message.into(); 134 135 assert_eq!(bytes, vec![Value::AuthenticatorGetAssertion as u8]); 136 137 message = Command { 138 value: Value::AuthenticatorGetInfo, 139 parameters: None, 140 }; 141 bytes = message.into(); 142 143 assert_eq!(bytes, vec![Value::AuthenticatorGetInfo as u8]); 144 145 message = Command { 146 value: Value::AuthenticatorClientPIN, 147 parameters: None, 148 }; 149 bytes = message.into(); 150 151 assert_eq!(bytes, vec![Value::AuthenticatorClientPIN as u8]); 152 153 message = Command { 154 value: Value::AuthenticatorGetNextAssertion, 155 parameters: None, 156 }; 157 bytes = message.into(); 158 159 assert_eq!(bytes, vec![Value::AuthenticatorGetNextAssertion as u8]); 160 } 161 162 #[test] translate_bytes_to_message() -> anyhow::Result<()>163 fn translate_bytes_to_message() -> anyhow::Result<()> { 164 let mut bytes = vec![Value::AuthenticatorReset as u8]; 165 let mut message: Command = bytes.try_into()?; 166 assert_eq!(message.value, Value::AuthenticatorReset); 167 assert_eq!(message.parameters, None); 168 169 bytes = vec![Value::AuthenticatorMakeCredential as u8]; 170 message = bytes.try_into()?; 171 assert_eq!(message.value, Value::AuthenticatorMakeCredential); 172 assert_eq!( 173 message.parameters, 174 Some(Parameters::AuthenticatorMakeCredential) 175 ); 176 177 bytes = vec![Value::AuthenticatorGetAssertion as u8]; 178 message = bytes.try_into()?; 179 assert_eq!(message.value, Value::AuthenticatorGetAssertion); 180 assert_eq!( 181 message.parameters, 182 Some(Parameters::AuthenticatorGetAssertion) 183 ); 184 185 bytes = vec![Value::AuthenticatorGetInfo as u8]; 186 message = bytes.try_into()?; 187 assert_eq!(message.value, Value::AuthenticatorGetInfo); 188 assert_eq!(message.parameters, None); 189 190 bytes = vec![Value::AuthenticatorClientPIN as u8]; 191 message = bytes.try_into()?; 192 assert_eq!(message.value, Value::AuthenticatorClientPIN); 193 assert_eq!(message.parameters, Some(Parameters::AuthenticatorClientPIN)); 194 195 bytes = vec![Value::AuthenticatorGetNextAssertion as u8]; 196 message = bytes.try_into()?; 197 assert_eq!(message.value, Value::AuthenticatorGetNextAssertion); 198 assert_eq!(message.parameters, None); 199 Ok(()) 200 } 201 202 #[test] translate_empty_bytes() -> anyhow::Result<()>203 fn translate_empty_bytes() -> anyhow::Result<()> { 204 let bytes = vec![]; 205 let res: anyhow::Result<Command> = bytes.try_into(); 206 match res { 207 Err(e) => { 208 assert_eq!(e.to_string(), "Binary message was empty."); 209 Ok(()) 210 } 211 _ => bail!("Should not parse empty bytes"), 212 } 213 } 214 215 #[test] translate_unknown_message() -> anyhow::Result<()>216 fn translate_unknown_message() -> anyhow::Result<()> { 217 let bytes = vec![0x10]; 218 let res: anyhow::Result<Command> = bytes.try_into(); 219 match res { 220 Err(e) => { 221 assert_eq!(e.to_string(), "Unknown message type."); 222 Ok(()) 223 } 224 _ => bail!("Should not parse unknown message."), 225 } 226 } 227 } 228