md-d2d-history.proto 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  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 `d2d_rendezvous.RendezvousInit` by following the Connection
  44. // Rendezvous Protocol. It encrypts the created `d2d_rendezvous.RendezvousInit`
  45. // with `DGHEK`, wraps it in a `url.HistoryExchangeRequestOrOffer` and offers it
  46. // in form of a URL or a QR code.
  47. //
  48. // RRD scans the QR code and parses the `url.HistoryExchangeRequestOrOffer`. It
  49. // will then decrypt the contained `d2d_rendezvous.RendezvousInit`. Once
  50. // decrypted, the enclosed `d2d_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 d2d_history;
  93. option java_package = "ch.threema.protobuf.d2d.history";
  94. option java_multiple_files = true;
  95. option php_namespace = "Threema\\Protocols\\D2d\\History";
  96. import "common.proto";
  97. import "md-d2d.proto";
  98. // Root message envelope for messages from the destination device (DD) to the
  99. // source device (SD).
  100. message DdToSd {
  101. // The enveloped message
  102. oneof content {
  103. GetSummary get_summary = 1;
  104. BeginTransfer begin_transfer = 2;
  105. }
  106. }
  107. // Root message envelope for messages from the source device (SD) to the
  108. // destination device (DD).
  109. message SdToDd {
  110. // The enveloped message
  111. oneof content {
  112. Summary summary = 1;
  113. common.BlobData blob_data = 2;
  114. Data data = 3;
  115. }
  116. }
  117. // Media type to transfer
  118. enum MediaType {
  119. // All media should be transferred
  120. ALL = 0;
  121. }
  122. // Sent by DD to get a summary of the conversation history available on SD.
  123. //
  124. // When receiving this message:
  125. //
  126. // 1. If `BeginTransfer` has been received before, close the connection and
  127. // abort these steps.
  128. // 2. If summary data is currently being retrieved for a previous `GetSummary`
  129. // message, abort that process.
  130. // 3. If cached properties from a previous `GetSummary` message exist, discard
  131. // those properties.
  132. // 4. Filter `media` in the following way:
  133. // 1. If the special media type _all_ is present, discard any other
  134. // entries.
  135. // 2. Remove duplicate entries.
  136. // 5. Cache the requested properties, including the `id`.
  137. // 4. Retrieve the summary data for the given timespan and send a `Summary`
  138. // message with the same `id` back to DD. For outgoing messages, the
  139. // timespan refers to the time the message was created. For incoming
  140. // messages, the timespan refers to the time the message was received.
  141. message GetSummary {
  142. // Unique identifier of the summary request
  143. uint32 id = 1;
  144. // Timespan to get a summary for
  145. common.Timespan timespan = 2;
  146. // Which types of media should be included
  147. repeated MediaType media = 3;
  148. }
  149. // Summary data for a given timespan as requested by DD.
  150. //
  151. // When receiving this message:
  152. //
  153. // 1. If `BeginTransfer` has been sent in the meantime, discard this message
  154. // and abort these steps.
  155. // 2. If `id` matches the id sent in the most recently sent `GetSummary`
  156. // message, display the summary data to the user. The user may change the
  157. // properties (timespan, media types, etc.) which will trigger another
  158. // `GetSummary` message. When the user commits to the currently selected
  159. // properties, it sends a `BeginTransfer` message.
  160. message Summary {
  161. // Refers to the unique identifier of the summary request
  162. uint32 id = 1;
  163. // Amount of messages that would be transferred
  164. uint32 messages = 2;
  165. // Estimated size in bytes of the messages including only the requested
  166. // media types
  167. uint64 size = 3;
  168. }
  169. // Sent by DD to initiate the conversation history transfer for a given
  170. // timespan.
  171. //
  172. // When receiving this message:
  173. //
  174. // 1. If `BeginTransfer` has been received before, close the connection and
  175. // abort these steps.
  176. // 2. Lookup the cached requested properties for the given `id`. If none could
  177. // be found, close the connection and abort these steps.
  178. // 3. Let `messages` an empty list. Let `size` be `0`.
  179. // 4. For each remaining message to be sent for the requested timespan:
  180. // 1. If the media types match this message, send the blob as a
  181. // `common.BlobData` message and update `size` with the byte size of
  182. // the media.
  183. // 2. Append the current message to `messages` and update `size` with the
  184. // byte size of the message (without media).
  185. // 3. If `messages` contains 100+ items or `size` is greater 100 MiB, abort
  186. // the loop.
  187. // 5. Send a `Data` message with the included `messages`.
  188. // 6. If there are remaining messages, restart these steps from the beginning.
  189. // 7. Wait until all buffered data on the connection has been written. Then,
  190. // close the connection.
  191. message BeginTransfer {
  192. // Refers to the unique identifier of the summary request
  193. uint32 id = 1;
  194. }
  195. // One or more messages of the conversation history sent by SD.
  196. //
  197. // When receiving this message:
  198. //
  199. // 1. Let `blobs` be the previously received set of `common.BlobData` prior to
  200. // this message.
  201. // 2. For each message of `messages`:
  202. // 1. If the message is not in the expected timespan, close the connection
  203. // and abort these steps.
  204. // 2. If the message type is unknown or cannot be parsed, discard the
  205. // message and abort these steps.
  206. // 3. Store the message. If the message already exists, overwrite it.
  207. // 4. If the message refers to a Blob ID, lookup the Blob in `blobs`. If
  208. // the Blob could be found, store it persistently and remove it from
  209. // `blobs`.
  210. // 3. Log a warning for each remaining Blob in `blobs` and discard them.
  211. // 4. If `remaining` is `0`, close the connection and consider the
  212. // conversation history transfer successfully completed.
  213. message Data {
  214. // Past messages
  215. repeated PastMessage messages = 1;
  216. // Amount of messages remaining to be transferred **after** this message
  217. uint64 remaining = 2;
  218. }
  219. // Contains a past incoming or outgoing message.
  220. message PastMessage {
  221. oneof message {
  222. PastIncomingMessage incoming = 1;
  223. PastOutgoingMessage outgoing = 2;
  224. }
  225. }
  226. // A reaction to a message
  227. message Reaction {
  228. // Unix-ish timestamp in milliseconds when the reaction happened.
  229. uint64 at = 1;
  230. // The reaction type.
  231. enum Type {
  232. // Message explicitly acknowledged
  233. ACKNOWLEDGE = 0;
  234. // Message explicitly declined
  235. DECLINE = 1;
  236. }
  237. Type type = 2;
  238. }
  239. // A past outgoing message
  240. message PastOutgoingMessage {
  241. // Enclosed outgoing message
  242. d2d.OutgoingMessage message = 1;
  243. // Unix-ish timestamp in milliseconds for when the message has been sent
  244. uint64 sent_at = 2;
  245. // Optional Unix-ish timestamp in milliseconds for when the message has been
  246. // marked as read
  247. optional uint64 read_at = 3;
  248. // Optional last reaction to the message
  249. Reaction last_reaction_at = 4;
  250. }
  251. // A past incoming message
  252. message PastIncomingMessage {
  253. // Enclosed incoming message
  254. d2d.IncomingMessage message = 1;
  255. // Unix-ish timestamp in milliseconds for when the message has been received
  256. uint64 received_at = 2;
  257. // Optional Unix-ish timestamp in milliseconds for when the message has been
  258. // marked as read
  259. optional uint64 read_at = 3;
  260. // Optional last reaction to the message
  261. Reaction last_reaction_at = 4;
  262. }