md-d2m.struct.yml 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. # Meta information
  2. meta:
  3. # Document name and ID
  4. id: md-d2m
  5. name: Device to Mediator Protocol
  6. # References used by the structs
  7. references:
  8. # A reflect message identifier
  9. reflect-id: &reflect-id u32-le
  10. # A Unix-ish timestamp in milliseconds
  11. timestamp: &timestamp u64-le
  12. # Virtual namespace, just containing the below docstring
  13. index: &index
  14. _doc: |-
  15. # Device to Mediator Protocol
  16. This protocol describes the communication between Threema client and
  17. mediator server.
  18. As transport protocol, WebSocket is used. Over this WebSocket connection,
  19. messages both between the device and the mediator server (D2M) and between
  20. the device and the chat server (CSP) are multiplexed.
  21. ## General Information
  22. **Encryption cipher:** XSalsa20-Poly1305, unless otherwise specified.
  23. All strings are UTF-8 encoded.
  24. ## Payload Format
  25. All messages are wrapped in the [`container`](ref:payload.container).
  26. Most payload messages contained within the container are encoded with
  27. protobuf, with a few exceptions for messages that are sent very frequently
  28. (proxying and reflection) and which use a more compact representation.
  29. ## Chat Server Proxying
  30. The chat server uses a TCP stream based protocol (chat server protocol /
  31. CSP). To be able to proxy this protocol over the message based WebSocket
  32. protocol, we need framing. Framed messages from/to the chat server are sent
  33. using [`proxy`](ref:payload.proxy) messages wrapped in
  34. [`container`](ref:payload.container).
  35. ## Size Limitations
  36. The device to mediator protocol currently allows for up to 65536 bytes
  37. within a single message. To elaborate this down to encrypted device to
  38. device messages, the limitations are:
  39. - 65536 bytes for a payload container struct,
  40. - 65532 bytes for a payload struct, before wrapping it with a container,
  41. - 65516 bytes for an encrypted `d2d.Envelope` in a
  42. [`reflected`](ref:payload.reflected) struct.
  43. - 65492 bytes for the plain `d2d.Envelope` to be sent within a
  44. [`reflected`](ref:payload.reflected) struct.
  45. Note that the header lengths of [`reflect`](ref:payload.reflect) and
  46. [`reflected`](ref:payload.reflected) are dynamic, so the maximum size of a
  47. `d2d.Envelope` may be reduced further in the future.
  48. ## Version Negotiation
  49. The server sends along the highest supported protocol version in the
  50. `ServerHello` message. The client then chooses a `ClientHello.version` <=
  51. `ServerHello.version`.
  52. If the server version sent in `ServerHello` is unsupported by the client,
  53. the client disconnects with a [close code](ref:index#close-codes) of 4110
  54. (Unsupported Protocol Version).
  55. If the client version sent in `ClientHello` is unsupported by the server,
  56. the server disconnects with a [close code](ref:index#close-codes) of 4110
  57. (Unsupported Protocol Version).
  58. Otherwise, the version from `ClientHello.version` is used for further
  59. communication.
  60. ## Close Codes
  61. WebSocket Internal Close Codes (1xxx):
  62. - `1000`: Normal closure, connection successfully completed
  63. - `1001`: Server is shutting down
  64. - `1011`: Server terminated the connection due to an internal error
  65. Chat Server Close Codes (400x and 410x):
  66. - `4000`: Chat server connection closed
  67. - `4001`: Chat server connection could not be established
  68. - `4009`: Internal error related to chat server connection
  69. Mediator Server Close Codes (40[1-9]x and 41[1-9]x):
  70. - `4010`: Protocol error
  71. - `4011`: Transaction TTL reached
  72. - `4012`: Unknown message acked
  73. - `4013`: Client idle timeout exceeded
  74. - `4110`: Unsupported protocol version
  75. - `4111`: Device limit reached
  76. - `4112`: Duplicate connection (i.e. the same device reconnected,
  77. terminating the previous connection)
  78. - `4113`: Dropped by other device
  79. - `4114`: Dropped by server because the reflection queue length limit was
  80. reached
  81. - `4115`: Device slot state mismatch
  82. Reconnect policy:
  83. - `1xxx` and `40xx` do allow for automatic reconnect.
  84. - `41xx` does not allow for automatic reconnect and require user interaction
  85. before a reconnect attempt may be made.
  86. - Any other close code should result in a warning in the log, but automatic
  87. reconnects are allowed.
  88. Important: Whenever a `close-error` message is being received from the Chat
  89. Server, the reconnect policy solely depends on the
  90. `close-error.can-reconnect` field and the (following) Close Code must be
  91. ignored.
  92. When automatically reconnecting, linear backoff should be applied. In case
  93. the connection fails repeatedly, user interaction should be required to
  94. continue reconnecting.
  95. ## Security
  96. The client must pin the TLS certificate of the server, so that the server
  97. can be authenticated. The client authenticates itself during the handshake
  98. with the server that it is part of the device group by responding to a
  99. challenge.
  100. A malicious server can connect arbitrary devices with one another but this
  101. would be detected eventually because decrypting reflected messages would
  102. fail.
  103. # Payload structs
  104. payload: &payload
  105. container:
  106. _doc: |-
  107. Contains a mediator message payload.
  108. Direction: Client <-> Server
  109. fields:
  110. - _doc: |-
  111. Identifies the payload type contained in the `payload` field.
  112. Chat server proxying:
  113. - `0x00`: [`proxy`](ref:payload.proxy)
  114. Handshake:
  115. - `0x10`: `d2m.ServerHello`
  116. - `0x11`: `d2m.ClientHello`
  117. - `0x12`: `d2m.ServerInfo`
  118. States:
  119. - `0x20`: `d2m.ReflectionQueueDry`
  120. - `0x21`: `d2m.RolePromotedToLeader`
  121. Device management:
  122. - `0x30`: `d2m.GetDevicesInfo`
  123. - `0x31`: `d2m.DevicesInfo`
  124. - `0x32`: `d2m.DropDevice`
  125. - `0x33`: `d2m.DropDeviceAck`
  126. - `0x34`: `d2m.SetSharedDeviceData`
  127. Transactions:
  128. - `0x40`: `d2m.BeginTransaction`
  129. - `0x41`: `d2m.BeginTransactionAck`
  130. - `0x42`: `d2m.CommitTransaction`
  131. - `0x43`: `d2m.CommitTransactionAck`
  132. - `0x44`: `d2m.TransactionRejected`
  133. - `0x45`: `d2m.TransactionEnded`
  134. Reflection:
  135. - `0x80`: [`reflect`](ref:payload.reflect)
  136. - `0x81`: [`reflect-ack`](ref:payload.reflect-ack)
  137. - `0x82`: [`reflected`](ref:payload.reflected)
  138. - `0x83`: [`reflected-ack`](ref:payload.reflected-ack)
  139. name: type
  140. type: u8
  141. - _doc: |-
  142. Should be set to all `0`s and ignored by the receiver.
  143. name: reserved
  144. type: b3
  145. - _doc: |-
  146. Message payload. Needs to be parsed according to the `type` field.
  147. name: payload
  148. type: b*
  149. proxy:
  150. _doc: |-
  151. Proxy a message to/from the chat server.
  152. fields:
  153. - _doc: |-
  154. The data to be proxied to/from the chat server, encrypted by
  155. following the Chat Server Protocol.
  156. name: data
  157. type: b*
  158. reflect:
  159. _doc: |-
  160. Reflect a message into the reflection queue of all other devices.
  161. Direction: Client --> Server
  162. fields:
  163. - _doc: |-
  164. Contains the byte length of all fields prior to the `envelope` field
  165. (`8` at the moment).
  166. name: header-length
  167. type: u8
  168. - _doc: |-
  169. Should be set to `0` and ignored by the receiver.
  170. name: reserved
  171. type: u8
  172. - _doc: |-
  173. Flags:
  174. - `0x00_01`: Ephemeral marker. The server will forward the message only
  175. to devices that are currently connected while still maintaining
  176. the order of the reflection queue. If the receiving device
  177. disconnects before the ephemeral message was forwarded to it, that
  178. message should be discarded. An acknowledgement must not be sent.
  179. name: flags
  180. type: u16-le
  181. - _doc: |-
  182. Unique number (per connection) used for acknowledgement.
  183. name: reflect-id
  184. type: *reflect-id
  185. - _doc: |-
  186. The protobuf-encoded and encrypted data to be reflected, encrypted by
  187. `DGRK.secret` and prefixed with a random nonce. See `d2d.proto` for
  188. details on the `Envelope` contents.
  189. name: envelope
  190. type: b*
  191. reflect-ack:
  192. _doc: |-
  193. Acknowledges that a message to be reflected to all other devices has been
  194. stored in their respective reflection queues.
  195. Direction: Client <-- Server
  196. fields:
  197. - _doc: |-
  198. Should be set to all `0`s and ignored by the receiver.
  199. name: reserved
  200. type: b4
  201. - _doc: |-
  202. Refers to the `Reflect ID` as sent in the `Reflect` message.
  203. name: reflect-id
  204. type: *reflect-id
  205. - _doc: |-
  206. Unix-ish timestamp in milliseconds when the message has been stored
  207. in the reflection queue of the mediator server.
  208. name: timestamp
  209. type: *timestamp
  210. reflected:
  211. _doc: |-
  212. Deliver a message from the device's reflection queue.
  213. Direction: Client <-- Server
  214. fields:
  215. - _doc: |-
  216. Contains the byte length of all fields prior to the `envelope` field
  217. (`16` at the moment).
  218. name: header-length
  219. type: u8
  220. - _doc: |-
  221. Should be set to `0` and ignored by the receiver.
  222. name: reserved
  223. type: u8
  224. - _doc: |-
  225. Flags:
  226. - `0x00_01`: Ephemeral marker. The sending device requested this
  227. message to only be reflected to devices that are currently online.
  228. An acknowledgement must not be sent.
  229. name: flags
  230. type: u16-le
  231. - _doc: |-
  232. Monotonically increasing unique number (per device slot) used for
  233. acknowledgement. May wrap.
  234. name: reflected-id
  235. type: *reflect-id
  236. - _doc: |-
  237. Unix-ish timestamp in milliseconds when the message has been stored
  238. in the reflection queue of the mediator server.
  239. name: timestamp
  240. type: *timestamp
  241. - _doc: |-
  242. The protobuf-encoded and encrypted data to be reflected, encrypted by
  243. `DGRK.secret` and prefixed with a random nonce. See `d2d.proto` for
  244. details on the `Envelope` contents.
  245. name: envelope
  246. type: b*
  247. reflected-ack:
  248. _doc: |-
  249. Acknowledges that a reflected message has been processed by the device.
  250. Direction: Client --> Server
  251. fields:
  252. - _doc: |-
  253. Should be set to all `0`s and ignored by the receiver.
  254. name: reserved
  255. type: b4
  256. - _doc: |-
  257. Refers to the `Reflected ID` as sent in the `Reflected` message.
  258. name: reflect-id
  259. type: *reflect-id
  260. # Parsed struct namespaces (mapped into separate files)
  261. namespaces:
  262. index: *index
  263. payload: *payload