-
Notifications
You must be signed in to change notification settings - Fork 310
Steganography and encryption
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.
- 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.
- 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 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.
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
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.
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.
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...