I built a FIDO2 hardware key. OpenAI says it isn't hardware enough A developer built a working FIDO2 hardware security key on an ESP32-S3 running Pico FIDO2 firmware, but OpenAI's Daybreak classified the registered credential as a Passkey rather than a hardware security key, even though Daybreak explicitly rejects software and synced passkeys and requires physical FIDO2 hardware keys. OpenAI Support told the developer that its documentation states no requirement that a key be listed in the FIDO Metadata Service or use attestation, yet the key is still not accepted as hardware. The device reports AAGUID 89fb94b7-06c9-3673-9b7e-30526d968145, self-derived from the first 16 bytes of SHA256("Pico FIDO2"), and supports CTAP 2.1, U2F, client PIN, resident credentials, credBlob, credProtect, hmac-secret and largeBlobKey. this started because OpenAI Daybreak requires physical FIDO2 hardware security keys and I had the obvious thought: fine, I'll build one. I already had an ESP32-S3. Pico FIDO2 runs on it. USB HID. CTAP 2.1. FIDO2. U2F. client PIN. resident credentials. the whole point of the thing is to be a security key. I flashed it, fixed a stupid Linux problem, registered it with OpenAI, authenticated with it, and then OpenAI filed the credential under Passkey . not security key. not hardware key. Passkey. that matters because Daybreak explicitly rejects software/synced passkeys and requires physical FIDO2 hardware keys. there is an ESP32-S3 physically sitting on my desk doing the cryptography, so I had questions. first, udev wasted a chunk of my afternoon initially Chromium could not see the key at all. /dev/hidraw8 600 root:root I had a sane udev rule: SUBSYSTEM=="hidraw", ATTRS{idVendor}=="feff", ATTRS{idProduct}=="fcfd", GROUP="plugdev", MODE="0660", TAG+="uaccess" udevadm test said the rule worked. the actual device did not care. eventually I checked the daemon instead of rewriting the same fucking rule again. pid 611 udevd ELAPSED 17-08:49 CPU TIME 1-23:59:58 11.5% 1 thread udevd had burned almost two days of CPU in a single-thread spin loop and was processing no events. KERNEL remove .../hidraw8 KERNEL add .../hidraw8 no UDEV event daemon is apparently busy contemplating death restart it: sudo sv restart /var/service/udevd and suddenly: /dev/hidraw8 660 root:plugdev /dev/hidraw9 660 root:plugdev fine. solved. also a useful reminder that udevadm test is a simulation. it can tell you the rules on disk are perfect while the actual daemon has been dead behind the eyes for seventeen days. the key works once Chromium could open it, the actual FIDO side was boring in the best possible way. proto: 0x02 major: 0x06 minor: 0x06 caps: 0x05 wink, cbor, msg version strings: U2F V2, FIDO 2 0, FIDO 2 1 extension strings: credBlob, credProtect, hmac-secret, largeBlobKey, minPinLength, thirdPartyPayment algorithms: es256 public-key , es384 public-key aaguid: 89fb94b706c936739b7e30526d968145 options: noep, rk, noalwaysUv, credMgmt, authnrCfg, clientPin, largeBlobs, pinUvAuthToken, setMinPINLength fwversion: 0x606 maxmsgsiz: 1024 maxcredlen: 1024 pin protocols: 1, 2 Chrome sees it. OpenAI registers a credential on it. OpenAI authenticates with it. ESP32-S3 | | USB HID v Chromium | | WebAuthn / CTAP2 v OpenAI | +-- registration works +-- authentication works -- classification: Passkey that last line is the problem. the original theory: AAGUID and attestation Pico FIDO2 reports this AAGUID: 89fb94b7-06c9-3673-9b7e-30526d968145 it is self-assigned by the firmware project. the source literally derives it from the first 16 bytes of SHA256 "Pico FIDO2" . it is not a commercial authenticator model sitting in the FIDO Metadata Service, and the firmware self-attests instead of presenting a manufacturer attestation chain. so my first theory was obvious: OpenAI is probably using AAGUID metadata and/or attestation to decide whether something gets the special "hardware security key" classification. that still looks plausible from the outside. but now I have Support's answer, and it makes the whole thing more interesting. support: no, MDS and attestation are not documented requirements I sent OpenAI the AAGUID, the CTAP output, the hardware and firmware details, the browser/device information, the time of the failure, and the fact that registration succeeds but the credential lands in the Passkey bucket. Support came back with this: the docs do not state any requirement that the key be MDS-listed or use attestation. good. that is exactly the distinction I wanted clarified. except they still do not accept the key as hardware. they also said there is no documented mechanism for adding an arbitrary AAGUID to an allowlist. and the documented workaround is basically: if this physical FIDO2 key works but we do not recognize it as hardware, use a different physical FIDO2 key. which is a workaround. it is not a definition of compatible. at this point the facts are: physical hardware: yes FIDO2 / CTAP 2.1: yes registration with OpenAI: works authentication with OpenAI: works MDS listing documented as required: no attestation documented as required: no arbitrary AAGUID allowlist path: no OpenAI counts it as hardware: no technical reason given: no so there is clearly another property being enforced somewhere. I just cannot tell you what it is because apparently neither the public docs nor the support answer will define it. and then support told me I actually need two fucking keys this part is almost funnier. Advanced Account Security requires two sign-in methods. normally that can be some combination of passkeys and hardware keys. Daybreak, however, disallows software/synced passkeys and wants physical hardware keys. Support explicitly pointed out that one qualifying hardware key is not enough. so the practical requirement is: Advanced Account Security: 2 sign-in methods required Daybreak: software/synced passkeys do not qualify therefore: 2 qualifying physical hardware keys I had Google Password Manager enrolled as another method. that has to go. my ESP32 key does not count. so I do not merely need to go buy a hardware key because OpenAI will not recognize the one I built. I effectively need to go buy two . that is a materially different requirement from reading "at least one compatible FIDO2 hardware security key" and thinking, reasonably, that you need one fucking key. the documentation can technically be reconciled. that does not make it good. I can see how this happened. the Daybreak page describes its hardware requirement. the Advanced Account Security page separately describes the two-method requirement. combine both policies and you arrive at two hardware keys. so this is not some impossible logical contradiction. it is just a really shitty way to communicate a requirement that costs money and can lock somebody out of access if they discover it at the deadline. if the actual onboarding requirement is: two physical FIDO2 hardware security keys recognized by OpenAI's classifier and no software/synced passkeys put that sentence on the page. do not make people solve a little requirements algebra problem between two help articles while a deadline is three days away. attestation still sucks when it becomes an invisible admission ticket the Support answer matters here because I do not want to claim something I cannot prove. I cannot say "OpenAI requires MDS attestation." Support specifically says that is not a published requirement, and I cannot see their classifier. I can say the thing in my hand is physical hardware, implements the protocol, works for authentication, and is still not considered a qualifying hardware key. something beyond protocol compatibility is therefore being used to make that decision. attestation and authenticator metadata remain the obvious suspects because that is exactly what those mechanisms are for: establishing authenticator provenance and model identity. and this is where I dislike the whole setup. attestation has legitimate uses. if a company wants to require a specific certified device model, fine. if it wants supply-chain assurance, fine. if it wants to exclude random homemade authenticators because it does not trust their implementation quality, fine. but those are different security properties from "physical FIDO2 key." my key does not become software because I built it. the private key does not magically leave the ESP32 because nobody paid to put the device in a vendor metadata registry. and if the policy is really "we only trust authenticator models we recognize," then the policy should say exactly that. otherwise an open protocol has an invisible corporate admission layer sitting on top of it. the protocol can be open. the firmware can be open source. I can implement the thing correctly enough for the relying party to use it. and then at the last step some opaque classifier says no because the device lacks whatever pedigree the backend expects. that mostly benefits established authenticator vendors and relying parties that want to outsource trust to vendor identity. it does not do much for open hardware, research hardware, or some asshole with an ESP32 who actually followed the standard. no, I am not going to make it pretend to be a yubikey the obvious shitty hack would be to copy the AAGUID from a known commercial authenticator into the firmware. no. first, that is lying about what the device is. second, if OpenAI really verifies attestation, the fake AAGUID accomplishes nothing because I do not have the manufacturer's attestation private key and certificate chain. third, making an authentication system happy by impersonating somebody else's authenticator is the exact opposite of what I am trying to test. I want the device accepted or rejected as what it actually is: a physical, self-built, standards-compatible FIDO2 authenticator if that class is not acceptable for Daybreak, cool. document that. what support actually resolved the answer was useful in one sense. we now know this is not supposed to be read as: MDS listing is explicitly required attestation is explicitly required because Support says neither requirement exists in the published guidance. we also know there is no documented arbitrary-AAGUID allowlist path. and we know the practical Daybreak setup needs two qualifying physical keys, because AAS needs two methods and Daybreak excludes the passkey class. what we still do not know is the simplest question: what technical property makes one working physical FIDO2 authenticator "hardware" to OpenAI and another one "Passkey"? that question remains unanswered. where it stands now udev problem: fixed ESP32-S3 FIDO2 key: works OpenAI registration: works OpenAI authentication: works OpenAI hardware classification: nope MDS requirement documented: nope attestation requirement documented: nope AAGUID allowlist path: nope Google Password Manager: has to be removed qualifying physical keys needed: two number of qualifying keys I have: zero, apparently so yes, the practical answer is probably that I buy two commercial security keys before the deadline. that does not make the documentation or classification behavior less stupid. if OpenAI wants only known commercial authenticators, say so. if it wants certified authenticators, say so. if it wants attested authenticators, say so. if it has an internal allowlist of models it considers hardware, say that. right now the requirement is effectively: buy two things that our backend decides are hardware. we do not publish the exact property that makes the decision. which is not a protocol requirement. it is a product policy hiding behind the word "compatible." references: OpenAI Advanced Account Security https://help.openai.com/en/articles/20001221-measure-results OpenAI Daybreak overview https://help.openai.com/en/articles/20001258-openai-daybreak-trusted-access-for-cyber-overview OpenAI Daybreak troubleshooting https://help.openai.com/en/articles/20001259-openai-daybreak-common-issues-and-troubleshooting FIDO Metadata Service https://fidoalliance.org/metadata/ Pico FIDO https://github.com/polhenarejos/pico-fido