Skip to main content

webauthn_rs_device_catalog/
lib.rs

1pub mod aaguid;
2pub mod certificate_authority;
3pub mod device;
4pub mod image;
5pub mod manufacturer;
6pub mod quirks;
7
8pub mod query;
9
10pub mod data;
11
12use std::fmt;
13
14use crate::device::*;
15use crate::query::Query;
16use std::rc::Rc;
17
18use webauthn_attestation_ca::AttestationCaList;
19
20pub mod prelude {
21    pub use crate::aaguid::Aaguid;
22    pub use crate::certificate_authority::Authority;
23    pub use crate::device::{Device, Sku};
24    pub use crate::manufacturer::Manufacturer;
25    pub use crate::query::Query;
26    pub use crate::{Data, DataBuilder};
27
28    pub(crate) use openssl::error::ErrorStack as OpenSSLErrorStack;
29    pub(crate) use openssl::x509;
30    pub(crate) use std::collections::BTreeSet;
31    pub(crate) use std::rc::Rc;
32}
33
34#[derive(Default)]
35pub struct DataBuilder {
36    devices: Vec<Rc<Device>>,
37}
38
39impl DataBuilder {
40    pub fn build(self) -> Data {
41        Data {
42            devices: self.devices,
43        }
44    }
45}
46
47pub struct Data {
48    devices: Vec<Rc<Device>>,
49}
50
51impl Default for Data {
52    fn default() -> Self {
53        Self::strict()
54    }
55}
56
57impl fmt::Display for Data {
58    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59        write!(f, "Data")
60    }
61}
62
63impl Data {
64    /// This is a list of webauthn authenticators that are of the highest
65    /// quality and guarantees for users and RP's. These are devices that not only
66    /// are secure, but user friendly, consistent, and correct.
67    pub fn strict() -> Self {
68        DataBuilder::default().add_yubico().build()
69    }
70
71    pub fn all_known_devices() -> Self {
72        DataBuilder::default().add_yubico().build()
73    }
74
75    pub fn query(&self, query: &Query) -> Option<Data> {
76        tracing::debug!(?query);
77
78        let devices: Vec<_> = self
79            .devices
80            .iter()
81            .filter(|dev| dev.query_match(query))
82            // This is cheap due to Rc,
83            .cloned()
84            .collect();
85
86        if devices.is_empty() {
87            None
88        } else {
89            Some(Data { devices })
90        }
91    }
92
93    pub fn iter(&self) -> impl Iterator<Item = &Device> {
94        self.devices.iter().map(|d| d.as_ref())
95    }
96}
97
98// Allowed as AttestationCaList is foreign.
99#[allow(clippy::from_over_into)]
100impl TryInto<AttestationCaList> for &Data {
101    type Error = crate::prelude::OpenSSLErrorStack;
102
103    fn try_into(self) -> Result<AttestationCaList, Self::Error> {
104        AttestationCaList::from_iter(self.devices.iter().flat_map(|dev| {
105            dev.aaguid
106                .ca
107                .iter()
108                .map(|ca| (ca.ca.clone(), dev.aaguid.id))
109        }))
110    }
111}
112
113#[cfg(test)]
114mod test {
115    use super::*;
116
117    #[test]
118    fn test_init_data() {
119        let data = Data::default();
120
121        println!("{data}");
122        for i in data.iter() {
123            println!("{0:?}", i);
124        }
125    }
126}