Monday, March 10, 2025

NFC and Contactless


I've been using contactless (or "wafty-wafty") credit cards for a good few years now. I'm not sure, but I would have said it was longer than Wikipedia seems to think they've been available. Before that (back in the '90s), I worked in offices that used ProxCard keys for entry both into the building and into specific secure areas. It was really cool that you could be granted access to (say) the server room for a week without having to have a new key or have to give it back. And it was super-cool that when someone was fired you didn't have to take their key back: the administrator just disabled it.

So, yes, it was cool and yes, from time to time I wondered how it all worked. But for the longest time I have had phones with NFC chips in them (possibly longer than I have had a wafty-wafty card) but until recently I hadn't used Google Wallet on any of them. But since I started, I have used it almost exclusively: it is amazing how much more effort it now seems to get my physical wallet out and get my cards out of that. Interestingly, my watch has much the same feature (Garmin Pay), but while I have used it, I have not been anything like as addicted to it: accessing and unlocking the functionality on my watch is about the same amount of effort as using a physical card. I generally only do it when I don't have my wallet or my phone on me (pretty much when I'm at the beach or a pool).

But there I was one day, a few months ago, in the Co-op at the bottom of my apartment building. And while I was checking out with my phone, it asked me "do you want a receipt?" and I thought, out of nowhere, "why don't you just send it back to my phone?" and then I realized something quite revolutionary (at least to me):

Google Wallet on a phone is a different beast to a wafty-wafty card.

Because a card is fairly much passive. It has just enough circuitry in it do some simple handling of tokens and signing. But a phone is a truly powerful device. To say it runs my life is an exaggeration, but it certainly has the ability to store and collate receipts. And why would I not want to do that? To have my phone automatically keep track of everything I do, at a very detailed level. And the more I thought about it, the more applications I thought up. So, I said to myself, I need to investigate this technology and see what it can and cannot do. And then possibly start talking to vendors about what they should be doing with it.

So, in this series of blog posts I am going to dig into the guts of NFC technology - particularly on phones, but obviously on the computer end as well, since we need two things to talk to each other - in the context of the following four applications:
  • If I use my phone in a store to make a payment, can I also use it to accept, store and process receipts? Can I do that all in one single motion, or does it require two separate steps?
  • A little while after my Co-op experience, I was in a government office in Georgia, USA, where they wanted me to fill out a form by hand, that they then copy-typed into a computer, that then went into a computer system. All the information could be found on my phone. Why can I not tap my phone on an NFC reader and have it (subject to my consent) fill in all the form fields directly?
  • In a similar vein, it would be possible to have a digital signing key on my phone which could be used to sign an arbitrary document and pass that signature to the remote device using NFC. Again, there needs to be a level of trust that what I am signing is what I am seeing, but it seems reasonable that a two-step transfer of URL from device to phone followed by passing back a signed hash of a "screenshot" could reasonably replace hand-signing of forms.
  • I dislike the way that train companies in the UK still use a reservation system that feels it has its roots in the 19th century. While your ticket allows you to catch any train, you have to book your reservation significantly in advance, and before it is clear how busy any train will be. It should be possible to make a reservation up until minutes before the train leaves your stop. On the other hand, it seems unfair to allow someone to book a seat halfway through a journey which is "now" occupied by someone who boarded at any earlier stop without a reservation. But NFC (either with a phone or paper ticket with RFID) would allow a passenger to "claim" a seat the moment they sat down.
As we proceed, it may well be the case that I come up with other applications, or variations on these applications and go down one or more bunnyholes. But the key elements are always going to be:
  • I have a phone that I trust.
  • I am working with a second-party that I don't (particularly) trust and who has no reason to trust me.
  • They have a NFC touch point that can interact with my phone.
  • We can do one, two or more touches to follow through a multi-step process.
For the first sprint here, I am going to focus on trying to develop the "receipt" application and see what issues it raises. I may then return to the others.

Gathering My Resources

Unlike almost everything else I do here, I really don't have a clue what I'm doing with this, or how any of it works. I have some kind of mental model about how it "surely" must work, but that will probably turn out to be seriously challenged as I go along. So let's take stock of where I'm at and what I do have.

I have an Android phone that definitely supports NFC because I use Google Wallet all the time. I also have some other (historical) phones that also have NFC in them, but I will probably use my most up-to-date phone because it's running Android 15 so it has the latest version of all the Android software on it.

After some investigation, I bought an ACR-1552U along with its SDK. Now, I don't know if this is any different to just buying a Square Reader but I didn't know if I could "get inside" a commercial product like this, and since the ACR-1552U came with an SDK, the implication was that I would be able to develop with it.

I want to use this with my Linux laptop (and possibly my Mac), although I'm concerned that the SDK is only for Windows. I may end up having to install things on Windows just to check that it works, but I'm not going to develop on Windows. In that case I will end up throwing away the SDK I just bought and will have to find a third-party SDK that works with Linux and/or Mac (to be clear, because of the way Mac works, there are a lot of things that you "can" do with homebrew or whatever that I just don't do: if it doesn't have a specific Mac version available, I don't use it; this is absolutely a personal preference).

I want to work in Go on the computer and Android/Java on the phone, which may make things interesting to say the least. It is the nature of developing on Android that you just have to work with whatever APIs they give you; whereas I will have to try and figure out what libraries are available in Go, or whether I need to put some effort into wrapping something that already exists in C in Go. Again, this is absolutely a personal choice: I'm still capable of writing C and C++, but I just don't enjoy it anymore.

I have read a number of articles on the subject of NFC communication, so, as is frequently the case at this point, I have a little knowledge, but I haven't actually tried doing anything yet, so that knowledge will probably get sucked in as I go along and I'll search for more information online.

I have not seen any books about NFC, let alone read them (Update: some time after writing this, I came across this book from 2014 which clarified a bunch of issues for me but was all using technologies and techniques at odds with the ones I have chosen.)

Working with the Terminal

It seems to me that it is the nature of communication that nothing can happen until there are two devices that can communicate ("who bought the first fax machine?"). My original thought was to just work with the phone, but I quickly realized that that would do me no good at all until I had something for it to communicate with. Hence I researched and bought a reader terminal. But now I need to do something with that. On the upside, it comes with some "cards" that presumably it can read, and I also have a collection of other cards - credit cards and hotel room cards, for example - that I might be able to use with it, as well as just seeing what happens if I show it my phone.

So let's unpack it and see what happens.

When I open the box, it says "download the SDK by going to https://sdk.acs.com.hk" and then gives me a serial number and validation code. So let's give this a go. As it indicated, I need to create an account with an email and password, and then it sends me an email to validate my email address. Predictably enough, it went to my spam folder, but once I recovered it I was able to authenticate my email address and log in.

It took me a couple of attempts to enter the correct serial number and validation code, but once I finally managed that an entry appeared in a table enabling me to download the SDK and read the release notes. The release notes were not very interesting - hopefully there is something else with the SDK.

The implication seems to be that I can only download the SDK for 30 days - I'm not sure what happens after that, and whether or not I am entitled to any updates they may later release. We'll see. But for now I have downloaded an SDK zip file. Let's unpack it and see what's there!

It's an ISO for a CD to install ... on a Windows box. It also has documentation explaining how to install it and that it's important to install the driver before plugging in the card reader. I could give in and just install all this on my Windows box, but it's not an environment I typically do development on and my Windows box is really old, so let's see what else we can do.

A small amount of googling turns up this project which claims to be an implementation of the PC/SC (personal computer/smart card) library I need for MacOSX and Linux. Flicking through the README doesn't enlighten me very much but does confirm that my ACR1552U-M1 is a supported reader. A bit more googling suggests that the library can be installed from the APT repo for Debian as libacsccid1. Let's try that:
$ sudo apt-get install libacsccid1
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following NEW packages will be installed
  libacsccid1
0 to upgrade, 1 to newly install, 0 to remove and 21 not to upgrade.
Need to get 73.0 kB of archives.
After this operation, 187 kB of additional disk space will be used.
Get:1 http://archive.ubuntu.com/ubuntu jammy/universe amd64 libacsccid1 amd64 1.1.8-1 [73.0 kB]
Fetched 73.0 kB in 1s (78.9 kB/s)
Selecting previously unselected package libacsccid1.
(Reading database ... 740443 files and directories currently installed.)
Preparing to unpack .../libacsccid11.1.8-1amd64.deb ...
Unpacking libacsccid1 (1.1.8-1) ...
Setting up libacsccid1 (1.1.8-1) ...
OK, that seems reasonable. Now what do I do?

It seems reasonable to me that this is a library which will enable me to communicate with the device; obviously I will have to plug it in at some point, but I feel I need some kind of application to interact with that. And since I'm interested in doing Go, I probably want an example in Go ... (more googling follows ...)

OK, so I came up with this which I can copy from the repo and has a couple of example projects in. It's README says that I need to install pcscd and libccid, which seem different to the libacsccid1 I've already installed. I'll probably uninstall the libacsccid1 if it turns out I don't need it, but who knows?
$ sudo apt-get install pcscd libccid
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following NEW packages will be installed
  libccid pcscd
0 to upgrade, 2 to newly install, 0 to remove and 21 not to upgrade.
Need to get 141 kB of archives.
After this operation, 464 kB of additional disk space will be used.
Get:1 http://archive.ubuntu.com/ubuntu jammy/universe amd64 libccid amd64 1.5.0-2 [83.1 kB]
Get:2 http://archive.ubuntu.com/ubuntu jammy-updates/universe amd64 pcscd amd64 1.9.5-3ubuntu1 [58.1 kB]
Fetched 141 kB in 1s (193 kB/s)
Selecting previously unselected package libccid.
(Reading database ... 740455 files and directories currently installed.)
Preparing to unpack .../libccid1.5.0-2amd64.deb ...
Unpacking libccid (1.5.0-2) ...
Selecting previously unselected package pcscd.
Preparing to unpack .../pcscd1.9.5-3ubuntu1amd64.deb ...
Unpacking pcscd (1.9.5-3ubuntu1) ...
Setting up libccid (1.5.0-2) ...
Setting up pcscd (1.9.5-3ubuntu1) ...
Created symlink /etc/systemd/system/sockets.target.wants/pcscd.socket → /lib/systemd/system/pcscd.socket.
pcscd.service is a disabled or a static unit, not starting it.
Processing triggers for man-db (2.10.2-1) ...
So far, so good. Let's check out the go project:
$ git clone git@github.com:deeper-x/gopcsc.git
Cloning into 'gopcsc'...
remote: Enumerating objects: 289, done.
remote: Counting objects: 100% (31/31), done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 289 (delta 23), reused 21 (delta 21), pack-reused 258 (from 1)
Receiving objects: 100% (289/289), 52.37 KiB | 714.00 KiB/s, done.
Resolving deltas: 100% (162/162), done.
This has two example applications apdu and show-atr. If I had done more research, these abbreviations might mean something to me. As it is, I guess that show-atr has something to do with showing attributes; apdu may well have something to do with payments. I don't know.

Anyway, I've installed all that lot, so it doesn't seem implausible to create launch configurations for these two example apps in VSCode, plug the gadget in, run a program, tap a card and see what happens.

My launch configuration looks like this:
{
        "version": "0.2.0",
        "configurations": [
                {
                        "name": "apdu",
                        "type": "go",
                        "request": "launch",
                        "program": "examples/apdu",
                        "args": []
                },
                {
                        "name": "show-atr",
                        "type": "go",
                        "request": "launch",
                        "program": "examples/show-atr",
                        "args": []
                }
        ]
}
When I plug the device in, a green light flashes for a moment, then there's a beep and a blue light comes on. So far, so good.

They've given me a number of "cards" in the box. I don't really know what they are or what they do, but the "ACOS6S" card (which looks like, and might well be, a SIM card) does nothing. The others (ACOS3 and MIFC1) both make the gadget beep, as do my hotel room keys. Good, good.

If I run the show-atr program, it says it is waiting for my card to be present but ... it doesn't notice when I present a card.
Starting: /home/gareth/go/bin/dlv dap --listen=127.0.0.1:45729 --log-dest=3 from /home/gareth/acs1552u/gopcsc/examples/show-atr
DAP server listening at: 127.0.0.1:45729
Type 'dlv help' for list of commands.


Waiting for card...
And when I try and run apdu, it says I need to "insert" a smart card, without waiting:
Starting: /home/gareth/go/bin/dlv dap --listen=127.0.0.1:34037 --log-dest=3 from /home/gareth/acs1552u/gopcsc/examples/apdu
DAP server listening at: 127.0.0.1:34037
Type 'dlv help' for list of commands.


please insert smart card
Process 480312 has exited with status 0
Detaching
OK, not a stellar start. I think that's almost enough for today, but let's do a little more googling before giving up.

Apparently, APDU stands for Application Protocol Data Unit, although, at the moment, I don't understand the description.

Meanwhile, ATR stands for Answer to Reset which appears to be some kind of initialization code.

Troubleshooting

One thing that stands out is that I installed a driver earlier (pcscd) but it isn't running. Looking at its manual page, I can see that it can be run by hand in the foreground with debugging on:
$ sudo pcscd -f --debug
00000000 [136004738781696] debuglog.c:299:DebugLogSetLevel() debug level=debug
00000145 [136004738781696] configfile.l:293:DBGetReaderListDir() Parsing conf directory: /etc/reader.conf.d
00000024 [136004738781696] configfile.l:329:DBGetReaderListDir() Skipping non regular file: ..
00000005 [136004738781696] configfile.l:329:DBGetReaderListDir() Skipping non regular file: .
00000004 [136004738781696] configfile.l:369:DBGetReaderList() Parsing conf file: /etc/reader.conf.d/libccidtwin
00000046 [136004738781696] pcscdaemon.c:663:main() pcsc-lite 1.9.5 daemon ready.
00004604 [136004738781696] hotpluglibudev.c:300:getdriver() Looking for a driver for VID: 0x1D6B, PID: 0x0002, path: /dev/bus/usb/001/001
00000187 [136004738781696] hotpluglibudev.c:300:getdriver() Looking for a driver for VID: 0x1D6B, PID: 0x0003, path: /dev/bus/usb/002/001
00000166 [136004738781696] hotpluglibudev.c:300:getdriver() Looking for a driver for VID: 0x1D6B, PID: 0x0002, path: /dev/bus/usb/003/001
00000100 [136004738781696] hotpluglibudev.c:300:getdriver() Looking for a driver for VID: 0x1D6B, PID: 0x0002, path: /dev/bus/usb/003/001
00000100 [136004738781696] hotpluglibudev.c:300:getdriver() Looking for a driver for VID: 0x072F, PID: 0x2303, path: /dev/bus/usb/003/033
00000103 [136004738781696] hotpluglibudev.c:300:getdriver() Looking for a driver for VID: 0x072F, PID: 0x2303, path: /dev/bus/usb/003/033
00000097 [136004738781696] hotpluglibudev.c:300:getdriver() Looking for a driver for VID: 0x072F, PID: 0x2303, path: /dev/bus/usb/003/033
00000090 [136004738781696] hotpluglibudev.c:300:getdriver() Looking for a driver for VID: 0x1D6B, PID: 0x0002, path: /dev/bus/usb/003/001
00000100 [136004738781696] hotpluglibudev.c:300:getdriver() Looking for a driver for VID: 0x8087, PID: 0x0026, path: /dev/bus/usb/003/003
00000098 [136004738781696] hotpluglibudev.c:300:getdriver() Looking for a driver for VID: 0x8087, PID: 0x0026, path: /dev/bus/usb/003/003
00000090 [136004738781696] hotpluglibudev.c:300:getdriver() Looking for a driver for VID: 0x1D6B, PID: 0x0002, path: /dev/bus/usb/003/001
00000102 [136004738781696] hotpluglibudev.c:300:getdriver() Looking for a driver for VID: 0x5986, PID: 0x214C, path: /dev/bus/usb/003/002
00000095 [136004738781696] hotpluglibudev.c:300:getdriver() Looking for a driver for VID: 0x5986, PID: 0x214C, path: /dev/bus/usb/003/002
00000167 [136004738781696] hotpluglibudev.c:300:getdriver() Looking for a driver for VID: 0x1D6B, PID: 0x0003, path: /dev/bus/usb/004/001
I don't know about you, but that looks to me as if it is scanning all of my USB ports and not finding anything it recognizes. Now, the gadget is clearly connected because otherwise it wouldn't have power, light up or beep. But I know from experience that USB can be a lot more tricky than that.

Let's dig in:
$ sudo lsusb
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 003 Device 002: ID 5986:214c Acer, Inc BisonCam,NB Pro
Bus 003 Device 003: ID 8087:0026 Intel Corp. AX201 Bluetooth
Bus 003 Device 033: ID 072f:2303 Advanced Card Systems, Ltd ACR1552
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
It's definitely there, on 072f:2303, and looking back at the output above, it reported checking that out three times (why 3? I don't know) but didn't then say "aha!" or anything like that, so I suspect that it doesn't recognize this as a device that it supports.

Back when I was looking at the acsccid project, I said that my reader (the ACR1552U-M1) was on the supported list, and this is what it said about it:
072F 2303 ACR1552U-M1 ACS ACR1552 1S CL Reader
Which are the corresponding numbers. But, of course, that is not the project I'm using now. So, that obviously raises the question of whether pcscd even supports my reader or, if it does, if I need to do something to get it to recognize it. From the debug output above, I see that there is a directory /etc/reader.conf.d which has configuration files in it. Let's go and check those.
$ cat /etc/reader.conf.d/libccidtwin
# Gemalto reader with serial communication
# - n is the serial port to use n in [0..3]
# - reader is the reader name. It is needed for multi-slot readers.
# Possible reader values are:
# GemCorePOSPro
# GemCoreSIMPro
# GemCoreSIMPro2
# GemPCPinPad
# GemPCTwin (default value)
# SEC1210 (Dual slot Reader)
# example: /dev/ttyS0:GemPCPinPad
#DEVICENAME /dev/ttySn[:reader]
#FRIENDLYNAME "GemPCTwin serial"
#LIBPATH /usr/lib/pcsc/drivers/serial/libccidtwin.so
I have to say that doesn't mean a lot to me (even the example) and I don't understand how I am supposed to use it: does this bind a device by name to a device, or is it expecting to look for the device at that path? I have no idea. But it's a starting point for next time.

Conclusion

I've come up with the idea of doing something with NFC on Android, and I've managed to follow several false trails in trying to set up my Linux box with a driver that works with my reader, but I haven't managed to figure it out.

Figuring that time is the great healer, I will return to this tomorrow.

No comments:

Post a Comment