-
Notifications
You must be signed in to change notification settings - Fork 40
Minimal FIDO2 Support (to support iOS) #12
Comments
Argh, parsing CBOR in Java Card is a total pain. I did find a CBOR encoder/decoder in this project, which is also Apache v2 licensed. It's bare bones though, and kind of a bear to use... But that's what I'd expect for doing something like this in Java Card. |
Pardon my ignorance as I have never fully followed the spec: what is the evolution of u2f to fido2 and can you point me to the card api specs and what is missing? |
It's a little complicated and subtle. I had to ask StackOverflow about some details myself before I fully understood it. But I'll explain it here for posterity: U2F and CTAP1First there was U2F. U2F had two operations: Register and Authenticate. Both took a field called the Application Parameter, which in U2F is defined to be the SHA256 hash of the "application identifier" (app id), which is a URL like " FIDO2 was designed to be usable with existing U2F security keys. However, FIDO2 has no concept of an application parameter/identifier. Instead, it has a concept of a "relying party identifier", which is a string that is a domain name. So FIDO2 simply takes the existing U2F protocol, redefines how you calculate the "Application Parameter" (now calculated from the SHA256 of the "relying party identifier"), and calls that resulting protocol CTAP1. So, to be clear:
While the authenticators are the same, old U2F registrations are incompatible with FIDO2 registrations. To work around this, there is the "appid" extension in Webauthn that provides the "legacy" U2F URL to the browser so that it can try passing a hash of that instead of the hash of the relying party ID. This way people can still log-in once a site upgrades to Webauthn. U2F (based on app ids) is effectively deprecated by FIDO2, but CTAP1 (based on relying party identifiers) is not. Since the only difference that matters between the two is at the service/website level (U2F vs Webauthn), all U2F security keys are also CTAP1 security keys. So the security keys themselves aren't deprecated, just the way in which they were originally used is. CTAP2Support for CTAP1 is optional on new FIDO2 authenticators (security keys). CTAP2 is where all of the fancy new FIDO2 action happens. CTAP2 is an entirely new non-APDU-based protocol that uses CBOR for encoding structured requests and responses. The protocol handles a lot more cases than CTAP1, such as resident keys, user verification, and even symmetric secret key generation (via the CTAP1 and CTAP2The magic is that if you don't specify any features that couldn't be satisfied by a CTAP1 authenticator, then the resulting parameters are defined to be calculated in the exact same way. In this way, CTAP2 is defined to be a functional superset of CTAP1. Indeed, the official CTAP documentation outlines how CTAP2 maps onto CTAP1. This is assumed to be built into the platform/web-browser. If the browser or platform supports CTAP1, then everything just works. However, support for CTAP1 is optional, in both the authenticator and the platform. So far, it seems like everyone who is implementing support for Webauthn is supporting both CTAP1 authenticators and CTAP2 authenticators, with one notable exception: iOS. So what can we do?Well, we could just write an entirely new FIDO2 authenticator. But that might not be necessary if we only want to support the U2F use case: If we can perform the CTAP2/CTAP1 translation inside the authenticator, then perhaps that could be something that we can just add to this project that would allow it to be used on iOS. Doing so has the following challenges:
Hopefully that clarifies things. I'm slowly trying to write something up myself, but it will likely be another week or two before I can find enough time to actually get something workable put together. I'm aiming to just get the authentication/assertion step working first and then I'll throw it up on GitHub. But I also don't want to stop anyone from giving it a try themselves. Who knows, I could get distracted and not end up finishing it (I'm not getting paid to do this). If anyone else wants to work on this, please do. I'll be happy to share my non-functional work-in-progress if it would help. I'll keep this thread updated. |
Actually... I looks like this might be a bug in the way that iOS implements their NFC authenticator interface. It looks like if I just add support for the CTAP2 GetInfo command and only indicate that it supports Registration doesn't seem to work, but then again that has never worked well on iOS. |
It looks like registration isn't working because there is a bug in the handling of extended APDU registration requests. This extended registration APDU doesn't work (fails with 6F00):
But this normal registration APDU does work:
I bet this is just a simple oversight somewhere in the code. |
By the way, the key to making U2F/CTAP1 keys work with NFC on iOS is to handle this APDU:
and then respond with this:
Which is just an CTAP OK response and If the applet can do that then it can respond to authenticate requests properly on iOS. Registration requests still won't work for the reason outlined above. |
I hope to find my good card for testing things out. Do you have a branch you work on? |
I'll push a branch out and let you know once I get things working and cleaned up. |
And here it is in two commits: It's available on this branch. To be honest, this looks like a plain old bug in iOS, so |
Great, Will have a look at the "Javacard parts" of it. Not a huge user of fido things, where does one test the registration + usage flow these days? |
https://webauthn.org is what I've been using, but you can also use https://demo.yubico.com/webauthn-technical/registration. |
Here is a quick installer script using your GlobalPlatformPro tool that you might find helpful: https://gist.github.com/darconeous/adb1b2c4b15d3d8fbc72a5097270cdaf |
Gave it a try. Nothing worked with Chrome and Samsung s10e nor iphone xs :) Might be my phone, as iphone does not detect NDEF messages either (used to, but not any more). Or a crappy form-factor for the JavaCard (a ring. will try a few others). Also looked a bit into the specs and the code in detail. I think it makes sense to write a new authenticator, that would have some integrity built into it as well. Wrote some notes here: https://github.com/martinpaljak/javacard-fido-applet/wiki Will need to read the specs in full, right now just browsed a bit (coronavirus quarantine with kids, yay!). Setting up a repeatable test environment with repeatable instructions would be a must have. |
If the ring you are using is the NFC Ring Omni, know that it is a Type B card, and I have yet to find a device that will use a Type B FIDO2 authenticator. They all seem to want Type A. I made my own Type A ring and it works great on both my iPhone 8 and Nexus 5. Also tested with a NXP P71 card configured for Type A and it also worked great. |
Thank you for the tip, it is indeed this ring and indeed TypeB, dang! Had hoped it would work. Will try with a TypeA token instead .... And with TypeA token registration on the demo app succeeds with iphone and your ios branch, authentication fails ("found no credentials on this device"). Android fails on registration "no supported app for this nfc tag" after touching once the "usb/nfc/ble" selection window appears, might be I don't know how to properly use Android. I have taken what you have written about iOS, digged through the u2f specs (mostly old v1) and written an applet from scratch, that should have the necessary structure to make it resilient and "extendable" and almost-production-ready-secure as well. But I'll continue with that in the repo linked above. |
Weird. I bet there are some additional differences between JavaCard runtimes that might be throwing it off. What type of card are you using? I've tested with an NXP J3H145 and an NXP P71 (not sure the JCOP name tag) and it worked flawlessly on both. I did make a few changes to the branch though after testing, maybe those threw things off. Will check. |
Ah, I totally messed it up in my post-testing changes. Another reminder to me to always test! Argh! |
Ok, I just added a4a83aa to the iOS-support branch. iOS should work fine now. Not sure what's up with your android phone. |
By the way, from your wiki notes:
Except under specific circumstances (security keys issued by a company strictly for use by their employees to access company resources, for example) the attestation key and certificate are used to know what kind of device it is, not which device it is. The same certificate and key are intended to be used across all devices in a lot, should should be large enough to include thousands of physical keys. This was done for privacy reasons.
For the U2F use case, the site keys are typically encrypted and stored in the key handle. That way the number of keys is not limited by the storage space of the authenticator. FIDO2 supports a concept called "resident keys", which works a bit differently than the U2F flow. Those keys are guaranteed to be stored on the authenticator. Unfortunately there is no standardized way to manage such registrations so your only option if you run out of space is to reset the whole security key—which is lousy. This is one of the reasons I'm not a fan of resident keys. You might find this article interesting about key wrapping, which describes a method that is different than what this project is doing. That method seems to be what the CCU2F project is using. |
All noted and read. |
Extended APDU support (as well as standard APDU support) is a "MUST" for NFC FIDO U2F authenticators, according to that spec. Same thing for FIDO2:
On the other hand, platforms need only support one or the other. There seems to be no guidance as to which is preferred. |
FYI: After some more hacking, my personal fork has diverged a bit. I've made bunch of additional changes (multiple counters, support for |
Hi Robert, Hi Martin, BTW: have you seen VK-U2F with CTAP2 support? We've also done another fork for tests from Tobias CCU2F implementation w/o custom NXP APIs, extended length support for small APDU buffers, CTAP2 minimal "support" (version & invalid command), reworked counter designs, etc. Below is my increment counter function. The counters Best regards,
|
BTW: there is another good reason to split up the counters for tokens that may be used very often and for a long period of time (rings, implants, keyfobs ...): EEPROM write endurance. |
Yes. I reached out to Riley about it (and my own commits) a few days ago. However, if you take a closer look at the diff you will notice that the CTAP2 support is just a boilerplate. I actually got fairly far in my own CTAP2 implementation (full parsing and authentication, no registration though) before I realized that I could get iOS compatibility by just returning a CTAP2 error.
I belong to way too many mailing lists already. I prefer web forums these days in order to avoid information overload.
Nice, is the fork public? I'd love to have a look.
Mine is a bit more heavy-handed in some ways, but less heavy-handed in others. For example, I have a
I really like that approach (which I assume is similar to the Yubico method), and I'd use it if I had more cards that had support for JC 3.0.5. Since this is a hobby project I refuse to sign any NDAs, so I'm somewhat limited in what APIs I can use. |
is fido2 support still planned? |
Not on my fork, no. It works good enough for me. |
FIDO2 will be shared on github? |
iOS now supports NFC FIDO2 CTAP2 authenticators. Unfortunately, Apple doesn't support the use of NFC U2F/CTAP1 authenticators, with or without the
appid
extension. Thus, this applet is currently not compatible with iOS devices when used with NFC.(It is unclear if iOS supports CTAP1 via USB, but Safari on macOS does support CTAP1 for USB devices)
I'm proposing writing a small FIDO2 "wrapper" that will enable this applet to be compatible with iOS devices. Such a wrapper would effectively make the applet a FIDO2 compliant token that doesn't support resident keys or pin codes, but it would work with iOS. It wouldn't work with all services (like those that require pin codes), but it would work with any service that previously worked with a U2F token.
I mention this because it is likely the path of least resistance to implementing something that works with iOS: Just take the existing U2F/CTAP1 authenticator and implement a little CBOR parsing to extract the fields, do the U2F/CTAP1 processing, and then wrap that in more CBOR to return a FIDO2 result. Easy-ish.
The alternative is to effectively start an entirely new FIDO2 authenticator project, which is a lot of work.
The text was updated successfully, but these errors were encountered: