Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  csp-e2e-fs.proto   Sprache: unbekannt

 
Spracherkennung für: .proto vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

// # Forward Security Subprotocol
//
// This protocol specifies forward security for end-to-end encrypted chat server
// messages. It is based on sessions established between two parties, where each
// session has a unique ID and is associated with ephemeral key material
// negotiated between the parties using a key exchange mechanism and a hash
// chain based key derivation.
//
// Each party is either an initiator or a responder in a given session. Once
// established, a session will be used bidirectionally.
//
// Content messages can take any other type that could normally be sent without
// Forward Security, and wrap the message contained within using a separate
// cryptographic layer that provides Forward Security.
//
// ## Terminology
//
// - `CK`: Client Key (permanent secret key associated to the Threema ID)
// - `FS`: Forward Security
// - `SI`: Session Initiator
// - `SR`: Session Responder
// - `2DH`: Unidirectional forward security with two DH calculations
// - `4DH`: Bidirectional forward security with four DH calculations
// - `FSSK`: Forward Security Session Key (ephemeral key pair generated by both
//   sides for each session)
// - `2DHK`: 2DH key (current iteration)
// - `4DHK`: 4DH key (current iteration)
// - `XDHK`: 2DH or 4DH key (current iteration)
// - `XDHMK`: 2DH or 4DH message key (current iteration)
//
// ## Modes
//
// Note: This is an informational section. The concrete steps surrounding modes
// are described in the steps for the `Envelope`.
//
// A key negotiation normally needs active participation by both involved
// parties before any actual messages can be exchanged. This is not practical in
// a messaging app, as the other party may not be online at the time when the
// first message is sent.
//
// Thus, the protocol specifies two modes, called 2DH and 4DH which will be
// described briefly in the following sections.
//
// ### 2DH Mode
//
// 2DH mode can be used immediately, even in a new session, as it does not
// involve any additional key material from the responder (i.e. remote party).
// However, it only protects against a compromise of the initiator's permanent
// secret key, not of the responder's permanent secret key. It is still better
// than sending all messages without Forward Security until a bidirectional 4DH
// session has been negotiated.
//
// ### 4DH Mode
//
// A session enters 4DH mode when the initiator has processed the responder's
// `Accept` message and sent an `Encapsulated.Confirm` message back to the
// responder. At this point, both sides have the required key material to derive
// the 4DH root chain key from. Messages sent from this point on are secure even
// in the event of a future compromise of the permanent secret key of either
// party.
//
// ## Keys
//
// Each party uses two different asymmetric X25519 key pairs:
//
// - `CK`: Client Key, the permanent secret key associated to the user's Threema
//   ID.
// - `FSSK`: Forward Security Session Key, an ephemeral key pair for the FS
//   session.
//
// Each party also learns the remote's `FSSK.public` from the `Init` or `Accept`
// message. The remote's `CK.public` of the peer's Threema ID is assumed to be
// already known.
//
// ## Key Derivation
//
// The two parties maintain the following keys during a session:
//
// - Local 2DHK (initiator only)
// - Remote 2DHK (responder only)
// - Local 4DHK (both parties)
// - Remote 4DHK (both parties)
//
// For each key, the current iteration of the key and a counter (see below) must
// be stored.
//
// The initiator derives the initial keys as follows:
//
//     local-2DHK = BLAKE2b(
//       input=
//            X25519HSalsa20(<local.CK>.secret, <remote.CK>.public)
//         || X25519HSalsa20(<local.FSSK>.secret, <remote.CK>.public),
//       salt='ke-2dh-<local-threema-id>',
//       personal='3ma-e2e',
//     )
//
//     local-4DHK = BLAKE2b(
//       key=BLAKE2b(
//         out-length=64,
//         input=
//              X25519HSalsa20(<local.CK>.secret, <remote.CK>.public)
//           || X25519HSalsa20(<local.FSSK>.secret, <remote.CK>.public)
//           || X25519HSalsa20(<local.CK>.secret, <remote.FSSK>.public)
//           || X25519HSalsa20(<local.FSSK>.secret, <remote.FSSK>.public)
//       ),
//       salt='ke-4dh-<local-threema-id>',
//       personal='3ma-e2e',
//     )
//
//     remote-4DHK = BLAKE2b(
//       key=BLAKE2b(
//         out-length=64,
//         input=
//              X25519HSalsa20(<local.CK>.secret, <remote.CK>.public)
//           || X25519HSalsa20(<local.FSSK>.secret, <remote.CK>.public)
//           || X25519HSalsa20(<local.CK>.secret, <remote.FSSK>.public)
//           || X25519HSalsa20(<local.FSSK>.secret, <remote.FSSK>.public)
//       ),
//       salt='ke-4dh-<remote-threema-id>',
//       personal='3ma-e2e',
//     )
//
// The responder calculates the initial keys as follows:
//
//     remote-2DHK = BLAKE2b(
//      input=
//              X25519HSalsa20(<local.CK>.secret, <remote.CK>.public)
//           || X25519HSalsa20(<local.CK>.secret, <remote.FSSK>.public),
//       salt='ke-2dh-<remote-threema-id>',
//       personal='3ma-e2e',
//     )
//
//     local-4DHK = BLAKE2b(
//       key=BLAKE2b(
//         out-length=64,
//         input=
//              X25519HSalsa20(<local.CK>.secret, <remote.CK>.public)
//           || X25519HSalsa20(<local.CK>.secret, <remote.FSSK>.public)
//           || X25519HSalsa20(<local.FSSK>.secret, <remote.CK>.public)
//           || X25519HSalsa20(<local.FSSK>.secret, <remote.FSSK>.public)
//       ),
//       salt='ke-4dh-<local-threema-id>',
//       personal='3ma-e2e',
//     )
//
//     remote-4DHK = BLAKE2b(
//       key=BLAKE2b(
//         out-length=64,
//         input=
//              X25519HSalsa20(<local.CK>.secret, <remote.CK>.public)
//           || X25519HSalsa20(<local.CK>.secret, <remote.FSSK>.public)
//           || X25519HSalsa20(<local.FSSK>.secret, <remote.CK>.public)
//           || X25519HSalsa20(<local.FSSK>.secret, <remote.FSSK>.public)
//       ),
//       salt='ke-4dh-<remote-threema-id>',
//       personal='3ma-e2e',
//     )
//
// Each of the `*DHK` keys will be superseded after a message has been encrypted
// in the respective direction as follows:
//
//     XDHK' = BLAKE2b(
//       key=<local|remote>-XDHK,
//       salt='kdf-ck',
//       personal='3ma-e2e',
//     )
//
// However, actual messages are encrypted by:
//
//     XDHMK = BLAKE2b(
//       key=<local|remote>-XDHK,
//       salt='kdf-aek',
//       personal='3ma-e2e',
//     )
//
// So, for example, when an outgoing 4DH message is being sent, it will be
// encrypted by deriving `XDHMK` from `local-4DHK` and then `local-4DHK` will be
// replaced by applying `XDKH'` to `local-4DHK`.
//
// ### Key Counters
//
// Note: This is an informational section. The concrete processing steps
// required for keys and key counters are deeply integrated into the processing
// steps of the `Envelope`.
//
// Each local/remote 2DHK/4DHK is associated with a counter, which describes how
// many times the XDHK' has been applied since the initial key derivation.
// Whenever a new message has been sent in a session, the corresponding counter
// must be incremented and the key replaced by applying the XDHK' derivation on
// it. As this operation cannot be reversed, counter values cannot go back.
//
// To account for lost messages (e.g. when the recipient has been offline for an
// extended period of time or when a short-lived message such as
// `typing-indicator` or `call-offer` has been removed from the chat server),
// either party must be prepared to accept counters that have skipped a few
// values, and apply XDHK' as many times as is needed to reach the new counter
// value. To limit the CPU impact, the permissible counter delta is limited to
// 25'000.
//
// Note: A counter delta of 25'000 allows for roughly 100 hours of typing while
// the recipient is offline. Users who exceed that are considered insane and out
// of scope!
//
// ## Session States
//
// This section explains the various possible session states.
//
// Note: This is an informational section. The concrete state creation and
// transformation steps are deeply integrated into the processing steps of the
// `Envelope`.
//
// The _local_ session states are only valid for SI while the _remote_ session
// states are only valid for SR.
//
// A session is considered _bidirectional_ 4DH if the session state is
// `local-out-4dh-in-4dh` (`L44`) or `remote-in-4dh-out-4dh` (`R44`).
//
// ### `local-out-2dh-in-none` (`L20`)
//
// - An `Init` has been sent to remote.
// - Outgoing messages are protected with 2DH.
// - Incoming messages are not allowed by this state.
// - Once an `Accept` has been received for this session, we move into
//   `local-out-4dh-in-4dh` (`L44`) state.
//
// ### `local-out-4dh-in-4dh` (`L44`)
//
// - An `Accept` has been received for this session.
// - Outgoing and incoming messages are protected with 4DH.
//
// ### `remote-in-2dh-out-none` (`R20`)
//
// - An `Init` has been received and the session has been created. An `Accept`
//   is supposed to be sent with the next outgoing message to remote.
// - Incoming messages are protected with 2DH.
// - Outgoing messages are not allowed by this state.
// - Once the `Accept` has been sent, we move into `remote-in-2dh-out-4dh`
//   (`R24`).
//
// ### `remote-in-2dh-out-4dh` (`R24`)
//
// - An `Accept` has been sent to remote.
// - Incoming messages are protected with 2DH.
// - Outgoing messages are protected with 4DH.
// - Once a 4DH encrypted `Encapsulated` message has been received for this
//   session, we move into `remote-in-4dh-out-4dh` (`R44`).
//
// ### `remote-in-4dh-out-4dh` (`R44`)
//
// - An incoming 4DH protected `Encapsulated` message has been received for this
//   session.
// - Outgoing and incoming messages are protected with 4DH.
//
// ## Protocol Flow
//
// Note: This is an informational section. The concrete flows are deeply
// integrated into the incoming/outgoing message processing steps.
//
// ### Session Establishment
//
// An FS session negotiation is typically started when a user sends the first
// message to a remote Threema ID. The user assumes the role of SI, creates a
// new `L20` session by sending an `Init` message, followed by any number of
// `Encapsulated` messages in 2DH mode.
//
//     SI ------------- Init -------------> SR   [1]
//     SI ------ Encapsulated (2DH) ------> SR   [0..N]
//
// Note that `Encapsulated` messages in 2DH mode are limited to the minimum
// version that is announced in the `Init` because the initiator does not know
// yet which version is supported by the responder.
//
// SR will create a corresponding `R20` session. Once an outgoing message
// towards SI is being triggered by SR, it will send an `Accept` and move the
// session into `R24` state, followed by any number of `Encapsulated` messages
// in 4DH mode.
//
//     SI <----------- Accept ------------- SR   [1]
//     SI <----- Encapsulated (4DH) ------- SR   [1..N]
//
// When SI receives the `Accept`, it will move the session into `L44` state,
// followed by any number of `Encapsulated` messages in 4DH mode.
//
//     SI ------ Encapsulated (4DH) ------> SR   [0..N]
//
// With the first `Encapsulated` message received, SR will move the session into
// `R44` state.
//
// At this point, the session has been fully established as a _bidirectional_
// 4DH session and either side may send any number of `Encapsulated` messages in
// 4DH mode.
//
// At any point either party may `Terminate` a session, for example when the
// user is about to delete their account (or FS session information) and wants
// to notify the peer that the key material should be discarded.
//
// If any party receives a `Encapsulated` message that it cannot decrypt (e.g.
// due to having lost the FS key material), it eventually sends a `Reject`
// message to inform the other party that it can no longer use this particular
// FS session, and also tells it the ID of the message that could not be
// decrypted. The sender can then (after manual confirmation by the user)
// re-send the message in a new FS session.
//
// ## Handling Session Loss
//
// A party could lose FS sessions information, e.g. due to reinstallation or
// relocation of the app to a new device. FS sessions are not included in
// backups, as this would conflict with their ephemeralness. When a party
// receives a message that it cannot decrypt due to a missing session, it
// signals this to the other party using a `Reject` message.
//
// ## Race Conditions
//
// It is possible for both parties to create an FS session independently, before
// having received the other party's `Init`. In that case, both parties
// determinstically run a conflict resolve mechanism based on the session ID.
//
// Each party will proceed normally to ensure any messages sent while the race
// is ongoing will be received. When choosing a session for sending a new
// message and there is more than one _bidirectional_ 4DH session available for
// the desired peer, the one with the lowest session ID will be chosen and the
// other one removed. This will ensure that both parties will eventually start
// using the same session in both directions.
//
// ## Notifying the User
//
// To prevent undetected MITM attacks by a third party that has gained access to
// the permanent secret key of either party, the user will be informed whenever
// a new session is negotiated or a non-FS message has been received even though
// an FS session was available.
//
// ## Session Implementation
//
// Note: This is a normative section. The steps will imply that these rules are
// being applied!
//
// Session uniqueness is determined by the following tuple:
//
// - the remote Threema ID,
// - a session ID.
//
// When looking up an FS session, this tuple must always be provided!
//
// An FS session has the following properties:
//
// - a remote Threema ID,
// - a session ID, and
// - a state and all necessary properties associated to that state.
//
// An FS session must be stored in a standalone storage and not as part of the
// contact because FS sessions can be created prior to the contact being
// created.
//
// When an FS session state _transitions_, _moves_ or _advances_, all keys that
// are no longer required must be erased securely.
//
// When an XDHK key iteration is being _replaced_, it must be erased securely.
//
// When an FS session is being _removed_ or _replaced_, it and all associated
// properties must be erased securely.
//
// When a contact is being removed, all associated FS sessions must be erased
// securely as well.
//
// ### State Properties
//
// The following properties, and **only** those, must be associated to the
// state.
//
// `L20` properties:
//
// - `version-range`: Supported version range as announced in the `Init` by
//   local (immutable)
// - `local-fssk`: Local FSSK (secret key, immutable)
// - `local-2dhk`: Current key state of the `local-2DHK` for outgoing messages
//   (using version `version-range.min`, immutable)
//
// `R20` properties:
//
// - `version-range`: Supported version range as announced in the `Init` by
//   remote (immutable)
// - `remote-fssk`: Remote FSSK (public key, immutable)
// - `remote-2dhk`: Current key state of the `remote-2DHK` for incoming messages
//   (using version `version-range.min`, mutable)
//
// `R24` properties:
//
// - `remote-2dhv`: Current version used for incoming messages (immutable)
// - `remote-2dhk`: Current key state of the `remote-2DHK` for incoming messages
//   still in flight (mutable).
// - `local-4dhv`: Current version used for outgoing messages (immutable)
// - `local-4dhk`: Current key state of the `local-4DHK` for outgoing messages
//   (mutable).
// - `remote-4dhv`: Version used for incoming messages in `R44` state
//   (immutable)
// - `remote-4dhk`: Key for incoming messages in `R44` state (immutable).
//
// `L44` and `R44` properties:
//
// - `local-4dhv`: Current version used for outgoing messages (mutable).
// - `local-4dhk`: Current key state of the `local-4DHK` for outgoing messages
//   (mutable).
// - `remote-4dhv`: Current version used for incoming messages (mutable).
// - `remote-4dhk`: Current key state of the `remote-4DHK` for incoming messages
//   (mutable).
//
// ### Key State
//
// A key state contains a key iteration, i.e. a local or remote `XDHK`, and the
// associated counter.
//
// The key state is bound to a specific direction (i.e. _local_ for outgoing
// messages or _remote_ for incoming messages).
//
// The following steps are defined as the _XDHK Incoming Advance Steps_:
//
// 1. Let `target-counter` be the targeted `XDHK` key counter.
// 2. Let `xdhk` be the current `XDHK` iteration of the state.
// 3. Let `distance` be the counter distance between the counter associated to
//    `xdhk` and `target-counter`.
// 4. If `distance` is negative or if `distance` is greater than `1000`, log a
//    warning and return undefined.
// 5. Loop `distance` times:
//    1. Set `xdhk` to the output of XDHK' applied to `xdhk`.
// 6. Return the tuple of `xdhk` and a context handle that if run, runs the
//    _XDHK Replace Steps_ with `xdhk`.
//
// The following steps are defined as the _XDHK Replace Steps_:
//
// 1. Let `xdhk` be the `XDHK` that has just been used (e.g. most likely because
//    an incoming message has been processed).
// 2. Apply XDHK' to `xdhk` and replace the current `XDHK` iteration of the
//    state with the result of that derivation.

syntax = "proto3";

package csp_e2e_fs;

option java_package = "ch.threema.protobuf.csp.e2e.fs";
option java_multiple_files = true;

import "common.proto";

// A forward security version.
//
// Note: The most significant byte is the major version and the least
// significant byte is the minor version.
//
// IMPORTANT: Don't remove any versions. Apps may serialize and deserialize this
// enum into the database.
enum Version {
  // The version is unspecified.
  UNSPECIFIED = 0;

  // V1.0
  //
  // - The initial FS release with some backwards compatible adjustments.
  // - Encapsulates only the following E2E messages:
  //   - [`text`](ref:e2e.text)
  //   - [`location`](ref:e2e.location)
  //   - [`file`](ref:e2e.file)
  //   - [`poll-setup`](ref:e2e.poll-setup)
  //   - [`poll-vote`](ref:e2e.poll-vote)
  V1_0 = 0x0100;

  // V1.1
  //
  // - Builds on V1.0 with backwards compatibility.
  // - If the remote side offered support for V1.1, all local 1:1 E2E messages
  //   will be encapsulated.
  V1_1 = 0x0101;

  // V1.2
  //
  // - Builds on V1.1 with backwards compatibility.
  // - If the remote side offered support for V1.2, all local group E2E messages
  //   will be encapsulated (i.e. all messages are now encapsulated).
  V1_2 = 0x0102;
};

// Forward security version range.
message VersionRange {
  // Minimum supported version.
  //
  // Note: This is the version that will be applied to 2DH message of the
  // sender.
  uint32 min = 1;

  // Maximum supported version.
  uint32 max = 2;
}

// A forward security envelope associated to an FS session, containing session
// signalling or an `Encapsulated` message.
//
// When receiving this message with FS unsupported by the client¹:
//
// 1. Let `sender` be the sender of this message.
// 2. If the `content` variant is not `Encapsulated`, discard the message and
//    abort these steps.
// 3. Run the steps to send a `Reject` with cause `DISABLED_BY_LOCAL`.
//
// ¹: The steps require basic support for FS (at least decoding the `Envelope`).
// Outdated apps may not provide that in which case case we rely on the feature
// mask correctly excluding FS.
//
// When receiving this message with FS supported by the client:
//
// 1. Let `sender` be the sender of this message.
// 2. Lookup a session with the sender with `envelope.session_id` and let
//    `session` be the result.
// 3. If the `content` variant is unknown, discard the message, log a warning
//    and abort these steps.
// 4. Invoke the steps associated to receiving the `content` variant. If
//    `content` is `Encapsulated`, return the inner message type as
//    `inner-type`, the decapsulated message as `inner-message` and the
//    `fs-commit-fn` function.
message Envelope {
  // Forward security session ID, 16 bytes
  bytes session_id = 1;

  oneof content {
    Init init = 2;
    Accept accept = 3;
    Reject reject = 4;
    Terminate terminate = 5;
    Encapsulated encapsulated = 6;
  }
}

// Initialises a new session.
//
// When receiving this variant:
//
// 1. Let `session` be the associated FS session or undefined.
// 2. If `session` is defined, log a warning that the `Init` has been repeated,
//    discard the message and abort these steps.
// 3. If the `supported_version` range does not include a supported version, log
//    a warning, discard the message and abort these steps.
// 4. Ensure that `fssk` contains a valid Curve25519 public key, otherwise log a
//    warning, discard the message and abort these steps.
// 5. [...]
// 6. Send an `Accept` and await acknowledgement of the `Accept` message.
// 7. _Acknowledge_ the message that contained this variant.
// 8. Commit the R24 session to storage.
message Init {
  // The version range supported by the initiator.
  //
  // If not provided, assume V1.0 (`0x0100`) for both `min` and `max`.
  VersionRange supported_version = 2;

  // Ephemeral X25519 public key.
  bytes fssk = 1;
}

// Accepts a session.
//
// When receiving this variant:
//
// 1. [...]
// 2. If the `supported_version` range does not include a supported version, log
//    a warning, discard the message and abort these steps.
// 3. Ensure that `fssk` contains a valid Curve25519 public key, otherwise log a
//    warning, discard the message and abort these steps.
// 4. [...]
// 5. _Acknowledge_ the message that contained this variant.
// 6. Commit the session transition to L44 to storage.
message Accept {
  // The version range supported by the responder.
  //
  // If not provided, assume V1.0 (`0x0100`) for both `min` and `max`.
  VersionRange supported_version = 2;

  // Ephemeral X25519 public key.
  bytes fssk = 1;
}

// Sent when receiving a `Encapsulated` message that cannot be decrypted (e.g.
// because the recipient has lost the session information).
//
// When creating this variant:
//
// 1. Let `encapsulated` be the `Encapsulated` message that triggers this
//    reject.
// 2. Set `message_id` to `encapsulated.message_id`.`
// 3. Set `group_identity` to `encapsulated.group_identity`.
//
// When sending this variant:
//
// 1. Let `session` be the associated session (if any).
// 2. Await acknowledgement of this message.
// 3. If `session` is defined, terminate `session` due to `cause` and remove
//    `session` from storage.
//
// When receiving this variant:
//
// 1. Let `session` be the associated session (if any).
// 2. If `session` is defined or `cause` is `DISABLED_BY_LOCAL`, schedule a task
//    to refresh the feature mask of the sender.
// 3. If `session` is defined, terminate `session` due to `cause` and remove
//    `session` from storage.
// 4. If `group_identity` has not been provided:
//    1. Lookup the message for `message_id` in the associated 1:1 conversation
//       and let `message` be the result.
//    2. If `message` is not defined or the user is not the sender of `message`,
//       abort these steps.
//    3. If the _when rejected_ property associated to `message` allows to
//       re-send after confirmation, mark `message` with _re-send requested_.
// 5. If `group_identity` has been provided:
//    1. Run the _Common Group Receive Steps_ and let `group` be the associated
//       group to `group_identity`. If the message has been discarded, abort
//       these steps.
//    2. Lookup the message for `message_id` in the `group` and let `message` be
//       the result.
//    3. If `message` is not defined:
//       1. If the user is the creator of the group, assume that a
//          `group-sync-request` has been received from the sender and run the
//          associated steps.
//       2. Abort these steps.
//    4. If the user is not the sender of (the original) `message`, abort these
//       steps.
//    5. If the _when rejected_ property associated to `message` allows to
//       re-send after confirmation, mark `message` with _re-send requested_ and
//       add `sender` to the list of group members requesting a re-send for
//       `message`.
//
// When the user signals that a message marked with _re-send requested_ should
// be re-sent, run the following steps¹:
//
// 1. Let `message` be the message to be re-sent.
// 2. If `message` is part of a 1:1 conversation:
//    1. Let `receivers` be a list of a single entry with the other party.
//    2. Remove the _re-send requested_ mark on `message`
// 3. If `message` is part of a group conversation:
//    1. Let `receivers` be a copy of the list of group members requesting a
//       re-send of `message`.
//    2. Request consent from the user to re-send the message to all `receivers`
//       and (asynchronously) wait for the response. If the request was denied,
//       abort these steps.
//    3. If `receivers` includes the list of group members requesting a re-send
//       of `message`², remove the _re-send requested_ mark on `message` (but
//       retain the list of group members requiring a re-send).
// 4. Schedule a persistent task that runs the following steps with `message`
//    and `receivers`:
//    1. If `message` has since been removed from the device, discard the task
//       and abort these steps.
//    2. Let `receivers` be the provided (snapshot of) receivers that requested
//       a re-sent.
//    3. Run the _Common Send Steps_ with `message` and `receivers` (and discard
//       the resulting receivers list).
//    4. Remove all `receivers` from the list of receivers requiring a re-send
//       for `message`.
//
// The following steps are defined as the _Rejected Messages Refresh Steps_ and
// will be run every time the group members are being updated:
//
// 1. Let `group` be the group conversation.
// 2. If `group` is marked as _left_:
//    1. For each `message` of `group` that has a _re-send requested_ mark,
//       remove the mark and the list of receivers requiring a re-send.
// 3. If `group` is not marked as _left_:
//    1. Let `members` be the current list of members for `group`.
//    2. For each `message` of `group` that has a _re-send requested_ mark:
//       1. Let `receivers` be the list of receivers requiring a re-send for
//          `message`.
//       2. Remove all entries from `receivers` that are not present in
//          `members`.
//       3. If `receivers` is now empty, remove the _re-send requested_ mark on
//          `message`.
//
// ¹: The steps ensure that multiple tasks scheduled to re-send the same message
// but with potential different receivers have no ill-effect. In edge cases,
// this may result in sending the same message more than once to a remote party
// which requested a re-send. This is deemed acceptable.
//
// ²: While requesting consent from the user, the list of group members
// requesting a re-send for the message may be asynchronously updated, making
// this check necessary so that a subsequent re-send for the remaining group
// members can be triggered.
message Reject {
  // Message ID of the `Encapsulated` message that could not be decrypted and
  // that should be sent again in a new session or without a session.
  fixed64 message_id = 1;

  // The group in which the message has been sent (if any).
  common.GroupIdentity group_identity = 3;

  // Cause for the reject.
  enum Cause {
    // General state mismatch. Explicitly includes the following cases:
    //
    // - The message could not be decrypted.
    // - The DH type of the message does not match the expected DH type.
    // - An unexpected (major) version has been used.
    STATE_MISMATCH = 0;

    // No session could be found matching the given Session ID.
    UNKNOWN_SESSION = 1;

    // The active local feature set does not include support for forward
    // security.
    DISABLED_BY_LOCAL = 2;
  }
  Cause cause = 2;
}

// Signals that the sender will not send any further `Encapsulated` messages in
// this session. The recipient should discard all key material related to this
// session.
//
// When sending this variant:
//
// 1. Let `session` be the associated session.
// 2. Await acknowledgement of this message.
// 3. Terminate `session` due to `cause` and remove `session` from storage.
// 4. If `cause` is `UNKNOWN_SESSION` or `RESET`, schedule initiating a new
//    session with the receiver.
//
// When receiving this variant:
//
// 1. Let `session` be the associated session (if any).
// 2. If `session` is defined or `cause` is `DISABLED_BY_LOCAL`, schedule a task
//    to refresh the feature mask of the sender.
// 3. If `session` is defined, terminate `session` due to `cause` and remove
//    `session` from storage.
message Terminate {
  // Cause for termination.
  enum Cause {
    // No session could be found matching the given Session ID.
    UNKNOWN_SESSION = 0;

    // The session is being reset
    RESET = 1;

    // The active local feature set does not include support for forward
    // security.
    DISABLED_BY_LOCAL = 2;

    // The remote feature set (fetched from the Directory) does not indicate
    // support for forward security.
    DISABLED_BY_REMOTE = 3;
  }
  Cause cause = 1;
}

// A forward security encapsulated CSP E2EE message or a control message.
//
// The following steps are defined as the _FS Refresh Steps_:
//
// 1. Let `contacts` be the provided list of contacts.
// 2. For each `contact` of `contacts`:
//    1. If the `contact` does not support FS, abort these sub-steps.
//    2. Lookup a session with `contact` and let `session` be the result.
//    3. If `session` is undefined, initiate a new `L20` session and set
//       `session` to the newly created session. Set `message` to the `Init`
//       message for `session` with type `0xa0`.
//    4. If `session` is not a newly created session, create an `Encapsulated`
//       message using `session` from inner type `0xfc` (_empty_) and set
//       `message` to the encrypted and encoded result with type `0xa0`.
//    5. Send `message` to `contact` and wait for acknowledgement.
//    6. Set `session`'s _updated_ mark to the current timestamp and commit the
//       `session` changes to storage.
//
// The following steps are defined as the _FS Encapsulation Steps_:
//
// 1. Let `inner-type` be any message type except `0xa0` and `inner-message` be
//    the message data and its nonce. Let `receiver` be the receiver of the
//    message.
// 2. If either sender or receiver do not support FS, return `inner-message` and
//    a stub `fs-commit-fn` function.
// 3. Let `outer-messages` be an empty list.
// 4. Lookup a session with `receiver` and let `session` be the result.
// 5. If `session` is undefined, initiate a new `L20` session and set `session`
//    to the newly created session. Append the `Init` message for `session` to
//    `outer-messages` with type `0xa0` and a random nonce.
// 6. If `inner-type` is not eligible to be encapsulated by `session`:
//    1. If `session` is not a newly created session and the `session`'s
//       _updated_ mark is older than 24h ago, create an `Encapsulated` message
//       using `session` from inner type `0xfc` (_empty_) and append the
//       encrypted and encoded result to `outer-messages` with type `0xa0` and a
//       random nonce.
//    2. Append `inner-message` as a non-encapsulated message to
//       `outer-messages` with type `inner-type` and the nonce of
//       `inner-message`.
// 7. If `inner-type` is eligible to be encapsulated by `session`, create an
//    `Encapsulated` message using `session` from `inner-type` and
//    `inner-message` and append the encrypted and encoded result to
//    `outer-messages` with type `0xa0` and the nonce of `inner-message`.
// 8. Return `outer-messages` and an `fs-commit-fn` function that sets the
//    `session`'s _updated_ mark to the current timestamp and commits the
//    `session` changes to storage when called.
//
// When receiving this variant:
//
// 1. Let `session` be the associated session (if any).
// 2. If `session` is not defined, `Reject` the message with `UNKNOWN_SESSION`
//    and abort these steps.
// 3. [...]
// 4. If `offered_version` is `0`, set it to V1.0.
// 5. If `applied_version` is `0`, set it to `offered_version`.
// 6. If `applied_version` is > `offered_version`, `Reject` the `session` and
//    the message with a `STATE_MISMATCH` and abort these steps.
// 7. If `session.state.dh_type` is `TWODH`:
//    1. If `session.state` is not `R20` and not `R24`, `Reject` the `session`
//       and the message with a `STATE_MISMATCH` and abort these steps.
//    2. If `session.state` is `R20`, let `expected-version` be
//       `session.state.version-range.min`.
//    3. If `session.state` is `R24`, let `expected-version` be
//       `session.state.remote-2dhv`.
//    4. If `offered_version` and `applied_version` are not equal to
//       `expected-version`, `Reject` the `session` and the message with a
//       `STATE_MISMATCH`, and abort these steps.
// 8. If `session.state.dh_type` is `FOURDH`:
//    1. If `session.state` is not any of `L44`, `R44` or `R24`, `Reject` the
//       `session` and the message with a `STATE_MISMATCH` and abort these
//       steps.¹
//    2. Let `pending-versions` be a set of local and remote versions to be
//       filled by the following steps.
//    3. If `offered_version` has a different major version, or is lower than
//       `session.state.local-4dhv`, `Reject` the `session` and the message with
//       a `STATE_MISMATCH`, and abort these steps.
//    4. If `offered_version` has a higher minor version than
//       `session.state.local-4dhv`, let `commonly-supported-version` be the
//       maximum commonly supported version with `offered_version`.
//    5. If `applied_version` has a different major version, or is lower than
//       `session.state.remote-4dhv`, or is not a supported version, `Reject`
//       the `session` and the message with a `STATE_MISMATCH` and abort these
//       steps.
//    6. If `applied_version` has a higher minor version than
//       `session.state.remote-4dhv`, set `pending-versions.remote-4dhv` to
//       `applied_version`.
//    7. If `commonly-supported-version` is defined and different to
//       `pending-versions.local-4dhv`, update it with that value, create an
//       `Encapsulated` message using `session` from inner type `0xfc`
//       (_empty_), send the encrypted and encoded result with type `0xa0` to
//       the sender and wait for acknowledgement of that message.¹
//     8. Update `session.state` with `pending-versions`.
// 9. Return an `fs-commit-fn` function that sets the `session`'s _updated_ mark
//    to the current timestamp and commits the `session` changes to storage when
//    called.
//
// ¹: This an exception to the silent ping rule which ensures that FS sessions
// are updated to use the highest available version ASAP.
message Encapsulated {
  // Encryption scheme applied to the encapsulated message.
  enum DHType {
    // The message is encrypted with the 2DH encryption scheme.
    TWODH = 0;
    // The message is encrypted with the 4DH encryption scheme.
    FOURDH = 1;
  }
  DHType dh_type = 1;

  // A monotonically increasing counter, starting at 1 for the first 2DH or 4DH
  // `Encapsulated` message sent in this session, and incrementing by 1 for each
  // successive `Encapsulated` message.
  //
  // - Counters for 2DH and 4DH are separate, as they are based on different
  //   root keys.
  // - Counters for each direction are separate.
  // - Can be used by the recipient as a hint of how many times to
  //   rotate/ratchet the KDF, in case an intermediate `Encapsulated` message
  //   went missing.
  uint64 counter = 2;

  // The major negotiated version with the _offered_ minor version.
  uint32 offered_version = 4;

  // The major negotiated version with the _applied_ minor version.
  uint32 applied_version = 5;

  // The group in which the message has been sent.
  //
  // Required if the inner message type's _kind_ property is _Group_. Must not
  // be set for _1:1_.
  common.GroupIdentity group_identity = 6;

  // A message as defined by `e2e.container` (but **without** PKCS#7 padding),
  // encrypted by:
  //
  // ```text
  // XSalsa20-Poly1305(
  //   key=XDHMK,
  //   nonce=00..00,
  //   data=<e2e.container>,
  // )
  // ```
  bytes encrypted_inner = 3;
}

[Dauer der Verarbeitung: 0.29 Sekunden, vorverarbeitet 2026-04-27]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge