Using U2F for Door Access Control Systems

I was looking at trying to securely implement a door access control system. This usually involves some kind of card that you tap at a reader and the door unlocks.

Because it uses NFC, the NFC reader and electronics can be located safely on the inside, leaving no exposed DIY electronics on the outside for attackers to fiddle around with. Here’s an example project using a 3D-printed enclosure:

photo of a DIY NFC door lock found on Instructables.com, with all the electronics & parts on the interior side of the door

A lot of those DIY projects work, but they are just not secure:

Just look at the code and you will see what I mean. They generally look like this:

uint32_t uid = nfc.readCardId();    // read the card's unique ID
if (uid == 0xAAAAAAAA || uid == 0xBBBBBBBB || ...) {
    unlock(); // YES!!
}

Unfortunately, consumer smart locks like a Yale or Samsung do pretty much the same thing, without hard-coding UIDs of course. When you enroll cards, the door lock will simply record the UID and will unlock when you present a card (or tag) with that UID. MIFARE Classic cards are commonly used for this purpose because they are very inexpensive. They are factory-programmed with a unique identifier stored in sector 0, which is read-only.

However, you can easily buy “UID 魔术卡” (UID “magic cards”) that allow their sector 0 to be modified and rewritten. This essentially enables you to create clones of any valid card.

Some HID access control systems can also be configured to accept these cards, either for legacy staff cards or to save cost without being locked-in to HID proprietary cards. This was demonstrated at an office space that I had access to:

a labeled UID 'magic card' held in front of a HID iClass door reader, which turned green to indicate 'access granted'

We should not rely on the card’s UID for authentication, and here I would like to discuss about solving that.

🔑 Key Requirements

Here’s what we need from our ideal “key”:

  • Uncloneable. Most of these NFC door access systems are based on card UIDs, which is assumed to be a read-only number programmed into the card at the factory. As we have demonstrated here, this is simply not true. A system that relies on a static UID can be easily cloned.

  • Non-replayable. Someone can’t just walk up to you (or your desk), read your card and replay them to the card reader.

  • Low-cost. It shouldn’t be too expensive and should ideally be easily available and replaceable.

Enter Universal 2nd Factor, or U2F, a standard that is seeing wider adoption as of late.

What is U2F?

FIDO Universal 2nd Factor (U2F) is an open standard that is used for, as its name suggests, performing two-factor authentication (2FA). It is a standard that governs hardware devices to be the second factor (i.e. “something you have”). These devices work similarly to a smart card, and are available in several convenient form factors: card, token or key fob.

While it has been designed for 2FA, we would like to adapt it to work as a first factor. (Yes I’m also aware of FIDO2, and elaborate on that below.)

How it Works

The core technology that powers U2F is public key cryptography, or more specifically, elliptic-curve cryptography (ECC). The private key is generated on the device and never leaves it. During authentication, the device need only sign requests, which does not need the private key to be handed over. Using only the public key (which the device will gladly provide), we can verify the signatures generated. This means we can be sure that a signature was produced by this particular device, because the private key only exists within.

Freshness is achieved through a challenge (or nonce) issued by the reader, which has to be signed by the device. Since it originates from the verifying party (usually the website, or door lock in this case), it is not controlled or predictable by the attacker and thus, prevents replay attacks.

Why U2F?

U2F was announced quite some time ago (around 2014?) and it is an open standard, not just some closed proprietary product. By now, there are several vendors of U2F devices, such as Yubico, Feitian and VASCO. You can also get these devices in many form factors: a keyfob like a car remote, a flat USB stick which fits on your keyring, or just a regular smart card. I bought a few of them to test out1:

U2F implements a challenge-response protocol that meets our security requirements, and it does so over NFC, a contactless protocol (unless you prefer to insert your USB “key” into your door lock). It fits the bill nicely (almost), and there are various form factors available to choose from. U2F is widely available now, which also means that prices are reasonable.

Ideally, this “key” should be something that the user will be frequently using. U2F devices can be used to login to popular platforms with 2FA, like Facebook or Coinbase. For a device that also has smart card functionality, it can also be used for encrypted emails (PGP) or SSH. All these additional use cases will reduce the likelihood of a missing or stolen device going unnoticed.

What about FIDO2?

FIDO2, the successor to U2F, was fairly recently announced around RSA Conference 2018. The original Client to Authenticator Protocol (CTAP) used to communicate with U2F tokens was simple and not very extensible, so with FIDO2 comes a new CTAP2 protocol. While the new CTAP2 has provisions for using authenticator devices as a first factor, it has also become more complex because of new functionality like sessions and support for user PINs, and requiring the use of CBOR for message encoding.

Most FIDO2 devices also have support for U2F/CTAP1 (although it is not mandatory). By implementing CTAP1, both older and newer devices can be used.

Constraints

U2F requires a stored key handle

The U2F protocol requires that you hand the authenticator back a key handle (a binary blob) which it gave you during registration. A diagram might better help to illustrate this. Notice the key handle blob being created by the U2F device during registration, and later passed back again for authentication.

registration and authentication message exchange between a U2F device and a server, also known as the 'relying party'

This is also why U2F alone cannot be used to sign in to websites — you need to first login so that the website can retrieve this key handle to pass it back to the authenticator. U2F devices were made simple (which enabled low cost), without much storage. This trick allowed them to handle registrations with unlimited sites without incurring storage space, but at the same time, leaves them unable to function as a username/password replacement.

For U2F authenticators to be usable in this scenario, we need to work around this.

Workaround

NFC devices that are ISO 14443-A compliant, like U2F tokens, will perform an anti-collision procedure upon startup, which involves devices identifying themselves with a unique ID (UID) so that NFC readers can address a particular card. From the card’s reply of ATQA, SAK and ATS values, it is possible to identify MIFARE device types. The UID is also supposed to be unique, so we can combine ATQA, SAK and the UID to uniquely identify a particular device.

In my testing, I have found that this identifier tuple is also persistent. If the purpose of the UID was solely for anti-collision, it could very well have been randomly generated on power up, but fortunately for us, this wasn’t the case. For example, Solo keys use a AS3956 NFC interface which combines the device family ID with the device serial number to form a persistent 7-byte ID.

We shall use of this unique device identifier to be associated with the key handle.

Note that this information leakage cannot be abused by websites because there is intentionally no provision in the U2F protocol for querying the device’s unique identifier. Having such an API would allow websites to track a particular device (and thus, user) when they use the same device across multiple websites.

No user presence over NFC

One of the important aspects of a hardware U2F token is the ability to request for user presence. The token will not sign any requests unless the user confirms it by physically acknowledging the request. This is usually done by touching the token’s capacitive button.

However, over NFC the token will happily sign anything without the user knowing. This means someone can walk up to you with a NFC device and request your token to authenticate, while it is in your pocket or bag.

Of course, the 256-bit challenge parameter (the nonce) makes this attack impractical. However, someone could still do relay attacks. This could be mitigated by reducing the authentication time window to the bare minimum to ensure that authentication is not being relayed.

Proof of Concept

I used a Adafruit PN532 NFC reader to communicate with the U2F tokens. It has a well-tuned antenna, unlike some others you might get from AliExpress.

a U2F NFC token, presented over the Adafruit PN532 NFC shield

I also used the micro-ecc library for verifying ECC signatures. It is optimized for certain architectures with assembly and is fully self-contained, making it suitable for embedded use. However, the hash function used in the ECC signatures is SHA-256, so you will need a separate implementation for that. Lastly, the ECC signatures and attestation certificate are transferred using ASN.1 encoding, so a parser is also required to fully parse and validate U2F responses.

Note that the PN532 only supports short APDUs, not the extended ones. Its internal buffer can only support ~260 byte frames.

General Flow

This is similar to what the browser would do when it requests the U2F token for authentication, except for an initial lookup process at the start:

  1. When a U2F token is presented over NFC, use its UID obtained during the ISO 14443-A anti-collision sequence as the key’s unique identifier.

  2. Using this NFC unique identifier as a lookup key, determine if there is a key handle associated with this key in the database. This key handle would have been stored when this key was initially registered using U2F_REGISTER.

  3. Generate a random nonce, and together with the retrieved key handle, construct and send the U2F_AUTHENTICATE message over NFC to the key.

  4. Parse the reply to determine whether the U2F_AUTHENTICATE succeeded.

Unfortunately, there is no working code sample that I can share, but the above-outlined steps should suffice. The implementation hints that I provided below should help, if you are unfamiliar with the cryptography code required for the U2F (CTAP1) protocol.

Implementation Hints

U2F uses elliptic curve cryptography (ECC) for its implementation, more specifically with the NIST P-256 curve (also called secp256r1 or prime256v1). The specifications come with example messages, allowing you to verify your implementation against known messages.

The ECC public keys are specified using the X9.62 format: it starts with a 0x4 for uncompressed points, followed by the two points X and Y, 32 bytes each. That makes a total of 65 bytes. micro-ecc expects a 64-byte input with X and Y directly (skipping the 0x4 byte).

ECC signatures are 71 to 73 bytes in length, encoded using an ASN.1 sequence containing the signature integers r and s. Handy tools to assist with decoding ASN.1 objects are:

An example ECC signature from the U2F specs can be interactively analyzed here: https://lapo.it/asn1js/#304502…1EF871

micro-ecc’s uECC_verify function expects just a 64-byte buffer for the signatures, containing just r and s. You will therefore need to parse the ASN.1 sequence to extract the integers first. The same goes for attestation certificate, which you need to parse for verification during registration.

Adam Langley also wrote some sample Python code and elaborated on the various parts of the U2F protocol. Solo Keys are open source, so you can refer to its implementation of the CTAP1 and 2 protocols. See the Further Reading section below if you are interested.

Security Considerations

Security should be implemented in layers; an unclonable key is one of them, but that alone won’t stop determined attackers. If the key is lost or stolen, it can still be used to gain unauthorized entry. The key should be paired with another factor, like a fingerprint or PIN number for better security.

Also, the more useful the key is, the more frequently the user would reach for it and thus, notice sooner when it is lost or stolen.

Future Work

This idea looks like it might actually work in production. I have only tested Feitian and Yubico keys, but their UIDs seem stable. From reviewing Solo key’s source code, those will probably work as well. If I do implement it in a real door lock, I’ll probably write another post about it. Since FIDO2 has native support for resident keys, I should probably also evaluate how much additional code/work it is to use that instead of U2F.

I look forward to the day when smart lock manufacturers will consider supporting FIDO2 or U2F tokens, instead of the de-facto MIFARE Classic cards. Sure, the cost of U2F cards are definitely higher, but users may already have their own security keys they could use. It could also co-exist with the dumb MIFARE cards, allowing end users to decide between lower cost and higher security.

Apple recently announced digital home keys at the WWDC 2021, a feature coming to iOS 15. While this might incentivize door lock manufacturers to implement more secure authentication, I am quite sure it will also be more expensive. Besides, you can’t expect everybody to carry iPhones, especially if you are to deploy this access control system in a corporate setting.

Further Reading

Adam Langley reviewed some security keys in August and in October a few years back. Brad Hill also did some reviews of U2F keys. In particular, Brad reviewed the Fidesmo NFC card which I have not tested.

Adam Langley also walked through the Web Authentication standard, describing the U2F (CTAP1) and its successor the CTAP2 protocol, with some code.

Solo keys are open-source, and also uses micro-ecc, so you can reference its source: https://github.com/solokeys/solo

And of course if you wish to implement it yourself, you can download and read the FIDO U2F specifications.


  1. Ummm yeah, this article was supposed to have been published some time ago. Sometimes articles sit around as drafts until I am satisfied with the final edit. 

One comment on “Using U2F for Door Access Control Systems

  1. […] Tan at irq5.io writes a detailed article about securely implementing electronic door access control […]

Leave a reply to Using U2F for Door Access Control Systems #Security « Adafruit Industries – Makers, hackers, artists, designers and engineers! Cancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.