Skip to main content

webauthn_kit/
aaguid.rs

1//! Well-known authenticator AAGUIDs (informational registry).
2//!
3//! The AAGUID (Authenticator Attestation Globally Unique Identifier) is the
4//! 16-byte model identifier embedded in attested credential data during
5//! registration (CTAP2 §6.1). [`known_aaguid`] maps a small set of widely
6//! deployed AAGUIDs to human-readable authenticator names for display and
7//! inventory purposes.
8//!
9//! # This is NOT a metadata service
10//!
11//! This table is **informational only**. Real authenticator policy — attestation
12//! root certificates, authenticator capabilities, security notices, revocation
13//! — requires the [FIDO Metadata Service](https://fidoalliance.org/metadata/)
14//! (MDS), whose signed blob is versioned and refreshed continuously. MDS
15//! lookup is future work (see THREAT-MODEL.md OPEN-3); an AAGUID absent from
16//! this table is *unknown*, not *untrusted*, and an entry here confers no
17//! trust beyond the attestation verification that already happened.
18//!
19//! # Sources
20//!
21//! - Hardware security keys: FIDO Metadata Service (MDS3) metadata
22//!   statements, `https://mds3.fidoalliance.org/` (retrieved 2026-09).
23//! - Platform authenticators (Apple/Google/Microsoft) and password managers:
24//!   the community-maintained
25//!   [`passkey-authenticator-aaguids`](https://github.com/passkeydeveloper/passkey-authenticator-aaguids)
26//!   registry (retrieved 2026-09).
27//! - Windows Hello hardware/software/VBS AAGUIDs additionally match
28//!   Microsoft's published documentation.
29
30/// YubiKey 5 Series (USB-A/C, firmware 5.x). Source: FIDO MDS3.
31pub const AAGUID_NAME_YUBIKEY_5_SERIES: &str = "YubiKey 5 Series";
32/// YubiKey 5 Series with NFC (firmware 5.x). Source: FIDO MDS3.
33pub const AAGUID_NAME_YUBIKEY_5_NFC: &str = "YubiKey 5 Series with NFC";
34/// YubiKey 5 Series with Lightning connector (5Ci). Source: FIDO MDS3.
35pub const AAGUID_NAME_YUBIKEY_5C: &str = "YubiKey 5 Series with Lightning";
36/// YubiKey 5 FIPS Series (USB). Source: FIDO MDS3.
37pub const AAGUID_NAME_YUBIKEY_5_FIPS: &str = "YubiKey 5 FIPS Series";
38/// YubiKey 5 FIPS Series with NFC. Source: FIDO MDS3.
39pub const AAGUID_NAME_YUBIKEY_5_FIPS_NFC: &str = "YubiKey 5 FIPS Series with NFC";
40/// YubiKey Bio Series — FIDO Edition (fingerprint). Source: FIDO MDS3.
41pub const AAGUID_NAME_YUBIKEY_BIO: &str = "YubiKey Bio Series - FIDO Edition";
42/// Security Key by Yubico (USB). Source: FIDO MDS3.
43pub const AAGUID_NAME_SECURITY_KEY_YUBICO: &str = "Security Key by Yubico";
44/// Security Key NFC by Yubico. Source: FIDO MDS3.
45pub const AAGUID_NAME_SECURITY_KEY_YUBICO_NFC: &str = "Security Key NFC by Yubico";
46/// Feitian ePass FIDO2 (USB). Source: FIDO MDS3.
47pub const AAGUID_NAME_FEITIAN_EPASS_FIDO2: &str = "Feitian ePass FIDO2 Authenticator";
48/// Feitian ePass FIDO2-NFC. Source: FIDO MDS3.
49pub const AAGUID_NAME_FEITIAN_EPASS_FIDO2_NFC: &str = "Feitian ePass FIDO2-NFC Authenticator";
50/// Feitian BioPass FIDO2 Pro (fingerprint). Source: FIDO MDS3.
51pub const AAGUID_NAME_FEITIAN_BIOPASS_PRO: &str = "Feitian BioPass FIDO2 Pro Authenticator";
52/// SoloKeys Solo (Secp256R1 build). Source: FIDO MDS3.
53pub const AAGUID_NAME_SOLO_SECP256R1: &str = "SoloKeys Solo Secp256R1 FIDO2 Authenticator";
54/// SoloKeys Solo Tap. Source: FIDO MDS3.
55pub const AAGUID_NAME_SOLO_TAP: &str = "SoloKeys Solo Tap Secp256R1 FIDO2 Authenticator";
56/// Nitrokey 3 (AM build). Source: FIDO MDS3.
57pub const AAGUID_NAME_NITROKEY_3: &str = "Nitrokey 3 AM";
58/// Google Titan Security Key v2. Source: FIDO MDS3.
59pub const AAGUID_NAME_GOOGLE_TITAN_V2: &str = "Google Titan Security Key v2";
60/// Apple iCloud Keychain synced passkeys. Source: passkey-authenticator-aaguids.
61pub const AAGUID_NAME_APPLE_ICLOUD_KEYCHAIN: &str = "Apple iCloud Keychain (Managed)";
62/// Apple Passwords app. Source: passkey-authenticator-aaguids.
63pub const AAGUID_NAME_APPLE_PASSWORDS: &str = "Apple Passwords";
64/// Google Password Manager (Android/Chrome synced passkeys).
65/// Source: passkey-authenticator-aaguids.
66pub const AAGUID_NAME_GOOGLE_PASSWORD_MANAGER: &str = "Google Password Manager";
67/// Chrome profile-bound passkeys on macOS. Source: passkey-authenticator-aaguids.
68pub const AAGUID_NAME_CHROME_ON_MAC: &str = "Chrome on Mac";
69/// Windows Hello hardware authenticator (TPM). Source: Microsoft documentation / FIDO MDS3.
70pub const AAGUID_NAME_WINDOWS_HELLO_HARDWARE: &str = "Windows Hello Hardware Authenticator";
71/// Windows Hello software authenticator. Source: Microsoft documentation / FIDO MDS3.
72pub const AAGUID_NAME_WINDOWS_HELLO_SOFTWARE: &str = "Windows Hello Software Authenticator";
73/// Windows Hello VBS (virtualization-based security) hardware authenticator.
74/// Source: Microsoft documentation / FIDO MDS3.
75pub const AAGUID_NAME_WINDOWS_HELLO_VBS: &str = "Windows Hello VBS Hardware Authenticator";
76/// 1Password. Source: passkey-authenticator-aaguids.
77pub const AAGUID_NAME_ONEPASSWORD: &str = "1Password";
78/// Bitwarden. Source: passkey-authenticator-aaguids.
79pub const AAGUID_NAME_BITWARDEN: &str = "Bitwarden";
80/// Dashlane. Source: passkey-authenticator-aaguids.
81pub const AAGUID_NAME_DASHLANE: &str = "Dashlane";
82/// Keeper. Source: passkey-authenticator-aaguids.
83pub const AAGUID_NAME_KEEPER: &str = "Keeper";
84/// KeePassXC. Source: passkey-authenticator-aaguids.
85pub const AAGUID_NAME_KEEPASSXC: &str = "KeePassXC";
86/// Samsung Pass. Source: passkey-authenticator-aaguids.
87pub const AAGUID_NAME_SAMSUNG_PASS: &str = "Samsung Pass";
88
89/// AAGUID → name table entry (the 16-byte AAGUID is written in UUID order,
90/// exactly as it appears in attested credential data).
91const KNOWN_AAGUIDS: &[([u8; 16], &str)] = &[
92    // --- Yubico (FIDO MDS3) ---
93    (
94        uuid("cb69481e-8ff7-4039-93ec-0a2729a154a8"),
95        AAGUID_NAME_YUBIKEY_5_SERIES,
96    ),
97    (
98        uuid("fa2b99dc-9e39-4257-8f92-4a30d23c4118"),
99        AAGUID_NAME_YUBIKEY_5_NFC,
100    ),
101    (
102        uuid("a02167b9-ae71-4ac7-9a07-06432ebb6f1c"),
103        AAGUID_NAME_YUBIKEY_5C,
104    ),
105    (
106        uuid("57f7de54-c807-4eab-b1c6-1c9be7984e92"),
107        AAGUID_NAME_YUBIKEY_5_FIPS,
108    ),
109    (
110        uuid("c1f9a0bc-1dd2-404a-b27f-8e29047a43fd"),
111        AAGUID_NAME_YUBIKEY_5_FIPS_NFC,
112    ),
113    (
114        uuid("d8522d9f-575b-4866-88a9-ba99fa02f35b"),
115        AAGUID_NAME_YUBIKEY_BIO,
116    ),
117    (
118        uuid("f8a011f3-8c0a-4d15-8006-17111f9edc7d"),
119        AAGUID_NAME_SECURITY_KEY_YUBICO,
120    ),
121    (
122        uuid("b7d3f68e-88a6-471e-9ecf-2df26d041ede"),
123        AAGUID_NAME_SECURITY_KEY_YUBICO_NFC,
124    ),
125    // --- Feitian (FIDO MDS3) ---
126    (
127        uuid("833b721a-ff5f-4d00-bb2e-bdda3ec01e29"),
128        AAGUID_NAME_FEITIAN_EPASS_FIDO2,
129    ),
130    (
131        uuid("ee041bce-25e5-4cdb-8f86-897fd6418464"),
132        AAGUID_NAME_FEITIAN_EPASS_FIDO2_NFC,
133    ),
134    (
135        uuid("4c0cf95d-2f40-43b5-ba42-4c83a11c04ba"),
136        AAGUID_NAME_FEITIAN_BIOPASS_PRO,
137    ),
138    // --- SoloKeys (FIDO MDS3) ---
139    (
140        uuid("8876631b-d4a0-427f-5773-0ec71c9e0279"),
141        AAGUID_NAME_SOLO_SECP256R1,
142    ),
143    (
144        uuid("8976631b-d4a0-427f-5773-0ec71c9e0279"),
145        AAGUID_NAME_SOLO_TAP,
146    ),
147    // --- Nitrokey (FIDO MDS3) ---
148    (
149        uuid("2cd2f727-f6ca-44da-8f48-5c2e5da000a2"),
150        AAGUID_NAME_NITROKEY_3,
151    ),
152    // --- Google hardware (FIDO MDS3) ---
153    (
154        uuid("42b4fb4a-2866-43b2-9bf7-6c6669c2e5d3"),
155        AAGUID_NAME_GOOGLE_TITAN_V2,
156    ),
157    // --- Platform authenticators (passkey-authenticator-aaguids / Microsoft) ---
158    (
159        uuid("dd4ec289-e01d-41c9-bb89-70fa845d4bf2"),
160        AAGUID_NAME_APPLE_ICLOUD_KEYCHAIN,
161    ),
162    (
163        uuid("fbfc3007-154e-4ecc-8c0b-6e020557d7bd"),
164        AAGUID_NAME_APPLE_PASSWORDS,
165    ),
166    (
167        uuid("ea9b8d66-4d01-1d21-3ce4-b6b48cb575d4"),
168        AAGUID_NAME_GOOGLE_PASSWORD_MANAGER,
169    ),
170    (
171        uuid("adce0002-35bc-c60a-648b-0b25f1f05503"),
172        AAGUID_NAME_CHROME_ON_MAC,
173    ),
174    (
175        uuid("08987058-cadc-4b81-b6e1-30de50dcbe96"),
176        AAGUID_NAME_WINDOWS_HELLO_HARDWARE,
177    ),
178    (
179        uuid("9ddd1817-af5a-4672-a2b9-3e3dd95000a9"),
180        AAGUID_NAME_WINDOWS_HELLO_SOFTWARE,
181    ),
182    (
183        uuid("6028b017-b1d4-4c02-b4b3-afcdafc96bb2"),
184        AAGUID_NAME_WINDOWS_HELLO_VBS,
185    ),
186    // --- Password managers (passkey-authenticator-aaguids) ---
187    (
188        uuid("bada5566-a7aa-401f-bd96-45619a55120d"),
189        AAGUID_NAME_ONEPASSWORD,
190    ),
191    (
192        uuid("d548826e-79b4-db40-a3d8-11116f7e8349"),
193        AAGUID_NAME_BITWARDEN,
194    ),
195    (
196        uuid("531126d6-e717-415c-9320-3d9aa6981239"),
197        AAGUID_NAME_DASHLANE,
198    ),
199    (
200        uuid("0ea242b4-43c4-4a1b-8b17-dd6d0b6baec6"),
201        AAGUID_NAME_KEEPER,
202    ),
203    (
204        uuid("fdb141b2-5d84-443e-8a35-4698c205a502"),
205        AAGUID_NAME_KEEPASSXC,
206    ),
207    (
208        uuid("53414d53-554e-4700-0000-000000000000"),
209        AAGUID_NAME_SAMSUNG_PASS,
210    ),
211];
212
213/// Parse the canonical textual UUID form into its 16 big-endian bytes
214/// (the layout used in attested credential data).
215const fn uuid(s: &str) -> [u8; 16] {
216    // hex nibble → value, const-friendly.
217    const fn nibble(c: u8) -> u8 {
218        match c {
219            b'0'..=b'9' => c - b'0',
220            b'a'..=b'f' => c - b'a' + 10,
221            b'A'..=b'F' => c - b'A' + 10,
222            _ => panic!("invalid hex digit in AAGUID constant"),
223        }
224    }
225    let bytes = s.as_bytes();
226    let mut out = [0u8; 16];
227    let mut out_i = 0;
228    let mut i = 0;
229    // Walk the canonical 8-4-4-4-12 form, skipping '-' separators.
230    while i < bytes.len() {
231        let c = bytes[i];
232        if c == b'-' {
233            i += 1;
234            continue;
235        }
236        if out_i >= 16 || i + 1 >= bytes.len() {
237            panic!("AAGUID constant is not a 16-byte UUID");
238        }
239        out[out_i] = (nibble(c) << 4) | nibble(bytes[i + 1]);
240        out_i += 1;
241        i += 2;
242    }
243    if out_i != 16 {
244        panic!("AAGUID constant is not a 16-byte UUID");
245    }
246    out
247}
248
249/// Look up a known authenticator model by its AAGUID.
250///
251/// Returns the human-readable authenticator name for well-known AAGUIDs, or
252/// `None` if the AAGUID is not in this build's registry. An unknown AAGUID is
253/// *not* evidence of a bad registration — the vast majority of authenticators
254/// are not in this deliberately small informational table. Security policy
255/// must be based on attestation verification, not on this lookup.
256///
257/// # Requirements
258/// REQ-WA-131
259pub fn known_aaguid(aaguid: &[u8; 16]) -> Option<&'static str> {
260    KNOWN_AAGUIDS
261        .iter()
262        .find(|(known, _)| known == aaguid)
263        .map(|(_, name)| *name)
264}
265
266// Tests exercise failure paths and invariants directly; unwrap/expect,
267// slicing, and panicking asserts are acceptable here — violations
268// surface as test failures, not production panics.
269#[allow(
270    clippy::unwrap_used,
271    clippy::expect_used,
272    clippy::indexing_slicing,
273    clippy::panic
274)]
275#[cfg(test)]
276mod tests {
277    use super::*;
278
279    /// REQ-WA-131: well-known AAGUIDs resolve to their authenticator names.
280    #[test]
281    fn known_entries_resolve() {
282        let yubikey5 = uuid("cb69481e-8ff7-4039-93ec-0a2729a154a8");
283        assert_eq!(known_aaguid(&yubikey5), Some(AAGUID_NAME_YUBIKEY_5_SERIES));
284
285        let windows_hello = uuid("08987058-cadc-4b81-b6e1-30de50dcbe96");
286        assert_eq!(
287            known_aaguid(&windows_hello),
288            Some(AAGUID_NAME_WINDOWS_HELLO_HARDWARE)
289        );
290
291        let gpm = uuid("ea9b8d66-4d01-1d21-3ce4-b6b48cb575d4");
292        assert_eq!(
293            known_aaguid(&gpm),
294            Some(AAGUID_NAME_GOOGLE_PASSWORD_MANAGER)
295        );
296    }
297
298    /// REQ-WA-131: unknown and all-zero AAGUIDs return `None` (never a name).
299    #[test]
300    fn unknown_aaguid_returns_none() {
301        assert_eq!(known_aaguid(&[0u8; 16]), None);
302        assert_eq!(known_aaguid(&[0xEE; 16]), None);
303    }
304
305    /// The `uuid` const helper must produce the big-endian byte layout used
306    /// in attested credential data (UUID text form → raw bytes).
307    #[test]
308    fn uuid_helper_layout() {
309        // 53414d53-554e-4700-0000-000000000000 is ASCII "SAMSUNG" — a
310        // self-checking vector for byte order.
311        let samsung = uuid("53414d53-554e-4700-0000-000000000000");
312        assert_eq!(&samsung[0..7], b"SAMSUNG");
313        assert_eq!(samsung[7], 0x00);
314        assert_eq!(&samsung[8..], &[0u8; 8]);
315    }
316}