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