A technical deep-dive into the NHS COVID-19 contact tracing app
The NHS COVID-19 contact tracing beta is now open source for both iOS and Android, along with some documentation. As a follow-up to our “Staying alive” post, we’ve taken a deep-dive into the source code. It’s pleasantly surprising to find it licensed under MIT, indicating an NHSX commitment to transparency and quality.
Over the course of the last two days, a number of engineers have been reviewing the app to discover whether — and how! — it is able to stay alive. Understanding this has required a deeper understanding of the relevant
CoreBluetooth framework than most developers possess. It’s not a framework that engineers typically need to delve into.
Fairly early on, we spotted and reported how the trick worked for Android devices. Tonight, we’re sharing just what’s happening on iOS. Let’s step through it.
On the first launch, the app makes a
PUT request to
https://api.svc-covid19.nhs.uk/api/devices/registrations with an activation code, a push notification token, and the first half of the user-entered postcode. In response the server replies with a
linkingId that is stored in the app’s settings.
The app also requests for push notifications and Bluetooth permissions, as one would expect. It does not request location permissions, and as such doesn’t validate the user’s postcode. Only the first few digits of a postcode are required.
There have been claims that the Android app accesses location data, as the prompt for Bluetooth API access on Android devices appears to ask for location permissions. However, we debunked this yesterday: this is a consequence of how Android manages requests for Bluetooth permissions.
Keeping alive on Android
Preventing an app from sleeping in the background is trivial on Android, as the stock APIs support this functionality, giving engineers low-level access to Bluetooth hardware.
However, on iOS the story isn’t quite as simple. When apps are backgrounded on iOS, their functionality is significantly reduced. This is also the case with
CoreBluetooth, the Apple framework that gives developers access to Bluetooth. Short of a clever workound, this make it substantially harder for the app to detect iPhones that might be running in background mode. We tested with a number of iOS devices left alone over the course of several hours, and noted that polling happened less frequently, but that the devices still communicated overnight.
Using Android to wake iPhones
Yesterday we also reported that when backgrounded, iOS apps continue to broadcast Bluetooth advertisements, but in a different way that’s specific to Apple. We believed the NHSX / Pivotal engineers had reverse-engineered this, and added support to it to the Android app. We can confirm that this is the case, and their technical details about it can be found here. What this means is that in addition to iOS devices being able to keep the app backgrounded, Android devices can wake a “sleeping” iOS device up regardless of how long it has been backgrounded for.
So what are the implications of this? Well, it means that for the app to work, an iPhone must be in range of an Android device fairly often, so it can send keepalives to keep itself backgrounded. That on its own might not be a workable solution, but...
Using iOS devices to wake… other iOS devices
In yesterday’s “Staying alive” post, we reported that we believed the iOS app was using “keepalives” to circumvent backgrounding restrictions. After examining the iOS app source code, we concluded that this is indeed the case. How exactly do they work, though?
Simply put, a keepalive — in this context — is an idea that the app’s developers came up with: it’s a notification sent between two iOS devices running the app to keep the connection between the devices alive. The interesting thing about this is that it also has the effect of allowing the app to stay awake for significantly longer, as when a device receives a keepalive, iOS allows the app to stay awake to do some additional processing.
From a practical perspective, this means that the app will be able to maintain its alive state given it’s in proximity to other devices. This, in a sense, is the rationale of the COVID-19 tracing project: even without this technical trick, a critical mass of users will need to run the tracing app in order to achieve a good public safety outcome.
Irrespective of this, the app doesn’t immediately fall asleep and enter a suspended state when backgrounded. The question thus becomes whether most users will be around enough iOS or Android devices to make the workarounds feasible. Our view is that this seems likely, and all the more so for users who are more regularly in populated areas. Bluetooth has a decent effective range.
A few observers have suggested that the app’s ability to receive silent notifications meant it was using these capabilities to remotely wake itself up. However, these capabilities are simply used to inform the user as to whether they have been near anyone who has contracted COVID-19. The app also makes use of local notifications which detect when the app is killed by the system, instructing users to reopen the app. A similar notification is used when the user disables Bluetooth.
Reincubate’s CEO, Aidan Fitzpatrick, says:
Yes, it is the case that the coming Apple
ExposureNotificationframework in iOS 13.5 obviates the need for these keepalives. However, it’s worth noting that:
- iOS 13.5 has not been released, and may not be for some weeks
- Prior to yesterday, the last iOS 13.5 beta had a major security flaw, suggesting heavy lifting is going on in Apple’s engineering teams
- Once it is released, it will likely take months for a majority of iOS users to install it (unusually, the equivalent Android adoption may be more rapid)
- Older iOS devices — such as the iPhone 6 — cannot run iOS 13, and will not be able to use the Apple technique
- There’s no reason why the NHS COVID-19 app won’t be able to automatically transition in future to using Apple’s framework — or even dual-running both mechanisms
The app will become increasingly effective as more people use it, and the benefit of mass adoption will create a flywheel effect in this sense. The Australian COVIDSafe app struggled as it didn’t support detecting backgrounded iOS devices from an Android device, and it didn’t have this clever iOS-to-iOS keepalive mechanism.
It's important to reflect on which circumstances make use of technology like this appropriate. If contact tracing is successful in saving lives, which criteria should be assessed as to whether it’s applied for future diseases? Once the precedent is set, is there an argument that this technology might be used to combat pre-existing infectious diseases? It’s foreseeable that different societies will evaluate the trade-offs differently.
Whilst writing up a detailed analysis of the mathematics behind the protocol is a little out of scope for this post, we did take a look at privacy safeguards. The app appears to strictly abide by the paper that the NCSC released. We can confirm that no key data leaves the user’s device until they report symptoms, and only then do the anonymised keys of devices it has been in close proximity to leave the device. The first half of the user-entered postcode is sent to the API as part of the registration service.
Ultimately, the code suggests there's little technical privacy risk from using the app until such time as a user reports symptoms and contacts the NHS to arrange a test. At that point, their test result would be managed by the NHS, though this isn't any different with or without the app. However, at such time as multiple users come into contact, report symptoms and arrange tests using the identifier that the app has provided them, it may or may not be possible to centrally tie them together. That's a back-end question, and as NHSX states on their app privacy report, code is truth in this regard.
Guarding against false reports
As the back-end of the system has not been not open-sourced it’s not possible to confirm this, but it appears as though no push notifications are sent to other devices until citizens test positive for COVID-19. Once a user reports symptoms, they’re given a reference code which can be used to book a test. (This code happens to be the same as the
linkingId we described in the registration section.)
The termination notification seems unreliable in my testing of the production app. Might be that the app sometimes terminates before the notification has been scheduled.
They probably need to always have one scheduled a few minutes ahead and keep updating it further into the future on a timer while the app is running. That way when the app dies or is killed the notification is already scheduled.