An ephemeral X25519 key can protect an offline QR for a pre-provisioned verifier, but freshness, replay resistance and sender authentication still require explicit protocol state.
An offline phone can encrypt data for another phone without waiting for a reply. The sender needs an authentic copy of the verifier’s public key, generates a fresh X25519 key pair, derives a shared secret and places the encrypted message in a QR code. The verifier scans once and decrypts locally. That is a useful one-way transfer. It is not, by itself, an authenticated handshake.
The distinction matters in field systems. A QR can cross an air gap and complete in one visual movement, but the absence of a return message removes information that interactive protocols normally use. The sender cannot know that the intended verifier is present. The verifier cannot know that the image is being shown for the first time. A fresh sender key does not make a static recipient key forward secret.
The design therefore starts with a narrow claim: establish a unique encryption context for one offline message addressed to a known verifier key. Authentication and freshness are separate properties that must be added according to the deployment model.
Define the trust already present
Assume the verifier owns a long-term X25519 key pair (skV, pkV). The sender has received pkV through an authenticated provisioning process before going offline. Provisioning might occur during enrollment, in a signed configuration package or through a managed application update. Merely scanning an unauthenticated public key from a nearby display would allow substitution by an attacker.
The sender creates a new ephemeral X25519 key pair (esk, epk) for every QR. X25519 takes and returns 32-byte strings, so the ephemeral public key has a predictable transport cost. The sender computes a Diffie-Hellman result from esk and pkV; the verifier obtains the same result from skV and epk.
The raw X25519 output must not be used directly as an encryption key. It needs a defined key schedule, domain separation and an authenticated-encryption construction. A practical choice is Hybrid Public Key Encryption, or HPKE, using DHKEM(X25519, HKDF-SHA256) with either ChaCha20-Poly1305 or AES-GCM. HPKE specifies the KEM, key derivation, context binding and AEAD setup together. That removes several opportunities for incompatible or unsafe local conventions.
HPKE Base mode is enough for confidentiality to the holder of skV. It does not authenticate the sender because anyone who knows the public pkV can create a valid ciphertext. HPKE Auth mode can bind a sender static KEM key, but a system may instead sign the complete envelope with a dedicated signing key. The choice must follow the identity and deniability requirements, not the convenience of a library call.
A compact single-message envelope
The QR can carry a deterministic binary structure such as canonical CBOR. One reasonable envelope is:
| Field | Purpose | Typical size |
|---|---|---|
version |
Fixes the parsing and validation rules | 1 to 2 bytes |
suite_id |
Identifies the KEM, KDF and AEAD | 1 application-profile byte, or 6 bytes for the three HPKE identifiers |
recipient_key_id |
Selects the provisioned verifier key | 4 to 16 bytes |
epk or HPKE enc |
Carries the sender’s ephemeral encapsulation | 32 bytes for X25519 |
issued_at |
States sender wall-clock time | 4 to 8 bytes |
message_id |
Identifies this attempt | 16 random bytes |
counter |
Optional authenticated sender sequence | 4 to 8 bytes |
ciphertext |
Protects the application payload | Payload length |
tag |
Detects modification | 16 bytes for the named AEADs |
The clear header should be authenticated as associated data. In particular, version, suite_id, recipient_key_id, issued_at and any routing metadata must be covered by the AEAD tag. Otherwise an intermediary could change fields that influence key selection or policy without changing the encrypted body.
The recipient key identifier is not proof of recipient identity. Its purpose is deterministic key lookup and protection against accidental cross-key processing. The provisioning record binds that identifier to the expected verifier and protocol suite.
The sender should erase esk and the derived encryption context immediately after rendering the QR. The verifier should erase the shared secret and message key after opening the single message. A new QR gets a new encapsulation, even if the application content is identical.
Let the encryption construction own the nonce
A time-based one-time password must never be placed in an AEAD nonce or IV. A TOTP is predictable to every party holding the shared seed and repeats throughout its time step. It can therefore repeat while the same encryption key remains in use. ChaCha20-Poly1305 requires that a nonce never repeat under a key; AES-GCM also depends critically on IV uniqueness.
The safer design is to let HPKE derive its AEAD key and base nonce from the KEM shared secret and key schedule. The first and only message uses sequence number zero. Every QR uses fresh encapsulation randomness, producing a new context. No wall-clock value or TOTP is needed for nonce construction.
If a deployment cannot use HPKE, it must still specify an equivalent extract-and-expand schedule. HKDF should derive separate key and nonce material from the X25519 result, a protocol-specific context and fresh session input. A 96-bit AEAD nonce must be unique for that derived key. This is protocol machinery, not a place to reuse an application timestamp.
The random generator is part of the security boundary. Repeated or predictable ephemeral X25519 secrets can repeat shared secrets and key-nonce pairs. A field implementation should use the operating system cryptographic generator, fail closed when it is unavailable and include repeated-ephemeral-key tests in telemetry that does not expose the keys themselves.
State exactly what the exchange provides
The one-way construction provides confidentiality and integrity for the payload addressed to pkV, assuming correct provisioning, uncompromised keys and sound implementations. It also binds the authenticated header to that payload.
It provides limited forward secrecy on only one side. If the sender erases esk, later compromise of the sender does not recover the key for a previously captured QR. If an attacker later obtains the verifier’s static skV, however, the attacker can combine it with the recorded epk and decrypt past messages. HPKE explicitly excludes forward secrecy with respect to recipient compromise.
Full recipient-side forward secrecy needs fresh recipient contribution. That normally means an interactive challenge, such as a verifier QR scanned before the sender displays its response. A genuinely one-way alternative is a provisioned pool of one-time recipient public prekeys. Each private prekey must be erased after one accepted message. That moves the interaction into an earlier provisioning phase and introduces allocation, exhaustion and recovery problems.
The construction also does not authenticate the sender in Base mode. To identify the sender, add one of the following inside or around the protected envelope:
- A signature over the protocol context,
enc, authenticated header and ciphertext, verified against a provisioned sender public key. - HPKE Auth mode with a provisioned sender static KEM public key, when its authentication and compromise properties match the application.
- A MAC derived from a sender-specific pre-shared secret, when symmetric attribution and shared liability are acceptable.
An identity claim inside ciphertext is not authentication. An attacker can write any name into a new ciphertext because the verifier public key is public.
Time limits exposure, state stops replay
An authenticated issued_at and expiry policy can bound the period in which a message is eligible. They do not make it unique. A screenshot replayed thirty seconds later still contains a valid timestamp and a valid AEAD tag.
Replay resistance requires verifier state. Give every message a uniformly random 128-bit message_id. After successful decryption and policy validation, the verifier records a digest of the protocol version, sender identity if authenticated, recipient key identifier and message ID. A repeated digest is rejected. The record must live at least as long as the maximum acceptance window, plus the largest tolerated clock error and operational delay.
A sender counter can strengthen ordering when sender authentication exists. The verifier stores the highest accepted counter for that sender and rejects older values. This design needs a recovery policy for device restoration, concurrent sender devices and out-of-order delivery. Without sender authentication, an attacker can generate a new encrypted envelope containing any counter and the counter has no trustworthy owner.
Multiple disconnected verifiers create a harder boundary. Local replay caches prevent a second use at the same terminal, but the same captured QR may be accepted once at every terminal that shares skV. There are only three general remedies: assign the QR to a specific verifier key, synchronize spent-message state, or obtain a fresh challenge from the accepting terminal. A timestamp cannot replace any of them.
Treat clock drift as an acceptance policy
Phones can have wrong wall clocks, and users or software may move them. The verifier should compare the authenticated sender time with its own trusted-enough time under an explicit asymmetric window. For example, a field policy might accept a QR up to 90 seconds old and at most 30 seconds in the future. Those numbers are risk decisions, not cryptographic constants.
If TOTP is used as an additional proof of a shared secret, keep it inside the authenticated plaintext and validate it according to a defined number of time steps. RFC 6238 recommends a 30-second step and warns that a larger window increases exposure. It also requires the verifier not to accept the same OTP again after successful validation. That requirement itself needs persistent replay state.
Clock-drift learning must be bound to an authenticated sender and updated only after the entire message passes cryptographic and policy checks. An unauthenticated QR must not be allowed to adjust the verifier’s stored drift estimate. Large corrections should require re-enrollment or an online administrative event.
For rollback resistance, store replay records, highest counters and key epochs in storage that an application backup cannot silently restore to an earlier value. Hardware-backed keys help protect secret material, but they do not automatically make an application database monotonic. The implementation needs a protected version or checkpoint strategy and must define what happens after device restore, factory reset or loss of state. A conservative verifier rejects offline transactions until state is re-established.
The acceptance order is part of the protocol
A robust verifier can process the scan in this order:
- Parse with strict size, depth and canonical-encoding limits.
- Select an enabled protocol suite and recipient key without trusting unauthenticated claims.
- Open the HPKE ciphertext and reject any authentication failure.
- Validate the authenticated protocol version, recipient binding and application type.
- Verify sender authentication, if the transaction requires it.
- Check expiry, future skew, key epoch and counter policy.
- Check the replay cache for the authenticated message identity.
- Commit the replay record atomically before releasing the protected action.
The last step closes a common race. Two scanner threads must not both observe an unused message and then both approve it. Decryption can be repeated harmlessly; authorization must be committed exactly once according to local policy.
Calling this flow 0-RTT describes latency, not security. The visual channel carries one sender flight, and no network round trip is required. The result is a useful encrypted offline transfer with bounded freshness. It becomes sender-authenticated only when a sender credential is verified, replay-resistant only where spent state is authoritative, and forward-secret against verifier compromise only when the verifier’s private contribution is also ephemeral or one-time.
Context and scope
This analysis abstracts an offline mobile exchange designed for field use. The commissioning organisation, enrollment system, device fleet and operating location are omitted. The stated security properties derive from the cited protocol definitions rather than from unidentified production measurements.
References
- RFC 7748: Elliptic Curves for Security
- RFC 9180: Hybrid Public Key Encryption
- RFC 5869: HMAC-based Extract-and-Expand Key Derivation Function
- RFC 8439: ChaCha20 and Poly1305 for IETF Protocols
- NIST SP 800-38D: Galois/Counter Mode and GMAC
- RFC 6238: TOTP, Time-Based One-Time Password Algorithm
- RFC 8949: Concise Binary Object Representation
- NIST SP 800-63B: Authentication and Authenticator Management



