slauth/webauthn/proto/
raw_message.rs

1use crate::{
2    base64::*,
3    webauthn::{
4        error::Error,
5        proto::{
6            constants::{
7                ECDSA_Y_PREFIX_NEGATIVE, ECDSA_Y_PREFIX_POSITIVE, ECDSA_Y_PREFIX_UNCOMPRESSED, WEBAUTHN_FORMAT_ANDROID_KEY,
8                WEBAUTHN_FORMAT_ANDROID_SAFETYNET, WEBAUTHN_FORMAT_FIDO_U2F, WEBAUTHN_FORMAT_NONE, WEBAUTHN_FORMAT_PACKED,
9                WEBAUTHN_FORMAT_TPM, WEBAUTH_PUBLIC_KEY_TYPE_EC2, WEBAUTH_PUBLIC_KEY_TYPE_OKP, WEBAUTH_PUBLIC_KEY_TYPE_RSA,
10            },
11            tpm::TPM,
12        },
13    },
14};
15use byteorder::{BigEndian, ReadBytesExt};
16use bytes::Buf;
17use indexmap::IndexMap;
18use serde_cbor::{to_vec, Value};
19use serde_derive::*;
20use std::{
21    collections::BTreeMap,
22    fmt::Display,
23    io::{Cursor, Read, Write},
24    str::FromStr,
25};
26
27#[derive(Serialize, Deserialize, Clone, Debug)]
28#[serde(rename_all = "camelCase")]
29pub struct RawAttestationObject {
30    auth_data: serde_cbor::Value,
31    fmt: String,
32    att_stmt: serde_cbor::Value,
33}
34
35#[derive(Serialize, Deserialize, Clone, Debug)]
36#[serde(rename_all = "camelCase")]
37pub struct AttestationObject {
38    pub auth_data: AuthenticatorData,
39    pub raw_auth_data: Vec<u8>,
40    pub fmt: String,
41    #[serde(skip_serializing_if = "Option::is_none")]
42    pub att_stmt: Option<AttestationStatement>,
43}
44
45#[derive(Serialize, Deserialize, Clone, Debug)]
46pub struct Packed {
47    pub alg: i64,
48    #[serde(with = "serde_bytes")]
49    pub sig: Vec<u8>,
50    #[serde(skip_serializing_if = "Option::is_none")]
51    pub x5c: Option<serde_cbor::Value>,
52    #[serde(skip_serializing_if = "Option::is_none")]
53    pub ecdaa_key_id: Option<serde_cbor::Value>,
54}
55
56#[derive(Serialize, Deserialize, Clone, Debug)]
57pub struct FidoU2F {
58    #[serde(with = "serde_bytes")]
59    pub sig: Vec<u8>,
60    #[serde(skip_serializing_if = "Option::is_none")]
61    pub x5c: Option<serde_cbor::Value>,
62}
63
64#[derive(Serialize, Deserialize, Clone, Debug)]
65pub struct AndroidKey {
66    pub alg: i64,
67    #[serde(with = "serde_bytes")]
68    pub sig: Vec<u8>,
69    #[serde(skip_serializing_if = "Option::is_none")]
70    pub x5c: Option<serde_cbor::Value>,
71}
72
73#[derive(Serialize, Deserialize, Clone, Debug)]
74pub struct AndroidSafetynet {
75    pub ver: String,
76    #[serde(with = "serde_bytes")]
77    pub response: Vec<u8>,
78}
79
80#[derive(Serialize, Deserialize, Clone, Debug)]
81#[serde(untagged, rename_all = "camelCase")]
82pub enum AttestationStatement {
83    Packed(Packed),
84    TPM(TPM),
85    FidoU2F(FidoU2F),
86    AndroidKey(AndroidKey),
87    AndroidSafetynet(AndroidSafetynet),
88    None,
89}
90
91impl AttestationStatement {
92    pub fn to_cbor(self) -> Result<Value, Error> {
93        match self {
94            AttestationStatement::Packed(value) => serde_cbor::value::to_value(&value).map_err(Error::CborError),
95            AttestationStatement::TPM(value) => serde_cbor::value::to_value(&value).map_err(Error::CborError),
96            AttestationStatement::FidoU2F(value) => serde_cbor::value::to_value(&value).map_err(Error::CborError),
97            AttestationStatement::AndroidKey(value) => serde_cbor::value::to_value(&value).map_err(Error::CborError),
98            AttestationStatement::AndroidSafetynet(value) => serde_cbor::value::to_value(value).map_err(Error::CborError),
99            AttestationStatement::None => Ok(Value::Map(BTreeMap::new())),
100        }
101    }
102}
103
104#[derive(Serialize, Deserialize, Clone, Debug)]
105#[serde(rename_all = "camelCase")]
106pub struct AuthenticatorData {
107    pub rp_id_hash: [u8; 32],
108    pub flags: u8,
109    pub sign_count: u32,
110    pub attested_credential_data: Option<AttestedCredentialData>,
111    pub extensions: serde_cbor::Value,
112}
113
114impl AuthenticatorData {
115    pub fn from_vec(data: Vec<u8>) -> Result<(Self, Vec<u8>), Error> {
116        let mut cursor = Cursor::new(data);
117
118        let mut rp_id_hash = [0u8; 32];
119        cursor.read_exact(&mut rp_id_hash)?;
120
121        let flags = cursor.read_u8()?;
122        let has_attested_credential_data = flags & (1 << 6) > 0;
123        let has_extensions = flags & (1 << 7) > 0;
124
125        let sign_count = cursor.read_u32::<BigEndian>()?;
126
127        let mut remaining_cbor = Value::Null;
128        let attested_credential_data = if has_attested_credential_data {
129            let mut aaguid = [0u8; 16];
130            cursor.read_exact(&mut aaguid)?;
131
132            let length = cursor.read_u16::<BigEndian>()?;
133
134            let mut credential_id = vec![0u8; length as usize];
135            cursor.read_exact(&mut credential_id[..])?;
136
137            let mut remaining = vec![0u8; cursor.remaining()];
138            cursor.read_exact(&mut remaining[..])?;
139            let public_key_cbor = match serde_cbor::from_slice::<serde_cbor::Value>(remaining.as_slice()) {
140                Ok(cred) => cred,
141                Err(e) if has_extensions && e.is_syntax() => {
142                    // serde_cbor will send a `ErrorImpl` with code: `ErrorCode::TrailingData` and offset: offset of
143                    // first extra byte if we have Extensions blob afterward.
144                    // Since `ErrorImpl` is not public, the best we can do is catch the syntax category and retry
145                    // the slice before the first offset error.
146
147                    // The offset is incorectly reported as of serde_cbor 0.11.2;
148                    // If, for example, a buffer of 93 bytes contain a valid CBOR payload from [0..77] (77 bytes,
149                    // bytes from 0 to 76 as the 77 bound is exclusive), the reported offset in the error will be 78.
150                    let offset = (e.offset() - 1) as usize;
151
152                    remaining_cbor = serde_cbor::from_slice::<serde_cbor::Value>(&remaining[offset..])?;
153                    serde_cbor::from_slice::<serde_cbor::Value>(&remaining[..offset])?
154                }
155                Err(e) => return Err(Error::CborError(e)),
156            };
157
158            let credential_public_key = CredentialPublicKey::from_value(&public_key_cbor)?;
159
160            Some(AttestedCredentialData {
161                aaguid,
162                credential_id,
163                credential_public_key,
164            })
165        } else {
166            if has_extensions {
167                let mut remaining = vec![0u8; cursor.remaining()];
168                cursor.read_exact(&mut remaining[..])?;
169                remaining_cbor = serde_cbor::from_slice::<serde_cbor::Value>(remaining.as_slice()).map_err(Error::CborError)?;
170            }
171
172            None
173        };
174
175        let extensions = if has_extensions { remaining_cbor } else { Value::Null };
176
177        Ok((
178            AuthenticatorData {
179                rp_id_hash,
180                flags,
181                sign_count,
182                attested_credential_data,
183                extensions,
184            },
185            cursor.into_inner(),
186        ))
187    }
188
189    pub fn to_vec(self) -> Result<Vec<u8>, Error> {
190        let mut vec = vec![];
191        let _ = vec.write(&self.rp_id_hash)?;
192        vec.push(self.flags);
193        let _ = vec.write(&self.sign_count.to_be_bytes())?;
194
195        if let Some(att_cred_data) = self.attested_credential_data {
196            let _ = vec.write(&att_cred_data.aaguid)?;
197            let _ = vec.write(&(att_cred_data.credential_id.len() as u16).to_be_bytes())?;
198            let _ = vec.write(&att_cred_data.credential_id)?;
199            let _ = vec.write(&att_cred_data.credential_public_key.to_bytes()?)?;
200        }
201
202        Ok(vec)
203    }
204}
205
206#[derive(Serialize, Deserialize, Clone, Debug)]
207#[serde(rename_all = "camelCase")]
208pub struct AttestedCredentialData {
209    pub aaguid: [u8; 16],
210    pub credential_id: Vec<u8>,
211    pub credential_public_key: CredentialPublicKey,
212}
213
214#[derive(Serialize, Deserialize, Clone, Debug)]
215pub struct CredentialPublicKey {
216    pub key_type: i64,
217    pub alg: i64,
218    pub key_info: CoseKeyInfo,
219}
220
221#[derive(Serialize, Deserialize, Clone, Debug)]
222pub struct Rsa {
223    pub n: Vec<u8>,
224    pub e: Vec<u8>,
225}
226
227#[derive(Serialize, Deserialize, Clone, Debug)]
228pub struct EC2 {
229    pub curve: i64,
230    pub coords: Coordinates,
231}
232
233#[derive(Serialize, Deserialize, Clone, Debug)]
234pub struct OKP {
235    pub curve: i64,
236    pub coords: Coordinates,
237}
238
239#[derive(Serialize, Deserialize, Clone, Debug)]
240pub enum CoseKeyInfo {
241    OKP(OKP),
242    EC2(EC2),
243    RSA(Rsa),
244}
245
246impl CoseKeyInfo {
247    pub fn key_type(&self) -> i64 {
248        match self {
249            CoseKeyInfo::OKP(_) => 1,
250            CoseKeyInfo::EC2(_) => 2,
251            CoseKeyInfo::RSA(_) => 3,
252        }
253    }
254}
255
256impl CredentialPublicKey {
257    pub fn from_value(value: &serde_cbor::Value) -> Result<Self, Error> {
258        let map = match value {
259            Value::Map(m) => m,
260            _ => return Err(Error::Other("Invalid Cbor for CredentialPublicKey".to_string())),
261        };
262
263        let key_type = map
264            .get(&Value::Integer(1))
265            .map(|val| match val {
266                Value::Integer(i) => *i as i64,
267                _ => 0i64,
268            })
269            .ok_or_else(|| Error::Other("Key type missing".to_string()))?;
270
271        let alg = map
272            .get(&Value::Integer(3))
273            .map(|val| match val {
274                Value::Integer(i) => *i as i64,
275                _ => 0i64,
276            })
277            .ok_or_else(|| Error::Other("algorithm missing".to_string()))?;
278
279        match (key_type, CoseAlgorithmIdentifier::from(alg)) {
280            (WEBAUTH_PUBLIC_KEY_TYPE_EC2, CoseAlgorithmIdentifier::ES256) => {
281                let curve = map
282                    .get(&Value::Integer(-1))
283                    .map(|val| match val {
284                        Value::Integer(i) => *i as i64,
285                        _ => 0i64,
286                    })
287                    .ok_or_else(|| Error::Other("curve missing".to_string()))?;
288
289                let x = map
290                    .get(&Value::Integer(-2))
291                    .and_then(|val| match val {
292                        Value::Bytes(i) => {
293                            if i.len() < 32 {
294                                return None;
295                            }
296                            let mut array = [0u8; 32];
297                            array.copy_from_slice(&i[0..32]);
298                            Some(array)
299                        }
300                        _ => None,
301                    })
302                    .ok_or_else(|| Error::Other("x coordinate missing".to_string()))?;
303
304                let coords = map
305                    .get(&Value::Integer(-3))
306                    .and_then(|val| match val {
307                        Value::Bytes(i) => {
308                            if i.len() < 32 {
309                                return None;
310                            }
311                            let mut array = [0u8; 32];
312                            array.copy_from_slice(&i[0..32]);
313                            Some(Coordinates::Uncompressed { x, y: array })
314                        }
315
316                        Value::Bool(b) => Some(Coordinates::Compressed {
317                            x,
318                            y: if *b { ECDSA_Y_PREFIX_NEGATIVE } else { ECDSA_Y_PREFIX_POSITIVE },
319                        }),
320                        _ => None,
321                    })
322                    .ok_or_else(|| Error::Other("y coordinate missing".to_string()))?;
323
324                Ok(CredentialPublicKey {
325                    key_type,
326                    alg,
327                    key_info: CoseKeyInfo::EC2(EC2 { curve, coords }),
328                })
329            }
330            (WEBAUTH_PUBLIC_KEY_TYPE_OKP, CoseAlgorithmIdentifier::Ed25519) => {
331                let curve = map
332                    .get(&Value::Integer(-1))
333                    .map(|val| match val {
334                        Value::Integer(i) => *i as i64,
335                        _ => 0i64,
336                    })
337                    .ok_or_else(|| Error::Other("curve missing".to_string()))?;
338
339                let x = map
340                    .get(&Value::Integer(-2))
341                    .and_then(|val| match val {
342                        Value::Bytes(i) => {
343                            if i.len() < 32 {
344                                return None;
345                            }
346                            let mut array = [0u8; 32];
347                            array.copy_from_slice(&i[0..32]);
348                            Some(array)
349                        }
350                        _ => None,
351                    })
352                    .ok_or_else(|| Error::Other("x coordinate missing".to_string()))?;
353
354                let coords = map
355                    .get(&Value::Integer(-3))
356                    .and_then(|val| match val {
357                        Value::Bytes(i) => {
358                            if i.len() < 32 {
359                                return None;
360                            }
361                            let mut array = [0u8; 32];
362                            array.copy_from_slice(&i[0..32]);
363                            Some(Coordinates::Uncompressed { x, y: array })
364                        }
365
366                        Value::Bool(b) => Some(Coordinates::Compressed {
367                            x,
368                            y: if *b { ECDSA_Y_PREFIX_NEGATIVE } else { ECDSA_Y_PREFIX_POSITIVE },
369                        }),
370                        _ => None,
371                    })
372                    .ok_or_else(|| Error::Other("y coordinate missing".to_string()))?;
373
374                Ok(CredentialPublicKey {
375                    key_type,
376                    alg,
377                    key_info: CoseKeyInfo::OKP(OKP { curve, coords }),
378                })
379            }
380            (WEBAUTH_PUBLIC_KEY_TYPE_RSA, CoseAlgorithmIdentifier::RSA) => {
381                let n = map
382                    .get(&Value::Integer(-1))
383                    .and_then(|val| match val {
384                        Value::Bytes(i) => {
385                            let mut n = Vec::with_capacity(256);
386                            n.extend_from_slice(i);
387                            Some(n)
388                        }
389                        _ => None,
390                    })
391                    .ok_or_else(|| Error::Other("Invalid modulus for RSA key type".to_owned()))?;
392
393                let e = map
394                    .get(&Value::Integer(-2))
395                    .and_then(|val| match val {
396                        Value::Bytes(i) => {
397                            let mut e = Vec::with_capacity(3);
398                            e.extend_from_slice(i);
399                            Some(e)
400                        }
401                        _ => None,
402                    })
403                    .ok_or_else(|| Error::Other("Invalid exponent for RSA key type".to_owned()))?;
404
405                if n.len() != 256 || e.len() != 3 {
406                    return Err(Error::Other("Invalid RSA".to_owned()));
407                }
408
409                Ok(CredentialPublicKey {
410                    key_type,
411                    alg,
412                    key_info: CoseKeyInfo::RSA(Rsa { n, e }),
413                })
414            }
415            _ => Err(Error::Other("Cose key type not supported".to_owned())),
416        }
417    }
418
419    pub fn to_bytes(self) -> Result<Vec<u8>, Error> {
420        let mut map = IndexMap::new();
421        match self.key_info {
422            CoseKeyInfo::EC2(value) => {
423                map.insert(1, Value::Integer(WEBAUTH_PUBLIC_KEY_TYPE_EC2 as i128));
424                map.insert(3, Value::Integer(self.alg as i128));
425                map.insert(-1, Value::Integer(value.curve as i128));
426                match value.coords {
427                    Coordinates::Compressed { x, y } => {
428                        map.insert(-2, Value::Bytes(x.to_vec()));
429                        map.insert(-3, Value::Bool(y == ECDSA_Y_PREFIX_NEGATIVE));
430                    }
431
432                    Coordinates::Uncompressed { x, y } => {
433                        map.insert(-2, Value::Bytes(x.to_vec()));
434                        map.insert(-3, Value::Bytes(y.to_vec()));
435                    }
436
437                    Coordinates::None => {
438                        return Err(Error::Other("Invalid coordinates".to_string()));
439                    }
440                }
441            }
442
443            CoseKeyInfo::OKP(value) => {
444                map.insert(1, Value::Integer(WEBAUTH_PUBLIC_KEY_TYPE_OKP as i128));
445                map.insert(3, Value::Integer(self.alg as i128));
446                map.insert(-1, Value::Integer(value.curve as i128));
447                match value.coords {
448                    Coordinates::Compressed { x, y } => {
449                        map.insert(-2, Value::Bytes(x.to_vec()));
450                        map.insert(-3, Value::Bool(y == ECDSA_Y_PREFIX_NEGATIVE));
451                    }
452
453                    Coordinates::Uncompressed { x, y } => {
454                        map.insert(-2, Value::Bytes(x.to_vec()));
455                        map.insert(-3, Value::Bytes(y.to_vec()));
456                    }
457
458                    Coordinates::None => {
459                        return Err(Error::Other("Invalid coordinates".to_string()));
460                    }
461                }
462            }
463
464            CoseKeyInfo::RSA(value) => {
465                map.insert(1, Value::Integer(WEBAUTH_PUBLIC_KEY_TYPE_RSA as i128));
466                map.insert(3, Value::Integer(self.alg as i128));
467                map.insert(-1, Value::Bytes(value.n));
468                map.insert(-2, Value::Bytes(value.e));
469            }
470        };
471        to_vec(&map).map_err(Error::CborError)
472    }
473}
474
475pub trait Message {
476    fn from_base64(string: &str) -> Result<Self, Error>
477    where
478        Self: Sized;
479    fn from_bytes(raw_values: &[u8]) -> Result<Self, Error>
480    where
481        Self: Sized;
482
483    fn to_bytes(self) -> Result<Vec<u8>, Error>
484    where
485        Self: Sized;
486
487    fn to_base64(self) -> Result<String, Error>
488    where
489        Self: Sized;
490}
491
492impl Message for AttestationObject {
493    fn from_base64(string: &str) -> Result<Self, Error>
494    where
495        Self: Sized,
496    {
497        let raw_values = BASE64.decode(string)?;
498        Self::from_bytes(raw_values.as_slice())
499    }
500
501    fn from_bytes(raw_values: &[u8]) -> Result<Self, Error>
502    where
503        Self: Sized,
504    {
505        let value = serde_cbor::from_slice::<RawAttestationObject>(raw_values).map_err(Error::CborError)?;
506
507        let data = match value.auth_data {
508            Value::Bytes(vec) => Ok(vec),
509            _ => Err(Error::Other("Cannot proceed without auth data".to_string())),
510        }?;
511
512        let att_stmt = match value.fmt.as_str() {
513            WEBAUTHN_FORMAT_PACKED => serde_cbor::value::from_value::<Packed>(value.att_stmt)
514                .ok()
515                .map(AttestationStatement::Packed),
516
517            WEBAUTHN_FORMAT_FIDO_U2F => serde_cbor::value::from_value::<FidoU2F>(value.att_stmt)
518                .ok()
519                .map(AttestationStatement::FidoU2F),
520
521            WEBAUTHN_FORMAT_TPM => serde_cbor::value::from_value::<TPM>(value.att_stmt)
522                .ok()
523                .map(AttestationStatement::TPM),
524
525            WEBAUTHN_FORMAT_ANDROID_KEY => serde_cbor::value::from_value::<AndroidKey>(value.att_stmt)
526                .ok()
527                .map(AttestationStatement::AndroidKey),
528
529            WEBAUTHN_FORMAT_ANDROID_SAFETYNET => serde_cbor::value::from_value::<AndroidSafetynet>(value.att_stmt)
530                .ok()
531                .map(AttestationStatement::AndroidSafetynet),
532
533            WEBAUTHN_FORMAT_NONE => Some(AttestationStatement::None),
534
535            _ => None,
536        };
537
538        let (auth_data, raw_auth_data) = AuthenticatorData::from_vec(data)?;
539
540        Ok(AttestationObject {
541            auth_data,
542            raw_auth_data,
543            fmt: value.fmt,
544            att_stmt,
545        })
546    }
547
548    fn to_bytes(self) -> Result<Vec<u8>, Error>
549    where
550        Self: Sized,
551    {
552        let att_stmt = match self.att_stmt {
553            Some(v) => v.to_cbor()?,
554            None => Value::Null,
555        };
556
557        //Using an index map here because we must preserve ordering
558        let mut att_obj = IndexMap::new();
559        att_obj.insert("fmt".to_string(), Value::Text(self.fmt));
560        att_obj.insert("attStmt".to_string(), att_stmt);
561        att_obj.insert("authData".to_string(), Value::Bytes(self.auth_data.to_vec()?));
562        to_vec(&att_obj).map_err(Error::CborError)
563    }
564
565    fn to_base64(self) -> Result<String, Error>
566    where
567        Self: Sized,
568    {
569        Ok(BASE64.encode(Self::to_bytes(self)?))
570    }
571}
572
573#[derive(Serialize, Deserialize, Clone, Debug)]
574pub enum Coordinates {
575    Compressed { x: [u8; 32], y: u8 },
576    Uncompressed { x: [u8; 32], y: [u8; 32] },
577    None,
578}
579
580impl Coordinates {
581    pub fn to_vec(&self) -> Vec<u8> {
582        let mut key = Vec::new();
583        match self {
584            Coordinates::Compressed { x, y } => {
585                key.push(*y);
586                key.append(&mut x.to_vec());
587            }
588
589            Coordinates::Uncompressed { x, y } => {
590                key.push(ECDSA_Y_PREFIX_UNCOMPRESSED);
591                key.append(&mut x.to_vec());
592                key.append(&mut y.to_vec());
593            }
594
595            _ => {}
596        }
597
598        key
599    }
600}
601
602impl Display for Coordinates {
603    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
604        let mut key = Vec::new();
605        match self {
606            Coordinates::Compressed { x, y } => {
607                key.push(*y);
608                key.append(&mut x.to_vec());
609            }
610
611            Coordinates::Uncompressed { x, y } => {
612                key.push(ECDSA_Y_PREFIX_UNCOMPRESSED);
613                key.append(&mut x.to_vec());
614                key.append(&mut y.to_vec());
615            }
616
617            _ => {}
618        }
619
620        write!(f, "{}", BASE64_URLSAFE_NOPAD.encode(&key))
621    }
622}
623
624impl FromStr for Coordinates {
625    type Err = Error;
626
627    fn from_str(s: &str) -> Result<Self, Self::Err> {
628        let key = BASE64_URLSAFE_NOPAD.decode(s).map_err(Error::Base64Error)?;
629
630        match key[0] {
631            ECDSA_Y_PREFIX_UNCOMPRESSED => {
632                if key.len() == 65 {
633                    let mut x = [0u8; 32];
634                    let mut y = [0u8; 32];
635
636                    x.copy_from_slice(&key[1..33]);
637                    y.copy_from_slice(&key[33..65]);
638
639                    Ok(Coordinates::Uncompressed { x, y })
640                } else {
641                    Err(Error::Other("Key is wrong length".to_string()))
642                }
643            }
644
645            ECDSA_Y_PREFIX_POSITIVE => {
646                if key.len() == 33 {
647                    let mut x = [0u8; 32];
648                    x.copy_from_slice(&key[1..32]);
649
650                    Ok(Coordinates::Compressed {
651                        x,
652                        y: ECDSA_Y_PREFIX_POSITIVE,
653                    })
654                } else {
655                    Err(Error::Other("Key is wrong length".to_string()))
656                }
657            }
658
659            ECDSA_Y_PREFIX_NEGATIVE => {
660                if key.len() == 33 {
661                    let mut x = [0u8; 32];
662                    x.copy_from_slice(&key[1..32]);
663
664                    Ok(Coordinates::Compressed {
665                        x,
666                        y: ECDSA_Y_PREFIX_NEGATIVE,
667                    })
668                } else {
669                    Err(Error::Other("Key is wrong length".to_string()))
670                }
671            }
672
673            _ => Err(Error::Other("Key prefix missing".to_string())),
674        }
675    }
676}
677
678#[derive(PartialEq, Debug, Default, Serialize, Deserialize, Clone, Copy)]
679pub enum CoseAlgorithmIdentifier {
680    Ed25519 = -8,
681    #[serde(alias = "EC2")]
682    ES256 = -7,
683    RSA = -257,
684    RS1 = -65535,
685    #[default]
686    NotSupported,
687}
688
689impl From<i64> for CoseAlgorithmIdentifier {
690    fn from(value: i64) -> Self {
691        match value {
692            -65535 => CoseAlgorithmIdentifier::RS1,
693            -257 => CoseAlgorithmIdentifier::RSA,
694            -7 => CoseAlgorithmIdentifier::ES256,
695            -8 => CoseAlgorithmIdentifier::Ed25519,
696            _ => CoseAlgorithmIdentifier::NotSupported,
697        }
698    }
699}
700
701impl From<i32> for CoseAlgorithmIdentifier {
702    fn from(value: i32) -> Self {
703        match value {
704            -65535 => CoseAlgorithmIdentifier::RS1,
705            -257 => CoseAlgorithmIdentifier::RSA,
706            -7 => CoseAlgorithmIdentifier::ES256,
707            -8 => CoseAlgorithmIdentifier::Ed25519,
708            _ => CoseAlgorithmIdentifier::NotSupported,
709        }
710    }
711}
712
713impl From<CoseAlgorithmIdentifier> for i64 {
714    fn from(value: CoseAlgorithmIdentifier) -> Self {
715        match value {
716            CoseAlgorithmIdentifier::RS1 => -65535,
717            CoseAlgorithmIdentifier::RSA => -257,
718            CoseAlgorithmIdentifier::ES256 => -7,
719            CoseAlgorithmIdentifier::Ed25519 => -8,
720            _ => -65536, //Unassigned
721        }
722    }
723}
724
725#[derive(PartialEq, Debug, Copy, Clone)]
726pub enum AttestationFlags {
727    UserPresent = 1,
728    //Reserved for future = 2
729    UserVerified = 4,
730    BackupEligible = 8,
731    BackedUp = 16,
732    //Reserved for future = 32
733    AttestedCredentialDataIncluded = 64,
734    ExtensionDataIncluded = 128,
735}