Check-In via Mobile Phone App





The following secrets are involved in this process:


Use / Purpose


data secret

The Guest’s secret seed to derive both the data encryption key and the data authentication key. This seed will be encrypted for the Health Department in this process before being transported to the Scanner Frontend via a QR code and protects the Guest’s Contact Data stored on the Luca Server.

Securely stored locally on the mobile device (see Guest Registration for further details)

data authentication key

A symmetric key derived from the data secret, used to bind the Check-In data to the current time stamp of the Check-In event.

Not stored

tracing secret

Used to generate an anonymous trace ID to facilitate contact tracing by the Health Department (after the Guest granted access to the tracing secret – rendering them an Infected Guest).

Securely stored locally on the mobile device

daily keypair

Used to encrypt the above-mentioned data secret on the Guest’s mobile device before transferring it to the Scanner Frontend via a QR code.

The public key is obtained from the Luca Server 1. The private key is known to all Health Departments (see Daily Public Key Rotation for further details).


Verifies the authenticity of the daily keypair public key by checking its signature.

Certificate is provided by the Luca Server. The associated private key is kept by the issuing Health Department.


This describes how Guests use luca’s Guest App to generate so-called Check-Ins at specific venues. For that, Venue Owners deploy Scanner Frontends that read QR codes generated by the Guest App. Note that there are other ways a Guest might check-in to a venue: Please refer to Badge Check-In and Check-In via a Printed QR Code for further details.

Check-Ins can be used at a later time by Health Departments to reconstruct an Infected Guest’s Check-In History (given that the Infected Guest has given their consent). Check-Ins of other Guests can be associated with the Infected Guest’s Check-In History to allow for Tracing the Check-In History of an Infected Guest.

In any case the Check-In data that is transferred and stored on the Luca Server does not reveal information about the Guest’s identity (O1, O2). Neither does the Luca system learn about a Guest’s habits (O3).



Before generating any QR codes to perform Check-Ins the Guest App will fetch the latest daily keypair public key from the Luca Server (see Daily Public Key Rotation). The provided public key comes with a reference to the issuing Health Department, a creation timestamp and a signature by a Health Department’s HDSKP certificate. The Guest App must verify the trust chain of the HDSKP certificate as well as the signature of the daily keypair (see Verification of Health Department Keypair Certificates). Furthermore, keys that are older than seven days are not considered valid anymore.

Scanner Check-In

QR codes generated by the Guest App are valid for a short period of time and the whole generation process described below is repeated every minute. Each trace ID is generated as HMAC-SHA256 of the Guest’s user ID and a current quantized timestamp (clamped to the latest full minute) as data and the tracing secret as key. The resulting value is truncated to the first 16 bytes. Subsequently, the Guest App asymmetrically encrypts the Guest’s user ID and the data secret for the daily keypair. The IV is defined as the first 16 bytes of the ephemeral public key used in the DLIES. The Guest App then calculates a verification tag as HMAC-SHA256 of the timestamp and the encrypted data as data and the data authentication key as key, truncated to the first 8 bytes. A four-byte checksum (truncated SHA256) of all the previously generated data blob is appended as an integrity check to detect faulty QR code reads 2.

QR Code Generation and Check-In

The app generates a new QR code every minute, for each code the app generates the following:

timestamp        = UNIX timestamp rounded down to the last full minute (little endian encoding)
trace_id         = HMAC-SHA256(user_id || timestamp, tracing_secret)  # truncated to 16 bytes
ephemeral_keys   = a new secp256r1 key pair (for DLIES with the daily public key)
dh_key           = ECDH(ephemeral_keys.private, daily_keypair.public)
enc_key          = SHA256(dh_key || 0x01)  # truncated to 16 bytes
iv               = ephemeral_keys.public   # truncated to 16 bytes
enc_data         = AES-128-CTR(userId || data_secret, enc_key, iv)
verification_tag = HMAC-SHA256(timestamp || enc_data, data_authentication_key)

Security Considerations


The trace ID (trace_id) depends on the user ID (user_id) of the Guest, the current quantized timestamp and the Guest’s tracing secret. Hence, all trace IDs for any given minute can be calculated given the user ID and the tracing secret (which is stored securely inside the Guest App). Without the tracing secret, the Guest’s trace IDs can neither be linked to (a) the Guest themselves (fulfilling O2) nor (b) to other trace IDs of the same Guest (fulfilling O3).

If tested positive for Sars-CoV2 a Guest may consent to sharing their tracing secret with the Health Department (rendering them an Infected Guest). This facilitates the Health Department to trace the Infected Guest’s Check-In History (fulfilling O4). See Tracing the Check-In History of an Infected Guest for further details.

To restrict the disclosed time interval of the Infected Guest’s Check-In History the Guest App regularly changes the tracing secret (see Rotating the Tracing Secret). The Guest App shares only the tracing secrets that were valid in an epidemiologically relevant time frame (about two weeks) with the Health Department (fulfilling O5).


The encrypted data enc_data is not authenticated as it would usually be the case (cf. Encrypt-then-MAC). We assume that neither the Luca Server nor the Venue Owner can benefit from altering enc_data in any meaningful way. Instead, the verification_tag binds the Check-In’s timestamp to the data secret to avoid replay attacks by an adversary that learned about enc_data but not the data secret. Otherwise, said adversary might use enc_data to create Check-Ins with the identity of the Guest that owns the data secret. Binding the timestamp to the data secret mitigates this replay to a short window of opportunity (about one minute) assuming that the Scanner Frontend validates that timestamps in Check-Ins are recent.

QR Code Construction

The App then displays a QR code containing:

  • version (QR code protocol version)

  • device_type

  • key_id (ID of the daily keypair used for this Check-In)

  • timestamp

  • trace_id

  • enc_data

  • ephemeral_keys.public

  • verification_tag

  • checksum

The payload is concatenated and encoded with ASCII85 to be displayed as a QR code.

QR Code Scanning, Validation and Check-In Upload

The Scanner Frontend reads the above QR code using either a mobile phone camera or a dedicated scanner hardware. Before doing any further processing, it validates the checksum to detect reader errors. Furthermore, the contained timestamp is compared to the Scanner’s local clock with a reasonable grace period. If either the checksum or the timestamp checks fail, no further processing is performed. Any further cryptographic validity checks cannot be performed by the Scanner Frontend.

Next, the relevant Check-In data fields are encrypted by the Scanner Frontend using the venue keypair’s public key (whose private key is in possession of the Venue Owner) as follows:

eph_scanner_keys = a new secp256r1 key pair (for DLIES with the venue public key)
dh_key           = ECDH(eph_scanner_keys.private, venue_keypair.public)
enc_key          = SHA256(dh_key || 0x01)  # truncated to 16 bytes
auth_key         = SHA256(dh_key || 0x02)
iv               = random_bytes(16)

version       = 0x03  # protocol version of the encrypted data record
check_in_data = version || key_id || ephemeral_keys.public || verification_tag || enc_data

venue_enc_data     = AES-128-CTR(check_in_data, enc_key, iv)
venue_enc_data_mac = HMAC-SHA256(venue_enc_data, auth_key)

At last, the following data is uploaded to the Luca Server for each successful Check-In.

  • trace_id

  • scanner_id

  • device_type

  • timestamp

  • venue_enc_data

  • venue_enc_data_mac

  • iv

  • eph_scanner_keys.public

When storing this information the Luca Server associates it with the venue_id (determined via the scanner_id) and the Check-In time. No further processing is done in the Luca Server.

Security Considerations

Second Layer of Encryption

As required by O6: Venue Consent, the Health Department shall be prevented from single-handedly decrypting Guest’s Contact Data. The Luca system is designed to “replace” the paper-based guest lists in physical venues that provide the same security guarantee. Hence, Scanner Frontends encrypt the already encrypted Contact Data in Check-Ins and remove this encryption layer only on authoritative request of a Health Department. See Tracing the Check-In History of an Infected Guest for further details.

Authenticity of venue keypair

When encrypting the (encrypted) user data (enc_data) and the additional data with the venue keypair’s public key the authenticity of that public key is crucial. Plese refer to the security considerations regarding Venue Registration for further details.


The scanner_id sent as part of the Check-In data is the only indicator luca can use in order to infer the associated venue. Forging a non-existent scanner_id could potentially allow an attacker to send bogus data to the Luca Server. However, this does not reveal any information to the attacker in any scenario.

On a similar note, knowing the scanner_id of a venue basically allows the impersonation of the venue’s Scanner Frontend. This is accepted; more specifically, this is specifically desired in the Self Check-In scenario.

QR Code Scanning Feedback

The described process relies on the uni-directional communication from the Guest App to the Scanner Frontend to perform a Check-In by scanning a dynamic QR code. Theoretically, this allows Guest Check-Ins even without a constant internet connection of the Guest App. Nevertheless, user feedback by the Guest App for a successfully scanned QR code is seen as desirable.

Therefore, the Guest App polls the Luca Server via an unauthenticated connection. This inquires whether a Check-In was uploaded by a Scanner Frontend with a trace ID that the Guest App recently generated. Once this inquiry polling request is acknowledged by the Luca Server, the Guest App assumes that a successful QR code scan and Check-In was performed. Some UI feedback is provided to the Guest.

Security Considerations

This polling request might leak information about the association of a just checked-in trace ID and the identity of the Guest (directly contradicting O2). As mobile phone network typically use NAT, the fact that the Luca Server does not log any IP addresses and the connection being unauthenticated, we do accept this risk.


The provided public key of the daily keypair is signed by a Health Department using their HDSKP. This signature is provided by the Luca Server along with said public key.


The QR code standard already includes an error correction mechanism. However, some dedicated QR code scanner hardware acts as keyboard input device to forward QR code data to the luca web application. As this data transfer appears to be error prone, we added checksumming on application level as well.