Skip to content

Steganography and encryption

Andy Wang edited this page Aug 8, 2019 · 14 revisions

Cloak works fundamentally by shaping whatever traffic is sent through it as HTTPS traffic. This would have been a trivial task if Cloak simply tunnels the traffic through TCP port 443 and add the TLS headers. This would be able to trick simple classifiers like the one Wireshark uses, which looks only for fixed byte patterns and magic numbers; this is perhaps what most commercial firewalls and ISPs in non-authoritarian countries do. However since our state-level adversaries have been known to employ sophisticated deep-packet-inspection techniques looks for "fingerprints" throughout an entire network session, it is a non-trivial task to defeat these measures.

Attack vectors

Passive

  • Port number correlation. For instance, TCP/UDP 1194 is the default port for OpenVPN, and rarely used by anything else. Firewalls can simply block these ports with little collateral damage.
  • Simple protocol recognition, such as the handshake protocol of SOCKS5.
  • Deep packet inspection. Analysing the packet contents with more sophisticated methods to identify its protocol
  • Traffic pattern recognition. Correlating metadata such as packet timing and packet size with specific proxy protocols.

Active/intrusive

  • Probing. Sending carefully constructed packets to suspected proxy servers to further identify them 1.
  • Traffic modification. In cases where the proxy protocol did not use authenticated encryption (such as early versions of Shadowsocks), a MITM may alter bytes at particular positions (such as the bytes representing content length) to trigger identifiable reactions from the proxy server 2.

Cloak aims to address all of these attack vectors except for traffic pattern recognition, one which is difficult to implement by the adversaries, and the countermeasure of which may introduce significant overheads and latency.

A chronology of a Cloak session

Configurations

Server

A 16-byte long UID is generated by the server and distributed to one user through a secure channel.

A pair of Curve25519 public and private key is generated by the server. They will be known as static public key and static private key. The static private key is kept secret on the server. The static public key is distributed publicly.

Redirection address is an IP address with TCP port. If the server determines any incoming connection does not belong to an authorised Cloak client user, the server will serve as a transparent proxy between this incoming connection and the redirection address.

Client

The user decides the proxy method they wish to use, which is an ASCII string of maximally 12 characters. This is normally the name of the underlying proxy protocol the client wishes to use, such as "openvpn".

The user decides the encryption method they wish to use. In the configuration file this can be either plain, aes-gcm or chacha20-poly1305. This is parsed as a single byte, which the client and the server has an agreement (hard-coded) on what value represents which algorithm.

The user decides the server name, which is a domain name. This will be transmitted in plaintext over the wire, and it's what the client would like the firewall to believe the client is visiting. Therefore it most be an innocent, unblocked domain name

Construction of the first message by a Cloak client

A 32-bit unsigned integer session id is generated.

One or several TCP connection is established with the Cloak server. The amount of TCP connections is determined by client-side configuration. Each TCP connection undergoes its own handshake.

For each TCP connection

Another pair of Curve25519 public and private key is generated by the client. They will be known as ephemeral public key and ephemeral private key.

The client computes scalar multiplication from ephemeral private key and static public key, generating a 32-byte long shared secret.

A authentication data field is then constructed:

UID Proxy Method Encryption Method Timestamp Session Id null bytes
16 bytes 12 bytes 1 byte 8 bytes 4 bytes 7 bytes

The null bytes are reserved for future use. This field is 48 bytes long in total.

The ephemeral public key is then marshalled into a 32-byte long representation.

Authentication data is then encrypted using AES-GCM (AES-GCM is used regardless of encryption method), with the first 12 bytes of the marshalled ephemeral public key as nonce, and the 32-byte shared secret as key. This will produce a 48-byte long authentication ciphertext followed by a 16-byte long authentication tag.

A standard TLS1.3 ClientHello message is composed. Since different applications and browsers add in slightly different data to their ClientHello messages, some particular fields (such as Cipher Suites) can be used for "fingerprinting" the application. Cloak will imitate the "fingerprint" of Chrome and Firefox.

The Random field of ClientHello is substituted with the marshalled ephemeral public key.

The Session ID field of ClientHello is substituted with the first 32 bytes of authentication ciphertext.

The "TLS extension" field Server Name Indication is generated according to the server name set in the configuration file.

The extension field Key Share will have a Key Share Entry for Group x25519 (the identifiers are specified in RFC 8446). The Key Exchange field is substituted with the remaining 16 bytes of authentication ciphertext followed by the 16 byte authentication tag

This ClientHello message is sent off to the server through this TCP connection.

Upon the establishment of a TCP connection on the server side

The server will attempt to read a full TLS message according to the length specified in its "record layer". If the data does not have a valid "record layer", or if the server is waiting too long to receive the full length of data, the server will close the connection.

The server will attempt to parse the first TLS message as a ClientHello message. If this message is not a ClientHello, or if the message is malformed, the server will send the full TLS message to redirection address and turn into a transparent proxy between redirection address and the connection originator. Such action will be referred to as "rejecting" below. Rejection will always send the full first TLS message to redirection address first regardless of when the rejection happens. Otherwise the ClientHello is unmarshalled into an object.

The Random field of the ClientHello message is looked up in its used random cache. If there is a hit, it means the ClientHello message has been replayed, the connection is then rejected.

The Random field is then unmarshalled as the ephemeral public key. The server computes scalar multiplication from its static private key and the ephemeral public key to derive the 32-byte shared secret.

The server reconstructs the authentication ciphertext from Session ID field and the first 16 bytes of Group x25519 entry in the "TLS extension" field. If the said extension field is absent, the connection is rejected. Otherwise it then retrieves the authentication tag from the last 16 bytes of Group x25519 entry.

The authentication ciphertext and the authentication tag is decrypted and authenticated using AES-GCM. If the authentication fails, the connection is rejected. Otherwise the authentication data is obtained in plaintext.

The authentication data is unmarshalled according to its construction method mentioned above. This gives the server the UID, proxy method, encryption method, timestamp and session id.

If the timestamp is outside of server's time plus or minus the tolerance, the connection is rejected.

TODO...

Clone this wiki locally