NFC Integration for Plasma Mobile
Following the recent look at the state of GNSS API on Linux Mobile, I did a similar exploration of where we are with the Near Field Communication (NFC) stack.
Use cases
Around NFC there’s a whole bunch of interleaved standards and protocols. Trading simplicity for accuracy here there’s basically two modes of operation:
- Reading static messages from “dumb” tags that are essentially just raw memory. The most relevant format for this is the NFC Data Exchange Format (NDEF), which is essentially a space-efficient MIME-typed container.
- Wireless communication with what is ultimately a smart card, ie. some form of application with its own specific bidirectional protocol running on the tag or card.
Reading static NDEF messages is kind of the “hello world” application here, but practically it’s the least relevant one, as QR codes have taken over practically all applications for this, having the better UX.
The smart card approach has more interesting applications:
- FIDO2 authenticator tokens, e.g. for Passkey authentication.
- Government issued id cards, e.g. the AusweisApp for interacting with German id cards.
- Authenticating on NFC-based door locks, e.g. using the Aliro protocol.
- NFC keys in Apple Wallet passes, e.g. used by some hotels.
The by far most common use is probably mobile payment though, but that has a bunch of harder problems to solve than NFC access before we can also have that on mobile Linux.
Orthogonal to that are the roles between the reader and tag/card that’s being interacted with. Those are often obvious and fixed, for tags/cards without their own power supply. NFC readers however can also pretend to be tags towards other readers, which is the basis for Host Card Emulation (HCE). That’s how mobile payment works, your phone pretends to be a credit card.
What we have
Driver stack
There’s two different driver stacks for accessing NFC readers on Linux:
- User-space PC/SC drivers. This is coming from smart cards originally and is primarily used for USB-connected desktop readers.
- The Linux Kernel NFC subsystem. This is what readers in phones use, and also e.g. the NFC reader in a Thinkpad T14s I have here.
The protocol NFC readers speak on a higher level is fortunately standardized with the NFC Controller Interface (NCI).
Middleware
Next up in the stack we have a service bridging the hardware access to applications. The canonical solution for this on Linux is neard. That provides a D-Bus interface for NFC adapters and tags, similar to e.g. BlueZ does for Bluetooth.
Unfortunately it’s in a not particularly convincing state:
- It terminates when encountering a Type 1 NFC tag (PR 35).
- It terminates when encountering a multi-lingual smart poster tag (PR 37).
- It only supports NDEF read/write operations, there is no interface for sending custom commands, nor support for HCE.
Distribution packaging on openSUSE was in a similarly concerning state:
- A wrong path in the systemd serivce file made it fail to start (fix).
- A patch to the D-Bus policy breaks half it’s functionality.
My fix for the startup issue was merged and deployed in less than 24h by the openSUSE team at least,
my patches for neard have yet to see any reaction.
There’s one potential alternative, nfcd from the SailfishOS team. That seems newer and more active, and seems to have all relevant features. However, it doesn’t have a backend for the Linux NFC subsystem, but rather for the Android NFC interface.
Application API
For bringing NFC access into applications then, there’s the Qt NFC API. While the API covers everything we need, there’s a few practical limitations:
- There’s two backends for Linux (PC/SC and
neard), but unlike in other Qt modules the selection happens solely at compile-time. Build the PC/SC backend and you wont be able to useneardand vice versa. - Raw commands are available in the API, but not supported by
neard, so that will just not do anything. - Writing NDEF messages is implemented but skipped as that apparently previously
crashed
neard(see QTBUG-43802). - Power and polling state handling seemed a bit shaky when something else also changes this, or when there’s more than one NFC reader present. Probably the easiest to fix of all this though.
It does come with a decent NDEF parser though, that’s useful even when directly talking to neard
for everything else.
Applications
Finally we need something to actually make use of NFC in the end. So far there seems to be no integration for any of the Linux mobile platforms. In terms of applications, I’m mainly aware of the following two:
- The already mentioned AusweisApp to authenticate to online services with German id cards.
It does come with a Qt NFC and a PC/SC backend, the Qt NFC one will fail to work with
neardthough as custom commands wont work there. - credentialsd which aims at providing the Linux platform API for FIDO2/WebAuthn/Passkeys. That’s also using PC/SC directly it seems.
Development Tools
Working with hardware tends to be inconvenient, so before looking at filling gaps in the stack it makes sense to look at development tools. I fortunately have access to a Proxmark3, an open-source hardware device that can work as an NFC reader, emulate an NFC tag/card and monitor the communication between an NFC reader and card. That’s very useful functionality, but it doesn’t help with making things more convenient, you now have another slightly fragile device to handle.
Fortunately, the Linux kernel has support for virtual NCI devices, which we can use for emulating an NFC reader and NFC tags entirely in software. Perfect for testing and reproducability, and doesn’t require any kind of physical NFC hardware.
But while the kernel has all necessary infrastructure for this, I haven’t found a single user-space tool making use of that so far. So I wrote one. This is fairly basic, but it’s at least enough to test power and polling states of readers and to present Type 1 and Type 2 tags with readable and writable memory. That’s enough for the basic NDEF use cases, but not for the more advanced applications. The challenging part there would be to write a software emulation for the actual application though, not the NFC/NCI part.
There’s a few more things worth investigating:
- A bridge between the virtual NCI interface and the Proxmark3. While using the Proxmark3 as a regular NFC reader is complete overkill, it’s still useful if that’s the only NFC hardware you currently have handy.
- Exposing (virtual) NFC devices to a VM, for e.g. testing in postmarketOS images.
- Logging of the NCI communication between the kernel and a hardware NFC reader. Probably doable with some eBPF magic, and maybe outputting in a Wireshark-compatible format.
Platform Integration
Compared to the non-Linux mobile platforms the first thing to notice is that we don’t even have a simple switch to turn NFC on or off on your device. So I wrote a Plasma applet for that, inspired by how this is done for Bluetooth.
For Bluetooth this is backed by bluedevil as a daemon process in the user session, likewise we now have neardevil doing this for NFC. This takes care of the following:
- Monitor and change power and polling state for NFC readers.
- Show notifications for detected tags containing static NDEF messages, and allowing to open URLs contained in those.
- Allow to set up Wi-Fi connections and pair with Bluetooth devices based on corresponding information in static NDEF messages. For this also a dynamic protocol exists where both parties exchange keys over NFC, that’s not implemented and also unlikely to be added later as BlueZ removed the corresponding API for security reasons some time ago.
- Receive vCard contact information.
This is a prototype at best and there’s of course much more that could still be done here, like keeping a tag history, detecting and handing over to tag-specific apps for e.g. your id card, etc. But it’s a start at least.
How to continue?
So far this is all based on neard, which means as of right now there’s no direct path towards actually supporting the interesting use cases
requiring sending and receiving application-specific commands or host card emulation.
There’s a few options on how to address this:
neardcomes back to live and we get the missing features implemented there.- We implement a Linux backend plugin for
nfcdand rebase everything on top of that. - We implement our own, by forking or by starting from scratch.
“We” here isn’t just KDE though, we need something that works for the entire Linux mobile ecosystem, this is shared platform infrastructure which usually has exclusive hardware access, so everyone bringing their own isn’t going to work.
Thoughts and input on this highly appreciated!