webauthn_authenticator_rs/ctap2/commands/
get_assertion.rs1use base64urlsafedata::Base64UrlSafeData;
2use serde::{Deserialize, Serialize};
3use serde_cbor_2::Value;
4use std::{collections::BTreeMap, str::FromStr};
5use webauthn_rs_proto::{AllowCredentials, AuthenticatorTransport, PublicKeyCredentialDescriptor};
6
7use crate::ctap2::commands::{value_to_map, value_to_vec, value_to_vec_string};
8
9use super::{
10 value_to_bool, value_to_set_string, value_to_string, value_to_u32, value_to_vec_u8, CBORCommand,
11};
12
13#[derive(Serialize, Debug, Clone)]
17#[serde(into = "BTreeMap<u32, Value>", try_from = "BTreeMap<u32, Value>")]
18pub struct GetAssertionRequest {
19 pub rp_id: String,
20 pub client_data_hash: Vec<u8>,
21 pub allow_list: Vec<AllowCredentials>,
22 pub options: Option<BTreeMap<String, bool>>,
24 pub pin_uv_auth_param: Option<Vec<u8>>,
25 pub pin_uv_auth_proto: Option<u32>,
26}
27
28impl CBORCommand for GetAssertionRequest {
29 const CMD: u8 = 0x02;
30 type Response = GetAssertionResponse;
31}
32
33#[derive(Deserialize, Serialize, Debug, Clone)]
38#[serde(rename_all = "camelCase")]
39pub struct GetAssertionResponse {
40 pub credential: Option<PublicKeyCredentialDescriptor>,
41 pub auth_data: Option<Vec<u8>>,
42 pub signature: Option<Vec<u8>>,
43 pub number_of_credentials: Option<u32>,
45 pub user_selected: Option<bool>,
46 pub large_blob_key: Option<Vec<u8>>,
47 }
49
50impl From<GetAssertionRequest> for BTreeMap<u32, Value> {
51 fn from(r: GetAssertionRequest) -> Self {
52 let GetAssertionRequest {
53 rp_id,
54 client_data_hash,
55 allow_list,
56 options,
57 pin_uv_auth_param,
58 pin_uv_auth_proto,
59 } = r;
60
61 let mut keys = BTreeMap::new();
62 keys.insert(0x01, Value::Text(rp_id));
63 keys.insert(0x02, Value::Bytes(client_data_hash));
64
65 if !allow_list.is_empty() {
66 keys.insert(
67 0x03,
68 Value::Array(
69 allow_list
70 .iter()
71 .map(|a| {
72 let mut m = BTreeMap::from([
73 (
74 Value::Text("type".to_string()),
75 Value::Text(a.type_.to_owned()),
76 ),
77 (Value::Text("id".to_string()), Value::Bytes(a.id.to_vec())),
78 ]);
79
80 if let Some(transports) = &a.transports {
81 let transports: Vec<Value> = transports
82 .iter()
83 .map(|t| Value::Text(t.to_string()))
84 .collect();
85
86 if !transports.is_empty() {
87 m.insert(
88 Value::Text("transports".to_string()),
89 Value::Array(transports),
90 );
91 }
92 }
93
94 Value::Map(m)
95 })
96 .collect(),
97 ),
98 );
99 }
100 if let Some(v) = options {
102 keys.insert(
103 0x05,
104 Value::Map(BTreeMap::from_iter(
105 v.iter()
106 .map(|(k, o)| (Value::Text(k.to_owned()), Value::Bool(*o))),
107 )),
108 );
109 }
110
111 if let Some(v) = pin_uv_auth_param {
112 keys.insert(0x06, Value::Bytes(v));
113 }
114 if let Some(v) = pin_uv_auth_proto {
115 keys.insert(0x07, Value::Integer(v.into()));
116 }
117
118 keys
119 }
120}
121
122impl TryFrom<BTreeMap<u32, Value>> for GetAssertionRequest {
123 type Error = &'static str;
124 fn try_from(mut raw: BTreeMap<u32, Value>) -> Result<Self, Self::Error> {
125 trace!("raw: {:?}", raw);
126 Ok(Self {
127 rp_id: raw
128 .remove(&0x01)
129 .and_then(|v| value_to_string(v, "0x01"))
130 .ok_or("parsing rpId")?,
131 client_data_hash: raw
132 .remove(&0x02)
133 .and_then(|v| value_to_vec_u8(v, "0x02"))
134 .ok_or("parsing clientDataHash")?,
135 allow_list: raw
136 .remove(&0x03)
137 .and_then(|v| value_to_vec(v, "0x03"))
138 .map(|v| {
139 v.into_iter()
140 .filter_map(|a| {
141 let mut a = value_to_map(a, "0x03")?;
142 let type_ = value_to_string(
143 a.remove(&Value::Text("type".to_string()))?,
144 "type",
145 )?;
146 let id = Base64UrlSafeData::from(value_to_vec_u8(
147 a.remove(&Value::Text("id".to_string()))?,
148 "id",
149 )?);
150 let transports = a
151 .remove(&Value::Text("transports".to_string()))
152 .and_then(|v| value_to_vec_string(v, "transports"))
153 .map(|v| {
154 v.into_iter()
155 .filter_map(|t| AuthenticatorTransport::from_str(&t).ok())
156 .collect()
157 });
158 Some(AllowCredentials {
159 id,
160 type_,
161 transports,
162 })
163 })
164 .collect()
165 })
166 .unwrap_or_default(),
167 options: None,
169 pin_uv_auth_param: None,
170 pin_uv_auth_proto: None,
171 })
172 }
173}
174
175impl From<GetAssertionResponse> for BTreeMap<u32, Value> {
176 fn from(r: GetAssertionResponse) -> Self {
177 let GetAssertionResponse {
178 credential,
179 auth_data,
180 signature,
181 number_of_credentials,
182 user_selected,
183 large_blob_key,
184 } = r;
185
186 let mut keys = BTreeMap::new();
187 if let Some(credential) = credential {
188 let mut m = BTreeMap::from([
189 (
190 Value::Text("id".to_string()),
191 Value::Bytes(credential.id.into()),
192 ),
193 (
194 Value::Text("type".to_string()),
195 Value::Text(credential.type_),
196 ),
197 ]);
198 if let Some(transports) = credential.transports {
199 let transports = transports
200 .into_iter()
201 .map(|t| Value::Text(t.to_string()))
202 .collect();
203 m.insert(
204 Value::Text("transports".to_string()),
205 Value::Array(transports),
206 );
207 };
208 keys.insert(0x01, Value::Map(m));
209 }
210 if let Some(auth_data) = auth_data {
211 keys.insert(0x02, Value::Bytes(auth_data));
212 }
213 if let Some(signature) = signature {
214 keys.insert(0x03, Value::Bytes(signature));
215 }
216 if let Some(number_of_credentials) = number_of_credentials {
217 keys.insert(0x05, Value::Integer(number_of_credentials.into()));
218 }
219 if let Some(user_selected) = user_selected {
220 keys.insert(0x06, Value::Bool(user_selected));
221 }
222 if let Some(large_blob_key) = large_blob_key {
223 keys.insert(0x07, Value::Bytes(large_blob_key));
224 }
225
226 keys
227 }
228}
229
230impl TryFrom<BTreeMap<u32, Value>> for GetAssertionResponse {
231 type Error = &'static str;
232 fn try_from(mut raw: BTreeMap<u32, Value>) -> Result<Self, Self::Error> {
233 trace!(?raw);
234 Ok(Self {
235 credential: raw.remove(&0x01).and_then(|v| {
236 if let Value::Map(mut v) = v {
237 let id = v
238 .remove(&Value::Text("id".to_string()))
239 .and_then(|v| value_to_vec_u8(v, "0x01.id"))
240 .map(Base64UrlSafeData::from);
241 let type_ = v
242 .remove(&Value::Text("type".to_string()))
243 .and_then(|v| value_to_string(v, "0x01.type"));
244
245 let transports: Option<Vec<AuthenticatorTransport>> = v
246 .remove(&Value::Text("transports".to_string()))
247 .and_then(|v| value_to_set_string(v, "0x01.transports"))
248 .map(|v| {
249 v.iter()
250 .filter_map(|t| AuthenticatorTransport::from_str(t).ok())
251 .collect()
252 });
253 id.and_then(|id| {
254 type_.map(|type_| PublicKeyCredentialDescriptor {
255 type_,
256 id,
257 transports,
258 })
259 })
260 } else {
261 None
262 }
263 }),
264 auth_data: raw.remove(&0x02).and_then(|v| value_to_vec_u8(v, "0x02")),
265 signature: raw.remove(&0x03).and_then(|v| value_to_vec_u8(v, "0x03")),
266 number_of_credentials: raw.remove(&0x05).and_then(|v| value_to_u32(&v, "0x05")),
268 user_selected: raw.remove(&0x06).and_then(|v| value_to_bool(&v, "0x06")),
269 large_blob_key: raw.remove(&0x07).and_then(|v| value_to_vec_u8(v, "0x07")),
270 })
271 }
272}
273
274crate::deserialize_cbor!(GetAssertionRequest);
275crate::deserialize_cbor!(GetAssertionResponse);
276
277#[cfg(test)]
278mod tests {
279 }