webauthn_authenticator_rs/ctap2/commands/
mod.rs1use serde::Serialize;
3use serde_cbor_2::{ser::to_vec_packed, Value};
4use std::borrow::Borrow;
5use std::collections::{BTreeMap, BTreeSet};
6
7#[cfg(any(all(doc, not(doctest)), feature = "ctap2-management"))]
8mod bio_enrollment;
9mod client_pin;
10#[cfg(any(all(doc, not(doctest)), feature = "ctap2-management"))]
11mod config;
12#[cfg(any(all(doc, not(doctest)), feature = "ctap2-management"))]
13mod credential_management;
14mod get_assertion;
15mod get_info;
16mod make_credential;
17#[cfg(any(all(doc, not(doctest)), feature = "ctap2-management"))]
18mod reset;
19mod selection;
20
21#[cfg(any(all(doc, not(doctest)), feature = "ctap2-management"))]
22pub use self::bio_enrollment::*;
23pub use self::client_pin::*;
24#[cfg(any(all(doc, not(doctest)), feature = "ctap2-management"))]
25pub use self::config::*;
26#[cfg(any(all(doc, not(doctest)), feature = "ctap2-management"))]
27pub use self::credential_management::*;
28pub use self::get_assertion::*;
29pub use self::get_info::*;
30pub use self::make_credential::*;
31#[cfg(any(all(doc, not(doctest)), feature = "ctap2-management"))]
32pub use self::reset::*;
33pub use self::selection::*;
34use crate::error::WebauthnCError;
35use crate::transport::iso7816::ISO7816RequestAPDU;
36
37const FRAG_MAX: usize = 0xF0;
38
39pub trait CBORResponse: Sized + std::fmt::Debug + Send {
43 fn try_from(i: &[u8]) -> Result<Self, WebauthnCError>;
44}
45
46pub trait CBORCommand: Serialize + Sized + std::fmt::Debug + Send {
50 const CMD: u8;
52
53 const HAS_PAYLOAD: bool = true;
58
59 type Response: CBORResponse;
61
62 fn cbor(&self) -> Result<Vec<u8>, serde_cbor_2::Error> {
64 if !Self::HAS_PAYLOAD {
68 return Ok(vec![Self::CMD]);
69 }
70
71 trace!("Sending: {:?}", self);
72 let mut b = to_vec_packed(self)?;
73 trace!(
74 "CBOR: cmd={}, cbor={:?}",
75 Self::CMD,
76 serde_cbor_2::from_slice::<'_, serde_cbor_2::Value>(&b[..])
77 );
78
79 b.reserve(1);
80 b.insert(0, Self::CMD);
81 Ok(b)
82 }
83}
84
85pub fn to_short_apdus(cbor: &[u8]) -> Vec<ISO7816RequestAPDU> {
88 let chunks = cbor.chunks(FRAG_MAX).rev();
89 let mut o = Vec::with_capacity(chunks.len());
90 let mut last = true;
91
92 for chunk in chunks {
93 o.insert(
94 0,
95 ISO7816RequestAPDU {
96 cla: if last { 0x80 } else { 0x90 },
97 ins: 0x10,
98 p1: 0x00,
99 p2: 0x00,
100 data: chunk.to_vec(),
101 ne: if last { 256 } else { 0 },
102 },
103 );
104 last = false;
105 }
106
107 o
108}
109
110pub fn to_extended_apdu(cbor: Vec<u8>) -> ISO7816RequestAPDU {
113 ISO7816RequestAPDU {
114 cla: 0x80,
115 ins: 0x10,
116 p1: 0, p2: 0x00,
118 data: cbor,
119 ne: 65536,
120 }
121}
122
123fn value_to_vec_string(v: Value, loc: &str) -> Option<Vec<String>> {
124 if let Value::Array(v) = v {
125 let mut x = Vec::with_capacity(v.len());
126 for s in v.into_iter() {
127 if let Value::Text(s) = s {
128 x.push(s);
129 } else {
130 error!("Invalid value inside {}: {:?}", loc, s);
131 }
132 }
133 Some(x)
134 } else {
135 error!("Invalid type for {}: {:?}", loc, v);
136 None
137 }
138}
139
140fn value_to_set_string(v: Value, loc: &str) -> Option<BTreeSet<String>> {
141 if let Value::Array(v) = v {
142 let mut x = BTreeSet::new();
143 for s in v.into_iter() {
144 if let Value::Text(s) = s {
145 x.insert(s);
146 } else {
147 error!("Invalid value inside {}: {:?}", loc, s);
148 }
149 }
150 Some(x)
151 } else {
152 error!("Invalid type for {}: {:?}", loc, v);
153 None
154 }
155}
156
157fn value_to_set_u64(v: Value, loc: &str) -> Option<BTreeSet<u64>> {
158 if let Value::Array(v) = v {
159 let mut x = BTreeSet::new();
160 for i in v.into_iter() {
161 if let Value::Integer(i) = i {
162 if let Ok(i) = u64::try_from(i) {
163 x.insert(i);
164 continue;
165 }
166 }
167 error!("Invalid value inside {}: {:?}", loc, i);
168 }
169 Some(x)
170 } else {
171 error!("Invalid type for {}: {:?}", loc, v);
172 None
173 }
174}
175
176fn value_to_vec(v: Value, loc: &str) -> Option<Vec<Value>> {
177 if let Value::Array(v) = v {
178 Some(v)
179 } else {
180 error!("Invalid type for {}: {:?}", loc, v);
181 None
182 }
183}
184
185fn value_to_map(v: Value, loc: &str) -> Option<BTreeMap<Value, Value>> {
186 if let Value::Map(v) = v {
187 Some(v)
188 } else {
189 error!("Invalid type for {}: {:?}", loc, v);
190 None
191 }
192}
193
194fn value_to_vec_u32(v: Value, loc: &str) -> Option<Vec<u32>> {
195 value_to_vec(v, loc).map(|v| {
196 v.into_iter()
197 .filter_map(|i| value_to_u32(&i, loc))
198 .collect()
199 })
200}
201
202#[cfg(feature = "ctap2-management")]
203pub(crate) fn value_to_u8(v: &Value, loc: &str) -> Option<u8> {
204 if let Value::Integer(i) = v {
205 u8::try_from(*i)
206 .map_err(|_| error!("Invalid value inside {}: {:?}", loc, i))
207 .ok()
208 } else {
209 error!("Invalid type for {}: {:?}", loc, v);
210 None
211 }
212}
213
214pub(crate) fn value_to_u32(v: &Value, loc: &str) -> Option<u32> {
215 if let Value::Integer(i) = v {
216 u32::try_from(*i)
217 .map_err(|_| error!("Invalid value inside {}: {:?}", loc, i))
218 .ok()
219 } else {
220 error!("Invalid type for {}: {:?}", loc, v);
221 None
222 }
223}
224
225#[cfg(any(doc, feature = "cable"))]
226pub(crate) fn value_to_u64(v: &Value, loc: &str) -> Option<u64> {
227 if let Value::Integer(i) = v {
228 u64::try_from(*i)
229 .map_err(|_| error!("Invalid value inside {}: {:?}", loc, i))
230 .ok()
231 } else {
232 error!("Invalid type for {}: {:?}", loc, v);
233 None
234 }
235}
236
237fn value_to_i128(v: impl Borrow<Value>, loc: &str) -> Option<i128> {
238 let v = v.borrow();
239 if let Value::Integer(i) = v {
240 Some(*i)
241 } else {
242 error!("Invalid type for {}: {:?}", loc, v);
243 None
244 }
245}
246
247fn value_to_usize(v: impl Borrow<Value>, loc: &str) -> Option<usize> {
248 let v = v.borrow();
249 if let Value::Integer(i) = v {
250 usize::try_from(*i)
251 .map_err(|_| error!("Invalid value inside {}: {:?}", loc, i))
252 .ok()
253 } else {
254 error!("Invalid type for {}: {:?}", loc, v);
255 None
256 }
257}
258
259pub(crate) fn value_to_bool(v: &Value, loc: &str) -> Option<bool> {
261 if let Value::Bool(b) = v {
262 Some(*b)
263 } else {
264 error!("Invalid type for {}: {:?}", loc, v);
265 None
266 }
267}
268
269pub(crate) fn value_to_vec_u8(v: Value, loc: &str) -> Option<Vec<u8>> {
271 if let Value::Bytes(b) = v {
272 Some(b)
273 } else {
274 error!("Invalid type for {}: {:?}", loc, v);
275 None
276 }
277}
278
279pub(crate) fn value_to_string(v: Value, loc: &str) -> Option<String> {
280 if let Value::Text(s) = v {
281 Some(s)
282 } else {
283 error!("Invalid type for {}: {:?}", loc, v);
284 None
285 }
286}
287
288#[derive(Debug)]
290pub struct NoResponse {}
291
292impl CBORResponse for NoResponse {
293 fn try_from(_raw: &[u8]) -> Result<Self, WebauthnCError> {
294 Ok(Self {})
295 }
296}
297
298fn map_int_keys(m: BTreeMap<Value, Value>) -> Result<BTreeMap<u32, Value>, WebauthnCError> {
299 m.into_iter()
300 .map(|(k, v)| {
301 let k = value_to_u32(&k, "map_int_keys").ok_or(WebauthnCError::Internal)?;
302
303 Ok((k, v))
304 })
305 .collect()
306}
307
308#[macro_export]
310macro_rules! deserialize_cbor {
311 ($name:ident) => {
312 impl $crate::ctap2::commands::CBORResponse for $name {
313 fn try_from(i: &[u8]) -> Result<Self, $crate::error::WebauthnCError> {
314 if i.is_empty() {
315 TryFrom::try_from(std::collections::BTreeMap::new()).map_err(|e| {
316 error!("Tried to deserialise empty input, got error: {:?}", e);
317 $crate::error::WebauthnCError::Cbor
318 })
319 } else {
320 let v =
322 serde_cbor_2::from_slice::<'_, serde_cbor_2::Value>(&i).map_err(|e| {
323 error!("deserialise: {:?}", e);
324 $crate::error::WebauthnCError::Cbor
325 })?;
326
327 let v = if let serde_cbor_2::Value::Map(v) = v {
329 Ok(v)
330 } else {
331 error!("deserialise: unexpected CBOR type {:?}", v);
332 Err($crate::error::WebauthnCError::Cbor)
333 }?;
334
335 let v = $crate::ctap2::commands::map_int_keys(v)?;
337
338 TryFrom::try_from(v).map_err(|_| {
339 error!("deserialising structure");
340 $crate::error::WebauthnCError::Cbor
341 })
342 }
343 }
344 }
345 };
346}