Spracherkennung für: .proto vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]
// ## Connection Rendezvous Protocol
//
// Some mechanisms may request a 1:1 connection between two devices in order to
// transmit data as direct as possible. Establishing such a connection should
// always require user interaction.
//
// The protocol runs an authentication **handshake** on multiple paths
// simultaneously and applies a heuristic to determine the best available path.
// One of the devices is eligible to **nominate** a path after which arbitrary
// encrypted payloads may be exchanged.
//
// ### Terminology
//
// - `RID`: Rendezvous Initiator Device
// - `RRD`: Rendezvous Responder Device
// - `AK`: Authentication Key
// - `ETK`: Ephemeral Transport Key
// - `STK`: Shared Transport Key
// - `PID`: Path ID
// - `RPH`: Rendevous Path Hash
// - `RIDAK`: RID's Authentication Key
// - `RRDAK`: RRD's Authentication Key
// - `RIDTK`: RID's Transport Key
// - `RRDTK`: RRD's Transport Key
// - `RIDSN`: RID's Sequence Number
// - `RRDSN`: RRD's Sequence Number
//
// ### General Information
//
// **Sequence number:** The sequence number starts with `1` and is counted
// separately for each direction (i.e. there is one sequence number counter for
// the client and one for the server). We will use `RIDSN+` and `RRDSN+` in this
// document to denote that the counter should be increased **after** the value
// has been inserted (i.e. semantically equivalent to `x++` in many languages).
//
// **Framing:** An `extra.transport.frame` is being used to frame all
// transmitted data even if the transport supports datagrams. This intentionally
// allows to fragment a frame across multiple datagrams (e.g. useful for limited
// APIs that cannot deliver data in a streamed fashion).
//
// ### Key Derivation
//
// RIDAK = BLAKE2b(key=AK.secret, salt='rida', personal='3ma-rendezvous')
// RRDAK = BLAKE2b(key=AK.secret, salt='rrda', personal='3ma-rendezvous')
//
// STK = BLAKE2b(
// key=
// AK.secret
// || X25519HSalsa20(<local.ETK>.secret, <remote.ETK>.public)
// salt='st',
// personal='3ma-rendezvous'
// )
//
// RIDTK = BLAKE2b(key=STK.secret, salt='ridt', personal='3ma-rendezvous')
// RRDTK = BLAKE2b(key=STK.secret, salt='rrdt', personal='3ma-rendezvous')
//
// ### Encryption Schemes
//
// RID's encryption scheme is defined in the following way:
//
// ChaCha20-Poly1305(
// key=<RID*K.secret>,
// nonce=u32-le(PID) || u32-le(RIDSN+) || <4 zero bytes>,
// )
//
// RRD's encryption scheme is defined in the following way:
//
// ChaCha20-Poly1305(
// key=<RRD*K.secret>,
// nonce=u32-le(PID) || u32-le(RRDSN+) || <4 zero bytes>,
// )
//
// ### Rendezvous Path Hash Derivation
//
// A Rendezvous Path Hash (RPH) can be used to ensure that both parties are
// connected to each other and not to some other party who was able to intercept
// AK:
//
// RPH = BLAKE2b(
// out-length=32,
// salt='ph',
// personal='3ma-rendezvous',
// input=STK.secret,
// )
//
// ### Path Matrix
//
// | Name | Multiple Paths |
// |-------------------|----------------|
// | Direct TCP Server | Yes |
// | Relayed WebSocket | No |
//
// ### Protocol Flow
//
// Connection paths are formed by transmitting a `rendezvous.RendezvousInit`
// from RID to RRD as defined in the description of that message.
//
// The connections are then simultaneously established in the background and
// each path must go through the handshake flow with its authentication
// challenges. While doing so, the peers measure the RTT between challenge and
// response in order to determine a good path candidate for nomination.
//
// One of the peers, defined by the upper-layer protocol, nominates one of the
// established paths. Once nominated, both peers close all other paths (WS:
// `1000`).
//
// Once a path has been nominated, that path will be handed to the upper-layer
// protocol for arbitrary data transmission. That data must be protected by
// continuing the respective encryption scheme of the associated role.
//
// ### Handshake Flow
//
// RRD and RID authenticate one another by the following flow:
//
// RRD ---- Handshake.RrdToRid.Hello ---> RID
// RRD <- Handshake.RidToRrd.AuthHello -- RID
// RRD ---- Handshake.RrdToRid.Auth ----> RID
//
// Before the path can be used by the upper-layer protocol, the chosen path must
// be `Nominate`d by either side. The upper-layer protocol must define which
// side may `Nominate`.
//
// R*D ------- Handshake.Nominate ------> R*D
//
// ### Path Nomination
//
// The following algorithm should be used to determine which path is to be
// nominated. The upper-layer protocol must clearly define whether RRD or RID
// does nomination.
//
// 1. Let `established` be the list of established connection paths.
// 2. Asynchronously, with each connection becoming established, update
// `established` with the RTT that was measured during the handshake.
// 3. Wait for the first connection path to become established.
// 4. After a brief timeout (or on a specific user interaction), nominate the
// connection path in the following way, highest priority first:
// 1. Path with the lowest RTT on a mutually unmetered, fast network
// 2. Path with the lowest RTT on a mutually unmetered, slow network
// 3. Path with the lowest RTT on any other network
//
// Note: It is recommended to warn the user if a metered connection path has
// been nominated in case large amounts of data are to be transmitted.
//
// ### WebSocket Close Codes
//
// When WebSocket is used as rendezvous transport, the following close codes
// should be used:
//
// - Normal (`1000`): The rendezvous connection was not nominated or the
// upper-layer protocol exited successfully.
// - Rendezvous Protocol Error (`4000`): The rendezvous protocol was violated.
// Possible examples: Invalid WebSocket path, session full. Error details may
// be included in the WebSocket close _reason_.
// - Init Timeout (`4003`): The other device did not connect in time.
// - Other Device Disconnected (`4004`): The other device disconnected without a
// reflectable close code.
// - Upper-Layer Protocol Error (`4100`): The rendezvous connection was
// nominated but an upper-layer protocol error occurred.
//
// The device should log all other close codes but treat them as a _Rendezvous
// Protocol Error_ (`4000`).
//
// Close codes in the `41xx` range as well as `1000` are reflected by the
// rendezvous server to the other device.
//
// ### Security
//
// To prevent phishing attacks, the CORS `Access-Control-Allow-Origin` of any
// WebSocket rendezvous relay server should be set to the bare minimum required
// by the use case.
//
// ### Threat Model
//
// The security of the protocol relies on the security of the secure channel
// where the `RendezvousInit` is being exchanged.
//
// Arbitrary WebSocket URLs and arbitrary IPv4/IPv6 addresses can be provided by
// RID where RRD would connect to. It is therefore required that RRD can trust
// RID to not be malicious.
//
// AK must be exchanged over a sufficiently secure channel. Concretely, AK must
// be sufficiently protected to at least resist a brute-force attack for the
// time between AK being exchanged and the handshake being fulfilled.
//
// A PID must be unique and not be re-used for a specific AK.
syntax = "proto3";
package rendezvous;
option java_package = "ch.threema.protobuf.d2d.rendezvous";
// Contains the data necessary to initialise a 1:1 connection between two
// devices.
//
// When creating this message, run the following sub-steps simultaneously and
// wait for them to finish:
//
// 1. If the device is able to create a TCP server socket:
// 1. Bind to _any_ IP address with a random port number. Silently ignore
// failures.
// 2. If successful, let `addresses` be the list of available IP addresses on
// network interfaces the server has been bound to.
// 3. Drop any loopback and duplicate IP addresses from `addresses`.
// 4. Drop link-local IPv6 addresses associated to interfaces that only
// provide link-local IPv6 addresses.
// 5. Sort `addresses` in the following way, highest priority first:
// 1. IP addresses on unmetered, fast networks
// 2. IP addresses on unmetered, slow networks
// 3. IP addresses on metered, fast networks
// 4. Any other addresses
// 6. Complete the subroutine and provide `addresses` and other necessary
// data in the `direct_tcp_server` field.
// 2. Connect to a WebSocket relay server:
// 1. Generate a random 32 byte hex-encoded rendezvous path.
// 2. Connect to the WebSocket relay server URL as provided by the context
// with the generated hex-encoded rendezvous path.
// 3. Once connected, complete the subroutine and provide the necessary data
// in the `relayed_web_socket` field.
//
// When receiving this message:
//
// 1. If `version` is unsupported, abort these steps.
// 2. If any `path_id` is not unique, abort these steps.
// 3. If the device is able to create a TCP client connection:
// 1. Let `addresses` be the IP addresses of `direct_tcp_server`.
// 2. Filter `addresses` by discarding IPs with unsupported families (e.g. if
// the device has no IPv6 address, drop any IPv6 addresses).
// 3. For each IP address in `addresses`:
// 1. Connect to the given IP address in the background.
// 2. Wait 100ms.
// 4. Connect to the provided relayed WebSocket server in the background.
// 5. On each successful direct or relayed connection made in the background,
// forward an event to the upper-layer protocol in order for it to select one
// of the paths for nomination.
message RendezvousInit {
enum Version {
// Initial version.
V1_0 = 0;
}
Version version = 1;
// 32 byte ephemeral secret Authentication Key (AK).
bytes ak = 2;
// Network cost of an interface
enum NetworkCost {
// It is unknown whether the interface is metered or unmetered
UNKNOWN = 0;
// The interface is unmetered
UNMETERED = 1;
// The interface is metered
METERED = 2;
}
// Relayed WebSocket path
message RelayedWebSocket {
// Unique Path ID (PID) of the path
uint32 path_id = 1;
// Network cost
NetworkCost network_cost = 2;
// Full URL to the WebSocket server with a random 32 byte hex-encoded
// rendezvous path. Must begin with `wss://`.
string url = 3;
}
RelayedWebSocket relayed_web_socket = 3;
// Direct path to a TCP server created by the initiator
message DirectTcpServer {
// Random 16 bit port. Values greater than 65535 are invalid.
uint32 port = 1;
// List of associated IP addresses. Each IP address creates its own path.
repeated IpAddress ip_addresses = 2;
// An IP address
message IpAddress {
// Unique Path ID (PID) of the path
uint32 path_id = 1;
// Network cost
NetworkCost network_cost = 2;
// IPv4 or IPv6 address
string ip = 3;
}
}
DirectTcpServer direct_tcp_server = 4;
}
// Messages required for the initial lock-step handshake between RRD and RID.
message Handshake {
// Handshake messages from RRD to RID.
message RrdToRid {
// Initial message from RRD containing its authentication challenge,
// encrypted by RRD's encryption scheme with RRDAK.
message Hello {
// 16 byte random authentication challenge for RID.
bytes challenge = 1;
// 32 byte ephemeral public key (`ETK.public`).
bytes etk = 2;
}
// Final message from RRD responding to RID's authentication challenge,
// encrypted by RRD's encryption scheme with RRDAK.
//
// When receiving this message:
//
// 1. If the challenge `response` from RRD does not match the challenge sent
// by RID, close the connection with a protocol error (WS: `4000`) and
// abort these steps.
message Auth {
// 16 byte repeated authentication challenge from RRD.
bytes response = 1;
}
}
// Handshake messages from RID to RRD.
message RidToRrd {
// Initial message from RID responding to RRD's authentication challenge and
// containing RID's authentication challenge, encrypted by RID's encryption
// scheme with RIDAK.
//
// When receiving this message:
//
// 1. If the challenge `response` from RID does not match the challenge sent
// by RRD, close the connection with a protocol error (WS: `4000`) and
// abort these steps.
message AuthHello {
// 16 byte repeated authentication challenge from RRD.
bytes response = 1;
// 16 byte random authentication challenge for RRD.
bytes challenge = 2;
// 32 byte ephemeral public key (`ETK.public`).
bytes etk = 3;
}
}
}
// Nominates the path. The upper-layer protocol defines whether RID or RRD may
// nominate and is encrypted by the respective encryption scheme with RIDTK or
// RRDTK.
//
// When receiving this message:
//
// 1. If the sender was not eligible to `Nominate`, close the connection with a
// protocol error (WS: `4000`) and abort these steps.
// 2. Close all other pending or established connection paths (WS: `1000`).¹
//
// ¹: Closing other paths is only triggered by the receiver as it may otherwise
// lead to a race between nomination and close detection.
message Nominate {}