md-d2d-history.proto 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. // ## History Exchange Protocol
  2. //
  3. // This protocol specifies how to transfer the conversation history from one
  4. // device to another.
  5. //
  6. // ### Terminology
  7. //
  8. // - `SD`: Source device
  9. // - `DD`: Destination device
  10. // - `DGHEK`: Device Group History Exchange Key
  11. //
  12. // ### Key Derivation
  13. //
  14. // DGHEK = BLAKE2b(key=DGK, salt='he', personal='3ma-mdev')
  15. //
  16. // ### Protocol Flow
  17. //
  18. // SD or DD may choose to start the protocol. If DD starts the protocol it is
  19. // _requesting to receive conversation history data_. If SD starts the
  20. // protocol it is _offering to send conversation history data_.
  21. //
  22. // If SD started the protocol:
  23. //
  24. // - `purpose` must be set to _offer to send history data_.
  25. // - SD takes the role of RID
  26. // - DD takes the role of RRD
  27. //
  28. // If DD started the protocol:
  29. //
  30. // - `purpose` must be set to _request to receive history data_.
  31. // - DD takes the role of RID
  32. // - SD takes the role of RRD
  33. //
  34. // If the protocol was transitioned into from the Device Join Protocol:
  35. //
  36. // - ED becomes SD
  37. // - ND becomes DD
  38. // - the Connection Setup part is to be skipped since we already have a
  39. // connection
  40. //
  41. // #### Connection Setup
  42. //
  43. // RID creates an `rendezvous.RendezvousInit` by following the Connection
  44. // Rendezvous Protocol. It encrypts the created `rendezvous.RendezvousInit`
  45. // with `DGHEK`, wraps it in a `url.HistoryExchangeRequestOrOffer` and offers
  46. // it in form of a URL or a QR code.
  47. //
  48. // RRD scans the QR code and parses the `url.HistoryExchangeRequestOrOffer`.
  49. // It will then decrypt the contained `rendezvous.RendezvousInit`. Once
  50. // decrypted, the enclosed `rendezvous.RendezvousInit` must be handled
  51. // according to the Connection Rendezvous Protocol.
  52. //
  53. // Once the Connection Rendezvous Protocol has established at least one
  54. // connection path, DD waits another 3s or until all connection paths have
  55. // been established. Nomination is then done by DD following the Connection
  56. // Rendezvous Protocol.
  57. //
  58. // Note that all messages on the nominated connection path must be end-to-end
  59. // encrypted as defined by the Connection Rendezvous Protocol. All transmitted
  60. // messages are to be wrapped in:
  61. //
  62. // - `FromDestinationDeviceEnvelope` when sending from DD to SD, and
  63. // - `FromSourceDeviceEnvelope` when sending from SD to DD.
  64. //
  65. // #### History Transfer Flow
  66. //
  67. // If invoked by the Device Join Protocol or as soon as one of the connection
  68. // paths has been nominated, DD should show ask the user for which timespan
  69. // the user wants to transfer the conversation history from SD to DD.
  70. //
  71. // Once the user made a timespan selection, DD sends a `GetSummary` message
  72. // and SD responds with a `Summary` message. This may be repeated.
  73. //
  74. // DD ----- GetSummary ---> SD
  75. // DD <------ Summary ----- SD
  76. //
  77. // Once the user chooses to transmit the conversation history of a selected
  78. // timespan, DD sends a `BeginTransfer` message.
  79. //
  80. // DD --- BeginTransfer --> SD
  81. //
  82. // SD will now send `Data` (with `common.BlobData` ahead) repetitively until
  83. // the conversation history of the selected timespan has been fully
  84. // transmitted.
  85. //
  86. // DD <- common.BlobData -- SD [0..N]
  87. // DD <- common.Data -- SD [1]
  88. //
  89. // SD may now close the connection once all buffered data has been written. DD
  90. // may close the connection when it received the last `Data` message.
  91. syntax = "proto3";
  92. package history;
  93. option java_package = "ch.threema.protobuf.d2d.history";
  94. import "common.proto";
  95. import "md-d2d.proto";
  96. // Root message envelope for messages from the destination device (DD) to the
  97. // source device (SD).
  98. message DdToSd {
  99. // The enveloped message
  100. oneof content {
  101. GetSummary get_summary = 1;
  102. BeginTransfer begin_transfer = 2;
  103. }
  104. }
  105. // Root message envelope for messages from the source device (SD) to the
  106. // destination device (DD).
  107. message SdToDd {
  108. // The enveloped message
  109. oneof content {
  110. Summary summary = 1;
  111. common.BlobData blob_data = 2;
  112. Data data = 3;
  113. }
  114. }
  115. // Media type to transfer
  116. enum MediaType {
  117. // All media should be transferred
  118. ALL = 0;
  119. }
  120. // Sent by DD to get a summary of the conversation history available on SD.
  121. //
  122. // When receiving this message:
  123. //
  124. // 1. If `BeginTransfer` has been received before, close the connection and
  125. // abort these steps.
  126. // 2. If summary data is currently being retrieved for a previous `GetSummary`
  127. // message, abort that process.
  128. // 3. If cached properties from a previous `GetSummary` message exist, discard
  129. // those properties.
  130. // 4. Filter `media` in the following way:
  131. // 1. If the special media type _all_ is present, discard any other
  132. // entries.
  133. // 2. Remove duplicate entries.
  134. // 5. Cache the requested properties, including the `id`.
  135. // 4. Retrieve the summary data for the given timespan and send a `Summary`
  136. // message with the same `id` back to DD. For outgoing messages, the
  137. // timespan refers to the time the message was created. For incoming
  138. // messages, the timespan refers to the time the message was received.
  139. message GetSummary {
  140. // Unique identifier of the summary request
  141. uint32 id = 1;
  142. // Timespan to get a summary for
  143. common.Timespan timespan = 2;
  144. // Which types of media should be included
  145. repeated MediaType media = 3;
  146. }
  147. // Summary data for a given timespan as requested by DD.
  148. //
  149. // When receiving this message:
  150. //
  151. // 1. If `BeginTransfer` has been sent in the meantime, discard this message
  152. // and abort these steps.
  153. // 2. If `id` matches the id sent in the most recently sent `GetSummary`
  154. // message, display the summary data to the user. The user may change the
  155. // properties (timespan, media types, etc.) which will trigger another
  156. // `GetSummary` message. When the user commits to the currently selected
  157. // properties, it sends a `BeginTransfer` message.
  158. message Summary {
  159. // Refers to the unique identifier of the summary request
  160. uint32 id = 1;
  161. // Amount of messages that would be transferred
  162. uint32 messages = 2;
  163. // Estimated size in bytes of the messages including only the requested
  164. // media types
  165. uint64 size = 3;
  166. }
  167. // Sent by DD to initiate the conversation history transfer for a given
  168. // timespan.
  169. //
  170. // When receiving this message:
  171. //
  172. // 1. If `BeginTransfer` has been received before, close the connection and
  173. // abort these steps.
  174. // 2. Lookup the cached requested properties for the given `id`. If none could
  175. // be found, close the connection and abort these steps.
  176. // 3. Let `messages` an empty list. Let `size` be `0`.
  177. // 4. For each remaining message to be sent for the requested timespan:
  178. // 1. If the media types match this message, send the blob as a
  179. // `common.BlobData` message and update `size` with the byte size of
  180. // the media.
  181. // 2. Append the current message to `messages` and update `size` with the
  182. // byte size of the message (without media).
  183. // 3. If `messages` contains 100+ items or `size` is greater 100 MiB, abort
  184. // the loop.
  185. // 5. Send a `Data` message with the included `messages`.
  186. // 6. If there are remaining messages, restart these steps from the beginning.
  187. // 7. Wait until all buffered data on the connection has been written. Then,
  188. // close the connection.
  189. message BeginTransfer {
  190. // Refers to the unique identifier of the summary request
  191. uint32 id = 1;
  192. }
  193. // One or more messages of the conversation history sent by SD.
  194. //
  195. // When receiving this message:
  196. //
  197. // 1. Let `blobs` be the previously received set of `common.BlobData` prior to
  198. // this message.
  199. // 2. For each message of `messages`:
  200. // 1. If the message is not in the expected timespan, close the connection
  201. // and abort these steps.
  202. // 2. If the message type is unknown or cannot be parsed, discard the
  203. // message and abort these steps.
  204. // 3. Store the message. If the message already exists, overwrite it.
  205. // 4. If the message refers to a Blob ID, lookup the Blob in `blobs`. If
  206. // the Blob could be found, store it persistently and remove it from
  207. // `blobs`.
  208. // 3. Log a warning for each remaining Blob in `blobs` and discard them.
  209. // 4. If `remaining` is `0`, close the connection and consider the
  210. // conversation history transfer successfully completed.
  211. message Data {
  212. // Past messages
  213. repeated PastMessage messages = 1;
  214. // Amount of messages remaining to be transferred **after** this message
  215. uint64 remaining = 2;
  216. }
  217. // Contains a past incoming or outgoing message.
  218. message PastMessage {
  219. oneof message {
  220. PastIncomingMessage incoming = 1;
  221. PastOutgoingMessage outgoing = 2;
  222. }
  223. }
  224. // A reaction to a message
  225. message Reaction {
  226. // Unix-ish timestamp in milliseconds when the reaction happened.
  227. uint64 at = 1;
  228. // Threema ID of the sender.
  229. string identity = 2;
  230. // A single fully-qualified [emoji codepoint sequence that is part of the
  231. // currently supported Unicode standard][emoji-test.txt]
  232. //
  233. // [emoji-test.txt]:
  234. // https://www.unicode.org/Public/emoji/latest/emoji-test.txt
  235. bytes reaction = 3;
  236. }
  237. // A past outgoing message marked as as _Include in history_.
  238. message PastOutgoingMessage {
  239. // Enclosed outgoing message
  240. d2d.OutgoingMessage message = 1;
  241. // Unix-ish timestamp in milliseconds for when the message has been sent
  242. uint64 sent_at = 2;
  243. // Optional Unix-ish timestamp in milliseconds for when the message has been
  244. // marked as read
  245. optional uint64 read_at = 3;
  246. // Optional reactions to the message
  247. repeated Reaction reactions = 4;
  248. }
  249. // A past incoming message marked as as _Include in history_.
  250. message PastIncomingMessage {
  251. // Enclosed incoming message
  252. d2d.IncomingMessage message = 1;
  253. // Unix-ish timestamp in milliseconds for when the message has been received
  254. uint64 received_at = 2;
  255. // Optional Unix-ish timestamp in milliseconds for when the message has been
  256. // marked as read
  257. optional uint64 read_at = 3;
  258. // Optional reactions to the message
  259. repeated Reaction reactions = 4;
  260. }