group-call.proto 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331
  1. // # Group Call Protocol
  2. //
  3. // Note that group calls are not necessarily bound to a Threema group. _Group_
  4. // refers to a group of call participants and is a way to distinguish from 1:1
  5. // Threema calls.
  6. //
  7. // There are two primary variants which use the same technology underneath:
  8. //
  9. // - A group call scoped to a (Threema) group is simple and easy to use. It does
  10. // not have any advanced functionality such as administration or external
  11. // guests. Only one group call is intended to run within a group.
  12. // - A conference call is a more advanced type of group call and delivers more
  13. // advanced functionality such as administration. Concrete specification
  14. // pending.
  15. //
  16. // The theoretical maximum amount of participants is 790 (due to the way we
  17. // derive WebRTC MIDs) but the practical limit is way below that.
  18. //
  19. // ## Terminology
  20. //
  21. // - `GCK`: Group Call Key, only used for key derivation
  22. // - `GCKH`: Group Call Key Hash
  23. // - `GCNHAK`: Group Call Normal Handshake Authentication Key
  24. // - `GCHK`: Group Call Handshake Key
  25. // - `GCSK`: Group Call State Key
  26. // - `GCAK`: Group Call Administrator Key, only used for key derivation
  27. // - `GCAMK`: Group Call Administrator Message Key
  28. // - `PCK`: Participant Call Key
  29. // - `PCMK`: Participant Call Media Key, only used for key derivation
  30. // - `PCMK`': Ratchet iteration of PCMK
  31. // - `PCMFK`: Participant Call Media Frame Key
  32. // - `PCCK`: Participant Call Cookie
  33. // - `PCSN`: Participant Call Sequence Number
  34. // - `MFSN`: Media Frame Sequence Number
  35. //
  36. // ## General Information
  37. //
  38. // **Endianness**: All integers use little-endian encoding.
  39. //
  40. // **Encryption cipher**: XSalsa20-Poly1305, unless otherwise specified.
  41. //
  42. // **Nonce format**:
  43. //
  44. // - a 16 byte cookie (PCCK), followed by
  45. // - a monotonically increasing sequence number (PCSN, u64-le).
  46. //
  47. // **Sequence number**: The sequence number starts with `1` and is counted
  48. // separately for each direction (i.e. there is one sequence number counter for
  49. // the sender and one for the receiver). We will use `PCSN+` in this document to
  50. // denote that the counter should be increased **after** the value has been
  51. // inserted (i.e. semantically equivalent to `x++` in many languages).
  52. //
  53. // Note: This format is equivalent to the CSP transport encryption.
  54. //
  55. // ## Key Derivation
  56. //
  57. // Note: All keys that are not derived from `GCK` directly will be derived using
  58. // `GCKH` as input. This ensures that exchanged secret keys are useless if the
  59. // Group Call ID has been exposed (unless `GCK` is also known to the attacker).
  60. //
  61. // GCKH = BLAKE2b(key=GCK, salt='#', personal='3ma-call')
  62. //
  63. // GCHK = BLAKE2b(key=GCK, salt='h', personal='3ma-call')
  64. // GCSK = BLAKE2b(key=GCK, salt='s', personal='3ma-call')
  65. //
  66. // GCAMK = BLAKE2b(key=GCAK, salt='am', personal='3ma-call', input=GCKH)
  67. //
  68. // PCMK' = BLAKE2b(key=PCMK, salt="m'", personal='3ma-call')
  69. // PCMFK = BLAKE2b(key=PCMK, salt='mf', personal='3ma-call', input=GCKH)
  70. //
  71. // ## Group Call ID Derivation
  72. //
  73. // For group calls scoped to groups, the Group Call ID is derived by running
  74. // BLAKE2b on specific data provided by the `GroupCallStart`:
  75. //
  76. // group-call-id = BLAKE2b(
  77. // out-length=32,
  78. // salt='i',
  79. // personal='3ma-call',
  80. // input=
  81. // group-creator-identity
  82. // || group-id
  83. // || u8(GroupCallStart.protocol_version)
  84. // || GroupCallStart.gck
  85. // || utf8-encode(GroupCallStart.sfu_base_url),
  86. // )
  87. //
  88. // ## Protocol Flow
  89. //
  90. // ### Obtain SFU Information
  91. //
  92. // Before a call can be joined or created, SFU information and an authentication
  93. // token need to be obtained via the Directory Server API. The obtained
  94. // information includes the following items referenced in subsequent sections:
  95. //
  96. // - _SFU Base URL_: Base URL used to create and distribute new calls.
  97. // - _Allowed SFU Hostname Suffixes_: A set of allowed hostname suffixes to be
  98. // applied against the _SFU Base URL_ when joining calls.
  99. // - _SFU Token_: An opaque token used to authenticate against the SFU.
  100. //
  101. // When receiving the SFU information, ensure the _SFU Base URL_ uses the scheme
  102. // `https` and the included hostname ends with one of the _Allowed SFU Hostname
  103. // Suffixes_.
  104. //
  105. // ### Scoped to Group
  106. //
  107. // #### Periodic Refresh
  108. //
  109. // The following steps are defined as the _Group Call Refresh Steps_ and will be
  110. // applied to update the group calls that are currently considered running
  111. // within a group, determining which one of them is the chosen call and
  112. // potentially join the chosen call:
  113. //
  114. // 1. Let `running` be the list of group calls that are currently considered
  115. // running within the group.
  116. // 2. Let `calls` be a copy of `running`. Reset the _token-refreshed_ mark of
  117. // each `call` of `calls` (or simply scope it to the execution of these
  118. // steps).
  119. // 3. For each `call` of `calls`, run the following steps (labelled _peek-call_)
  120. // concurrently and wait for them to return:
  121. // 1. If the user is currently participating in `call`, abort the _peek-call_
  122. // sub-steps.
  123. // 2. _Peek_ the `call` via a `SfuHttpRequest.Peek` request. If this does not
  124. // result in a response within 5s, remove `call` from `calls` and abort
  125. // the _peek-call_ sub-steps.
  126. // 3. If the received status code for `call` is `401` and `call` is not
  127. // marked with _token-refreshed_:
  128. // 1. Refresh the _SFU Token_. If the _SFU Token_ refresh fails or does
  129. // not yield an _SFU Token_ within 10s, remove `call` from `calls` and
  130. // abort the _peek-call_ sub-steps.
  131. // 2. Mark the `call` as _token-refreshed_.
  132. // 3. Restart the _peek-call_ sub-steps for this `call`.
  133. // 4. If the server could not be reached or the received status code is not
  134. // `200` or if the _Peek_ response could not be decoded:
  135. // 1. Remove `call` from `calls`.
  136. // 2. If the received status code is `404`, remove `call` from `running`
  137. // and abort the _peek-call_ sub-steps.
  138. // 3. If the `call`'s _failed_ counter is `>= 3` and the `call` was
  139. // received more than 10h ago, remove `call` from `running` and abort
  140. // the _peek-call_ sub-steps.
  141. // 4. Increase the _failed_ counter for `call` by `1` and abort the
  142. // _peek-call_ sub-steps.
  143. // 5. Reset the `call`'s _failed_ counter to `0`.
  144. // 6. If the protocol version of the `call` is not supported, remove `call`
  145. // from `calls`, log a warning that a group call with an unsupported
  146. // version is currently running and abort the _peek-call_ sub-steps.
  147. // 7. (`call` is kept in `calls` and in `running`.)
  148. // 4. If `running` is empty, cancel the timer to periodically re-run the _Group
  149. // Call Refresh Steps_ of this group. Otherwise, restart or schedule the
  150. // timer to re-run the _Group Call Refresh Steps_ of this group in 10s.
  151. // 5. Let `chosen-call` be any call of `calls` with the highest `started_at`
  152. // value (i.e. the most recently created call) as provided by the _peek_
  153. // result.
  154. // 6. If `chosen-call` is not defined, signal that no group call is currently
  155. // running within the group, abort these steps and return `chosen-call`.
  156. // 7. Signal `chosen-call` as the currently running group call within the group.
  157. // 8. If the _Group Call Join Steps_ are currently running with a different (or
  158. // new) group call than `chosen-call`, cancel and restart the _Group Call
  159. // Join Steps_ asynchronously with the same `intent` but with the
  160. // `chosen-call`.
  161. // 9. If the user is currently participating in a group call of this group that
  162. // is different to `chosen-call`, exit the running group call and run the
  163. // _Group Call Join Steps_ asynchronously with the `intent` to _only join_
  164. // `chosen-call`.
  165. // 10. Return `chosen-call`.
  166. //
  167. // Note: The above steps have been carefully crafted to gracefully handle cases
  168. // where the SFU of one call cannot be reached for a short period of time.
  169. //
  170. // When the Threema app is active, run the _Group Call Refresh Steps_ for each
  171. // group. This will start a timer to refresh any group call status.
  172. //
  173. // When the user leaves a group call, run the _Group Call Refresh Steps_ for the
  174. // respective group.
  175. //
  176. // The above described timer may be cancelled when the Threema app is inactive.
  177. // The timer interval may be increased to 30s in case the group conversation is
  178. // currently not visible to the user.
  179. //
  180. // #### Create or Join
  181. //
  182. // The following steps are to be run when a user wants to join a group call of a
  183. // group where a group call is currently considered running (e.g. the user hits
  184. // _join_ in the UI) or when the user intents to create a group call for a group
  185. // where no group call is currently considered running (e.g. the user hits the
  186. // _call_ button in the UI):
  187. //
  188. // 1. Let `intent` be the user's intent, i.e. to either _only join_ or _create
  189. // or join_ a group call.
  190. // 2. Refresh the _SFU Token_ if necessary. If the _SFU Token_ refresh fails
  191. // within 10s, abort these steps and notify the user.
  192. // 3. Run the _Group Call Refresh Steps_ for the respective group and let `call`
  193. // be the result.
  194. // 4. If `call` is undefined and `intent` is to _only join_, abort these steps
  195. // and notify the user that no group call is running / the group call is no
  196. // longer running.
  197. // 5. If `call` is undefined, create (but don't send) a `GroupCallStart`
  198. // message, apply it to `call` and mark `call` as _new_.
  199. // 6. Run the _Group Call Join Steps_ with the `intent` and `call`.
  200. //
  201. // The following steps are defined as the _Group Call Join Steps_ (also applied
  202. // for creating a group call).:
  203. //
  204. // 1. Let `intent` be either _only join_ or _create or join_. Let `call` be the
  205. // given group call to be joined (or created).
  206. // 2. _Join_ (or implicitly create) the group call via a `SfuHttpRequest.Join`
  207. // request. If this does not result in a response within 10s, abort these
  208. // steps and notify the user.
  209. // 3. If the received status code is `503`, notify the user that the group call
  210. // is full and abort these steps.
  211. // 4. If the server could not be reached or the received status code is not
  212. // `200` or if the _Join_ response could not be decoded, abort these steps
  213. // and notify the user.
  214. // 5. Establish a WebRTC connection to the SFU with the information provided in
  215. // the _Join_ response. Wait until the SFU sent the initial
  216. // `SfuToParticipant.Hello` message via the associated data channel. Let
  217. // `hello` be that message.
  218. // 6. If the `hello.participants` contains less than 4 items, set the initial
  219. // capture state of the microphone to _on_.
  220. // 7. If `call` is marked as _new_:
  221. // 1. Optionally add an artificial wait period of 2s minus the time elapsed
  222. // since step 1.[^1]
  223. // 2. Let `message-id` be a random message ID.
  224. // 3. Schedule a persistent task to run he _Bundled Messages Send Steps_ with
  225. // the following properties:
  226. // - `id` set to `message-id`,
  227. // - `receivers` set to all group members that have `GROUP_CALL_SUPPORT`,
  228. // - to construct a `GroupCallStart` message from `call`.
  229. // 4. Add the created `call` to the list of group calls that are currently
  230. // considered running.
  231. // 5. Asynchronously run the _Group Call Refresh Steps_.[^2]
  232. // 8. The group call is now considered established and should asynchronously
  233. // invoke the SFU to Participant and Participant to Participant flows.
  234. //
  235. // [^1]: This prevents butter-fingered user from accidentally starting a group
  236. // call.
  237. //
  238. // [^2]: This will initiate the refresh timer for a newly created call and
  239. // signal it to the UI.
  240. //
  241. // Note: Implementations need to ensure that only one group call can be active
  242. // at the same time in the application. This means that only one invocation of
  243. // the _Create or Join_ flow and only one invocation of the _Group Call Join
  244. // Steps_ can be active. Be aware that these steps can be cancelled by the user
  245. // and by the _Group Call Refresh Steps_.
  246. //
  247. // ### SFU to Participant Flow
  248. //
  249. // Upon successful joining via `SfuHttpRequest.Join`, the SFU waits for the
  250. // client to establish a WebRTC connection and then announces all participants
  251. // to the newly joined participant in its `SfuToParticipant.Hello` message.
  252. //
  253. // When another participant joins or leaves, a `ParticipantJoined` or
  254. // `ParticipantLeft` message will be sent.
  255. //
  256. // At any time, participants may subscribe and unsubscribe receiving microphone,
  257. // camera and screen data from other participants.
  258. //
  259. // If the user is alone in a call for more than 3 minute, the call should be
  260. // left to save resources. The SFU will automatically drop such calls after 5
  261. // minutes but this results in non-ideal UX.
  262. //
  263. // ### Participant to Participant Flow
  264. //
  265. // Unlike the other flows, this one is more complicated and needs to be done
  266. // separately for each other participant. During the handshake, ephemeral
  267. // encryption keys will be established.
  268. //
  269. // Note that multiple participants with the same Threema ID in the same call are
  270. // **explicitly allowed**. Not only can this happen in case the connection has
  271. // been lost (e.g. the client already reconnected but the SFU has not detected
  272. // connection loss yet), but it is also a feature for multi-device capable
  273. // clients.
  274. //
  275. // #### Handshake
  276. //
  277. // When a new participant (NP) joins, it must authenticate each other existing
  278. // participant (EP) and establish an ephemeral shared secret (`PCK`). The flow
  279. // depends on whether NP and EP are normal or guest participants:
  280. //
  281. // If both are normal participants:
  282. //
  283. // NP ----- Hello ---> EP
  284. // NP <---- Hello ---- EP
  285. // NP <---- Auth ----- EP
  286. // NP ----- Auth ----> EP
  287. //
  288. // If both are guest participants:
  289. //
  290. // NP -- GuestHello -> EP
  291. // NP <- GuestHello -- EP
  292. // NP <- GuestAuth --- EP
  293. // NP -- GuestAuth --> EP
  294. //
  295. // If NP is a normal participant and EP is a guest participant:
  296. //
  297. // NP ----- Hello ---> EP
  298. // NP <- GuestHello -- EP
  299. // NP <- GuestAuth --- EP
  300. // NP -- GuestAuth --> EP
  301. //
  302. // If NP is a guest participant and EP is a normal participant:
  303. //
  304. // NP -- GuestHello -> EP
  305. // NP <---- Hello ---- EP
  306. // NP <- GuestAuth --- EP
  307. // NP -- GuestAuth --> EP
  308. //
  309. // Note: This looks more intimidating than it really is. Basically, if either is
  310. // a guest, we fulfill the guest handshake but both always start with sending
  311. // their respective role's _hello_ variant.
  312. //
  313. // For group calls scoped to groups:
  314. //
  315. // - Only handshake messages from Threema IDs that are part of the group are
  316. // allowed.
  317. // - External guests are not allowed and therefore the guest handshake is not
  318. // allowed.
  319. //
  320. // #### Post-Handshake
  321. //
  322. // After the handshake, **both** sides run the following steps:
  323. //
  324. // 1. Subscribe to the other participant's microphone feed (i.e. send a
  325. // `ParticipantMicrophone` message to the SFU).
  326. // 2. If the user is an administrator, send an `Admin.ReportAsAdmin` message to
  327. // the other participant.
  328. // 3. If _hold_ is currently active, send a `Hold` message to the other
  329. // participant.
  330. // 4. If _hold_ is not currently active, send a `CaptureState` message to the
  331. // other participant for each device (camera, microphone, ...) that is
  332. // currently activated (`Mode` is `ON`).
  333. //
  334. // #### Join/Leave of Other Participants
  335. //
  336. // When a new participant joins, all other participants run the following steps:
  337. //
  338. // 1. Let `pcmk` be the currently _applied_ PCMK with the associated context.
  339. // 2. If the amount of ratchet rounds for `pcmk` is `255`, abort the call with
  340. // an error condition and abort these steps.
  341. // 3. Advance the ratchet of `pcmk` once (i.e. replace the key by deriving
  342. // PCMK') and apply for media encryption immediately. Note: Do **not** reset
  343. // the MFSN!
  344. // 4. Set the _handshake state_ of this participant to `await-np-hello`.
  345. //
  346. // Note: The announcement of the new participant is guaranteed to be sent prior
  347. // to any handshake messages of the new participant.
  348. //
  349. // When a participant leaves, all other participants run the following steps:
  350. //
  351. // 1. Let `pending-pcmk` be the currently _pending_ PCMK the associated context.
  352. // 2. If `pending-pcmk` exists, additionally mark `pending-pcmk` as _stale_ and
  353. // abort these steps.
  354. // 3. Let `current-pcmk` be the currently _applied_ PCMK with the associated
  355. // context.
  356. // 4. Set `pending-pcmk` in the following way:
  357. // 1. Generate a new cryptographically secure random PCMK and assign it to
  358. // `pending-pcmk`.
  359. // 2. Set `pending-pcmk.epoch` to `current-pcmk.epoch + 1`, wrap back to `0`
  360. // if it would be `256`.
  361. // 3. Set `pending-pcmk.ratchet_counter` to `0`.
  362. // 4. Do **not** reset the MFSN! Continue the existing MFSN counter of the
  363. // previous PCMK.
  364. // 5. Send `pending-pcmk` to all authenticated participants via a _rekey_
  365. // message.
  366. // 6. Schedule a task to run the following steps after 2s:
  367. // 1. Apply `pending-pcmk` for media encryption. This means that
  368. // `pending-pcmk` now replaces the _applied_ PCMK and is no longer
  369. // _pending_.
  370. // 2. If `pending-pcmk` is marked as _stale_, run the parent steps from the
  371. // beginning.
  372. //
  373. // When a participant receives a _rekey_ message from another participant.
  374. //
  375. // 1. Let `current-pcmk` be the PCMK and its associated context used for the
  376. // participant.
  377. // 2. Let `new-pcmk` be the media keys (PCMK) of the received message.
  378. // 3. Store `new-pcmk` as a successor to `current-pcmk` (and any other successor
  379. // already stored on `current-pcmk`) and follow the description of the media
  380. // frame on when to apply it.
  381. //
  382. // Note: The result of the above steps is that re-keying is throttled but always
  383. // catches up to the current participant state with a maximum delay of 4s.
  384. //
  385. // #### State Update
  386. //
  387. // One of the participants is deterministically designated to update the
  388. // peekable call state every 10s and additionally every time a participant joins
  389. // or leaves. If the call state has not been updated/refreshed for 30s, the SFU
  390. // will delete it.
  391. //
  392. // After each change to the list of participants, run the following steps to
  393. // determine whether the user is designated:
  394. //
  395. // 1. Cancel any running timer to update the call state.
  396. // 2. Let `candidates` be a list of all currently authenticated non-guest
  397. // participants.
  398. // 3. If `candidates` is empty, add all currently authenticated guest
  399. // participants to the list.
  400. // 4. If the user is not in `candidates`, abort these steps.
  401. // 5. If the user does not have the lowest participant ID in `candidates`, abort
  402. // these steps.
  403. // 6. Send a `ParticipantToSfu.UpdateCallState` message to the SFU and schedule
  404. // a repetitive timer to repeat this step every 10s.
  405. //
  406. // Note: The above algorithm is prone to races since the authentication process
  407. // is asynchronous for each participant pair. However, this should not be an
  408. // issue as they'd essentially post the same status (eventually).
  409. syntax = "proto3";
  410. package groupcall;
  411. option java_package = "ch.threema.protobuf.groupcall";
  412. option java_multiple_files = true;
  413. import "common.proto";
  414. // Current call state as announced by the designated client.
  415. //
  416. // Note: The `CallState` accurateness must not be relied upon as it can be out
  417. // of date and can be replayed by the SFU.
  418. message CallState {
  419. // Random amount of padding, ignored by the receiver.
  420. bytes padding = 1;
  421. // Participant ID of the designated client that created this message.
  422. uint32 state_created_by = 2;
  423. // UNIX-ish timestamp in milliseconds the designated client created this
  424. // message.
  425. uint64 state_created_at = 3;
  426. // Information for a single participant.
  427. message Participant {
  428. reserved 1; // Redundant participant ID
  429. // A _normal_ participant, i.e. a Threema client.
  430. message Normal {
  431. // Threema ID of the sender.
  432. string identity = 1;
  433. // Nickname associated to the Threema ID (without `~` prefix).
  434. string nickname = 2;
  435. }
  436. // A _guest_ participant.
  437. message Guest {
  438. // The guest's self-assigned name.
  439. string name = 1;
  440. }
  441. // Type-specific information.
  442. oneof participant {
  443. Normal threema = 2;
  444. Guest guest = 3;
  445. }
  446. }
  447. // Information for each participant of the group call.
  448. map<uint32, Participant> participants = 4;
  449. }
  450. // Request payloads sent to the SFU as part of an HTTP request.
  451. message SfuHttpRequest {
  452. // Peeks for the current state of the group call for the given Group Call ID.
  453. //
  454. // IMPORTANT: The _peek_ process is considered stable across different
  455. // protocol versions. Therefore, the message **should** maintain backwards
  456. // compatibility!
  457. //
  458. // The URL is formed in the following way:
  459. //
  460. // <sfu_base_url>/v1/peek/<call_id-as-hex>
  461. //
  462. // When sending this request:
  463. //
  464. // 1. Use `POST` as method.
  465. // 2. Set the `Authorization` header to `ThreemaSfuToken <sfu-token>`.
  466. // 3. Set the encoded `SfuHttpRequest.Peek` message as body.
  467. //
  468. // When receiving this request:
  469. //
  470. // 1. If the `Authorization` header is missing, the provided `sfu-token` in
  471. // the `Authorization` header is invalid or expired, respond with status
  472. // code `401` and abort these steps.
  473. // 2. If the provided data is invalid, respond with status code `400` and
  474. // abort these steps.
  475. // 3. If `call_id` does not equal the Call ID from the URL (decoded
  476. // `call_id-as-hex`), respond with status code `400` and abort these steps.
  477. // 4. If no group call for the given `call_id` is currently running, respond
  478. // with status code `404` and abort these steps.
  479. // 5. Respond with status code `200` and an encoded `SfuHttpResponse.Peek`
  480. // message as body.
  481. message Peek {
  482. // Group Call ID associated to the group call.
  483. bytes call_id = 1;
  484. }
  485. // Requests to join the group call with the given Group Call ID.
  486. //
  487. // The URL is formed in the following way:
  488. //
  489. // <sfu_base_url>/v1/join/<call_id-as-hex>
  490. //
  491. // When sending this request:
  492. //
  493. // 1. Use `POST` as method.
  494. // 2. Set the `Authorization` header to `ThreemaSfuToken <sfu-token>`.
  495. // 3. Set the encoded `SfuHttpRequest.Join` message as body.
  496. //
  497. // When receiving this request:
  498. //
  499. // 1. If the `Authorization` header is missing, the provided `sfu-token` in
  500. // the `Authorization` header is invalid or expired, respond with status
  501. // code `401` and abort these steps.
  502. // 2. If the provided data is invalid, respond with status code `400` and
  503. // abort these steps.
  504. // 3. If `call_id` does not equal the Call ID from the URL (decoded
  505. // `call_id-as-hex`), respond with status code `400` and abort these steps.
  506. // 4. If the `protocol_version` is unsupported by the SFU, respond with status
  507. // code `419` and abort these steps.
  508. // 5. If no more participants can join the group call for the given `call_id`,
  509. // respond with status code `503` and abort these steps.
  510. // 6. Respond with status code `200` and an encoded `SfuHttpResponse.Join`
  511. // message as body.
  512. // 7. Once the WebRTC connection has been established, announce the newly
  513. // joined participant to all other participants via the corresponding data
  514. // channel. If no WebRTC connection is being established within 30s, the
  515. // participant ID is no longer reserved for the client and the group call
  516. // must be teared down if no other participant started joining this group
  517. // call.
  518. message Join {
  519. // Group Call ID associated to the group call.
  520. bytes call_id = 1;
  521. // Protocol version the call was announced with.
  522. uint32 protocol_version = 2;
  523. // DTLS fingerprint of the x509 certificate that will be used by the client.
  524. //
  525. // Note: This is the authentication anchor for the WebRTC connection towards
  526. // the SFU.
  527. bytes dtls_fingerprint = 3;
  528. }
  529. }
  530. // Response payloads sent back from the SFU as part of an HTTP request.
  531. message SfuHttpResponse {
  532. // Information returned for a running group call.
  533. //
  534. // IMPORTANT: The _peek_ process is considered stable across different
  535. // protocol versions. Therefore, the message **should** maintain backwards
  536. // compatibility!
  537. //
  538. // Note: The included `CallState` information may not be accurate and should
  539. // not be relied upon.
  540. message Peek {
  541. // Unix-ish timestamp in milliseconds for when the first participant joined
  542. // the Group Call ID and therefore started the group call.
  543. uint64 started_at = 1;
  544. // Maximum amount of participants allowed in the group call.
  545. uint32 max_participants = 2;
  546. // Call state (`CallState`), encrypted by `GCSK.secret` and prefixed with a
  547. // random nonce.
  548. //
  549. // Not provided in case the call is currently running but no participant has
  550. // sent a call state to the SFU, or if the call state expired.
  551. //
  552. // The content of the call state is protocol version dependent and should
  553. // therefore be ignored if a client does not support the particular protocol
  554. // version the group call is associated with.
  555. optional bytes encrypted_call_state = 3;
  556. }
  557. // Information returned when joining a group call.
  558. //
  559. // When receiving this response, initiate the WebRTC connection to the SFU and
  560. // consider the connection established when the `SfuToParticipant.Hello`
  561. // message has been received on the associated data channel.
  562. message Join {
  563. // Unix-ish timestamp in milliseconds for when the first participant joined
  564. // the Group Call ID and therefore started the group call.
  565. uint64 started_at = 1;
  566. // Maximum amount of participants allowed in the group call.
  567. uint32 max_participants = 2;
  568. // Participant ID assigned to the client.
  569. //
  570. // Note: The client needs to know the participant ID early to derive MIDs
  571. // required to be present in the O/A SDP.
  572. uint32 participant_id = 3;
  573. // Address the SFU is listening for a WebRTC connection.
  574. message Address {
  575. // Protocol.
  576. enum Protocol { UDP = 0; }
  577. Protocol protocol = 1;
  578. // Port.
  579. uint32 port = 2;
  580. // IPv4 or IPv6 address.
  581. string ip = 3;
  582. }
  583. // List of addresses the SFU listens for a WebRTC connection.
  584. //
  585. // Note: One UDP IPv4 address is mandatory! One IPv6 address is recommended.
  586. repeated Address addresses = 4;
  587. // ICE username fragment for the WebRTC connection.
  588. string ice_username_fragment = 5;
  589. // ICE password for the WebRTC connection.
  590. string ice_password = 6;
  591. // DTLS fingerprint of the x509 certificate that will be used by the SFU.
  592. //
  593. // Note: This is the authentication anchor for the WebRTC connection towards
  594. // the SFU.
  595. bytes dtls_fingerprint = 7;
  596. }
  597. }
  598. // Messages sent from the SFU to a participant via a data channel.
  599. //
  600. // Data Channel Parameters:
  601. //
  602. // - `ordered`: `true`
  603. // - `negotiated`: `true`
  604. // - `id`: `0`
  605. message SfuToParticipant {
  606. // The enveloped message from the SFU.
  607. //
  608. // When relaying a message from one participant to another, omit any
  609. // additional padding.
  610. //
  611. // IMPORTANT: The format of the `SfuToParticipant.Envelope` and
  612. // `ParticipantToSfu.Envelope` must be compatible for the relay case, so the
  613. // SFU can forward the data without having to re-encode.
  614. message Envelope {
  615. // Random amount of padding, ignored by the receiver.
  616. bytes padding = 1;
  617. oneof content {
  618. ParticipantToParticipant.OuterEnvelope relay = 2;
  619. Hello hello = 3;
  620. ParticipantJoined participant_joined = 4;
  621. ParticipantLeft participant_left = 5;
  622. }
  623. }
  624. // Announces all other participants to a newly joined participant.
  625. //
  626. // When receiving this message:
  627. //
  628. // 1. If a `Hello` was received before (i.e. if the receiver is not a newly
  629. // joined participant), log a warning and abort these steps.
  630. // 2. Initiate the participant to participate handshake for each participant
  631. // listed in this message.
  632. message Hello {
  633. // All participants in the group call. This **excludes** the client's
  634. // participant ID.
  635. repeated uint32 participant_ids = 1;
  636. }
  637. // Announces that a new participant joined to existing participants.
  638. //
  639. // When receiving this message:
  640. //
  641. // 1. Look up the participant. If it already exists (i.e. never _left_), log a
  642. // warning and abort these steps.
  643. // 2. Run the corresponding steps described by the _Join/Leave_ section.
  644. message ParticipantJoined { uint32 participant_id = 1; }
  645. // Announces that a participant left to existing participants.
  646. //
  647. // When receiving this message:
  648. //
  649. // 1. Look up the participant. If it was never announced to have _joined_ by
  650. // an associated `ParticipantJoined` message, log a warning and abort these
  651. // steps.
  652. // 2. Run the corresponding steps described by the _Join/Leave_ section.
  653. message ParticipantLeft { uint32 participant_id = 1; }
  654. }
  655. // Messages sent from a participant to the SFU via a data channel.
  656. //
  657. // Data Channel Parameters:
  658. //
  659. // - `ordered`: `true`
  660. // - `negotiated`: `true`
  661. // - `id`: `0`
  662. message ParticipantToSfu {
  663. // The enveloped message towards the SFU.
  664. //
  665. // When relaying a message from one participant to another, omit any
  666. // additional padding.
  667. //
  668. // IMPORTANT: The format of the `SfuToParticipant.Envelope` and
  669. // `ParticipantToSfu.Envelope` must be compatible for the relay case, so the
  670. // SFU can forward the data without having to re-encode.
  671. message Envelope {
  672. // Random amount of padding, ignored by the receiver.
  673. bytes padding = 1;
  674. oneof content {
  675. ParticipantToParticipant.OuterEnvelope relay = 2;
  676. UpdateCallState update_call_state = 3;
  677. ParticipantMicrophone request_participant_microphone = 6;
  678. ParticipantCamera request_participant_camera = 4;
  679. ParticipantScreen request_participant_screen_share = 5;
  680. }
  681. }
  682. // Update the call state that can be retrieved via a _peek_.
  683. //
  684. // Note: Only the currently designated client should send this to the SFU.
  685. //
  686. // When receiving this message:
  687. //
  688. // 1. Store the encrypted call state and make it accessible via _peek_ HTTP
  689. // requests.
  690. // 2. Start a timer to purge the call state after 30s. Subsequent
  691. // `UpdateCallState` messages will update the call state and reset the
  692. // timer.
  693. message UpdateCallState {
  694. // Call state (`CallState`), encrypted by `GCSK` and prefixed with
  695. // a random nonce.
  696. bytes encrypted_call_state = 1;
  697. }
  698. // Subscribe or unsubscribe to a participant's microphone feed.
  699. //
  700. // When receiving this message:
  701. //
  702. // 1. If the `participant_id` refers to the sender's participant ID or an
  703. // unknown participant ID, discard the message and abort these steps.
  704. // 2. If `subscribe` is set, forward the microphone feed to the client that
  705. // fits best to the provided parameters.
  706. // 3. If `unsubscribe` is set, stop forwarding microphone feed of this
  707. // participant to the client.
  708. message ParticipantMicrophone {
  709. // Participant ID whose microphone feed should be subscribed or unsubscribed
  710. // from.
  711. uint32 participant_id = 1;
  712. // Subscribe to a participant's microphone feed.
  713. message Subscribe {}
  714. // Unsubscribe a participant's microphone feed.
  715. message Unsubscribe {}
  716. oneof action {
  717. Subscribe subscribe = 2;
  718. Unsubscribe unsubscribe = 3;
  719. }
  720. }
  721. // Subscribe or unsubscribe to a participant's camera feed.
  722. //
  723. // When receiving this message:
  724. //
  725. // 1. If the `participant_id` refers to the sender's participant ID or an
  726. // unknown participant ID, discard the message and abort these steps.
  727. // 2. If `subscribe` is set, forward the camera feed to the client that fits
  728. // best to the provided parameters.
  729. // 3. If `unsubscribe` is set, stop forwarding camera feed of this participant
  730. // to the client.
  731. message ParticipantCamera {
  732. // Participant ID whose camera feed should be subscribed or unsubscribed
  733. // from.
  734. uint32 participant_id = 1;
  735. // Subscribe to a participant's camera feed.
  736. message Subscribe {
  737. // Desired resolution. The client should use the canvas' resolution the
  738. // camera feed be displayed in. The SFU will select the spatial layer that
  739. // fits best.
  740. common.Resolution desired_resolution = 1;
  741. // Desired frame rate. The SFU will select the temporal layer that fits
  742. // best.
  743. uint32 desired_fps = 2;
  744. }
  745. // Unsubscribe a participant's camera feed.
  746. message Unsubscribe {}
  747. oneof action {
  748. Subscribe subscribe = 2;
  749. Unsubscribe unsubscribe = 3;
  750. }
  751. }
  752. // Subscribe or unsubscribe to a participant's screen feed.
  753. message ParticipantScreen {
  754. // Participant ID whose screen feed should be subscribed or unsubscribed
  755. // from.
  756. uint32 participant_id = 1;
  757. // Subscribe to a participant's screen feed.
  758. message Subscribe {}
  759. // Unsubscribe a participant's screen feed.
  760. message Unsubscribe {}
  761. oneof action {
  762. Subscribe subscribe = 2;
  763. Unsubscribe unsubscribe = 3;
  764. }
  765. }
  766. }
  767. // Messages sent from one participant to another.
  768. //
  769. // Note that these are relayed via `SfuToParticipant.Envelope` and
  770. // `ParticipantToSfu.Envelope` in order to prevent races with
  771. // `ParticipantJoined`/`ParticipantLeft`.
  772. message ParticipantToParticipant {
  773. // Used for all messages that are relayed from one participant to another via
  774. // the SFU.
  775. //
  776. // When receiving a relayed message:
  777. //
  778. // 1. If the `receiver` is not the user's assigned participant id, discard the
  779. // message and abort these steps.
  780. // 2. If the `sender` is unknown, discard the message and abort these steps.
  781. // 3. Decrypt `encrypted_data` according to the current _handshake state_ and
  782. // handle the inner envelope:
  783. // - `await-ep-hello` or `await-np-hello`: Expect a
  784. // `Handshake.HelloEnvelope`.
  785. // - `await-auth`: Expect a `Handshake.AuthEnvelope`.
  786. // - `done`: Expect a post-auth `Envelope`.
  787. message OuterEnvelope {
  788. // Participant ID of the sender. Checked by the SFU to be correct, dropped
  789. // if not.
  790. uint32 sender = 1;
  791. // Participant ID of the receiver. Checked by the SFU to exist, dropped if
  792. // not.
  793. uint32 receiver = 2;
  794. // The inner envelope. Always encrypted. Key and nonce are to be inferred
  795. // from the current _handshake state_ towards the sending participant.
  796. bytes encrypted_data = 4;
  797. }
  798. // Messages required for the initial lock-step handshake between participants.
  799. message Handshake {
  800. // The first message (`HelloEnvelope(Hello)` or `HelloEnvelope(GuestHello)`)
  801. // of both sides is always encrypted by `GCHK`, prefixed with a
  802. // random nonce.
  803. message HelloEnvelope {
  804. // Random amount of padding, ignored by the receiver
  805. bytes padding = 1;
  806. oneof content {
  807. Hello hello = 2;
  808. GuestHello guest_hello = 3;
  809. }
  810. }
  811. // If both sides started the normal handshake, the second message is
  812. // encrypted in the following way:
  813. //
  814. // 1. Let `inner-nonce` be a random nonce.
  815. // 2. Let `inner-data` be encrypted by:
  816. //
  817. // ```text
  818. // S = X25519HSalsa20(<sender.CK>.secret, <receiver.CK>.public)
  819. // GCNHAK = Blake2b(
  820. // key=S, salt='nha', personal='3ma-call', input=GCKH)
  821. // XSalsa20-Poly1305(
  822. // key=GCNHAK,
  823. // nonce=<inner-nonce>,
  824. // data=<AuthEnvelope(Auth)>,
  825. // )
  826. // ```
  827. // 3. Let `outer-data` be encrypted by:
  828. //
  829. // ```text
  830. // XSalsa20-Poly1305(
  831. // key=X25519HSalsa20(<sender.PCK>.secret, <receiver.PCK>.public),
  832. // nonce=<sender.PCCK> || <sender.PCSN+>,
  833. // data=<inner-nonce> || <inner-data>,
  834. // )
  835. // ```
  836. // 4. Return `outer-data`.
  837. //
  838. // If either side started the guest handshake, the second message is
  839. // encrypted by:
  840. //
  841. // ```text
  842. // XSalsa20-Poly1305(
  843. // key=X25519HSalsa20(<sender.PCK>.secret, <receiver.PCK>.public),
  844. // nonce=<sender.PCCK> || <sender.PCSN+>,
  845. // data=<AuthEnvelope(GuestAuth)>,
  846. // )
  847. // ```
  848. //
  849. // When receiving this message:
  850. //
  851. // 1. If either side initiated a guest handshake via a `GuestHello`, expect
  852. // `guest_auth` to be set. If `guest_auth` is not set, log a warning and
  853. // abort these steps.
  854. // 2. If both sides initiated the (normal) handshake, expect `auth` to be
  855. // set. If `auth` is not set, log a warning and abort these steps.
  856. message AuthEnvelope {
  857. // Random amount of padding, ignored by the receiver
  858. bytes padding = 1;
  859. oneof content {
  860. Auth auth = 2;
  861. GuestAuth guest_auth = 3;
  862. }
  863. }
  864. // Initial handshake message.
  865. //
  866. // When creating this message as a newly joined participant towards another
  867. // participant:
  868. //
  869. // 1. Set the participant's _handshake state_ to `await-ep-hello`.
  870. // 2. Send this message.
  871. //
  872. // When receiving this message as a guest participant:
  873. //
  874. // 1. Map it to a `GuestHello` in the following way:
  875. // - `name`: `Hello.nickname`
  876. // - `pck`: `Hello.pck`
  877. // - `pcck`: `Hello.pcck`
  878. // 2. Handle the mapped `GuestHello` as if it had been received directly.
  879. //
  880. // When receiving this message as a regular participant:
  881. //
  882. // 1. (Placeholder for conference call PCK != GCAMK step.)
  883. // 2. If the group call is scoped to a (Threema) group and `identity` is not
  884. // part of the associated group (including the user itself), log a
  885. // warning and abort these steps.
  886. // 3. If the sender is a newly joined participant and therefore the
  887. // _handshake state_ was set to `await-np-hello` (as described by the
  888. // _Join/Leave_ section):
  889. // 1. Respond by sending a `Hello` message, immediately followed by an
  890. // `Auth` message.
  891. // 2. Set the participant's _handshake state_ to `await-auth` and abort
  892. // these steps.
  893. // 4. If the participant's _handshake state_ is `await-ep-hello`:
  894. // 1. If the `pck` reflects the local PCK.public or the `pcck` reflects
  895. // the local PCCK, log a warning and abort these steps.
  896. // 2. Respond by sending an `Auth` message.
  897. // 3. Set the participant's _handshake state_ to `await-auth` and abort
  898. // these steps.
  899. // 5. Log a warning and abort these steps.
  900. message Hello {
  901. // Threema ID of the sender.
  902. string identity = 1;
  903. // Nickname associated to the Threema ID (without `~` prefix).
  904. string nickname = 2;
  905. // 32 byte ephemeral public key (`PCK.public`) towards the remote
  906. // participant.
  907. //
  908. // Note: It is allowed to use the same `PCK` for multiple participants.
  909. bytes pck = 3;
  910. // 16 byte random cookie used for nonces by the sender in subsequent
  911. // messages.
  912. bytes pcck = 4;
  913. }
  914. // Second and final handshake message.
  915. //
  916. // When receiving this message:
  917. //
  918. // 1. If the participant's _handshake state_ is not `await-auth`, log a
  919. // warning and abort these steps.
  920. // 2. If the repeated `pck` does not equal the local `PCK.public` used
  921. // towards this participant, log a warning and abort these steps.
  922. // 3. If the repeated `pcck` does not equal the local `PCCK` used towards
  923. // this participant, log a warning and abort these steps.
  924. // 4. Set the participant's _handshake state_ to `done`.
  925. message Auth {
  926. // 32 byte repeated ephemeral public key from the `Hello` message.
  927. //
  928. // Note: Repeating the sender's `PCK.public` prevents replay attacks.
  929. bytes pck = 1;
  930. // 32 byte repeated random cookie from the `Hello` message.
  931. //
  932. // Note: Repeating the sender's `PCCK` prevents replay attacks while
  933. // allowing the sender to use the same `PCK` for multiple
  934. // participants.
  935. bytes pcck = 2;
  936. // The currently applied PCMK and any _pending_ PCMK used for media
  937. // encryption, specifically in that order.
  938. //
  939. // Note: An implementation can expect at least one media key to be
  940. // present.
  941. repeated MediaKey media_keys = 3;
  942. }
  943. // Initial guest handshake message.
  944. //
  945. // When creating this message as a newly joined guest participant towards
  946. // another participant:
  947. //
  948. // 1. Set the participant's _handshake state_ to `await-ep-hello`.
  949. // 2. Send this message.
  950. //
  951. // When receiving this message:
  952. //
  953. // 1. If guest participants are not allowed for this call, log a warning
  954. // and abort these steps.
  955. // 2. (Placeholder for conference call PCK != GCAMK step.)
  956. // 3. If the sender is a newly joined participant and therefore the
  957. // _handshake state_ was set to `await-np-hello` (as described by the
  958. // _Join/Leave_ section):
  959. // 1. Respond by sending a `GuestHello` message, immediately followed by
  960. // a `GuestAuth` message.
  961. // 2. Set the participant's _handshake state_ to `await-guest-auth` and
  962. // abort these steps.
  963. // 4. If the participant's _handshake state_ is `await-ep-hello`:
  964. // 1. If the `pck` reflects the local PCK.public or the `pcck` reflects
  965. // the local PCCK, log a warning and abort these steps.
  966. // 2. Respond by sending a `GuestAuth` message.
  967. // 3. Set the participant's _handshake state_ to `await-guest-auth` and
  968. // abort these steps.
  969. // 5. Log a warning and abort these steps.
  970. message GuestHello {
  971. // The guest's self-assigned name.
  972. string name = 1;
  973. // 32 byte ephemeral public key (`PCK.public`) towards the remote
  974. // participant.
  975. //
  976. // Note: It is allowed to use the same `PCK` for multiple participants.
  977. bytes pck = 2;
  978. // 16 byte random cookie used for nonces by the sender in subsequent
  979. // messages.
  980. bytes pcck = 3;
  981. }
  982. // Second and final handshake message triggered if either side initiated the
  983. // guest handshake.
  984. //
  985. // When receiving this message:
  986. //
  987. // 1. If the participant's _handshake state_ is not `await-guest-auth`, log
  988. // a warning and abort these steps.
  989. // 2. If the repeated `pck` does not equal the local `PCK.public` used
  990. // towards this participant, log a warning and abort these steps.
  991. // 3. If the repeated `pcck` does not equal the local `PCCK` used towards
  992. // this participant, log a warning and abort these steps.
  993. // 4. Set the participant's _handshake state_ to `done`.
  994. message GuestAuth {
  995. // 32 byte repeated ephemeral public key from the `GuestHello` message.
  996. //
  997. // Note: Repeating the sender's `PCK.public` prevents replay attacks.
  998. bytes pck = 1;
  999. // 32 byte repeated random cookie from the `GuestHello` message.
  1000. //
  1001. // Note: Repeating the sender's `PCCK` prevents replay attacks while
  1002. // allowing the sender to use the same `PCK` for multiple
  1003. // participants.
  1004. bytes pcck = 2;
  1005. // The currently applied PCMK and any _pending_ PCMK used for media
  1006. // encryption, specifically in that order.
  1007. //
  1008. // Note: An implementation can expect at least one media key to be
  1009. // present.
  1010. repeated MediaKey media_keys = 3;
  1011. }
  1012. }
  1013. // After fulfilling either the (normal) handshake or the guest handshake, all
  1014. // following messages are encoded in `Envelope` and encrypted by:
  1015. //
  1016. // ```text
  1017. // XSalsa20-Poly1305(
  1018. // key=X25519HSalsa20(<sender.PCK>.secret, <receiver.PCK>.public),
  1019. // nonce=<sender.PCCK> || <sender.PCSN+>,
  1020. // )
  1021. // ```
  1022. //
  1023. // Note: Since the guest handshake is TOFU, an attacker knowing `GCK` having
  1024. // control over the SFU may apply a MITM attack between a guest participant
  1025. // and another participant. The attacker would be able to silently eavesdrop
  1026. // all media traffic between the two participants. This is repeatable for all
  1027. // other participants and means the attacker is able to silently eavesdrop the
  1028. // whole call. Therefore, if a call is not open for guests, `GuestHello` (and
  1029. // `GuestAuth`) **must not** be accepted.
  1030. //
  1031. // When receiving this message:
  1032. //
  1033. // 1. If the participant's _handshake state_ is not `done`, log a warning and
  1034. // abort these steps.
  1035. // 2. Handle the message according to the content.
  1036. message Envelope {
  1037. // Random amount of padding, ignored by the receiver
  1038. bytes padding = 1;
  1039. oneof content {
  1040. // An `Admin.Envelope`, encrypted as described by that message.
  1041. bytes encrypted_admin_envelope = 2;
  1042. // Announces new media keys a participant will apply soon.
  1043. MediaKey rekey = 3;
  1044. // Announces capture state changes of a participant.
  1045. CaptureState capture_state = 4;
  1046. // Announces that the participant entered the _hold_ state.
  1047. HoldState hold_state = 5;
  1048. }
  1049. }
  1050. // Messages from admins towards participants (including admins).
  1051. message Admin {
  1052. // Message from an administrator, encrypted by:
  1053. //
  1054. // ```text
  1055. // XSalsa20-Poly1305(
  1056. // key=X25519HSalsa20(GCAMK.secret, <receiver.PCK>.public),
  1057. // nonce=<sender.PCCK> || <sender.PCSN+>,
  1058. // )
  1059. // ```
  1060. //
  1061. // IMPORTANT: The `ParticipantToParticipant.Envelope` that encapsulates this
  1062. // message shall be encrypted by the same `PCSN` as used for this
  1063. // `Envelope`. The only difference is that the sender uses `GCAMK` instead
  1064. // of its ephemeral `PCK`.
  1065. message Envelope {
  1066. oneof content {
  1067. ReportAsAdmin report_as_admin = 1;
  1068. PromoteToAdmin promote_to_admin = 2;
  1069. ForceLeave force_leave = 3;
  1070. ForceCaptureStateOff force_capture_state_off = 4;
  1071. ForceFocus force_focus = 5;
  1072. }
  1073. }
  1074. // Report as an administrator.
  1075. //
  1076. // When receiving this message, mark the sender as an administrator in the
  1077. // UI.
  1078. message ReportAsAdmin {}
  1079. // Promote the receiver to an administrator.
  1080. //
  1081. // Note: This is final for the scope of this Group Call. An administrator
  1082. // cannot be demoted.
  1083. //
  1084. // When receiving this message:
  1085. //
  1086. // 1. If the user already is an administrator, abort these steps.
  1087. // 2. Derive GCAMK and calculate the associated public key from the received
  1088. // `gcak`. If it does not match the known `GCAMK.public`, log a warning
  1089. // and abort these steps.
  1090. // 3. Send an `Admin.ReportAsAdmin` message to all other participants
  1091. // (including the sender who promoted the user to an admin).
  1092. // 4. Notify the user of its admin status and enable administration
  1093. // functionality in the UI.
  1094. message PromoteToAdmin { bytes gcak = 1; }
  1095. // Force the receiver to leave the call.
  1096. message ForceLeave {}
  1097. // Force the receiver's capture device to be turned off.
  1098. //
  1099. // Note: This is a momentary enforcement. A participant may immediately
  1100. // restart capturing a device (e.g. unmute itself) and the message is
  1101. // not repeated towards newly joined participants.
  1102. //
  1103. // When receiving this message:
  1104. //
  1105. // 1. Look up the corresponding device. If none could be found, abort these
  1106. // steps.
  1107. // 2. If the device's capture state is already _off_, abort these steps.
  1108. // 3. Send a `CaptureState` message for the device and follow the creation
  1109. // steps of that message (i.e. stop capturing, etc.).
  1110. message ForceCaptureStateOff {
  1111. enum Device {
  1112. // Stop capturing all devices
  1113. ALL = 0;
  1114. // Stop capturing the microphone (i.e. mute)
  1115. MICROPHONE = 1;
  1116. // Stop capturing the camera
  1117. CAMERA = 2;
  1118. // Stop capturing the screen
  1119. SCREEN = 3;
  1120. }
  1121. Device device = 1;
  1122. }
  1123. // Force focus on a specific participant.
  1124. //
  1125. // Note: This is a momentary enforcement. A participant may immediately
  1126. // remove the focus and the message is not repeated towards newly
  1127. // joined participants.
  1128. //
  1129. // When receiving this message:
  1130. //
  1131. // 1. Look up the participant to be focused. If none could be found, abort
  1132. // these steps.
  1133. // 2. Focus the participant in the UI. The camera or screen feed
  1134. // subscription may need to be created (e.g. participant was not visible
  1135. // in the viewport before) or updated (e.g. display resolution changes
  1136. // due to focus) by a corresponding `Subscribe` message sent to the SFU.
  1137. message ForceFocus { uint32 participant_id = 1; }
  1138. }
  1139. // Media keys a participant will use for sending.
  1140. //
  1141. // Will be sent towards new and existing participants as described by the
  1142. // _Join/Leave_ section.
  1143. message MediaKey {
  1144. // The current epoch reflecting the PCMK state.
  1145. //
  1146. // Initially, epoch is `0` and increases each time a participant leaves. The
  1147. // concrete mechanism is explained in the _Join/Leave_ section.
  1148. uint32 epoch = 1;
  1149. // The current ratchet counter reflecting the PCMK state.
  1150. //
  1151. // Initially (or when a participant leaves), the ratchet counter is `0` and
  1152. // increases each time a participant joins. The ratcheting mechanism is
  1153. // explained in the _Join/Leave_ section.
  1154. uint32 ratchet_counter = 2;
  1155. // The current state of the PCMK with the applied ratchet counter.
  1156. //
  1157. // Initially (or when a participant leaves), PCMK is a random 32 byte secret
  1158. // key. The concrete mechanism is explained in the _Join/Leave_ section.
  1159. //
  1160. // This key must be identical **towards** all participants.
  1161. bytes pcmk = 3;
  1162. }
  1163. // Signals a participant's device capturing state.
  1164. //
  1165. // When creating this message:
  1166. //
  1167. // 1. Let `device` be the device whose state is to be updated.
  1168. // 2. If `device` is to be turned _off_:
  1169. // 1. Stop capturing from the device.
  1170. // 2. Pause the corresponding media track.
  1171. // 3. If `device` is to be turned _on_:
  1172. // 1. Start capturing from the device.
  1173. // 2. Resume the corresponding media track.
  1174. // 4. Send the `CaptureState` message for the `device`.
  1175. //
  1176. // When receiving this message:
  1177. //
  1178. // 1. Let `device` be the device of the sender whose state has been updated.
  1179. // 2. If `device` was turned _off_ and the user is subscribed to the given
  1180. // `device`'s feed:
  1181. // 1. Stop displaying the corresponding media feed in the UI.
  1182. // 2. Pause the corresponding media track.
  1183. // 3. If `device` is `Microphone`, no further action is necessary.
  1184. // 4. If `device` is `Camera`, send a `ParticipantCamera.Unsubcribe`
  1185. // message to the SFU.
  1186. // 5. If `device` is `Screen`, send a `ParticipantScreen.Unsubcribe`
  1187. // message to the SFU.
  1188. // 3. If `device` was turned _on_ and the user is not subscribed to the given
  1189. // `device`'s feed:
  1190. // 1. Resume the corresponding media track.
  1191. // 2. Start displaying the corresponding media feed in the UI.
  1192. // 3. If `device` is `Microphone`, no further action is necessary.
  1193. // 4. If `device` is `Camera`, send a `ParticipantCamera.Subscribe` message
  1194. // to the SFU.
  1195. // 5. If `device` is `Screen`, send a `ParticipantScreen.Subscribe` message
  1196. // to the SFU.
  1197. message CaptureState {
  1198. // Capture state of the microphone.
  1199. message Microphone {
  1200. oneof state {
  1201. common.Unit on = 1;
  1202. common.Unit off = 2;
  1203. }
  1204. }
  1205. // Capture state of the camera.
  1206. message Camera {
  1207. oneof state {
  1208. common.Unit on = 1;
  1209. common.Unit off = 2;
  1210. }
  1211. }
  1212. // Capture state of the screen.
  1213. message Screen {
  1214. oneof state {
  1215. common.Unit on = 1;
  1216. common.Unit off = 2;
  1217. }
  1218. }
  1219. oneof state {
  1220. Microphone microphone = 1;
  1221. Camera camera = 2;
  1222. }
  1223. }
  1224. // Signals that a participant is currently on hold / temporarily away.
  1225. //
  1226. // When creating this message:
  1227. //
  1228. // 1. Send a `CaptureState` message for each capture device. Follow the
  1229. // creation steps of that message.
  1230. // 2. Send the `HoldState` message.
  1231. //
  1232. // When receiving this message:
  1233. //
  1234. // 1. Apply the _hold_ state in the UI for the participant.
  1235. // 2. Pause any video-based media tracks of the participant.
  1236. // 3. If subscribed to the participant's camera feed, send a
  1237. // `ParticipantCamera.Unsubcribe` message to the SFU.
  1238. // 4. If subscribed to the participant's screen feed, send a
  1239. // `ParticipantScreen.Unsubcribe` message to the SFU.
  1240. message HoldState {}
  1241. }