sender_key = HMAC-SHA256(prev_sender_key, "0")
== Server order ==
All clients see the same message order from the server. All messages are sent to all users. Aside from the presence messages sent by the server, messages are sent by users.
All messages in a room have a unique sequence number (0, 1, ...). We assume that the server is unaware of sequence numbers (e.g. XMPP MUC); clients must allocate them implicitly when receiving messages.
This version of the spec does ''not'' support servers that give explicit sequence numbers to messages; this would force us to handle the case where messages are received with explicitly out-of-order sequence numbers. For now, we rely on transcript consistency to detect out-of-order messages for the ''implicit'' sequence number case.
A new user synchronizes his view of sequence numbers via the QUERY / MEMBER_LIST messages (see below).
== Causal order ==
Some (all?) user-sent messages specify a "parent" sequence number which is the last message the user received before sending it. Note:
* If Alice sends messages (A,B) in a row, A will not be B's parent unless Alice waits till A is received back from the server. This is because the server may validly insert messages between parent(A) and A, in which case parent(B) being A would contradict the semantics we just stated.
* The parent of a message is different from the "previous" message in the server's ordering, e.g. in a "simultaneous send" case two messages will have the same parent.
To prevent the server re-ordering (A, B) when they have the same parent, each message also includes an explicit own-sequence-number, incremented by the sending client themselves.
We must enforce a few invariants. For every pair of messages A, B:
* ownseqnum(A) ≤ ownseqnum(B) ⇔ seqnum(A) ≤ seqnum(B) ⇔ seqnum(parent(A)) ≤ seqnum(parent(B)
In practice, and because currently we implicitly allocate seqnums, for each incoming message m we only need to check:
* let p be the latest message received from the same sender (of m). then, check that:
** ownseqnum(p) == ownseqnum(m) + 1
** seqnum(parent(p)) ≤ seqnum(parent(m))
* if p doesn't exist, then instead check that ownseqnum(m) == 0
Due to server ordering, the sender of message i must have seen all messages with seqnum between 0...parent(i), as well as all messages m with seqnum between parent(i)...i where ownseqnum(m) ≤ ownseqnum(i). Thus, every user-sent message i has a membership set, determined by the JOIN / USER_LEFT messages from 0...i's parent.
== Transcript hashes ==
Encrypted messages include a "transcript hash" of their parent and all prior messages as "additional authenticated data".
The hash also covers the sender_key for DATA messages (set to zeros for all other messages):
H(parent) = SHA256(sender_key[parent] || msg[parent] || H(parent-1))
Note that we defined H(parent) rather than H(message). This is because a hash may only be calculated once the subject is actually received back from the server (i.e. gets a sequence number). This differs from some other concepts of "message ID" that may be calculated locally.
== Timing ==