1use crate::error::{Error, Result};
6
7use soft_fido2_ctap::cbor::{MapParser, Value};
8use soft_fido2_ctap::types::{PublicKeyCredentialDescriptor, User};
9
10use alloc::string::String;
11use alloc::vec::Vec;
12
13#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct CredentialsMetadata {
18 pub existing_resident_credentials_count: u32,
20
21 pub max_possible_remaining_resident_credentials_count: u32,
26}
27
28impl CredentialsMetadata {
29 pub fn from_cbor(bytes: &[u8]) -> Result<Self> {
39 let parser = MapParser::from_bytes(bytes).map_err(|_| Error::Other)?;
40
41 let existing: i32 = parser.get(0x01).map_err(|_| Error::Other)?;
42 let remaining: i32 = parser.get(0x02).map_err(|_| Error::Other)?;
43
44 Ok(Self {
45 existing_resident_credentials_count: existing as u32,
46 max_possible_remaining_resident_credentials_count: remaining as u32,
47 })
48 }
49}
50
51#[derive(Debug, Clone, PartialEq, Eq)]
56pub struct RpInfo {
57 pub id: String,
59
60 pub name: Option<String>,
62
63 pub rp_id_hash: [u8; 32],
65}
66
67impl RpInfo {
68 pub fn from_cbor_value(rp_value: &Value, rp_id_hash: [u8; 32]) -> Result<Self> {
74 let Value::Map(map) = rp_value else {
75 return Err(Error::Other);
76 };
77
78 let mut id = None;
79 let mut name = None;
80
81 for (k, v) in map {
82 if let Value::Text(key) = k {
83 match key.as_str() {
84 "id" => {
85 if let Value::Text(val) = v {
86 id = Some(val.clone());
87 }
88 }
89 "name" => {
90 if let Value::Text(val) = v {
91 name = Some(val.clone());
92 }
93 }
94 _ => {} }
96 }
97 }
98
99 let id = id.ok_or(Error::Other)?;
100
101 Ok(Self {
102 id,
103 name,
104 rp_id_hash,
105 })
106 }
107}
108
109#[derive(Debug, Clone, PartialEq, Eq)]
113pub struct RpEnumerationBeginResponse {
114 pub rp: RpInfo,
116
117 pub total_rps: u32,
119}
120
121impl RpEnumerationBeginResponse {
122 pub fn from_cbor(bytes: &[u8]) -> Result<Self> {
133 let parser = MapParser::from_bytes(bytes).map_err(|_| Error::Other)?;
134
135 let rp_value: Value = parser.get(0x03).map_err(|_| Error::Other)?;
136 let rp_id_hash_vec: Vec<u8> = parser.get_bytes(0x04).map_err(|_| Error::Other)?;
137 let total: i32 = parser.get(0x05).map_err(|_| Error::Other)?;
138
139 let mut rp_id_hash = [0u8; 32];
140 if rp_id_hash_vec.len() != 32 {
141 return Err(Error::Other);
142 }
143 rp_id_hash.copy_from_slice(&rp_id_hash_vec);
144
145 let rp = RpInfo::from_cbor_value(&rp_value, rp_id_hash)?;
146
147 Ok(Self {
148 rp,
149 total_rps: total as u32,
150 })
151 }
152}
153
154pub type RpEnumerationNextResponse = RpInfo;
158
159impl RpEnumerationNextResponse {
160 pub fn from_cbor(bytes: &[u8]) -> Result<Self> {
170 let parser = MapParser::from_bytes(bytes).map_err(|_| Error::Other)?;
171
172 let rp_value: Value = parser.get(0x03).map_err(|_| Error::Other)?;
173 let rp_id_hash_vec: Vec<u8> = parser.get_bytes(0x04).map_err(|_| Error::Other)?;
174
175 let mut rp_id_hash = [0u8; 32];
176 if rp_id_hash_vec.len() != 32 {
177 return Err(Error::Other);
178 }
179 rp_id_hash.copy_from_slice(&rp_id_hash_vec);
180
181 RpInfo::from_cbor_value(&rp_value, rp_id_hash)
182 }
183}
184
185#[derive(Debug, Clone, PartialEq, Eq)]
189pub struct CredentialInfo {
190 pub user: User,
192
193 pub credential_id: PublicKeyCredentialDescriptor,
195
196 pub public_key: Option<Vec<u8>>,
201
202 pub cred_protect: Option<u8>,
209
210 pub large_blob_key: Option<Vec<u8>>,
212
213 pub third_party_payment: Option<bool>,
217}
218
219impl CredentialInfo {
220 fn from_parser(parser: &MapParser) -> Result<Self> {
222 let user: User = parser.get(0x06).map_err(|_| Error::Other)?;
224
225 let cred_id_value: Value = parser.get(0x07).map_err(|_| Error::Other)?;
227 let credential_id = parse_credential_descriptor(&cred_id_value)?;
228
229 let public_key = parser.get_opt::<Vec<u8>>(0x08).ok().flatten();
231
232 let cred_protect = parser.get_opt::<u8>(0x0A).ok().flatten();
234
235 let large_blob_key = if parser.get_raw(0x0B).is_some() {
237 parser.get_bytes(0x0B).ok()
238 } else {
239 None
240 };
241
242 let third_party_payment = parser.get_opt::<bool>(0x0C).ok().flatten();
244
245 Ok(Self {
246 user,
247 credential_id,
248 public_key,
249 cred_protect,
250 large_blob_key,
251 third_party_payment,
252 })
253 }
254}
255
256#[derive(Debug, Clone, PartialEq, Eq)]
258pub struct CredentialEnumerationBeginResponse {
259 pub credential: CredentialInfo,
261
262 pub total_credentials: u32,
264}
265
266impl CredentialEnumerationBeginResponse {
267 pub fn from_cbor(bytes: &[u8]) -> Result<Self> {
282 let parser = MapParser::from_bytes(bytes).map_err(|_| Error::Other)?;
283
284 let credential = CredentialInfo::from_parser(&parser)?;
285 let total: i32 = parser.get(0x09).map_err(|_| Error::Other)?;
286
287 Ok(Self {
288 credential,
289 total_credentials: total as u32,
290 })
291 }
292}
293
294pub type CredentialEnumerationNextResponse = CredentialInfo;
296
297impl CredentialEnumerationNextResponse {
298 pub fn from_cbor(bytes: &[u8]) -> Result<Self> {
311 let parser = MapParser::from_bytes(bytes).map_err(|_| Error::Other)?;
312 CredentialInfo::from_parser(&parser)
313 }
314}
315
316fn parse_credential_descriptor(value: &Value) -> Result<PublicKeyCredentialDescriptor> {
326 let Value::Map(map) = value else {
327 return Err(Error::Other);
328 };
329
330 let mut id = None;
331 let mut cred_type = None;
332
333 for (k, v) in map {
334 if let Value::Text(key) = k {
335 match key.as_str() {
336 "id" => {
337 if let Value::Bytes(bytes) = v {
338 id = Some(bytes.clone());
339 }
340 }
341 "type" => {
342 if let Value::Text(t) = v {
343 cred_type = Some(t.clone());
344 }
345 }
346 _ => {}
347 }
348 }
349 }
350
351 let id = id.ok_or(Error::Other)?;
352 let r#type = cred_type.ok_or(Error::Other)?;
353
354 Ok(PublicKeyCredentialDescriptor {
355 id,
356 r#type,
357 transports: None,
358 })
359}
360
361#[cfg(test)]
362mod tests {
363 use super::*;
364
365 #[test]
366 fn test_credentials_metadata_parsing() {
367 let cbor = vec![0xa2, 0x01, 0x0a, 0x02, 0x18, 0x32];
369 let metadata = CredentialsMetadata::from_cbor(&cbor).unwrap();
370 assert_eq!(metadata.existing_resident_credentials_count, 10);
371 assert_eq!(
372 metadata.max_possible_remaining_resident_credentials_count,
373 50
374 );
375 }
376
377 #[test]
378 fn test_rp_info_parsing_with_name() {
379 let rp_value = Value::Map(vec![
380 (
381 Value::Text("id".to_string()),
382 Value::Text("example.com".to_string()),
383 ),
384 (
385 Value::Text("name".to_string()),
386 Value::Text("Example".to_string()),
387 ),
388 ]);
389 let hash = [1u8; 32];
390
391 let rp = RpInfo::from_cbor_value(&rp_value, hash).unwrap();
392 assert_eq!(rp.id, "example.com");
393 assert_eq!(rp.name, Some("Example".to_string()));
394 assert_eq!(rp.rp_id_hash, hash);
395 }
396
397 #[test]
398 fn test_rp_info_parsing_without_name() {
399 let rp_value = Value::Map(vec![(
400 Value::Text("id".to_string()),
401 Value::Text("example.com".to_string()),
402 )]);
403 let hash = [2u8; 32];
404
405 let rp = RpInfo::from_cbor_value(&rp_value, hash).unwrap();
406 assert_eq!(rp.id, "example.com");
407 assert_eq!(rp.name, None);
408 }
409
410 #[test]
411 fn test_parse_credential_descriptor() {
412 let desc_value = Value::Map(vec![
413 (Value::Text("id".to_string()), Value::Bytes(vec![1, 2, 3])),
414 (
415 Value::Text("type".to_string()),
416 Value::Text("public-key".to_string()),
417 ),
418 ]);
419
420 let desc = parse_credential_descriptor(&desc_value).unwrap();
421 assert_eq!(desc.id, vec![1, 2, 3]);
422 assert_eq!(desc.r#type, "public-key");
423 }
424}