md-d2d.proto 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. // ## Device to Device Protocol
  2. //
  3. // ### General Information
  4. //
  5. // **Encryption cipher:** XSalsa20-Poly1305, unless otherwise specified.
  6. //
  7. // All strings are UTF-8 encoded.
  8. syntax = "proto3";
  9. package d2d;
  10. option java_package = "ch.threema.protobuf.d2d";
  11. import "common.proto";
  12. import "md-d2d-sync.proto";
  13. // D2D protocol versions.
  14. //
  15. // Note 1: The most significant byte is the major version and the least
  16. // significant byte is the minor version.
  17. //
  18. // Note 2: Once the D2D protocol is more stable, an unknown major version can be
  19. // interpreted as incompatible. For now, we only have 0.X versions that define
  20. // in which way they break compatibility.
  21. enum ProtocolVersion {
  22. // The version is unspecified.
  23. UNSPECIFIED = 0;
  24. // V0.1
  25. //
  26. // Devices using this version use the opportunistic (but problematic) group
  27. // sync mechanism via pure CSP reflection. D2D group sync reflection was
  28. // totally underspecified but is partially supported on the receiving side.
  29. V0_1 = 0x0001;
  30. // V0.2
  31. //
  32. // Builds on V0.1 with backwards compatibility to V0.1. Devices using this
  33. // version use the explicit group sync mechanism via D2D sync reflection.
  34. //
  35. // Upon reception, V0.2 devices detecting a reflected message will switch over
  36. // the version, and:
  37. //
  38. // - for V0.1 in combination with a CSP group message, fall back to the
  39. // backwards compatibility mode and update the group according to the
  40. // message.
  41. // - for V0.2+ in combination with a CSP group message, ignore it.
  42. V0_2 = 0x0002;
  43. // V0.3
  44. //
  45. // Builds on V0.2 but removes the backwards compatibility to V0.1.
  46. //
  47. // Upon reception, V0.2 devices detecting a reflected message will switch over
  48. // the version, and:
  49. //
  50. // - for V0.1 in combination with a CSP group message, spit out a warning that
  51. // the group is going to desync.
  52. // - for V0.2+ in combination with a CSP group message, ignore it.
  53. V0_3 = 0x0003;
  54. }
  55. // Data shared across all devices and transmitted during the handshake.
  56. message SharedDeviceData {
  57. // Random amount of padding, ignored by the receiver
  58. bytes padding = 1;
  59. // Current lowest protocol version that must be supported by all devices
  60. uint32 version = 2;
  61. }
  62. // Metadata about a device, determined by the device itself.
  63. message DeviceInfo {
  64. // Random amount of padding, ignored by the receiver
  65. bytes padding = 1;
  66. // Platform
  67. enum Platform {
  68. // Unknown platform
  69. UNSPECIFIED = 0;
  70. // Android
  71. ANDROID = 1;
  72. // Apple iOS
  73. IOS = 2;
  74. // Desktop application
  75. DESKTOP = 3;
  76. // Web application
  77. WEB = 4;
  78. }
  79. Platform platform = 2;
  80. // Platform details (smartphone model / browser), e.g. "Firefox 91.0.2" or
  81. // "iPhone 11 Pro"
  82. string platform_details = 3;
  83. // App version, e.g. "4.52" (Android) or "4.6.12b2653" (iOS)
  84. string app_version = 4;
  85. // User defined device label (e.g. "PC at Work"), may be empty if not set.
  86. // Recommended to not not exceed 64 grapheme clusters.
  87. string label = 5;
  88. }
  89. // A transaction scope. Used in the d2m transaction messages.
  90. message TransactionScope {
  91. enum Scope {
  92. USER_PROFILE_SYNC = 0;
  93. CONTACT_SYNC = 1;
  94. GROUP_SYNC = 2;
  95. DISTRIBUTION_LIST_SYNC = 3;
  96. SETTINGS_SYNC = 4;
  97. MDM_PARAMETER_SYNC = 5;
  98. NEW_DEVICE_SYNC = 6;
  99. }
  100. Scope scope = 1;
  101. }
  102. // Root message
  103. message Envelope {
  104. // Random amount of padding, ignored by the receiver
  105. bytes padding = 1;
  106. // Sender device id
  107. fixed64 device_id = 13;
  108. // D2D (`ProtocolVersion`) the device used when it sent this message.
  109. //
  110. // If `0`, assume V0.1 (`0x0001`).
  111. uint32 protocol_version = 3;
  112. // The enveloped reflected message
  113. oneof content {
  114. OutgoingMessage outgoing_message = 2;
  115. OutgoingMessageUpdate outgoing_message_update = 10;
  116. IncomingMessage incoming_message = 4;
  117. IncomingMessageUpdate incoming_message_update = 11;
  118. UserProfileSync user_profile_sync = 5;
  119. ContactSync contact_sync = 6;
  120. GroupSync group_sync = 7;
  121. DistributionListSync distribution_list_sync = 8;
  122. SettingsSync settings_sync = 9;
  123. MdmParameterSync mdm_parameter_sync = 12;
  124. };
  125. }
  126. // Unique conversation identifier.
  127. message ConversationId {
  128. // A contact's Threema ID, distribution list ID or group identity to identify
  129. // the conversation.
  130. oneof id {
  131. string contact = 1;
  132. fixed64 distribution_list = 2;
  133. common.GroupIdentity group = 3;
  134. }
  135. }
  136. // An outgoing message, reflected to other devices.
  137. //
  138. // When sending this message:
  139. //
  140. // 1. [...]
  141. // 2. Set `nonces` to the nonces of the associated CSP
  142. // `e2e.message-with-metadata` (or `e2e.legacy-message`) messages that
  143. // contained the `body` in encrypted form.¹
  144. //
  145. // When receiving this message:
  146. //
  147. // 1. Add all `nonces` to the CSP nonce storage (discarding any nonces that
  148. // already exist in the nonce storage).
  149. // 2. If a message with the same `message_id` exists within the associated
  150. // `conversation`, discard the message and abort these steps.
  151. // 3. [...]
  152. //
  153. // ¹: For contacts and distribution lists, there will be exactly one nonce. For
  154. // groups, there will be as many nonces as there are group members minus one.
  155. message OutgoingMessage {
  156. // Conversation ID of the enclosed message.
  157. //
  158. // Note: If the conversation is of type group, group and group creator id of
  159. // the enclosed CSP E2E message must match the values of the supplied group
  160. // identity. Otherwise, the message must be considered invalid.
  161. ConversationId conversation = 1;
  162. // Unique ID of the enclosed message
  163. fixed64 message_id = 2;
  164. // Optional thread message ID (the message ID of the last incoming message in
  165. // the current conversation)
  166. optional fixed64 thread_message_id = 6;
  167. // Unix-ish timestamp in milliseconds for when the enclosed message has been
  168. // created
  169. //
  170. // Note: Take this value from the
  171. // `csp.payload.legacy-message`/`csp.payload.message-with-metadata-box` that
  172. // enclosed the message.
  173. uint64 created_at = 3;
  174. // Enclosed message's type
  175. common.CspE2eMessageType type = 4;
  176. // The message's body, i.e. the unpadded `csp.e2e.container.padded-data`
  177. bytes body = 5;
  178. // Nonces the message was encrypted with towards each receiver (the shared
  179. // secret derived from the long-term keys).
  180. //
  181. // Optional for now, always required in a future version.
  182. repeated bytes nonces = 7;
  183. }
  184. // Update one or more existing outgoing messages.
  185. message OutgoingMessageUpdate {
  186. // Mark the referred message as sent (acknowledged by the chat server).
  187. //
  188. // Note 1: The timestamp of the `reflect-ack`/`reflected` message determines
  189. // the timestamp for when the referred message has been sent.
  190. //
  191. // Note 2: This indicates that the referred message has been successfully
  192. // stored in the message queue of the server. It does NOT indicate that the
  193. // referred message has been delivered to the intended receiver.
  194. message Sent {}
  195. message Update {
  196. // Conversation ID of the referred message.
  197. ConversationId conversation = 1;
  198. // Unique ID of the referred message
  199. fixed64 message_id = 2;
  200. // Update type
  201. oneof update {
  202. // Mark the referred message as sent
  203. Sent sent = 3;
  204. }
  205. }
  206. // Updates
  207. repeated Update updates = 1;
  208. }
  209. // An incoming message, reflected to other devices.
  210. //
  211. //
  212. // When sending this message:
  213. //
  214. // 1. [...]
  215. // 2. Set `nonce` to the nonce of `e2e.message-with-metadata` (or
  216. // `e2e.legacy-message`) that contained the `body` in encrypted form.
  217. //
  218. // When receiving this message:
  219. //
  220. // 1. Add `nonce` to the CSP nonce storage (discard a nonces that already exist
  221. // in the nonce storage).
  222. // 2. If a message with the same `message_id` exists within the associated
  223. // `conversation`, discard the message and abort these steps.
  224. // 3. [...]
  225. message IncomingMessage {
  226. reserved 4; // Skipped by accident, available to use!
  227. // Sender's Threema ID
  228. string sender_identity = 1;
  229. // Unique ID of the enclosed message
  230. fixed64 message_id = 2;
  231. // Unix-ish timestamp in milliseconds for when the enclosed message has been
  232. // created.
  233. //
  234. // Note: Take this value from the
  235. // `csp.payload.legacy-message`/`csp.payload.message-with-metadata-box` that
  236. // enclosed the message.
  237. uint64 created_at = 3;
  238. // Enclosed message's type.
  239. common.CspE2eMessageType type = 5;
  240. // The message's body, i.e. the unpadded `csp.e2e.container.padded-data`
  241. bytes body = 6;
  242. // Nonce the message was encrypted with by the sender (the shared secret
  243. // derived from the long-term keys).
  244. //
  245. // Optional for now, always required in a future version.
  246. bytes nonce = 7;
  247. }
  248. // Update one or more existing incoming messages.
  249. message IncomingMessageUpdate {
  250. // Mark the referred message as read.
  251. //
  252. // Note: This may only be used when _read receipts_ have been turned off, i.e.
  253. // as a replacement for reflecting `delivery-receipt` type _read_ (`0x02`).
  254. message Read {
  255. // Unix-ish timestamp in milliseconds for when the referred message has been
  256. // read.
  257. uint64 at = 1;
  258. }
  259. message Update {
  260. // Conversation ID of the referred message.
  261. ConversationId conversation = 1;
  262. // Unique ID of the referred message
  263. fixed64 message_id = 2;
  264. // Update type
  265. oneof update {
  266. // Mark the referred message as read
  267. Read read = 3;
  268. }
  269. }
  270. // Updates
  271. repeated Update updates = 1;
  272. }
  273. // User profile synchronisation message.
  274. message UserProfileSync {
  275. // Update the user's profile
  276. message Update { sync.UserProfile user_profile = 1; }
  277. // Synchronisation type
  278. oneof action { Update update = 1; }
  279. }
  280. // Contact synchronisation message.
  281. message ContactSync {
  282. // Create a Threema contact.
  283. message Create { sync.Contact contact = 1; }
  284. // Update a Threema contact.
  285. message Update { sync.Contact contact = 1; }
  286. // Synchronisation type
  287. oneof action {
  288. // Create a Threema contact
  289. Create create = 1;
  290. // Update a Threema contact
  291. Update update = 2;
  292. }
  293. }
  294. // Group synchronisation message.
  295. message GroupSync {
  296. // Create a group.
  297. message Create { sync.Group group = 1; }
  298. // Update a group.
  299. //
  300. // When receiving this variant:
  301. //
  302. // 1. Let `current` be a snapshot of the current group state.
  303. // 2. Persist the update to the group.
  304. // 3. Let `member-changes` be an empty list.
  305. // 4. For each `identity`, `state-change` pair of `member_state_changes`:
  306. // 1. If `state-change` is `ADDED` and `identity` does not exist in
  307. // `current.members`, add the tuple `identity`, `state-change` to
  308. // `member-changes`.
  309. // 2. If `state-change` is `KICKED` or `LEFT` and `identity` does exist in
  310. // `current.members`, add the tuple `identity`, `state-change` to
  311. // `member-changes`.
  312. // 5. Group `member-changes` by their associated `state-change` and add
  313. // appropriate status messages to the associated conversation.
  314. message Update {
  315. sync.Group group = 1;
  316. enum MemberStateChange {
  317. // The member has been added
  318. ADDED = 0;
  319. // The member has been kicked from the group.
  320. KICKED = 1;
  321. // The member left the group.
  322. LEFT = 2;
  323. }
  324. // A map of the member identity string to the member state change.
  325. map<string, MemberStateChange> member_state_changes = 2;
  326. }
  327. // Delete a group.
  328. message Delete {
  329. // Unique group identity
  330. common.GroupIdentity group_identity = 1;
  331. }
  332. // Synchronisation type
  333. oneof action {
  334. // Create a group
  335. Create create = 1;
  336. // Update a group
  337. Update update = 2;
  338. // Delete a group
  339. Delete delete = 3;
  340. }
  341. }
  342. // Distribution list synchronisation message.
  343. message DistributionListSync {
  344. // Create a distribution list.
  345. message Create { sync.DistributionList distribution_list = 1; }
  346. // Update a distribution list.
  347. message Update { sync.DistributionList distribution_list = 1; }
  348. // Delete a distribution list.
  349. message Delete {
  350. // Unique ID of the distribution list
  351. fixed64 distribution_list_id = 1;
  352. }
  353. // Synchronisation type
  354. oneof action {
  355. // Create a distribution list
  356. Create create = 1;
  357. // Update a distribution list
  358. Update update = 2;
  359. // Delete a distribution list
  360. Delete delete = 3;
  361. }
  362. }
  363. // Settings synchronisation message.
  364. message SettingsSync {
  365. // Update settings.
  366. message Update { sync.Settings settings = 1; }
  367. // Synchronisation type
  368. oneof action { Update update = 1; }
  369. }
  370. // MDM parameter synchronisation message.
  371. message MdmParameterSync {
  372. // Update MDM parameters.
  373. //
  374. // When receiving this variant, run the _MDM Merge And Apply Steps_.
  375. message Update { sync.MdmParameters parameters = 1; }
  376. // Synchronisation type
  377. oneof action { Update update = 1; }
  378. }