---
Proposed: 2023-12-29
Implemented: 2024-03-14
Standardized: 2026-03-09
Protocol: pqdr v1, agent-protocol v5
---

# Post-quantum double ratchet implementation

See [the previous doc](https://github.com/simplex-chat/simplex-chat/blob/stable/docs/rfcs/2023-09-30-pq-double-ratchet.md).

The main implementation consideration is that it should be both backwards and forwards compatible, to allow changing the connection DR to/from using PQ primitives (although client version downgrade may be impossible in this case), and also to decide whether to use PQ primitive on per-connection basis:
- use without links (in SMP confirmation or in SMP invitation via address or via member), don't use with links (as they would be too large).
- use in small groups, don't use in large groups.

Also note that for DR to work we need to have 2 KEMs running in parallel.

Possible combinations (assuming both clients support PQ):

| Stage        | No PQ kem | PQ key sent | PQ key + PQ ct sent |
|:------------:|:---------:|:-----------:|:-------------------:|
| inv   | + | + | - |
| conf, in reply to:   <br>no-pq inv <br>pq inv        | &nbsp;<br>+<br>+ | &nbsp;<br>+<br>- | &nbsp;<br>-<br>+ |
| 1st msg, in reply to:<br>no-pq conf<br>pq/pq+ct conf | &nbsp;<br>+<br>+ | &nbsp;<br>+<br>- | &nbsp;<br>-<br>+ |
| Nth msg, in reply to:<br>no-pq msg <br>pq/pq+ct msg  | &nbsp;<br>+<br>+ | &nbsp;<br>+<br>- | &nbsp;<br>-<br>+ |

These rules can be reduced to:
1. initial invitation optionally has PQ key, but must not have ciphertext.
2. all subsequent messages should be allowed without PQ key/ciphertext, but:
  - if the previous message had PQ key or PQ key with ciphertext, they must either have no PQ key, or have PQ key with ciphertext (PQ key without ciphertext is an error).
  - if the previous message had no PQ key, they must either have no PQ key, or have PQ key without ciphertext (PQ key with ciphertext is an error).

The rules for calculating the shared secret for received/sent messages are (assuming received message is valid according to the above rules):

|   sent msg ><br>V received msg | no-pq       | pq      | pq+ct           |
|:------------------------------:|:-----------:|:-------:|:---------------:|
| no-pq                          | DH / DH     | DH / DH | err             |
| pq (sent msg was NOT pq)       | DH / DH     | err     | DH / DH+KEM     |
| pq+ct (sent msg was NOT no-pq) | DH+KEM / DH | err     | DH+KEM / DH+KEM |

To summarize, the upgrade to DH+KEM secret happens in a sent message that has PQ key with ciphertext sent in reply to message with PQ key only (without ciphertext), and the downgrade to DH secret happens in the message that has no PQ key.

The type for sending PQ key with optional ciphertext is `Maybe E2ERachetKEM` where `data E2ERachetKEM = E2ERachetKEM KEMPublicKey (Maybe KEMCiphertext)`, and for SMP invitation it will be simply `Maybe KEMPublicKey`. Possibly, there is a way to encode the rules above in the types, these types don't constrain possible transitions to valid ones.
