md-d2d-rendezvous.proto 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. // ## Connection Rendezvous Protocol
  2. //
  3. // Some mechanisms may request a 1:1 connection between two devices in order to
  4. // transmit data as direct as possible. Establishing such a connection should
  5. // always require user interaction.
  6. //
  7. // The protocol runs an authentication **handshake** on multiple paths
  8. // simultaneously and applies a heuristic to determine the best available path.
  9. // One of the devices is eligible to **nominate** a path after which arbitrary
  10. // encrypted payloads may be exchanged.
  11. //
  12. // ### Terminology
  13. //
  14. // - `RID`: Rendezvous Initiator Device
  15. // - `RRD`: Rendezvous Responder Device
  16. // - `AK`: Authentication Key
  17. // - `ETK`: Ephemeral Transport Key
  18. // - `STK`: Shared Transport Key
  19. // - `PID`: Path ID
  20. // - `RPH`: Rendevous Path Hash
  21. // - `RIDAK`: RID's Authentication Key
  22. // - `RRDAK`: RRD's Authentication Key
  23. // - `RIDTK`: RID's Transport Key
  24. // - `RRDTK`: RRD's Transport Key
  25. // - `RIDSN`: RID's Sequence Number
  26. // - `RRDSN`: RRD's Sequence Number
  27. //
  28. // ### General Information
  29. //
  30. // **Sequence number:** The sequence number starts with `1` and is counted
  31. // separately for each direction (i.e. there is one sequence number counter for
  32. // the client and one for the server). We will use `RIDSN+` and `RRDSN+` in this
  33. // document to denote that the counter should be increased **after** the value
  34. // has been inserted (i.e. semantically equivalent to `x++` in many languages).
  35. //
  36. // **Framing:** An `extra.transport.frame` is being used to frame all
  37. // transmitted data even if the transport supports datagrams. This intentionally
  38. // allows to fragment a frame across multiple datagrams (e.g. useful for limited
  39. // APIs that cannot deliver data in a streamed fashion).
  40. //
  41. // ### Key Derivation
  42. //
  43. // RIDAK = BLAKE2b(key=AK.secret, salt='rida', personal='3ma-rendezvous')
  44. // RRDAK = BLAKE2b(key=AK.secret, salt='rrda', personal='3ma-rendezvous')
  45. //
  46. // STK = BLAKE2b(
  47. // key=
  48. // AK.secret
  49. // || X25519HSalsa20(<local.ETK>.secret, <remote.ETK>.public)
  50. // salt='st',
  51. // personal='3ma-rendezvous'
  52. // )
  53. //
  54. // RIDTK = BLAKE2b(key=STK.secret, salt='ridt', personal='3ma-rendezvous')
  55. // RRDTK = BLAKE2b(key=STK.secret, salt='rrdt', personal='3ma-rendezvous')
  56. //
  57. // ### Encryption Schemes
  58. //
  59. // RID's encryption scheme is defined in the following way:
  60. //
  61. // ChaCha20-Poly1305(
  62. // key=<RID*K.secret>,
  63. // nonce=u32-le(PID) || u32-le(RIDSN+) || <4 zero bytes>,
  64. // )
  65. //
  66. // RRD's encryption scheme is defined in the following way:
  67. //
  68. // ChaCha20-Poly1305(
  69. // key=<RRD*K.secret>,
  70. // nonce=u32-le(PID) || u32-le(RRDSN+) || <4 zero bytes>,
  71. // )
  72. //
  73. // ### Rendezvous Path Hash Derivation
  74. //
  75. // A Rendezvous Path Hash (RPH) can be used to ensure that both parties are
  76. // connected to each other and not to some other party who was able to intercept
  77. // AK:
  78. //
  79. // RPH = BLAKE2b(
  80. // out-length=32,
  81. // salt='ph',
  82. // personal='3ma-rendezvous',
  83. // input=STK.secret,
  84. // )
  85. //
  86. // ### Path Matrix
  87. //
  88. // | Name | Multiple Paths |
  89. // |-------------------|----------------|
  90. // | Direct TCP Server | Yes |
  91. // | Relayed WebSocket | No |
  92. //
  93. // ### Protocol Flow
  94. //
  95. // Connection paths are formed by transmitting a `rendezvous.RendezvousInit`
  96. // from RID to RRD as defined in the description of that message.
  97. //
  98. // The connections are then simultaneously established in the background and
  99. // each path must go through the handshake flow with its authentication
  100. // challenges. While doing so, the peers measure the RTT between challenge and
  101. // response in order to determine a good path candidate for nomination.
  102. //
  103. // One of the peers, defined by the upper-layer protocol, nominates one of the
  104. // established paths. Once nominated, both peers close all other paths (WS:
  105. // `1000`).
  106. //
  107. // Once a path has been nominated, that path will be handed to the upper-layer
  108. // protocol for arbitrary data transmission. That data must be protected by
  109. // continuing the respective encryption scheme of the associated role.
  110. //
  111. // ### Handshake Flow
  112. //
  113. // RRD and RID authenticate one another by the following flow:
  114. //
  115. // RRD ---- Handshake.RrdToRid.Hello ---> RID
  116. // RRD <- Handshake.RidToRrd.AuthHello -- RID
  117. // RRD ---- Handshake.RrdToRid.Auth ----> RID
  118. //
  119. // Before the path can be used by the upper-layer protocol, the chosen path must
  120. // be `Nominate`d by either side. The upper-layer protocol must define which
  121. // side may `Nominate`.
  122. //
  123. // R*D ------- Handshake.Nominate ------> R*D
  124. //
  125. // ### Path Nomination
  126. //
  127. // The following algorithm should be used to determine which path is to be
  128. // nominated. The upper-layer protocol must clearly define whether RRD or RID
  129. // does nomination.
  130. //
  131. // 1. Let `established` be the list of established connection paths.
  132. // 2. Asynchronously, with each connection becoming established, update
  133. // `established` with the RTT that was measured during the handshake.
  134. // 3. Wait for the first connection path to become established.
  135. // 4. After a brief timeout (or on a specific user interaction), nominate the
  136. // connection path in the following way, highest priority first:
  137. // 1. Path with the lowest RTT on a mutually unmetered, fast network
  138. // 2. Path with the lowest RTT on a mutually unmetered, slow network
  139. // 3. Path with the lowest RTT on any other network
  140. //
  141. // Note: It is recommended to warn the user if a metered connection path has
  142. // been nominated in case large amounts of data are to be transmitted.
  143. //
  144. // ### WebSocket Close Codes
  145. //
  146. // When WebSocket is used as rendezvous transport, the following close codes
  147. // should be used:
  148. //
  149. // - Normal (`1000`): The rendezvous connection was not nominated or the
  150. // upper-layer protocol exited successfully.
  151. // - Rendezvous Protocol Error (`4000`): The rendezvous protocol was violated.
  152. // Possible examples: Invalid WebSocket path, session full. Error details may
  153. // be included in the WebSocket close _reason_.
  154. // - Init Timeout (`4003`): The other device did not connect in time.
  155. // - Other Device Disconnected (`4004`): The other device disconnected without a
  156. // reflectable close code.
  157. // - Upper-Layer Protocol Error (`4100`): The rendezvous connection was
  158. // nominated but an upper-layer protocol error occurred.
  159. //
  160. // The device should log all other close codes but treat them as a _Rendezvous
  161. // Protocol Error_ (`4000`).
  162. //
  163. // Close codes in the `41xx` range as well as `1000` are reflected by the
  164. // rendezvous server to the other device.
  165. //
  166. // ### Security
  167. //
  168. // To prevent phishing attacks, the CORS `Access-Control-Allow-Origin` of any
  169. // WebSocket rendezvous relay server should be set to the bare minimum required
  170. // by the use case.
  171. //
  172. // ### Threat Model
  173. //
  174. // The security of the protocol relies on the security of the secure channel
  175. // where the `RendezvousInit` is being exchanged.
  176. //
  177. // Arbitrary WebSocket URLs and arbitrary IPv4/IPv6 addresses can be provided by
  178. // RID where RRD would connect to. It is therefore required that RRD can trust
  179. // RID to not be malicious.
  180. //
  181. // AK must be exchanged over a sufficiently secure channel. Concretely, AK must
  182. // be sufficiently protected to at least resist a brute-force attack for the
  183. // time between AK being exchanged and the handshake being fulfilled.
  184. //
  185. // A PID must be unique and not be re-used for a specific AK.
  186. syntax = "proto3";
  187. package rendezvous;
  188. option java_package = "ch.threema.protobuf.d2d.rendezvous";
  189. // Contains the data necessary to initialise a 1:1 connection between two
  190. // devices.
  191. //
  192. // When creating this message, run the following sub-steps simultaneously and
  193. // wait for them to finish:
  194. //
  195. // 1. If the device is able to create a TCP server socket:
  196. // 1. Bind to _any_ IP address with a random port number. Silently ignore
  197. // failures.
  198. // 2. If successful, let `addresses` be the list of available IP addresses on
  199. // network interfaces the server has been bound to.
  200. // 3. Drop any loopback and duplicate IP addresses from `addresses`.
  201. // 4. Drop link-local IPv6 addresses associated to interfaces that only
  202. // provide link-local IPv6 addresses.
  203. // 5. Sort `addresses` in the following way, highest priority first:
  204. // 1. IP addresses on unmetered, fast networks
  205. // 2. IP addresses on unmetered, slow networks
  206. // 3. IP addresses on metered, fast networks
  207. // 4. Any other addresses
  208. // 6. Complete the subroutine and provide `addresses` and other necessary
  209. // data in the `direct_tcp_server` field.
  210. // 2. Connect to a WebSocket relay server:
  211. // 1. Generate a random 32 byte hex-encoded rendezvous path.
  212. // 2. Connect to the WebSocket relay server URL as provided by the context
  213. // with the generated hex-encoded rendezvous path.
  214. // 3. Once connected, complete the subroutine and provide the necessary data
  215. // in the `relayed_web_socket` field.
  216. //
  217. // When receiving this message:
  218. //
  219. // 1. If `version` is unsupported, abort these steps.
  220. // 2. If any `path_id` is not unique, abort these steps.
  221. // 3. If the device is able to create a TCP client connection:
  222. // 1. Let `addresses` be the IP addresses of `direct_tcp_server`.
  223. // 2. Filter `addresses` by discarding IPs with unsupported families (e.g. if
  224. // the device has no IPv6 address, drop any IPv6 addresses).
  225. // 3. For each IP address in `addresses`:
  226. // 1. Connect to the given IP address in the background.
  227. // 2. Wait 100ms.
  228. // 4. Connect to the provided relayed WebSocket server in the background.
  229. // 5. On each successful direct or relayed connection made in the background,
  230. // forward an event to the upper-layer protocol in order for it to select one
  231. // of the paths for nomination.
  232. message RendezvousInit {
  233. enum Version {
  234. // Initial version.
  235. V1_0 = 0;
  236. }
  237. Version version = 1;
  238. // 32 byte ephemeral secret Authentication Key (AK).
  239. bytes ak = 2;
  240. // Network cost of an interface
  241. enum NetworkCost {
  242. // It is unknown whether the interface is metered or unmetered
  243. UNKNOWN = 0;
  244. // The interface is unmetered
  245. UNMETERED = 1;
  246. // The interface is metered
  247. METERED = 2;
  248. }
  249. // Relayed WebSocket path
  250. message RelayedWebSocket {
  251. // Unique Path ID (PID) of the path
  252. uint32 path_id = 1;
  253. // Network cost
  254. NetworkCost network_cost = 2;
  255. // Full URL to the WebSocket server with a random 32 byte hex-encoded
  256. // rendezvous path. Must begin with `wss://`.
  257. string url = 3;
  258. }
  259. RelayedWebSocket relayed_web_socket = 3;
  260. // Direct path to a TCP server created by the initiator
  261. message DirectTcpServer {
  262. // Random 16 bit port. Values greater than 65535 are invalid.
  263. uint32 port = 1;
  264. // List of associated IP addresses. Each IP address creates its own path.
  265. repeated IpAddress ip_addresses = 2;
  266. // An IP address
  267. message IpAddress {
  268. // Unique Path ID (PID) of the path
  269. uint32 path_id = 1;
  270. // Network cost
  271. NetworkCost network_cost = 2;
  272. // IPv4 or IPv6 address
  273. string ip = 3;
  274. }
  275. }
  276. DirectTcpServer direct_tcp_server = 4;
  277. }
  278. // Messages required for the initial lock-step handshake between RRD and RID.
  279. message Handshake {
  280. // Handshake messages from RRD to RID.
  281. message RrdToRid {
  282. // Initial message from RRD containing its authentication challenge,
  283. // encrypted by RRD's encryption scheme with RRDAK.
  284. message Hello {
  285. // 16 byte random authentication challenge for RID.
  286. bytes challenge = 1;
  287. // 32 byte ephemeral public key (`ETK.public`).
  288. bytes etk = 2;
  289. }
  290. // Final message from RRD responding to RID's authentication challenge,
  291. // encrypted by RRD's encryption scheme with RRDAK.
  292. //
  293. // When receiving this message:
  294. //
  295. // 1. If the challenge `response` from RRD does not match the challenge sent
  296. // by RID, close the connection with a protocol error (WS: `4000`) and
  297. // abort these steps.
  298. message Auth {
  299. // 16 byte repeated authentication challenge from RRD.
  300. bytes response = 1;
  301. }
  302. }
  303. // Handshake messages from RID to RRD.
  304. message RidToRrd {
  305. // Initial message from RID responding to RRD's authentication challenge and
  306. // containing RID's authentication challenge, encrypted by RID's encryption
  307. // scheme with RIDAK.
  308. //
  309. // When receiving this message:
  310. //
  311. // 1. If the challenge `response` from RID does not match the challenge sent
  312. // by RRD, close the connection with a protocol error (WS: `4000`) and
  313. // abort these steps.
  314. message AuthHello {
  315. // 16 byte repeated authentication challenge from RRD.
  316. bytes response = 1;
  317. // 16 byte random authentication challenge for RRD.
  318. bytes challenge = 2;
  319. // 32 byte ephemeral public key (`ETK.public`).
  320. bytes etk = 3;
  321. }
  322. }
  323. }
  324. // Nominates the path. The upper-layer protocol defines whether RID or RRD may
  325. // nominate and is encrypted by the respective encryption scheme with RIDTK or
  326. // RRDTK.
  327. //
  328. // When receiving this message:
  329. //
  330. // 1. If the sender was not eligible to `Nominate`, close the connection with a
  331. // protocol error (WS: `4000`) and abort these steps.
  332. // 2. Close all other pending or established connection paths (WS: `1000`).¹
  333. //
  334. // ¹: Closing other paths is only triggered by the receiver as it may otherwise
  335. // lead to a race between nomination and close detection.
  336. message Nominate {}