Skip to content

ShadowSocks

Dmitry Gusarov edited this page Feb 10, 2020 · 11 revisions

I've been searching for Shadow Socks documentation for a while. Unfortunately there is no detailed spec, so, after successful intensive reverse-engineering I'll try to cover the spec here.

Crypto Stream Layer

Crypto stream is a first layer in Shadow Socks conncetion. Crypto stream looks like this:

For chacha20:

[8 bytes nonce][encrypted data....]

For chacha20-ietf

[12 bytes nonce][encrypted data....]

When you are going to write chacha20 encrypted data, first you send 8 bytes of the nonce of your encrypter. When you are reading the stream, first you will get a 8 bytes nonce of remote encrypter to be able to decrypt this.

Algorythm state

Symmetric algorythms have an IV (Initialization Vector) and Key (Secret Preshared Part). IV is a public while key is private.

ChaCha20 IV contains Nonce and Counter. Strictly speaking nonce is a big number that includes counter, to make every transfer unique, but only counter is actually ticking and starts from 0 which allows to reduce nonce exchange to smaller size, so all text about ChaCha20 separate static nonce from dynamic counter.

To start encryption both parties must initialize algorithm same way. You must have same IV (Nonce, Counter) and pre-shared Key.

Key usually have a fixed size, e.g. 32 bytes for ChaCha, but to have efficient key with easy sharing, passwords are usually used. To get 32 byte key out of a password, Key Derivation Function exists. Using multi-pass sliding hash function it allows to generate efficient key out of small password. Use ChaCha20.Kdf("p@$$word") to generate key out of password.

Counter - encrypted stream starts from 0 counter and counter increments after every block. For ChaCha this is every 64 bytes. After current block finished, counter increments and the next block will be encrypted using new context, so it will be completely different.

Nonce - to initialize public nonce same way, both parties performs nonce exchange at the beginning.

Shadow Socks layer

After the nonce you should send all the data via encryption. But what is the data inside encrypted stream? This is a tiny part of SOCKS5 protocol. Specifically, there is no Auth negotiation, there is no Stream Command, the only thing that you are sending is an Address in a SOCKS5 manner.

[AddressType][Address][Port]

Example IPv4:

0x01 - Address Type = IPv4
0x7F - 127
0x00 - 0
0x00 - 0
0x01 - 1
0x00 - Port 80 (High 0)
0x50 - Port 80 (Low 80)

Same for IPv6 (address type 0x04) but with 16 bytes address.

Example DNS:

[0x03][1 byte - LEN][n bytes domain name][2 bytes port]

0x03 - Address Type = Domain Name
0xLL - Name Length (1-255)
0xSS - Domain Name
.... - ....
0xSS - Domain Name
0x00 - Port 80 (High 0)
0x50 - Port 80 (Low 80)

And this message is not expecting any confirmation like in socks. So, right after you have send address, you should consider stream established to the target machine! If there is any error - Shadow Socks Server will disconnect you without any notice.

Performance consideration

From the very beginning I decided to have a buffer stream for shadow socks. There is no need to send Nonce, then send encrypted address.. instead I'm buffering all of this and only sending first network packet when client actually writes data. So, first network packet contains it all! Nonce, Address header and the first chunk of data.

Clone this wiki locally