Get desktop application:
View/edit binary Protocol Buffers messages
Current call state as announced by the designated client. Note: The `CallState` accurateness must not be relied upon as it can be out of date and can be replayed by the SFU.
Random amount of padding, ignored by the receiver.
Participant ID of the designated client that created this message.
UNIX-ish timestamp in milliseconds the designated client created this message.
Information for each participant of the group call.
Information for a single participant.
Used in:
Type-specific information.
A _guest_ participant.
Used in:
The guest's self-assigned name.
A _normal_ participant, i.e. a Threema client.
Used in:
Threema ID of the sender.
Nickname associated to the Threema ID (without `~` prefix).
Messages sent from one participant to another. Note that these are relayed via `SfuToParticipant.Envelope` and `ParticipantToSfu.Envelope` in order to prevent races with `ParticipantJoined`/`ParticipantLeft`.
(message has no fields)
Messages from admins towards participants (including admins).
(message has no fields)
Message from an administrator, encrypted by: ```text XSalsa20-Poly1305( key=X25519HSalsa20(GCAMK.secret, <receiver.PCK>.public), nonce=<sender.PCCK> || <sender.PCSN+>, ) ``` IMPORTANT: The `ParticipantToParticipant.Envelope` that encapsulates this message shall be encrypted by the same `PCSN` as used for this `Envelope`. The only difference is that the sender uses `GCAMK` instead of its ephemeral `PCK`.
Force the receiver's capture device to be turned off. Note: This is a momentary enforcement. A participant may immediately restart capturing a device (e.g. unmute itself) and the message is not repeated towards newly joined participants. When receiving this message: 1. Look up the corresponding device. If none could be found, abort these steps. 2. If the device's capture state is already _off_, abort these steps. 3. Send a `CaptureState` message for the device and follow the creation steps of that message (i.e. stop capturing, etc.).
Used in:
Used in:
Stop capturing all devices
Stop capturing the microphone (i.e. mute)
Stop capturing the camera
Stop capturing the screen
Force focus on a specific participant. Note: This is a momentary enforcement. A participant may immediately remove the focus and the message is not repeated towards newly joined participants. When receiving this message: 1. Look up the participant to be focused. If none could be found, abort these steps. 2. Focus the participant in the UI. The camera or screen feed subscription may need to be created (e.g. participant was not visible in the viewport before) or updated (e.g. display resolution changes due to focus) by a corresponding `Subscribe` message sent to the SFU.
Used in:
Force the receiver to leave the call.
Used in:
(message has no fields)
Promote the receiver to an administrator. Note: This is final for the scope of this Group Call. An administrator cannot be demoted. When receiving this message: 1. If the user already is an administrator, abort these steps. 2. Derive GCAMK and calculate the associated public key from the received `gcak`. If it does not match the known `GCAMK.public`, log a warning and abort these steps. 3. Send an `Admin.ReportAsAdmin` message to all other participants (including the sender who promoted the user to an admin). 4. Notify the user of its admin status and enable administration functionality in the UI.
Used in:
Report as an administrator. When receiving this message, mark the sender as an administrator in the UI.
Used in:
(message has no fields)
Signals a participant's device capturing state. When creating this message: 1. Let `device` be the device whose state is to be updated. 2. If `device` is to be turned _off_: 1. Stop capturing from the device. 2. Pause the corresponding media track. 3. If `device` is to be turned _on_: 1. Start capturing from the device. 2. Resume the corresponding media track. 4. Send the `CaptureState` message for the `device`. When receiving this message: 1. Let `device` be the device of the sender whose state has been updated. 2. If `device` was turned _off_ and the user is subscribed to the given `device`'s feed: 1. Stop displaying the corresponding media feed in the UI. 2. Pause the corresponding media track. 3. If `device` is `Microphone`, no further action is necessary. 4. If `device` is `Camera`, send a `ParticipantCamera.Unsubcribe` message to the SFU. 5. If `device` is `Screen`, send a `ParticipantScreen.Unsubcribe` message to the SFU. 3. If `device` was turned _on_ and the user is not subscribed to the given `device`'s feed: 1. Resume the corresponding media track. 2. Start displaying the corresponding media feed in the UI. 3. If `device` is `Microphone`, no further action is necessary. 4. If `device` is `Camera`, send a `ParticipantCamera.Subscribe` message to the SFU. 5. If `device` is `Screen`, send a `ParticipantScreen.Subscribe` message to the SFU.
Used in:
Capture state of the camera.
Used in:
Capture state of the microphone.
Used in:
Capture state of the screen.
After fulfilling either the (normal) handshake or the guest handshake, all following messages are encoded in `Envelope` and encrypted by: ```text XSalsa20-Poly1305( key=X25519HSalsa20(<sender.PCK>.secret, <receiver.PCK>.public), nonce=<sender.PCCK> || <sender.PCSN+>, ) ``` Note: Since the guest handshake is TOFU, an attacker knowing `GCK` having control over the SFU may apply a MITM attack between a guest participant and another participant. The attacker would be able to silently eavesdrop all media traffic between the two participants. This is repeatable for all other participants and means the attacker is able to silently eavesdrop the whole call. Therefore, if a call is not open for guests, `GuestHello` (and `GuestAuth`) **must not** be accepted. When receiving this message: 1. If the participant's _handshake state_ is not `done`, log a warning and abort these steps. 2. Handle the message according to the content.
Random amount of padding, ignored by the receiver
An `Admin.Envelope`, encrypted as described by that message.
Announces new media keys a participant will apply soon.
Announces capture state changes of a participant.
Announces that the participant entered the _hold_ state.
Messages required for the initial lock-step handshake between participants.
(message has no fields)
Second and final handshake message. When receiving this message: 1. If the participant's _handshake state_ is not `await-auth`, log a warning and abort these steps. 2. If the repeated `pck` does not equal the local `PCK.public` used towards this participant, log a warning and abort these steps. 3. If the repeated `pcck` does not equal the local `PCCK` used towards this participant, log a warning and abort these steps. 4. Set the participant's _handshake state_ to `done`.
Used in:
32 byte repeated ephemeral public key from the `Hello` message. Note: Repeating the sender's `PCK.public` prevents replay attacks.
32 byte repeated random cookie from the `Hello` message. Note: Repeating the sender's `PCCK` prevents replay attacks while allowing the sender to use the same `PCK` for multiple participants.
The currently applied PCMK and any _pending_ PCMK used for media encryption, specifically in that order. Note: An implementation can expect at least one media key to be present.
If both sides started the normal handshake, the second message is encrypted in the following way: 1. Let `inner-nonce` be a random nonce. 2. Let `inner-data` be encrypted by: ```text S = X25519HSalsa20(<sender.CK>.secret, <receiver.CK>.public) GCNHAK = Blake2b( key=S, salt='nha', personal='3ma-call', input=GCKH) XSalsa20-Poly1305( key=GCNHAK, nonce=<inner-nonce>, data=<AuthEnvelope(Auth)>, ) ``` 3. Let `outer-data` be encrypted by: ```text XSalsa20-Poly1305( key=X25519HSalsa20(<sender.PCK>.secret, <receiver.PCK>.public), nonce=<sender.PCCK> || <sender.PCSN+>, data=<inner-nonce> || <inner-data>, ) ``` 4. Return `outer-data`. If either side started the guest handshake, the second message is encrypted by: ```text XSalsa20-Poly1305( key=X25519HSalsa20(<sender.PCK>.secret, <receiver.PCK>.public), nonce=<sender.PCCK> || <sender.PCSN+>, data=<AuthEnvelope(GuestAuth)>, ) ``` When receiving this message: 1. If either side initiated a guest handshake via a `GuestHello`, expect `guest_auth` to be set. If `guest_auth` is not set, log a warning and abort these steps. 2. If both sides initiated the (normal) handshake, expect `auth` to be set. If `auth` is not set, log a warning and abort these steps.
Random amount of padding, ignored by the receiver
Second and final handshake message triggered if either side initiated the guest handshake. When receiving this message: 1. If the participant's _handshake state_ is not `await-guest-auth`, log a warning and abort these steps. 2. If the repeated `pck` does not equal the local `PCK.public` used towards this participant, log a warning and abort these steps. 3. If the repeated `pcck` does not equal the local `PCCK` used towards this participant, log a warning and abort these steps. 4. Set the participant's _handshake state_ to `done`.
Used in:
32 byte repeated ephemeral public key from the `GuestHello` message. Note: Repeating the sender's `PCK.public` prevents replay attacks.
32 byte repeated random cookie from the `GuestHello` message. Note: Repeating the sender's `PCCK` prevents replay attacks while allowing the sender to use the same `PCK` for multiple participants.
The currently applied PCMK and any _pending_ PCMK used for media encryption, specifically in that order. Note: An implementation can expect at least one media key to be present.
Initial guest handshake message. When creating this message as a newly joined guest participant towards another participant: 1. Set the participant's _handshake state_ to `await-ep-hello`. 2. Send this message. When receiving this message: 1. If guest participants are not allowed for this call, log a warning and abort these steps. 2. (Placeholder for conference call PCK != GCAMK step.) 3. If the sender is a newly joined participant and therefore the _handshake state_ was set to `await-np-hello` (as described by the _Join/Leave_ section): 1. Respond by sending a `GuestHello` message, immediately followed by a `GuestAuth` message. 2. Set the participant's _handshake state_ to `await-guest-auth` and abort these steps. 4. If the participant's _handshake state_ is `await-ep-hello`: 1. If the `pck` reflects the local PCK.public or the `pcck` reflects the local PCCK, log a warning and abort these steps. 2. Respond by sending a `GuestAuth` message. 3. Set the participant's _handshake state_ to `await-guest-auth` and abort these steps. 5. Log a warning and abort these steps.
Used in:
The guest's self-assigned name.
32 byte ephemeral public key (`PCK.public`) towards the remote participant. Note: It is allowed to use the same `PCK` for multiple participants.
16 byte random cookie used for nonces by the sender in subsequent messages.
Initial handshake message. When creating this message as a newly joined participant towards another participant: 1. Set the participant's _handshake state_ to `await-ep-hello`. 2. Send this message. When receiving this message as a guest participant: 1. Map it to a `GuestHello` in the following way: - `name`: `Hello.nickname` - `pck`: `Hello.pck` - `pcck`: `Hello.pcck` 2. Handle the mapped `GuestHello` as if it had been received directly. When receiving this message as a regular participant: 1. (Placeholder for conference call PCK != GCAMK step.) 2. If the group call is scoped to a (Threema) group and `identity` is not part of the associated group (including the user itself), log a warning and abort these steps. 3. If the sender is a newly joined participant and therefore the _handshake state_ was set to `await-np-hello` (as described by the _Join/Leave_ section): 1. Respond by sending a `Hello` message, immediately followed by an `Auth` message. 2. Set the participant's _handshake state_ to `await-auth` and abort these steps. 4. If the participant's _handshake state_ is `await-ep-hello`: 1. If the `pck` reflects the local PCK.public or the `pcck` reflects the local PCCK, log a warning and abort these steps. 2. Respond by sending an `Auth` message. 3. Set the participant's _handshake state_ to `await-auth` and abort these steps. 5. Log a warning and abort these steps.
Used in:
Threema ID of the sender.
Nickname associated to the Threema ID (without `~` prefix).
32 byte ephemeral public key (`PCK.public`) towards the remote participant. Note: It is allowed to use the same `PCK` for multiple participants.
16 byte random cookie used for nonces by the sender in subsequent messages.
The first message (`HelloEnvelope(Hello)` or `HelloEnvelope(GuestHello)`) of both sides is always encrypted by `GCHK`, prefixed with a random nonce.
Random amount of padding, ignored by the receiver
Signals that a participant is currently on hold / temporarily away. When creating this message: 1. Send a `CaptureState` message for each capture device. Follow the creation steps of that message. 2. Send the `HoldState` message. When receiving this message: 1. Apply the _hold_ state in the UI for the participant. 2. Pause any video-based media tracks of the participant. 3. If subscribed to the participant's camera feed, send a `ParticipantCamera.Unsubcribe` message to the SFU. 4. If subscribed to the participant's screen feed, send a `ParticipantScreen.Unsubcribe` message to the SFU.
Used in:
(message has no fields)
Media keys a participant will use for sending. Will be sent towards new and existing participants as described by the _Join/Leave_ section.
Used in:
, ,The current epoch reflecting the PCMK state. Initially, epoch is `0` and increases each time a participant leaves. The concrete mechanism is explained in the _Join/Leave_ section.
The current ratchet counter reflecting the PCMK state. Initially (or when a participant leaves), the ratchet counter is `0` and increases each time a participant joins. The ratcheting mechanism is explained in the _Join/Leave_ section.
The current state of the PCMK with the applied ratchet counter. Initially (or when a participant leaves), PCMK is a random 32 byte secret key. The concrete mechanism is explained in the _Join/Leave_ section. This key must be identical **towards** all participants.
Used for all messages that are relayed from one participant to another via the SFU. When receiving a relayed message: 1. If the `receiver` is not the user's assigned participant id, discard the message and abort these steps. 2. If the `sender` is unknown, discard the message and abort these steps. 3. Decrypt `encrypted_data` according to the current _handshake state_ and handle the inner envelope: - `await-ep-hello` or `await-np-hello`: Expect a `Handshake.HelloEnvelope`. - `await-auth`: Expect a `Handshake.AuthEnvelope`. - `done`: Expect a post-auth `Envelope`.
Used in:
,Participant ID of the sender. Checked by the SFU to be correct, dropped if not.
Participant ID of the receiver. Checked by the SFU to exist, dropped if not.
The inner envelope. Always encrypted. Key and nonce are to be inferred from the current _handshake state_ towards the sending participant.
Messages sent from a participant to the SFU via a data channel. Data Channel Parameters: - `ordered`: `true` - `negotiated`: `true` - `id`: `0`
(message has no fields)
The enveloped message towards the SFU. When relaying a message from one participant to another, omit any additional padding. IMPORTANT: The format of the `SfuToParticipant.Envelope` and `ParticipantToSfu.Envelope` must be compatible for the relay case, so the SFU can forward the data without having to re-encode.
Random amount of padding, ignored by the receiver.
Subscribe or unsubscribe to a participant's camera feed. When receiving this message: 1. If the `participant_id` refers to the sender's participant ID or an unknown participant ID, discard the message and abort these steps. 2. If `subscribe` is set, forward the camera feed to the client that fits best to the provided parameters. 3. If `unsubscribe` is set, stop forwarding camera feed of this participant to the client.
Used in:
Participant ID whose camera feed should be subscribed or unsubscribed from.
Subscribe to a participant's camera feed.
Used in:
Desired resolution. The client should use the canvas' resolution the camera feed be displayed in. The SFU will select the spatial layer that fits best.
Desired frame rate. The SFU will select the temporal layer that fits best.
Unsubscribe a participant's camera feed.
Used in:
(message has no fields)
Subscribe or unsubscribe to a participant's microphone feed. When receiving this message: 1. If the `participant_id` refers to the sender's participant ID or an unknown participant ID, discard the message and abort these steps. 2. If `subscribe` is set, forward the microphone feed to the client that fits best to the provided parameters. 3. If `unsubscribe` is set, stop forwarding microphone feed of this participant to the client.
Used in:
Participant ID whose microphone feed should be subscribed or unsubscribed from.
Subscribe to a participant's microphone feed.
Used in:
(message has no fields)
Unsubscribe a participant's microphone feed.
Used in:
(message has no fields)
Subscribe or unsubscribe to a participant's screen feed.
Used in:
Participant ID whose screen feed should be subscribed or unsubscribed from.
Subscribe to a participant's screen feed.
Used in:
(message has no fields)
Unsubscribe a participant's screen feed.
Used in:
(message has no fields)
Update the call state that can be retrieved via a _peek_. Note: Only the currently designated client should send this to the SFU. When receiving this message: 1. Store the encrypted call state and make it accessible via _peek_ HTTP requests. 2. Start a timer to purge the call state after 30s. Subsequent `UpdateCallState` messages will update the call state and reset the timer.
Used in:
Call state (`CallState`), encrypted by `GCSK` and prefixed with a random nonce.
Request payloads sent to the SFU as part of an HTTP request.
(message has no fields)
Requests to join the group call with the given Group Call ID. The URL is formed in the following way: <sfu_base_url>/v1/join/<call_id-as-hex> When sending this request: 1. Use `POST` as method. 2. Set the `Authorization` header to `ThreemaSfuToken <sfu-token>`. 3. Set the encoded `SfuHttpRequest.Join` message as body. When receiving this request: 1. If the `Authorization` header is missing, the provided `sfu-token` in the `Authorization` header is invalid or expired, respond with status code `401` and abort these steps. 2. If the provided data is invalid, respond with status code `400` and abort these steps. 3. If `call_id` does not equal the Call ID from the URL (decoded `call_id-as-hex`), respond with status code `400` and abort these steps. 4. If the `protocol_version` is unsupported by the SFU, respond with status code `419` and abort these steps. 5. If no more participants can join the group call for the given `call_id`, respond with status code `503` and abort these steps. 6. Respond with status code `200` and an encoded `SfuHttpResponse.Join` message as body. 7. Once the WebRTC connection has been established, announce the newly joined participant to all other participants via the corresponding data channel. If no WebRTC connection is being established within 30s, the participant ID is no longer reserved for the client and the group call must be teared down if no other participant started joining this group call.
Group Call ID associated to the group call.
Protocol version the call was announced with.
DTLS fingerprint of the x509 certificate that will be used by the client. Note: This is the authentication anchor for the WebRTC connection towards the SFU.
Peeks for the current state of the group call for the given Group Call ID. IMPORTANT: The _peek_ process is considered stable across different protocol versions. Therefore, the message **should** maintain backwards compatibility! The URL is formed in the following way: <sfu_base_url>/v1/peek/<call_id-as-hex> When sending this request: 1. Use `POST` as method. 2. Set the `Authorization` header to `ThreemaSfuToken <sfu-token>`. 3. Set the encoded `SfuHttpRequest.Peek` message as body. When receiving this request: 1. If the `Authorization` header is missing, the provided `sfu-token` in the `Authorization` header is invalid or expired, respond with status code `401` and abort these steps. 2. If the provided data is invalid, respond with status code `400` and abort these steps. 3. If `call_id` does not equal the Call ID from the URL (decoded `call_id-as-hex`), respond with status code `400` and abort these steps. 4. If no group call for the given `call_id` is currently running, respond with status code `404` and abort these steps. 5. Respond with status code `200` and an encoded `SfuHttpResponse.Peek` message as body.
Group Call ID associated to the group call.
Response payloads sent back from the SFU as part of an HTTP request.
(message has no fields)
Information returned when joining a group call. When receiving this response, initiate the WebRTC connection to the SFU and consider the connection established when the `SfuToParticipant.Hello` message has been received on the associated data channel.
Unix-ish timestamp in milliseconds for when the first participant joined the Group Call ID and therefore started the group call.
Maximum amount of participants allowed in the group call.
Participant ID assigned to the client. Note: The client needs to know the participant ID early to derive MIDs required to be present in the O/A SDP.
List of addresses the SFU listens for a WebRTC connection. Note: One UDP IPv4 address is mandatory! One IPv6 address is recommended.
ICE username fragment for the WebRTC connection.
ICE password for the WebRTC connection.
DTLS fingerprint of the x509 certificate that will be used by the SFU. Note: This is the authentication anchor for the WebRTC connection towards the SFU.
Address the SFU is listening for a WebRTC connection.
Used in:
Port.
IPv4 or IPv6 address.
Protocol.
Used in:
Information returned for a running group call. IMPORTANT: The _peek_ process is considered stable across different protocol versions. Therefore, the message **should** maintain backwards compatibility! Note: The included `CallState` information may not be accurate and should not be relied upon.
Unix-ish timestamp in milliseconds for when the first participant joined the Group Call ID and therefore started the group call.
Maximum amount of participants allowed in the group call.
Call state (`CallState`), encrypted by `GCSK.secret` and prefixed with a random nonce. Not provided in case the call is currently running but no participant has sent a call state to the SFU, or if the call state expired. The content of the call state is protocol version dependent and should therefore be ignored if a client does not support the particular protocol version the group call is associated with.
Messages sent from the SFU to a participant via a data channel. Data Channel Parameters: - `ordered`: `true` - `negotiated`: `true` - `id`: `0`
(message has no fields)
The enveloped message from the SFU. When relaying a message from one participant to another, omit any additional padding. IMPORTANT: The format of the `SfuToParticipant.Envelope` and `ParticipantToSfu.Envelope` must be compatible for the relay case, so the SFU can forward the data without having to re-encode.
Random amount of padding, ignored by the receiver.
Announces all other participants to a newly joined participant. When receiving this message: 1. If a `Hello` was received before (i.e. if the receiver is not a newly joined participant), log a warning and abort these steps. 2. Initiate the participant to participate handshake for each participant listed in this message.
Used in:
All participants in the group call. This **excludes** the client's participant ID.
Announces that a new participant joined to existing participants. When receiving this message: 1. Look up the participant. If it already exists (i.e. never _left_), log a warning and abort these steps. 2. Run the corresponding steps described by the _Join/Leave_ section.
Used in:
Announces that a participant left to existing participants. When receiving this message: 1. Look up the participant. If it was never announced to have _joined_ by an associated `ParticipantJoined` message, log a warning and abort these steps. 2. Run the corresponding steps described by the _Join/Leave_ section.
Used in: