4.1. Retry requests and delivery receipts
If the sender or recipient device's state has been rolled back, or the recipient device's state has been deleted, then it is possible for the recipient device to receive a valid message that it can't decrypt.
To handle this without message loss the sending device may store a set of MessageRecords, indexed by some MessageID which is unique for each encrypted message. If a single message is encrypted to several recipient devices the sender will store a separate MessageRecord for each recipient device, each with a unique MessageID. Each MessageRecord stores the following values:
- The plaintext of the encrypted message.
- The UserID for the recipient device.
- The SessionID for the session the message was encrypted with.
When the recipient device receives an undecryptable message, the recipient device sends an unencrypted retry request message to the original sending device's mailbox, containing the undecryptable message's MessageID.
When the original sending device fetches a retry request along with the relevant UserID and DeviceID of the device that sent the retry request, the original sending device executes the following resending process:
If the MessageID doesn't refer to a current MessageRecord, then the retry request is discarded and this process terminates.
If the relevant MessageRecord doesn't contain a UserID that equals the UserID used to send the retry request, then the retry request is discarded and this process terminates. (Note that there is no similar check for DeviceID; for flexibility, the retry request is allowed from a different DeviceID than was originally sent to.)
If a non-stale UserRecord and non-stale DeviceRecord with an active session do not exist for the relevant UserID and DeviceID; or if such a session does exist but it matches the SessionID from the relevant MessageRecord, then:
- The resending device queries the server for the identity public key corresponding to the relevant UserID and DeviceID.
- If the server indicates that the recipient UserID or DeviceID do not exist, then the relevant records are marked stale, the retry request is discarded, and this process terminates.
- The sender then preps for encrypting to the tuple (UserID, DeviceID, public key).
- If the DeviceRecord's active session matches the SessionID from the relevant MessageRecord, then the sending device creates a new initiating session using the relevant public key for the DeviceRecord. The new session is inserted into the DeviceRecord. This prevents the sending device from repeatedly sending a message using an orphaned session which doesn't match any recipient session.
The resending device encrypts the plaintext from the MessageRecord using the active session from the relevant DeviceRecord.
The resending device sends the encrypted message to the server, along with the UserID and DeviceID indicating the recipient mailbox.
If the server indicates that the recipient UserID or DeviceID do not exist, then the relevant records are marked stale, the retry request is discarded, and this process terminates.
Otherwise, the server accepts the message and the message is sent to the relevant mailbox. The resending device deletes the old MessageRecord and adds a new MessageRecord for the new encrypted message.
MessageRecords might be deleted after some time has elapsed, or if the plaintext they refer to has been deleted from the sending device by the user. Devices might also send delivery receipts upon successful message decryption. Delivery receipts refer to some MessageID and notify the sender that the MessageRecord may be deleted. Delivery receipts may be encrypted or unencrypted (since they follow every received message, encrypting them doesn't accomplish much).
To avoid excessive resending, devices should impose some limit on the number of times they're willing to resend a message. If any other error occurs, then the resending device shall discard any state changes and terminate the process.
4.2. Session expiration
It may be desirable for devices to periodically replace old sessions with new sessions, for security purposes. One approach is to give each session a timestamp. The timestamp is set to the current time when an initiating session is created. When initiation messages are fetched, the server tells the recipient device the time difference between when the message arrived at the mailbox and the current time. The recipient device sets the timestamp for any initiated session to the current time minus this difference.
Time constants MAXSEND and MAXRECV are defined, where MAXRECV must be greater than MAXSEND + 2(MAXLATENCY). At time MAXSEND past its timestamp a session must no longer be used for encryption, and shall be moved to the head of the inactive sessions list if active. Attempts to activate such a session have no effect. At time MAXRECV past a session's timestamp the session may be deleted, after first fetching and processing all mailbox messages.