Changes
Dmitri moved page [[MpOTR/incremental consistency]] to [[Np1sec/incremental consistency]]: protocol renamed
This builds on the server-dictated ordered transcript hashes currently mentioned in [[SenderKeys]].
== Background ==
To summarise: we assume the central server reliably delivers messages to everyone, including the original sender, in the same order. (Discussion of potential failures and recovery will be covered elsewhere.)
Each message has an implicit server-sequence-number ("seqnum"), a receive-parent ("recv-parent" or "parent") and a sender-sequence-number ("own-seqnum"). Semantics of these is covered elsewhere.
Once a message m is received from the server (including own messages sent), a "transcript-hash" may be calculated for it, that commits to that message and all previous messages in the server-dictated order. This consists of:
* all messages seen by the sender when they sent m, namely:
** "recv-parent" p and messages with seqnum earlier than p
** all messages sent by the same sender (messages with own-seqnum earlier than m)
* ''as well as'' all messages inserted by the server, into the server-ordering between p and m
When a new message is sent, the transcript-hash of its recv-parent is included with it.
== Definitions ==
A message m is '''fully-acked''' (from the POV of a given member ''u'') iff, for all recipients ''r'', ''u'' has accepted a message by ''r'' where seqnum(parent(r)) >ge; seqnum(m).
* ''recipients'' possibly includes u, but certainly excludes m's sender
** we assume a server-dictated ordering, so accepting a message at seqnum i means we have already accepted all messages j ≤ i.
* if u did not send m, and they have not acked m by t+ACK_GRACE_INTERVAL, they should send an explicit-ack
* if m is not fully-acked (from their POV) by t+(2*BROADCAST_LATENCY)+ACK_GRACE_INTERVAL then issue a local UI warning. Cancel the warning if/when full-ack is reached later.
Case '''(b)''': when a member ''u'' wants to part, they send a "farewell" message ''m'':
* Everyone should explicit-ack ''m'' as soon as they receive it from the server.
** This ack may also bundle any cryptographic material needed for other members to agree on a key or keys that exclude ''u''. For example, in the SenderKeys scenario, this would contain the next sender-key, encrypted to everyone except ''u''.
* When ''m'' is fully-acked, ''u'' reaches consistency for all previous messages up to ''m'', and may leave.
* Messages that others send to ''u'' that are echoed back after ''m'', will not be acked by ''u''.
** ''u'' won't have a chance to reach consistency for these messages, even if ''u'' receives them.
** Others won't have a chance to check that ''u'' read them, though they may be able to check that everyone else did. But ''u'' may still read them in theory, since they were encrypted to ''u''.
** In either case, communicating this meaning to the user, should be already covered by the same UI as for case (a). In the first case, ''u'' may shortcut the timeout and just issue the warning straight away, or alternatively not display those messages to the user at all.
* TBD: need to think about simultaneous parts
=== Parameters and properties ===
Both of these should be defined to cover common cases (e.g. 95th-percentile) rather than being mean values.
The above checks should be applied for every single message. This guarantees that a warning shows up if we don't reach consistency within the timeout defined above, ensuring timeliness. In terms of overhead, effectively a user will and works even if people don't manually send a message at least every ACK_GRACE_INTERVAL time period, whilst the session has other people talking. When there is a lull in the conversation, there should be no further messages.
We are still vulnerable to a "drop everything" attack, but that can't be helped unless we have unconditionally-periodic heartbeats. Not sure if we want to put these in the upcoming spec.
Ensure that messages received out-of-order are highly visible to the user.
A message m is ''unsyncedbadly ordered'' if seqnum(parent(m)) < seqnum(m) - MAX_UNSYNC_COUNT
* MAX_UNSYNC_COUNT may either be constant, or a linear function of the number of members, TBD.