syntax = "proto2"; // TODO: We're using proto2 because it's the default on Ubuntu 16.04. // At some point we will want to migrate to proto3, but we are not // using any proto3 features yet. package tapdance; enum KeyType { AES_GCM_128 = 90; AES_GCM_256 = 91; // not supported atm } message PubKey { // A public key, as used by the station. optional bytes key = 1; optional KeyType type = 2; } message TLSDecoySpec { // The hostname/SNI to use for this host // // The hostname is the only required field, although other // fields are expected to be present in most cases. optional string hostname = 1; // The 32-bit ipv4 address, in network byte order // // If the IPv4 address is absent, then it may be resolved via // DNS by the client, or the client may discard this decoy spec // if local DNS is untrusted, or the service may be multihomed. optional fixed32 ipv4addr = 2; // The 128-bit ipv6 address, in network byte order optional bytes ipv6addr = 6; // The Tapdance station public key to use when contacting this // decoy // // If omitted, the default station public key (if any) is used. optional PubKey pubkey = 3; // The maximum duration, in milliseconds, to maintain an open // connection to this decoy (because the decoy may close the // connection itself after this length of time) // // If omitted, a default of 30,000 milliseconds is assumed. optional uint32 timeout = 4; // The maximum TCP window size to attempt to use for this decoy. // // If omitted, a default of 15360 is assumed. // // TODO: the default is based on the current heuristic of only // using decoys that permit windows of 15KB or larger. If this // heuristic changes, then this default doesn't make sense. optional uint32 tcpwin = 5; } // In version 1, the request is very simple: when // the client sends a MSG_PROTO to the station, if the // generation number is present, then this request includes // (in addition to whatever other operations are part of the // request) a request for the station to send a copy of // the current decoy set that has a generation number greater // than the generation number in its request. // // If the response contains a DecoyListUpdate with a generation number equal // to that which the client sent, then the client is "caught up" with // the station and the response contains no new information // (and all other fields may be omitted or empty). Otherwise, // the station will send the latest configuration information, // along with its generation number. // // The station can also send ClientConf messages // (as part of Station2Client messages) whenever it wants. // The client is expected to react as if it had requested // such messages -- possibly by ignoring them, if the client // is already up-to-date according to the generation number. message ClientConf { optional DecoyList decoy_list = 1; optional uint32 generation = 2; optional PubKey default_pubkey = 3; optional PhantomSubnetsList phantom_subnets_list = 4; optional PubKey conjure_pubkey = 5; } message DecoyList { repeated TLSDecoySpec tls_decoys = 1; } message PhantomSubnetsList{ repeated PhantomSubnets weighted_subnets = 1; } message PhantomSubnets{ optional uint32 weight = 1; repeated string subnets = 2; } // State transitions of the client enum C2S_Transition { C2S_NO_CHANGE = 0; C2S_SESSION_INIT = 1; // connect me to squid C2S_SESSION_COVERT_INIT = 11; // connect me to provided covert C2S_EXPECT_RECONNECT = 2; C2S_SESSION_CLOSE = 3; C2S_YIELD_UPLOAD = 4; C2S_ACQUIRE_UPLOAD = 5; C2S_EXPECT_UPLOADONLY_RECONN = 6; C2S_ERROR = 255; } // State transitions of the server enum S2C_Transition { S2C_NO_CHANGE = 0; S2C_SESSION_INIT = 1; // connected to squid S2C_SESSION_COVERT_INIT = 11; // connected to covert host S2C_CONFIRM_RECONNECT = 2; S2C_SESSION_CLOSE = 3; // TODO should probably also allow EXPECT_RECONNECT here, for DittoTap S2C_ERROR = 255; } // Should accompany all S2C_ERROR messages. enum ErrorReasonS2C { NO_ERROR = 0; COVERT_STREAM = 1; // Squid TCP connection broke CLIENT_REPORTED = 2; // You told me something was wrong, client CLIENT_PROTOCOL = 3; // You messed up, client (e.g. sent a bad protobuf) STATION_INTERNAL = 4; // I broke DECOY_OVERLOAD = 5; // Everything's fine, but don't use this decoy right now CLIENT_STREAM = 100; // My stream to you broke. (This is impossible to send) CLIENT_TIMEOUT = 101; // You never came back. (This is impossible to send) } enum TransportType { Null = 0; Min = 1; // Send a 32-byte HMAC id to let the station distinguish registrations to same host Obfs4 = 2; // Not implemented yet? Webrtc = 99; // UDP transport: WebRTC DataChannel } // Deflated ICE Candidate by seed2sdp package message WebRTCICECandidate { // IP is represented in its 16-byte form required uint64 ip_upper = 1; required uint64 ip_lower = 2; // Composed info includes port, tcptype (unset if not tcp), candidate type (host, srflx, prflx), protocol (TCP/UDP), and component (RTP/RTCP) required uint32 composed_info = 3; } // Deflated SDP for WebRTC by seed2sdp package message WebRTCSDP { required uint32 type = 1; repeated WebRTCICECandidate candidates = 2; // there could be multiple candidates } // WebRTCSignal includes a deflated SDP and a seed message WebRTCSignal { required string seed = 1; required WebRTCSDP sdp = 2; } message StationToClient { // Should accompany (at least) SESSION_INIT and CONFIRM_RECONNECT. optional uint32 protocol_version = 1; // There might be a state transition. May be absent; absence should be // treated identically to NO_CHANGE. optional S2C_Transition state_transition = 2; // The station can send client config info piggybacked // on any message, as it sees fit optional ClientConf config_info = 3; // If state_transition == S2C_ERROR, this field is the explanation. optional ErrorReasonS2C err_reason = 4; // Signals client to stop connecting for following amount of seconds optional uint32 tmp_backoff = 5; // Sent in SESSION_INIT, identifies the station that picked up optional string station_id = 6; // Random-sized junk to defeat packet size fingerprinting. optional bytes padding = 100; } message RegistrationFlags { optional bool upload_only = 1; optional bool dark_decoy = 2; optional bool proxy_header = 3; optional bool use_TIL = 4; optional bool prescanned = 5; } message ClientToStation { optional uint32 protocol_version = 1; // The client reports its decoy list's version number here, which the // station can use to decide whether to send an updated one. The station // should always send a list if this field is set to 0. optional uint32 decoy_list_generation = 2; optional C2S_Transition state_transition = 3; // The position in the overall session's upload sequence where the current // YIELD=>ACQUIRE switchover is happening. optional uint64 upload_sync = 4; // List of decoys that client have unsuccessfully tried in current session. // Could be sent in chunks repeated string failed_decoys = 10; optional SessionStats stats = 11; // NullTransport, MinTransport, Obfs4Transport, etc. Transport type we want from phantom proxy optional TransportType transport = 12; // Station is only required to check this variable during session initialization. // If set, station must facilitate connection to said target by itself, i.e. write into squid // socket an HTTP/SOCKS/any other connection request. // covert_address must have exactly one ':' colon, that separates host (literal IP address or // resolvable hostname) and port // TODO: make it required for initialization, and stop connecting any client straight to squid? optional string covert_address = 20; // Used in dark decoys to signal which dark decoy it will connect to. optional string masked_decoy_server_name = 21; // Used to indicate to server if client is registering v4, v6 or both optional bool v6_support = 22; optional bool v4_support = 23; // A collection of optional flags for the registration. optional RegistrationFlags flags = 24; // Transport Extensions optional WebRTCSignal webrtc_signal = 31; // Random-sized junk to defeat packet size fingerprinting. optional bytes padding = 100; } enum RegistrationSource { Unspecified = 0; Detector = 1; API = 2; DetectorPrescan = 3; BidirectionalAPI = 4; } message C2SWrapper { optional bytes shared_secret = 1; optional ClientToStation registration_payload = 3; optional RegistrationSource registration_source = 4; // client source address when receiving a registration optional bytes registration_address = 6; // Decoy address used when registering over Decoy registrar optional bytes decoy_address = 7; optional RegistrationResponse registration_response = 8; } message SessionStats { optional uint32 failed_decoys_amount = 20; // how many decoys were tried before success // Timings below are in milliseconds // Applicable to whole session: optional uint32 total_time_to_connect = 31; // includes failed attempts // Last (i.e. successful) decoy: optional uint32 rtt_to_station = 33; // measured during initial handshake optional uint32 tls_to_decoy = 38; // includes tcp to decoy optional uint32 tcp_to_decoy = 39; // measured when establishing tcp connection to decot } message StationToDetector { optional string phantom_ip = 1; optional string client_ip = 2; optional uint64 timeout_ns = 3; } // Adding message response from Station to Client for birdirectional API message RegistrationResponse { optional fixed32 ipv4addr = 1; // The 128-bit ipv6 address, in network byte order optional bytes ipv6addr = 2; // Respond with randomized port optional uint32 port = 3; // Future: station provides client with secret, want chanel present // Leave null for now optional bytes serverRandom = 4; // If registration wrong, populate this error string optional string error = 5; // ClientConf field (optional) optional ClientConf clientConf = 6; }