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 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 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, }
722 }
723}
724
725#[derive(PartialEq, Debug, Copy, Clone)]
726pub enum AttestationFlags {
727 UserPresent = 1,
728 UserVerified = 4,
730 BackupEligible = 8,
731 BackedUp = 16,
732 AttestedCredentialDataIncluded = 64,
734 ExtensionDataIncluded = 128,
735}