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