In our work at IBM building FIDO2 services for both on-premise (IBM Security Access Manager) and cloud (IBM Cloud Identity) offerings, we have been looking at scenarios for using FIDO2 authentication technology beyond the mainstream use case of browser-based authentication with WebAuthn. One scenario we decided to experiment with is FIDO2 for IoT devices – allowing standalone internet-capable devices to securely report on their operations when a human is not present.
With this goal in mind we set about putting together a prototype system including construction of a device that could initially be registered against a user’s account, and which would then perform authenticated transactions – i.e. send signed messages that provide non-repudiation that the message came from the same device that was registered to the user (subject to some assumptions about the real future construction of such a device … more on that later). So was born the hobby project – a Raspberry Pi based camera that utilises FIDO2 authentication technology to sign image files sent to a server. This article is a story about this project, providing a discussion of the technology used along the way and some future considerations for other real applications of FIDO2 authentication. I’d like to thank my colleague Lachlan Gleeson for his efforts to help bring this hobby project to life.
There is an assumption of basic knowledge about FIDO2 as you read this article – it is not a FIDO2-for-beginners article. There are plenty of other online resources to get you up and running with a technical overview of FIDO2 authentication. What we want to do here is explore “applied” FIDO2, to a context in which there are not currently widespread applications.
Note: The choice of a “camera” IoT device for this project was purely arbitrary and just a representative example – this pattern could apply to any IoT device that has important information to convey to a server and wants to do so in a manner that limits an attackers ability to spoof the device (i.e. submit transactions that appear to have come from the device but have actually come from the attacker). In the case of our IoT device the “information” is a picture file, but for other IoT devices it could be sensitive monitoring data or anything else for which strong integrity requirements exist.
Let’s take a look at the overall system architecture for our project:
As you can see there are several major components of the system. In the following sections we will dive into what these components are, the interfaces between them, and some interesting technologies we used to put the system together.
The IoT Service Application
The IoT service application is a web application that acts as a FIDO2 relying-party and exposes the following interfaces:
- For users (Emily): Web interfaces to login and initiate registration of approved IoT devices, and to view registered devices and their transactions.
- For devices (IoT device): Web services (APIs) to register and transact. These operations align directly with the standard FIDO2 operations of attestation and assertion. There is also an API to allow a device to check the validity of it’s current registration (in the event that a device registration is deleted from the server and re-registration needs to occur).
We decided to write the IoT Service Application in Node.js, using the express framework and host it on IBM Cloud, although some initial prototyping was also done on glitch.com (quite a cool web-based IDE and Node.js runtime env for this kind of project). The application required some simple storage capabilities, and for this purpose we used node-persist (simple file-based storage). Of course a more scalable, cloud-ready persistence service such as Cloudant would be a better choice for a full implementation. For a quick prototype though, file-based storage will suffice.
End-user login (for Emily) was implemented via OpenID Connect using node passport, with an IBM Cloud Identity tenant as the OpenID connect provider. Federated single sign-on via OpenID Connect is a great choice because it allows us to leverage IBM Cloud Identity’s authentication policies to decide end-user authentication requirements for the web application as well as handle all the other user lifecycle management operations. Using IBM Cloud Identity for end-user authentication we are even able to require MFA and provide password-less login for our users as they login to the IoT Service Application!
A view of the web application Emily may use to register devices and view transactions:
For FIDO2 tutorial reasons the application allows the expansion of the explanation of all the FIDO2 fields and signature verification that takes place on a transaction:
For the FIDOphiles, did you notice in the FIDO2 data that both the UP (user-presence) and UV (user-verified) flags are false? This is a departure from the WebAuthn processing rules that state UP must always be set. This is one of the things we modified the embedded FIDO2 authenticator to do (Solokey Hacker, discussed later).
When signed image files are sent to the IoT service as transactions, the IoT service verifies that the FIDO2 clientDataJSON contains the correct hash of the accompanying image file, then uses regular FIDO2 assertion processing to verify that the signature over the hash verifies with the registration’s public key. In this way the FIDO2 assertion data is “bound to” that particular image file (well at least to its hash, so the likelihood of the image being different comes down to how likely it is an attacker could build an image which produces the same hash).
IBM Cloud Identity Services
Our project (specifically the IoT Service Application component) required two core identity services – end user authentication via browsers, and FIDO2 services for managing registration and assertion operations for authenticators (or in our case devices with an embedded authenticator) associated with the user. For this purpose we leveraged IBM Cloud Identity – with its rich identity management APIs, federated single sign-on support, and soon-to-be-released FIDO2 services (we used the FIDO2 Beta announced here: https://statuspage.ibmcloudsecurity.com/incidents/p2lscz02kptw) this was the perfect choice for our system. We could equally have used IBM Security Access Manager for this project, however chose Cloud Identity because of the faster time-to-value and lower DevOps overhead that the SaaS IDaaS solution offers.
The IoT Camera
To build the camera we used:
- Raspberry Pi 3
- Pi Camera
- Solokey Hacker (for FIDO2 authenticator)
Here’s a couple of pictures taken (with a different camera!) during construction and development:
A custom mounting board was 3D-printed by Lachlan to secure the Pi and camera.
The mounting board was secured into a case, with cutouts for the power cable (a battery and switch is next on the list), shutter button, Solokey Hacker FIDO2 USB authenticator and a hole for the camera lens.
Assembled – the camera lens is visible, and the Solokey Hacker FIDO2 authenticator is plugged in on the left. Certainly not pretty, but will do the job!
Software on the Pi
The core software components on the Pi are written in Python, with two main “applications” running on startup, launched via the crontab daemon.
The first application (camera.py) is a trivial camera app that allows button pushes to take photos and dump the images into a directory on the Pi. It’s very simple, and really no further discussion of that application is required. Consider this simply the raw data collection events of our FIDO2 IoT device.
The second application (fido2iot.py) waits for a network connection, manages device registration, then monitors the photo directory and upon finding new files it will transact them. These are the FIDO2-authenticated events that our IoT device executes.
We decided to split the operations of the camera into these two asynchronous applications so that availability and performance of the network didn’t directly impact the ability to take photos. Of course there is an implied assumption that photos (or any data collected for other kinds of IoT devices) cannot be modified or injected on the device before the security aspects of the transactions takes place. Again this is a consideration that would need to be factored into the construction of a real IoT device using this type of security.
Let’s talk some more about the registration and transaction operations of the device.
Camera bootstrapping
Bootstrapping and registration involves:
- When the device boots, it connects to the internet via wifi. This was out-of-band for our project – we cheated and logged into the Pi to set that up. There are existing patterns for doing this for devices, including the device initially broadcasting it’s own Wifi access point which you then connect to with a phone/laptop and configure the device to connect to your home wifi. Just consider this as having been previously completed…
- The device “knows” it’s unique serial number – this is actually part of a unique attestation certificate that the FIDO2 security component has burned into it at manufacture. This is a bit of a leap of faith as far as our prototype is concerned but is something that a real chip manufacturer could do when the IoT device chip is constructed. In our prototype we used a modified Solokey Hacker (more on that later), containing a unique attestation certificate with a serial number in the DN. The signer of this attestation certificate formed part of the FIDO2 metadata installed on our IBM Cloud Identity FIDO2 server. In this way only devices issued with these trusted certificates will be accepted for registration.
- After a network connection is established, the bootstrapping application checks for an existing locally-stored registration record, and if found uses an IoT Service Application API to determine if the registration is still valid. Note that this registration record does NOT contain secret data. The private key used for FIDO2 signing operations is separate and securely stored in the FIDO2 authenticator chip (or in our case on the Solokey Hacker). The registration record does include the credentialId of the registration and the user to whom the device is registered. The server can use these to check if the credentialId is registered on the server for that user and simply tell the device if the registration is valid or not. We could make this more robust by actually performing a FIDO2 authentication operation to that API and if that is successful then we know the registration is valid. In our implementation though, and purely for reasons of simplicity, we made the validateRegistration API just return ok/failed based on whether or not the provided username and credentialId are part of a valid registration at the server.
- If there is no current valid registration record, the device will start polling to ask “Am I ready to register”, using its serial number for identification.
- When a user eventually enters that device’s serial number into the registration portal, this poll will come back with “Yes”, at which point a FIDO2 registration sequence takes place between the device and the IoT Service Application. The completion of this operation results in a valid registration, assigned to the user who entered the serial number into the registration portal. This is not a perfect registration system because the serial number could be entered by a malicious person. There are better ways to perform registration, including taking ideas from the OAuth device flow. I suspect that in real use cases registration should occur around the same time as network bootstrapping when a visual screen such as the owners mobile phone or laptop could be used to display a device-generated, one-time registration code that the user could enter on the portal to authenticate the registration process. The device could offer a simple embedded web application for this purpose, or in some cases have a display of its own.
- Once a valid registration is established (on reboot this is simply a confirmation check that the current registration is still valid), the camera enters a loop watching for images to appear in the photo directory and then performs transactions with them.
This sequence diagram depicts the operations that occur during device registration, after it is determined that a valid registration does not already exist:
Camera transactions
A transaction operates on a single photo file and involves:
- Initiating a FIDO2 assertion operation via the IoT Service Application. This retrieves a server-generated challenge.
- Using the Solokey Hacker to sign a combination of the server-provided challenge and a hash of the bytes of the photo file.
- Sending the signature data and the photo file to the IoT service for validation/storage.
- Provided the transaction succeeds, deleting the photo file from the device.
This sequence diagram depicts the operations that occur during transaction processing:
Solokey Hacker
The camera uses a modified Solokey Hacker (https://solokeys.com/collections/all/products/solo-hacker) to act as the FIDO2 authenticator and perform registration and signing operations. We could have done (and previously did) this completely in software, but for both technical and novelty reasons wanted to experiment with the idea of a separate chip for the FIDO2 component. Secure hardware is how at manufacturing time unique attestation certificates could be installed into the devices – this is one of the reasons we wanted to experiment with the Solokey Hacker as our authenticator.
The capabilities we changed on the Solokey Hacker from it’s out-of-the-box software were:
- Installation of a custom, unique attestation certificate and private key which uniquely represents the serial number of the IoT device. This is quite a departure from the normal batch attestation certificates that consumer FIDO2 authenticators ship for people to use at Websites. Batch attestation certificates (e.g. at least 100,000 authenticators shipped with the same attestation certificate) exist for privacy reasons – to prevent websites from colluding to track users. Privacy reasons don’t apply here – in our scenario IoT devices are all registering to the common web portal run by the manufacturer and we *want* to track individual devices.
- Dropped the need to push the authenticator button (user-presence) for FIDO2 operations. The device operates silently and does not set the FIDO2 user-presence flag in FIDO2 messages during its operations.
- We decided to blink the LED color to red (and in doing so introduce an artificial 500ms delay) when the device is performing FIDO2 operations so that you get a visual indication that the authenticator is “working”.
Yubico FIDO2 libraries and tools
Communication between the software running on the Pi and the SoloKey Hacker for both registration and assertion operations was achieved using Yubico’s python2-fido (https://github.com/Yubico/python-fido2/) library. We had initially used bash/shell and the fido2-token, fido2-cred, and fido2-assert command-line tools also from Yubico (https://github.com/Yubico/libfido2), however eventually moved everything into python to reduce complexity and improve operational performance. This has proven to be a great library for communicating with the authenticator in client prototypes like ours.
At one point during development of the camera solution we were actually using a Yubikey for FIDO2 operations instead of the Solokey Hacker, but switched to the Solokey Hacker so that we could implement the custom attestation certificate and silent (no user-presence) operations as previously described. The consumer Yubikey products are great, but remember they are designed for human user operations and scenarios where both privacy (hence the batch attestation certificate) and user-presence are driving requirements. Using the modified Solokey Hacker allowed us to make the FIDO2 operations of the IoT device behave in a manner well suited to standalone operations without a human present. This would be typical with a remote monitoring IoT device.
Device lifecycle managment
Devices may go through initial registration, then perhaps several registration / re-registration phases in their life. For example the device could change owner (legitimately or by way of theft), or the record of registration may be cleared/erased by the original owner at the server, requiring device re-registration.
Whether it is initial registration, or a re-registration event, it is important to have a clear and simple plan for how to handle these types of events. One of the things we learned when building this prototype was that it’s not always clear if you should allow a device to be re-registered. For example if Emily’s device is stolen, should the thief be able to perform some kind of factory reset then re-register the device to themselves, or should this require administrative intervention? Should a device be allowed to be re-registered at all?
Similarly, what happens if Emily is legitimately trying to register the device to her account, and an attacker gains ownership by way of hijacking the registration process? In our prototype this is easy if you know the serial number of the device you want to attack. Integrity of the registration process becomes very important if you need to securely link the registration of the device to a particular owner.
The takeaway here is that if you are putting together any FIDO2 authentication solution, not just for IoT, be very particular about the requirements for user and client authentication at the time of new credential registration! This is not a new problem – you have to be just as fussy when allowing users to register new multi-factor authentication methods at a website. If not, you risk having weak links in the overall authentication system that negate the benefits of applying strong authentication technology.
Alternatives to FIDO2 and FIDO2 variants
Of course FIDO2 is not the only game in town when it comes to authentication and making authenticated API calls. Some might argue that it’s not even the best choice for the scenario we have outlined in this article. Plenty of other options exist, and one that certainly came to mind was OAuth. At first it appeared the OAuth device registration flow, followed by OAuth-enabled APIs for the transactions would have been a superior choice. It’s definitely faster at runtime in that transactions are a single roundtrip – without the need for one API call to retrieve a server-supplied challenge and another to submit the signed data. Sure there might be the occasional refresh token flow in OAuth, but thats negligible. What you don’t get from OAuth alone though is a lasting proof that the transaction data stored at the server came from the device where the registration exists. Sure you can record an audit event to say that the request came in with a particular access token, but there is no binding between that record and the data itself. With the digital signature approach associated with the FIDO2 registration, and an assumption of integrity of the device from a hardware security point of view (the registration private key material cannot be extracted), we do get a lasting proof that a transaction (by way of it’s signed hash) was attested to by the private key associated with the device.
For future consideration: I actually think there is room here for a hybrid approach where the normal FIDO2 registration flow is used to establish the credential, then during transactions the client device generates “unsolicited” FIDO2 assertions without the challenge necessarily being provided from an options call to the server. This is possible because the IoT device essentially uses a resident key credential and doesn’t require an allowCredentials list from the server options. The signed client payload could included a timestamp, and policy on the IoT Service Application (FIDO2 relying party) could use the timestamp for “freshness” requirements, and the payload hash as a nonce to avoid replays. This is similar to how unsolicited IDP-initiated SAML SSO works at service providers. Of course this places some burden on the IoT Service Application to manage this state information instead of the FIDO2 server, however the benefit is in faster transaction time, and also possibly the ability to produce the signatures when not connected to the internet.
By way of example, imagine a digital camera that had a tamper-proof FIDO-enabled camera chip. During registration of the camera by it’s owner, attestation may be used to prove the make/model of the FIDO-enabled camera chip, and in the case of individual attestation certificates even the exact serial number. A resident key is established representing the current owner’s registration of that camera. When the shutter button is pressed, the camera chip atomically produces FIDO2-signed photographs using the owner’s registration credential. The assertion signature could be embedded in the EXIF data of the photo itself, later allowing for proof that a photo was taken not just by a particular camera, but also identifying the person that registered it. This is a practical example where soliciting challenges at the FIDO server in real time is undesirable.
Conclusion
The whole process of putting together a system leveraging FIDO2 for non-browser based scenarios has been very interesting and quite a lot of fun. We had the opportunity to work hands-on with some very new standards and technology, and it has caused us to ask several questions about what is really needed in the FIDO2 protocol for these different scenarios and what isn’t.
The importance of a strong registration process that encompasses re-registration and device lifecycle management became apparent very quickly, and is definitely a key consideration for any future real solution, as is the need to ensure that secure hardware authenticator technology is built directly into the chipsets of FIDO2-enabled IoT devices. With our prototype device and its removable Solokey Hacker, the “security” aspect of our solution is much too portable, but it did allow us to demonstrate the principals of using FIDO2 in IoT applications.
The FIDO2 alliance has recently kicked off a working group to focus on IoT use cases and we look forward to working with the group to refine these types of scenarios and the protocols needed to support them.
#SecurityExpert