What data is stored in the NHS COVID-19 domestic pass QR code?

I was curious about what exactly is stored in the NHS COVID-19 domestic pass QR code. The NHS states their verifier app does not store or transmit any personal data during the process, so let’s put this to the test! Thanks to the NHS Digital team’s efforts in making a lot of the NHS COVID-19 apps and technologies open source, we can take a look for ourselves.

Scanning the QR code with the official NHS COVID-19 Pass Verifier app, displays the following data when scanned:

  • First and last name
  • Pass status (can be either Valid, Expired or Not recognised)
  • Expiry date/time

Scanning the QR code with another QR code reader like zbarcam reveals the contents of my domestic COVID-19 pass is the following:

This appears to be some form of encoding. Which let’s be honest, you would expect, so no surprises here.

Refreshing the COVID-19 domestic pass and examining the QR code contents a second time reveals a slightly different value:

We can see part of the QR code has a fixed value, which is likely the payload data.

Breaking down the encoded data:

  • (Header, base64) S2V5MVBSTw==
  • (Payload, base64) MTIxMDgxNzIxMDdKQU1FUyBXSElURQ
  • (Signature, unreadable cryptographic nonsense) Lj584mKnwTbc464n9KZ6tb8BYkFi_jBOWyf5GOa006c42FCXnaVs0azOBbx6Zf49E03oECaS9GmoUbnhIzWqFg

The header corresponds to one of the public keys used by the verifier app as found in the source code: https://covid-status.service.nhsx.nhs.uk/pubkeys/keys.json. The decoded string value for my example is Key1PRO, which will vary depending on which key ID is provided in QR code data.

The payload itself when base64 decoded is:

It turns out that the whole chunk of encoded data is in fact a JSON web token (JWT), of sorts… Using https://jwt.io/ it is easy to decode with the caveat that the token itself does not have a valid JWT signature.

Breaking down the payload contents:

  • 1 — Not sure what this is related to, possibly a version identifier for a protocol?
  • 210817 — The expiry date in YYMMDD format
  • 2107 — Expiry time in UTC HHMM format
  • JAMES WHITE — My first and last name

This all corresponds to what was displayed in the verifier app, my name, the expiry date of 17 August 2021 and time 10:07 PM (UTC +1) as also displayed when the pass was originally generated from within the NHS COVID-19 app.

Determining if a domestic pass itself is valid is performed with cryptographic checks as they are signed, so you can’t just go around generating your own QR codes with any payload you like and passing them off as genuine, you’d need to sign them with the same process used by the NHS COVID-19 app and of course, NHS Digital holds the private key! The QR codes generated by the NHS COVID-19 app also follow a specific schema. Providing the QR code is cryptographically valid, it is then just a case of comparing the date and time payload data against the current date/time when scanning.

  • Valid — Date and time in the payload is in the future relative to the current date/time when scanned
  • Expired — Date and time in the payload is in the past relative to the current date/time when scanned
  • Not recognised - The QR code is not readable or damaged (too much information missing to recover with error correction).

The only personal data contained is your first and last name, otherwise there’s nothing else to see, what the verifier app shows is the only information that is there! One thing the verifier app does not seem to check is if it’s already scanned a COVID-19 pass, therefore sharing a screenshot of a currently valid COVID-19 pass is probably not a good idea as it can be reused multiple times with no challenge. Similarly, if two people with the same name data happen to generate their QR codes at the same time, there is no way to tell the difference either.

Perhaps a future idea might be to generate a unique value based off something like the individual’s NHS number in order for the verifier app to be able to flag if it has seen the same pass multiple times, but then it would have to keep an audit log of this somewhere. It is my understanding that the verifier app is deliberately designed to not do store or transmit data when verifying and instead relies on cryptography to ensure the pass and the data within is genuine.

Providing the private key management at NHS Digital is good, we should be OK!

References and further information

I'm a web developer, but also like writing about technical networking and security related topics, because I'm a massive nerd!