Offline authentication

How does a QR code identity check work?

A QR code identity check works by verifying a digital signature, not the code itself. Either the code carries a signed credential that the reader checks against an issuer key it already holds, as EU Digital COVID Certificates did, or it only starts a connection over which the holder's device sends signed data and proves it holds a device key, as ISO/IEC 18013-5 mobile driving licences do.

Learning objectives

After reading this article you will be able to:

  • Distinguish QR codes that carry a signed credential from codes that open a session
  • Describe how EU Digital COVID Certificates fit a signed payload into a QR code
  • List the checks every offline reader must make, including binding to the holder

The code is only a container

A QR code is an optical container for data, standardised as ISO/IEC 18004. DENSO WAVE, which has waived its QR code patent rights for standardised codes, lists its approval as an ISO standard in June 2000. The largest symbol holds at most 2,953 bytes in binary mode, and four error correction levels let a reader restore from about 7% up to about 30% of the codewords when a code is smeared or damaged.

None of that says anything about who presented the code. Anyone can generate a QR code containing any text. An identity check only means something if the reader verifies a digital signature against a key it already trusts. Deployed systems use the code in one of two ways.

Pattern one: the code carries the credential

The EU Digital COVID Certificate put a complete signed credential inside the code, designed so it could be shown on a phone screen or printed on paper. The design requirements in the eHealth Network’s hcert specification state that certificates are carried by the holder and must be able to be securely validated offline.

The payload is built in layers:

  1. The data is encoded as CBOR, a compact binary format, with field names shortened to save bytes.
  2. The signature wraps it in a COSE structure, the CBOR signing format specified at the time in RFC 8152 and now in RFC 9052. The specification’s Volume 3 describes an array of protected header, payload and signature, and lets the issuer put the key identifier in either header, which verifiers must accept.
  3. Compression with deflate shrinks it.
  4. Base45 turns the bytes into text. RFC 9285 explains why: QR readers try to interpret even byte mode as text, so binary data has to be converted first, and Base45 gives a more compact QR code than Base64.

The reader reverses those steps, looks up the signing key by its identifier and checks the signature. The trust model in the hcert specification is a single layer of country signing authorities that sign document signer certificates, published as a trusted list. Verification is described as generally offline and based purely on the trusted list of that day, and revocation works by omission: a key that is removed from the list stops verifying.

Pattern two: the code opens a session

Mobile driving licences under ISO/IEC 18013-5 use the QR code only to start the exchange. Google’s guide to in-person verification describes the phases:

  • Device engagement. The reader and wallet make first contact by an NFC tap or a QR code scan, exchanging engagement data and the reader’s ephemeral public key.
  • Data transport. They set up an encrypted Bluetooth Low Energy channel.
  • Request and consent. The reader asks for specific data elements, and the holder confirms on their phone.
  • Response. The wallet returns the issuer-signed data and device-signed data.

The reader then runs four checks: the issuer signature against trusted root certificates, the validity window against its own clock, a digest of each returned element against the signed list, and device authentication. That last step verifies a signature or MAC from the device key named in the issuer-signed data, computed over the session transcript, so it is bound to the reader’s ephemeral key. Google’s page says this prevents replay and man-in-the-middle attacks.

A screenshot of the engagement code is no use to anyone else, because the response has to be signed with the device key named in the issuer’s data.

What every reader has to check

Whichever pattern is used, an offline reader has to answer the same questions.

  • Is the signature valid, under a key I trust? The trusted keys must be installed before the check. Whether they come from a certificate chain or a plain list is a design choice, covered in whether you need a certificate authority offline.
  • Is it within its validity dates? This depends on the reader’s clock being right.
  • Does it belong to the person in front of me? A signed payload in a static code verifies the same way for anyone holding a copy. The EU certificate’s Volume 3 asks for the holder’s name in a form that should match their travel document, so a person can be compared against it. Session-based checks bind the response to a key on the holder’s device instead.
  • Has it been revoked? The reader knows only what its last update told it.

Phone-to-phone pairing codes

The same idea secures first contact between two devices. One phone shows a code containing its identifier and public key, and the other scans it. In Offline Protocol’s mesh SDK, parseInvite refuses an invite unless the address derives from the key and any signature present verifies. Its reference notes what that does not prove: an attacker’s correctly signed invite looks the same as a stranger’s, and only the out-of-band context, that this code was on this person’s screen, carries that assurance. Gate ticket checks face the same limit, which is why the physical moment of scanning still matters.

Frequently asked questions

Can someone screenshot a QR credential and use it?

A code that carries a complete signed credential verifies the same way for anyone who holds a copy, so the reader has to tie it to the person, for example by matching a name or photo against another document. Protocols that run a session, such as ISO/IEC 18013-5, have the holder's device sign over data from that session, which stops a recorded response being replayed.

Does the reader need internet to check a signed QR code?

Not at the moment of the check, if it already holds the trusted issuer keys and a correct clock. It needs a connection from time to time to refresh those keys and learn about revocations.

Sources

Build it with Offline Protocol

The identity API reference documents createInvite and parseInvite, which encode a device's address and public key for a QR code and verify a scanned invite before the app acts on it, including what that check does not prove.

Read the identity API reference