diff --git a/404.html b/404.html index 65fb35b..b1a2bb5 100644 --- a/404.html +++ b/404.html @@ -14,7 +14,7 @@
Skip to content

404

PAGE NOT FOUND

But if you don't change your direction, and if you keep looking, you may end up where you are heading.

This website is released under the MIT License.

- diff --git a/assets/doc_advanced.md.aaab6733.js b/assets/doc_advanced.md.dcc3bff7.js similarity index 97% rename from assets/doc_advanced.md.aaab6733.js rename to assets/doc_advanced.md.dcc3bff7.js index 70d23b0..e94628e 100644 --- a/assets/doc_advanced.md.aaab6733.js +++ b/assets/doc_advanced.md.dcc3bff7.js @@ -1,4 +1,4 @@ -import{_ as s,c as e,o as a,R as n}from"./chunks/framework.bdd825cc.js";const u=JSON.parse('{"title":"Advanced configurations","description":"","frontmatter":{},"headers":[],"relativePath":"doc/advanced.md","filePath":"doc/advanced.md","lastUpdated":1692058814000}'),o={name:"doc/advanced.md"},t=n(`

Advanced configurations

Optimize the shadowsocks server on Linux

First of all, upgrade your Linux kernel to 3.5 or later.

Step 1, increase the maximum number of open file descriptors

To handle thousands of concurrent TCP connections, we should increase the limit of file descriptors opened.

Edit the limits.conf

bash
vi /etc/security/limits.conf

Add these two lines

* soft nofile 51200
+import{_ as s,c as e,o as a,R as n}from"./chunks/framework.bdd825cc.js";const u=JSON.parse('{"title":"Advanced configurations","description":"","frontmatter":{},"headers":[],"relativePath":"doc/advanced.md","filePath":"doc/advanced.md","lastUpdated":1694046893000}'),o={name:"doc/advanced.md"},t=n(`

Advanced configurations

Optimize the shadowsocks server on Linux

First of all, upgrade your Linux kernel to 3.5 or later.

Step 1, increase the maximum number of open file descriptors

To handle thousands of concurrent TCP connections, we should increase the limit of file descriptors opened.

Edit the limits.conf

bash
vi /etc/security/limits.conf

Add these two lines

* soft nofile 51200
 * hard nofile 51200
 
 # for server running in root:
diff --git a/assets/doc_advanced.md.aaab6733.lean.js b/assets/doc_advanced.md.dcc3bff7.lean.js
similarity index 70%
rename from assets/doc_advanced.md.aaab6733.lean.js
rename to assets/doc_advanced.md.dcc3bff7.lean.js
index 00fa777..ba8a4b6 100644
--- a/assets/doc_advanced.md.aaab6733.lean.js
+++ b/assets/doc_advanced.md.dcc3bff7.lean.js
@@ -1 +1 @@
-import{_ as s,c as e,o as a,R as n}from"./chunks/framework.bdd825cc.js";const u=JSON.parse('{"title":"Advanced configurations","description":"","frontmatter":{},"headers":[],"relativePath":"doc/advanced.md","filePath":"doc/advanced.md","lastUpdated":1692058814000}'),o={name:"doc/advanced.md"},t=n("",17),p=[t];function l(c,i,r,d,h,C){return a(),e("div",null,p)}const A=s(o,[["render",l]]);export{u as __pageData,A as default};
+import{_ as s,c as e,o as a,R as n}from"./chunks/framework.bdd825cc.js";const u=JSON.parse('{"title":"Advanced configurations","description":"","frontmatter":{},"headers":[],"relativePath":"doc/advanced.md","filePath":"doc/advanced.md","lastUpdated":1694046893000}'),o={name:"doc/advanced.md"},t=n("",17),p=[t];function l(c,i,r,d,h,C){return a(),e("div",null,p)}const A=s(o,[["render",l]]);export{u as __pageData,A as default};
diff --git a/assets/doc_aead.md.04b1947a.js b/assets/doc_aead.md.1a1aa961.js
similarity index 99%
rename from assets/doc_aead.md.04b1947a.js
rename to assets/doc_aead.md.1a1aa961.js
index 61e31cf..54a82d9 100644
--- a/assets/doc_aead.md.04b1947a.js
+++ b/assets/doc_aead.md.1a1aa961.js
@@ -1 +1 @@
-import{_ as e,c as t,o as a,R as r}from"./chunks/framework.bdd825cc.js";const y=JSON.parse('{"title":"AEAD ciphers","description":"","frontmatter":{},"headers":[],"relativePath":"doc/aead.md","filePath":"doc/aead.md","lastUpdated":1692058814000}'),n={name:"doc/aead.md"},s=r('

AEAD ciphers

AEAD stands for Authenticated Encryption with Associated Data. AEAD ciphers simultaneously provide confidentiality, integrity, and authenticity. They have excellent performance and power efficiency on modern hardware. Users should use AEAD ciphers whenever possible.

The following AEAD ciphers are recommended. Compliant Shadowsocks implementations must support AEAD_CHACHA20_POLY1305. Implementations for devices with hardware AES acceleration should also implement AEAD_AES_128_GCM and AEAD_AES_256_GCM.

NameAliasKey SizeSalt SizeNonce SizeTag Size
AEAD_CHACHA20_POLY1305chacha20-ietf-poly130532321216
AEAD_AES_256_GCMaes-256-gcm32321216
AEAD_AES_128_GCMaes-128-gcm16161216

Please refer to IANA AEAD registry for naming scheme and specification.

The way Shadowsocks using AEAD ciphers is specified in SIP004 and amended in SIP007. SIP004 was proposed by @Mygod with design inspirations from @wongsyrone, @Noisyfox and @breakwa11. SIP007 was proposed by @riobard with input from @madeye, @Mygod, @wongsyrone, and many others.

Key Derivation

The master key can be input directly from user or generated from a password. The key derivation is still following EVP_BytesToKey(3) in OpenSSL. The detailed spec can be found here

HKDF_SHA1 is a function that takes a secret key, a non-secret salt, an info string, and produces a subkey that is cryptographically strong even if the input secret key is weak.

HKDF_SHA1(key, salt, info) => subkey

The info string binds the generated subkey to a specific application context. In our case, it must be the string "ss-subkey" without quotes.

We derive a per-session subkey from a pre-shared master key using HKDF_SHA1. Salt must be unique through the entire life of the pre-shared master key.

Authenticated Encryption/Decryption

AE_encrypt is a function that takes a secret key, a non-secret nonce, a message, and produces ciphertext and authentication tag. Nonce must be unique for a given key in each invocation.

AE_encrypt(key, nonce, message) => (ciphertext, tag)

AE_decrypt is a function that takes a secret key, non-secret nonce, ciphertext, authentication tag, and produces original message. If any of the input is tampered with, decryption will fail.

AE_decrypt(key, nonce, ciphertext, tag) => message

TCP

An AEAD encrypted TCP stream starts with a randomly generated salt to derive the per-session subkey, followed by any number of encrypted chunks. Each chunk has the following structure:

[encrypted payload length][length tag][encrypted payload][payload tag]

Payload length is a 2-byte big-endian unsigned integer capped at 0x3FFF. The higher two bits are reserved and must be set to zero. Payload is therefore limited to 16*1024 - 1 bytes.

The first AEAD encrypt/decrypt operation uses a counting nonce starting from 0. After each encrypt/decrypt operation, the nonce is incremented by one as if it were an unsigned little-endian integer. Note that each TCP chunk involves two AEAD encrypt/decrypt operation: one for the payload length, and one for the payload. Therefore each chunk increases the nonce twice.

UDP

An AEAD encrypted UDP packet has the following structure

[salt][encrypted payload][tag]

The salt is used to derive the per-session subkey and must be generated randomly to ensure uniqueness. Each UDP packet is encrypted/decrypted independently, using the derived subkey and a nonce with all zero bytes.

',26),o=[s];function i(d,c,p,h,l,u){return a(),t("div",null,o)}const f=e(n,[["render",i]]);export{y as __pageData,f as default}; +import{_ as e,c as t,o as a,R as r}from"./chunks/framework.bdd825cc.js";const y=JSON.parse('{"title":"AEAD ciphers","description":"","frontmatter":{},"headers":[],"relativePath":"doc/aead.md","filePath":"doc/aead.md","lastUpdated":1694046893000}'),n={name:"doc/aead.md"},s=r('

AEAD ciphers

AEAD stands for Authenticated Encryption with Associated Data. AEAD ciphers simultaneously provide confidentiality, integrity, and authenticity. They have excellent performance and power efficiency on modern hardware. Users should use AEAD ciphers whenever possible.

The following AEAD ciphers are recommended. Compliant Shadowsocks implementations must support AEAD_CHACHA20_POLY1305. Implementations for devices with hardware AES acceleration should also implement AEAD_AES_128_GCM and AEAD_AES_256_GCM.

NameAliasKey SizeSalt SizeNonce SizeTag Size
AEAD_CHACHA20_POLY1305chacha20-ietf-poly130532321216
AEAD_AES_256_GCMaes-256-gcm32321216
AEAD_AES_128_GCMaes-128-gcm16161216

Please refer to IANA AEAD registry for naming scheme and specification.

The way Shadowsocks using AEAD ciphers is specified in SIP004 and amended in SIP007. SIP004 was proposed by @Mygod with design inspirations from @wongsyrone, @Noisyfox and @breakwa11. SIP007 was proposed by @riobard with input from @madeye, @Mygod, @wongsyrone, and many others.

Key Derivation

The master key can be input directly from user or generated from a password. The key derivation is still following EVP_BytesToKey(3) in OpenSSL. The detailed spec can be found here

HKDF_SHA1 is a function that takes a secret key, a non-secret salt, an info string, and produces a subkey that is cryptographically strong even if the input secret key is weak.

HKDF_SHA1(key, salt, info) => subkey

The info string binds the generated subkey to a specific application context. In our case, it must be the string "ss-subkey" without quotes.

We derive a per-session subkey from a pre-shared master key using HKDF_SHA1. Salt must be unique through the entire life of the pre-shared master key.

Authenticated Encryption/Decryption

AE_encrypt is a function that takes a secret key, a non-secret nonce, a message, and produces ciphertext and authentication tag. Nonce must be unique for a given key in each invocation.

AE_encrypt(key, nonce, message) => (ciphertext, tag)

AE_decrypt is a function that takes a secret key, non-secret nonce, ciphertext, authentication tag, and produces original message. If any of the input is tampered with, decryption will fail.

AE_decrypt(key, nonce, ciphertext, tag) => message

TCP

An AEAD encrypted TCP stream starts with a randomly generated salt to derive the per-session subkey, followed by any number of encrypted chunks. Each chunk has the following structure:

[encrypted payload length][length tag][encrypted payload][payload tag]

Payload length is a 2-byte big-endian unsigned integer capped at 0x3FFF. The higher two bits are reserved and must be set to zero. Payload is therefore limited to 16*1024 - 1 bytes.

The first AEAD encrypt/decrypt operation uses a counting nonce starting from 0. After each encrypt/decrypt operation, the nonce is incremented by one as if it were an unsigned little-endian integer. Note that each TCP chunk involves two AEAD encrypt/decrypt operation: one for the payload length, and one for the payload. Therefore each chunk increases the nonce twice.

UDP

An AEAD encrypted UDP packet has the following structure

[salt][encrypted payload][tag]

The salt is used to derive the per-session subkey and must be generated randomly to ensure uniqueness. Each UDP packet is encrypted/decrypted independently, using the derived subkey and a nonce with all zero bytes.

',26),o=[s];function i(d,c,p,h,l,u){return a(),t("div",null,o)}const f=e(n,[["render",i]]);export{y as __pageData,f as default}; diff --git a/assets/doc_aead.md.04b1947a.lean.js b/assets/doc_aead.md.1a1aa961.lean.js similarity index 84% rename from assets/doc_aead.md.04b1947a.lean.js rename to assets/doc_aead.md.1a1aa961.lean.js index aa9c143..febb198 100644 --- a/assets/doc_aead.md.04b1947a.lean.js +++ b/assets/doc_aead.md.1a1aa961.lean.js @@ -1 +1 @@ -import{_ as e,c as t,o as a,R as r}from"./chunks/framework.bdd825cc.js";const y=JSON.parse('{"title":"AEAD ciphers","description":"","frontmatter":{},"headers":[],"relativePath":"doc/aead.md","filePath":"doc/aead.md","lastUpdated":1692058814000}'),n={name:"doc/aead.md"},s=r("",26),o=[s];function i(d,c,p,h,l,u){return a(),t("div",null,o)}const f=e(n,[["render",i]]);export{y as __pageData,f as default}; +import{_ as e,c as t,o as a,R as r}from"./chunks/framework.bdd825cc.js";const y=JSON.parse('{"title":"AEAD ciphers","description":"","frontmatter":{},"headers":[],"relativePath":"doc/aead.md","filePath":"doc/aead.md","lastUpdated":1694046893000}'),n={name:"doc/aead.md"},s=r("",26),o=[s];function i(d,c,p,h,l,u){return a(),t("div",null,o)}const f=e(n,[["render",i]]);export{y as __pageData,f as default}; diff --git a/assets/doc_configs.md.ffd0c0d9.js b/assets/doc_configs.md.dea687f7.js similarity index 94% rename from assets/doc_configs.md.ffd0c0d9.js rename to assets/doc_configs.md.dea687f7.js index d3083d1..cab5edd 100644 --- a/assets/doc_configs.md.ffd0c0d9.js +++ b/assets/doc_configs.md.dea687f7.js @@ -1,8 +1,8 @@ -import{_ as s,c as e,o as a,R as o}from"./chunks/framework.bdd825cc.js";const y=JSON.parse('{"title":"Config Format","description":"","frontmatter":{},"headers":[],"relativePath":"doc/configs.md","filePath":"doc/configs.md","lastUpdated":1692058814000}'),n={name:"doc/configs.md"},t=o(`

Config Format

Config File

Shadowsocks accepts JSON format configs like this:

json
{
+import{_ as s,c as e,o as a,R as o}from"./chunks/framework.bdd825cc.js";const y=JSON.parse('{"title":"Config Format","description":"","frontmatter":{},"headers":[],"relativePath":"doc/configs.md","filePath":"doc/configs.md","lastUpdated":1694046893000}'),n={name:"doc/configs.md"},t=o(`

Config Format

Config File

Shadowsocks accepts JSON format configs like this:

json
{
     "server":"my_server_ip",
     "server_port":8388,
     "local_port":1080,
     "password":"barfoo!",
     "method":"chacha20-ietf-poly1305"
 }

Explanation of each field:

  • server: your hostname or server IP (IPv4/IPv6).
  • server_port: server port number.
  • local_port: local port number.
  • password: a password used to encrypt transfer.
  • method: encryption method.

Encryption Method

The strongest option is an AEAD cipher. The recommended choice is "chacha20-ietf-poly1305" or "aes-256-gcm". Other stream ciphers are implemented but do not provide integrity and authenticity. Unless otherwise specified the encryption method defaults to "table", which is not secure.

URI and QR code

Shadowsocks for Android / iOS also accepts BASE64 encoded URI format configs:

ss://BASE64-ENCODED-STRING-WITHOUT-PADDING#TAG

Where the plain URI should be:

ss://method:password@hostname:port

Note that the above URI doesn't follow RFC3986. It means the password here should be plain text, not percent-encoded.

For example, we have a server at 192.168.100.1:8888 using bf-cfb encryption method and password test/!@#:. Then, with the plain URI ss://bf-cfb:test/!@#:@192.168.100.1:8888, we can generate the BASE64 encoded URI:

> console.log( "ss://" + btoa("bf-cfb:test/!@#:@192.168.100.1:8888") )
-ss://YmYtY2ZiOnRlc3QvIUAjOkAxOTIuMTY4LjEwMC4xOjg4ODg

To help organize and identify these URIs, you can append a tag after the BASE64 encoded string:

ss://YmYtY2ZiOnRlc3QvIUAjOkAxOTIuMTY4LjEwMC4xOjg4ODg#example-server

This URI can also be encoded to QR code. Then, just scan it with your Android / iOS devices:

SIP002

There is also a new URI scheme proposed in SIP002. Any client or server which supports SIP003 plugin should use SIP002 URI scheme instead.

`,21),l=[t];function p(c,r,i,d,h,u){return a(),e("div",null,l)}const F=s(n,[["render",p]]);export{y as __pageData,F as default}; +ss://YmYtY2ZiOnRlc3QvIUAjOkAxOTIuMTY4LjEwMC4xOjg4ODg

To help organize and identify these URIs, you can append a tag after the BASE64 encoded string:

ss://YmYtY2ZiOnRlc3QvIUAjOkAxOTIuMTY4LjEwMC4xOjg4ODg#example-server

This URI can also be encoded to QR code. Then, just scan it with your Android / iOS devices:

SIP002

There is also a new URI scheme proposed in SIP002. Any client or server which supports SIP003 plugin should use SIP002 URI scheme instead.

`,21),l=[t];function p(c,r,i,d,h,u){return a(),e("div",null,l)}const F=s(n,[["render",p]]);export{y as __pageData,F as default}; diff --git a/assets/doc_configs.md.ffd0c0d9.lean.js b/assets/doc_configs.md.dea687f7.lean.js similarity index 84% rename from assets/doc_configs.md.ffd0c0d9.lean.js rename to assets/doc_configs.md.dea687f7.lean.js index 20feffc..a25d0d5 100644 --- a/assets/doc_configs.md.ffd0c0d9.lean.js +++ b/assets/doc_configs.md.dea687f7.lean.js @@ -1 +1 @@ -import{_ as s,c as e,o as a,R as o}from"./chunks/framework.bdd825cc.js";const y=JSON.parse('{"title":"Config Format","description":"","frontmatter":{},"headers":[],"relativePath":"doc/configs.md","filePath":"doc/configs.md","lastUpdated":1692058814000}'),n={name:"doc/configs.md"},t=o("",21),l=[t];function p(c,r,i,d,h,u){return a(),e("div",null,l)}const F=s(n,[["render",p]]);export{y as __pageData,F as default}; +import{_ as s,c as e,o as a,R as o}from"./chunks/framework.bdd825cc.js";const y=JSON.parse('{"title":"Config Format","description":"","frontmatter":{},"headers":[],"relativePath":"doc/configs.md","filePath":"doc/configs.md","lastUpdated":1694046893000}'),n={name:"doc/configs.md"},t=o("",21),l=[t];function p(c,r,i,d,h,u){return a(),e("div",null,l)}const F=s(n,[["render",p]]);export{y as __pageData,F as default}; diff --git a/assets/doc_contributors.md.4e813132.js b/assets/doc_contributors.md.8415bd3e.js similarity index 95% rename from assets/doc_contributors.md.4e813132.js rename to assets/doc_contributors.md.8415bd3e.js index 41eabfe..f02fd21 100644 --- a/assets/doc_contributors.md.4e813132.js +++ b/assets/doc_contributors.md.8415bd3e.js @@ -1 +1 @@ -import{_ as r,c as e,o as t,R as o}from"./chunks/framework.bdd825cc.js";const u=JSON.parse('{"title":"Contributors","description":"","frontmatter":{},"headers":[],"relativePath":"doc/contributors.md","filePath":"doc/contributors.md","lastUpdated":1692058814000}'),a={name:"doc/contributors.md"},n=o('

Contributors

Core Contributors

@clowwindy

The creator of shadowsocks and the maintainer of shadowsocks-python/nodejs/gui/iOS.

@zonyitoo

The maintainer of shadowsocks-rust.

@cyfdecyf

The maintainer of shadowsocks-go and cow.

@madeye

The maintainer of shadowsocks-libev/android and this project site.

@linusyang

The maintainer of shadowsocks-libev and MobileShadowSocks.

@Mygod

The maintainer of shadowsocks-android.

@aa65535

The maintainer of openwrt-shadowsocks.

@librehat

The maintainer of shadowsocks-qt5 and libQtShadowsocks

Other Contributors

@ohdarling

The maintainer of GoAgentX.

@fqrouter

The maintainer of fqrouter.

',23),s=[n];function h(i,c,p,l,d,f){return t(),e("div",null,s)}const g=r(a,[["render",h]]);export{u as __pageData,g as default}; +import{_ as r,c as e,o as t,R as o}from"./chunks/framework.bdd825cc.js";const u=JSON.parse('{"title":"Contributors","description":"","frontmatter":{},"headers":[],"relativePath":"doc/contributors.md","filePath":"doc/contributors.md","lastUpdated":1694046893000}'),a={name:"doc/contributors.md"},n=o('

Contributors

Core Contributors

@clowwindy

The creator of shadowsocks and the maintainer of shadowsocks-python/nodejs/gui/iOS.

@zonyitoo

The maintainer of shadowsocks-rust.

@cyfdecyf

The maintainer of shadowsocks-go and cow.

@madeye

The maintainer of shadowsocks-libev/android and this project site.

@linusyang

The maintainer of shadowsocks-libev and MobileShadowSocks.

@Mygod

The maintainer of shadowsocks-android.

@aa65535

The maintainer of openwrt-shadowsocks.

@librehat

The maintainer of shadowsocks-qt5 and libQtShadowsocks

Other Contributors

@ohdarling

The maintainer of GoAgentX.

@fqrouter

The maintainer of fqrouter.

',23),s=[n];function h(i,c,p,l,d,f){return t(),e("div",null,s)}const g=r(a,[["render",h]]);export{u as __pageData,g as default}; diff --git a/assets/doc_contributors.md.4e813132.lean.js b/assets/doc_contributors.md.8415bd3e.lean.js similarity index 70% rename from assets/doc_contributors.md.4e813132.lean.js rename to assets/doc_contributors.md.8415bd3e.lean.js index d4f367d..ea9550a 100644 --- a/assets/doc_contributors.md.4e813132.lean.js +++ b/assets/doc_contributors.md.8415bd3e.lean.js @@ -1 +1 @@ -import{_ as r,c as e,o as t,R as o}from"./chunks/framework.bdd825cc.js";const u=JSON.parse('{"title":"Contributors","description":"","frontmatter":{},"headers":[],"relativePath":"doc/contributors.md","filePath":"doc/contributors.md","lastUpdated":1692058814000}'),a={name:"doc/contributors.md"},n=o("",23),s=[n];function h(i,c,p,l,d,f){return t(),e("div",null,s)}const g=r(a,[["render",h]]);export{u as __pageData,g as default}; +import{_ as r,c as e,o as t,R as o}from"./chunks/framework.bdd825cc.js";const u=JSON.parse('{"title":"Contributors","description":"","frontmatter":{},"headers":[],"relativePath":"doc/contributors.md","filePath":"doc/contributors.md","lastUpdated":1694046893000}'),a={name:"doc/contributors.md"},n=o("",23),s=[n];function h(i,c,p,l,d,f){return t(),e("div",null,s)}const g=r(a,[["render",h]]);export{u as __pageData,g as default}; diff --git a/assets/doc_deploying.md.c558b9e9.js b/assets/doc_deploying.md.09688cfb.js similarity index 99% rename from assets/doc_deploying.md.c558b9e9.js rename to assets/doc_deploying.md.09688cfb.js index 3a26baa..979e6ee 100644 --- a/assets/doc_deploying.md.c558b9e9.js +++ b/assets/doc_deploying.md.09688cfb.js @@ -1,4 +1,4 @@ -import{_ as s,c as a,o as e,R as o}from"./chunks/framework.bdd825cc.js";const y=JSON.parse('{"title":"Deploying","description":"","frontmatter":{},"headers":[],"relativePath":"doc/deploying.md","filePath":"doc/deploying.md","lastUpdated":1692058814000}'),n={name:"doc/deploying.md"},l=o(`

Deploying

Setup your own server

First, buy a server from any cloud provider. DigitalOcean is recommended by us:

DigitalOcean Referral Badge

Then, install Linux on your servers, Ubuntu 20.04 is recommended.

Python

shadowsocks-python is the initial version written by @clowwindy. It aims to provide a simple-to-use and easy-to-deploy implementation with basic features of shadowsocks.

PyPI

First, make sure you have Python 2.6 or 2.7.

bash
$ python --version
+import{_ as s,c as a,o as e,R as o}from"./chunks/framework.bdd825cc.js";const y=JSON.parse('{"title":"Deploying","description":"","frontmatter":{},"headers":[],"relativePath":"doc/deploying.md","filePath":"doc/deploying.md","lastUpdated":1694046893000}'),n={name:"doc/deploying.md"},l=o(`

Deploying

Setup your own server

First, buy a server from any cloud provider. DigitalOcean is recommended by us:

DigitalOcean Referral Badge

Then, install Linux on your servers, Ubuntu 20.04 is recommended.

Python

shadowsocks-python is the initial version written by @clowwindy. It aims to provide a simple-to-use and easy-to-deploy implementation with basic features of shadowsocks.

PyPI

First, make sure you have Python 2.6 or 2.7.

bash
$ python --version
 Python 2.6.8

Then install from PIP

bash
$ pip install shadowsocks

GitHub

Checkout the source codes and run the scripts directly.

https://github.com/shadowsocks

shadowsocks-python is licensed under the Apache License, Version 2.0.

Go

go-shadowsocks2 is the next-generation Shadowsocks in Go, maintained by @riobard, supersedes the discontinued shadowsocks-go.

GitHub

Use go get to install.

bash
$ go get -u -v github.com/shadowsocks/go-shadowsocks2

go-shadowsocks2 is licensed under the Apache License, Version 2.0.

Go from Outline

outline-ss-server is the shadowsocks implementation used by the Outline Server, but it can be used standalone. Main features:

  • Multiple users on a single port and multiple ports.
  • Whitebox monitoring of the service using prometheus.io
  • Live updates via config change + SIGHUP
  • Prohibits unsafe access to localhost ports and usage of non-AEAD ciphers

GitHub

Download pre-built binaries from the GitHub releases or build it from source:

go get github.com/Jigsaw-code/outline-ss-server
 $(go env GOPATH)/bin/outline-ss-server -config=config.yml -metrics=127.0.0.1:9091

outline-ss-server is licensed under the Apache License, Version 2.0.

C with libev

shadowsocks-libev is a lightweight and full featured port for embedded devices and low end boxes. It's a pure C implementation and has a very small footprint (several megabytes) for thousands of connections. This port is maintained by @madeye.

Debian/Ubuntu:

shadowsocks-libev is available in the official repository for Debian 9("Stretch"), unstable, Ubuntu 16.10 and later derivatives:

sudo apt update
 sudo apt install shadowsocks-libev

For Debian Jessie users, please install it from jessie-backports:

sudo sh -c 'printf "deb http://httpredir.debian.org/debian jessie-backports
diff --git a/assets/doc_deploying.md.c558b9e9.lean.js b/assets/doc_deploying.md.09688cfb.lean.js
similarity index 84%
rename from assets/doc_deploying.md.c558b9e9.lean.js
rename to assets/doc_deploying.md.09688cfb.lean.js
index 3adaad7..8b3870c 100644
--- a/assets/doc_deploying.md.c558b9e9.lean.js
+++ b/assets/doc_deploying.md.09688cfb.lean.js
@@ -1 +1 @@
-import{_ as s,c as a,o as e,R as o}from"./chunks/framework.bdd825cc.js";const y=JSON.parse('{"title":"Deploying","description":"","frontmatter":{},"headers":[],"relativePath":"doc/deploying.md","filePath":"doc/deploying.md","lastUpdated":1692058814000}'),n={name:"doc/deploying.md"},l=o("",65),t=[l];function p(r,i,c,h,d,C){return e(),a("div",null,t)}const b=s(n,[["render",p]]);export{y as __pageData,b as default};
+import{_ as s,c as a,o as e,R as o}from"./chunks/framework.bdd825cc.js";const y=JSON.parse('{"title":"Deploying","description":"","frontmatter":{},"headers":[],"relativePath":"doc/deploying.md","filePath":"doc/deploying.md","lastUpdated":1694046893000}'),n={name:"doc/deploying.md"},l=o("",65),t=[l];function p(r,i,c,h,d,C){return e(),a("div",null,t)}const b=s(n,[["render",p]]);export{y as __pageData,b as default};
diff --git a/assets/doc_getting-started.md.b448fa30.js b/assets/doc_getting-started.md.95c40c21.js
similarity index 98%
rename from assets/doc_getting-started.md.b448fa30.js
rename to assets/doc_getting-started.md.95c40c21.js
index 7baf680..a996e99 100644
--- a/assets/doc_getting-started.md.b448fa30.js
+++ b/assets/doc_getting-started.md.95c40c21.js
@@ -1 +1 @@
-import{_ as t,c as d,o as s,R as a}from"./chunks/framework.bdd825cc.js";const p=JSON.parse('{"title":"Getting Started","description":"","frontmatter":{},"headers":[],"relativePath":"doc/getting-started.md","filePath":"doc/getting-started.md","lastUpdated":1692058814000}'),e={name:"doc/getting-started.md"},o=a('

Getting Started

First, you need to pick a shadowsocks server and client implementation. Any implementation below is compatible with each other.

CLI implementations

  • shadowsocks: The original Python implementation.
  • shadowsocks-libev: Lightweight C implementation for embedded devices and low end boxes. Very small footprint (several megabytes) for thousands of connections.
  • go-shadowsocks2: Go implementation focusing on core features and code reusability.
  • shadowsocks-rust: A rust port of shadowsocks.

Feature comparison

ssss-libevgo-ss2ss-rust
TCP Fast Open
Multiuser
Management API
Redirect mode
Tunnel mode
UDP Relay
MPTCP
AEAD ciphers
Plugin
Plugin UDP (Experimental)

GUI Clients

Feature comparison

ss-winssx-ngss-qt5ss-android
System Proxy
CHNRoutes
PAC Configuration
Profile Switching
QR Code Scan
QR Code Generation
',10),r=[o];function h(i,n,c,l,m,u){return s(),d("div",null,r)}const g=t(e,[["render",h]]);export{p as __pageData,g as default}; +import{_ as t,c as d,o as s,R as a}from"./chunks/framework.bdd825cc.js";const p=JSON.parse('{"title":"Getting Started","description":"","frontmatter":{},"headers":[],"relativePath":"doc/getting-started.md","filePath":"doc/getting-started.md","lastUpdated":1694046893000}'),e={name:"doc/getting-started.md"},o=a('

Getting Started

First, you need to pick a shadowsocks server and client implementation. Any implementation below is compatible with each other.

CLI implementations

  • shadowsocks: The original Python implementation.
  • shadowsocks-libev: Lightweight C implementation for embedded devices and low end boxes. Very small footprint (several megabytes) for thousands of connections.
  • go-shadowsocks2: Go implementation focusing on core features and code reusability.
  • shadowsocks-rust: A rust port of shadowsocks.

Feature comparison

ssss-libevgo-ss2ss-rust
TCP Fast Open
Multiuser
Management API
Redirect mode
Tunnel mode
UDP Relay
MPTCP
AEAD ciphers
Plugin
Plugin UDP (Experimental)

GUI Clients

Feature comparison

ss-winssx-ngss-qt5ss-android
System Proxy
CHNRoutes
PAC Configuration
Profile Switching
QR Code Scan
QR Code Generation
',10),r=[o];function h(i,n,c,l,m,u){return s(),d("div",null,r)}const g=t(e,[["render",h]]);export{p as __pageData,g as default}; diff --git a/assets/doc_getting-started.md.b448fa30.lean.js b/assets/doc_getting-started.md.95c40c21.lean.js similarity index 85% rename from assets/doc_getting-started.md.b448fa30.lean.js rename to assets/doc_getting-started.md.95c40c21.lean.js index 998c4cd..52203b5 100644 --- a/assets/doc_getting-started.md.b448fa30.lean.js +++ b/assets/doc_getting-started.md.95c40c21.lean.js @@ -1 +1 @@ -import{_ as t,c as d,o as s,R as a}from"./chunks/framework.bdd825cc.js";const p=JSON.parse('{"title":"Getting Started","description":"","frontmatter":{},"headers":[],"relativePath":"doc/getting-started.md","filePath":"doc/getting-started.md","lastUpdated":1692058814000}'),e={name:"doc/getting-started.md"},o=a("",10),r=[o];function h(i,n,c,l,m,u){return s(),d("div",null,r)}const g=t(e,[["render",h]]);export{p as __pageData,g as default}; +import{_ as t,c as d,o as s,R as a}from"./chunks/framework.bdd825cc.js";const p=JSON.parse('{"title":"Getting Started","description":"","frontmatter":{},"headers":[],"relativePath":"doc/getting-started.md","filePath":"doc/getting-started.md","lastUpdated":1694046893000}'),e={name:"doc/getting-started.md"},o=a("",10),r=[o];function h(i,n,c,l,m,u){return s(),d("div",null,r)}const g=t(e,[["render",h]]);export{p as __pageData,g as default}; diff --git a/assets/doc_sip002.md.bdd62ea4.js b/assets/doc_sip002.md.7d5727dc.js similarity index 98% rename from assets/doc_sip002.md.bdd62ea4.js rename to assets/doc_sip002.md.7d5727dc.js index ba4d5ee..c6e7a76 100644 --- a/assets/doc_sip002.md.bdd62ea4.js +++ b/assets/doc_sip002.md.7d5727dc.js @@ -1,3 +1,3 @@ -import{_ as e,c as s,o,R as t}from"./chunks/framework.bdd825cc.js";const f=JSON.parse('{"title":"SIP002 URI scheme","description":"","frontmatter":{},"headers":[],"relativePath":"doc/sip002.md","filePath":"doc/sip002.md","lastUpdated":1692058814000}'),a={name:"doc/sip002.md"},n=t(`

SIP002 URI scheme

SIP002 purposed a new URI scheme, following RFC3986:

SS-URI = "ss://" userinfo "@" hostname ":" port [ "/" ] [ "?" plugin ] [ "#" tag ]
+import{_ as e,c as s,o,R as t}from"./chunks/framework.bdd825cc.js";const f=JSON.parse('{"title":"SIP002 URI scheme","description":"","frontmatter":{},"headers":[],"relativePath":"doc/sip002.md","filePath":"doc/sip002.md","lastUpdated":1694046893000}'),a={name:"doc/sip002.md"},n=t(`

SIP002 URI scheme

SIP002 purposed a new URI scheme, following RFC3986:

SS-URI = "ss://" userinfo "@" hostname ":" port [ "/" ] [ "?" plugin ] [ "#" tag ]
 userinfo = websafe-base64-encode-utf8(method  ":" password)
            method ":" password

Note that encoding userinfo with Base64URL is recommended but optional for Stream and AEAD (SIP004). But for AEAD-2022 (SIP022), userinfo MUST NOT be encoded with Base64URL. When userinfo is not encoded, method and password MUST be percent encoded.

The last / should be appended if plugin is present, but is optional if only tag is present. Example: ss://YmYtY2ZiOnRlc3Q@192.168.100.1:8888/?plugin=url-encoded-plugin-argument-value&unsupported-arguments=should-be-ignored#Dummy+profile+name. This kind of URIs can be parsed by standard libraries provided by most languages.

For plugin argument, we use the similar format as TOR_PT_SERVER_TRANSPORT_OPTIONS, which have the format like simple-obfs;obfs=http;obfs-host=example.com where colons, semicolons, equal signs and backslashes MUST be escaped with a backslash.

Examples:

With user info encoded with Base64URL:

  • ss://YWVzLTEyOC1nY206dGVzdA@192.168.100.1:8888#Example1
  • ss://cmM0LW1kNTpwYXNzd2Q@192.168.100.1:8888/?plugin=obfs-local%3Bobfs%3Dhttp#Example2

Plain user info:

  • ss://2022-blake3-aes-256-gcm:YctPZ6U7xPPcU%2Bgp3u%2B0tx%2FtRizJN9K8y%2BuKlW2qjlI%3D@192.168.100.1:8888#Example3
  • ss://2022-blake3-aes-256-gcm:YctPZ6U7xPPcU%2Bgp3u%2B0tx%2FtRizJN9K8y%2BuKlW2qjlI%3D@192.168.100.1:8888/?plugin=v2ray-plugin%3Bserver#Example3

FAQ:

Q1: Why parse user info to Base64URL?

A1: To safely encode all the characters in the key string. Note that we never try to "encrypt" your key in the URI.

Q2: Why not parse host name and port number into Base64URL?

A2: As mentioned above, we never try to "encrypt" anything in the URI. Additional parsing of host name and port number is not necessary.

Q3: Why not every client supports SIP002 URI scheme?

A3: Currently, SIP002 is still an optional feature unless the client supports SIP003 plugin.

Q4: Why the tags with space is truncated?

A4: White space is not legal in URI. It should be escaped. For example, ss://...#shadowsocks server 1 (illegal URI) should be escaped into ss://...#shadowsocks%20server%201 (legal URI).

`,20),p=[n];function r(c,i,l,d,u,h){return o(),s("div",null,p)}const g=e(a,[["render",r]]);export{f as __pageData,g as default}; diff --git a/assets/doc_sip002.md.bdd62ea4.lean.js b/assets/doc_sip002.md.7d5727dc.lean.js similarity index 84% rename from assets/doc_sip002.md.bdd62ea4.lean.js rename to assets/doc_sip002.md.7d5727dc.lean.js index 413d9b1..5b6b667 100644 --- a/assets/doc_sip002.md.bdd62ea4.lean.js +++ b/assets/doc_sip002.md.7d5727dc.lean.js @@ -1 +1 @@ -import{_ as e,c as s,o,R as t}from"./chunks/framework.bdd825cc.js";const f=JSON.parse('{"title":"SIP002 URI scheme","description":"","frontmatter":{},"headers":[],"relativePath":"doc/sip002.md","filePath":"doc/sip002.md","lastUpdated":1692058814000}'),a={name:"doc/sip002.md"},n=t("",20),p=[n];function r(c,i,l,d,u,h){return o(),s("div",null,p)}const g=e(a,[["render",r]]);export{f as __pageData,g as default}; +import{_ as e,c as s,o,R as t}from"./chunks/framework.bdd825cc.js";const f=JSON.parse('{"title":"SIP002 URI scheme","description":"","frontmatter":{},"headers":[],"relativePath":"doc/sip002.md","filePath":"doc/sip002.md","lastUpdated":1694046893000}'),a={name:"doc/sip002.md"},n=t("",20),p=[n];function r(c,i,l,d,u,h){return o(),s("div",null,p)}const g=e(a,[["render",r]]);export{f as __pageData,g as default}; diff --git a/assets/doc_sip003.md.a00aec58.js b/assets/doc_sip003.md.550330d8.js similarity index 99% rename from assets/doc_sip003.md.a00aec58.js rename to assets/doc_sip003.md.550330d8.js index 9ba6af2..d6618e6 100644 --- a/assets/doc_sip003.md.a00aec58.js +++ b/assets/doc_sip003.md.550330d8.js @@ -1,4 +1,4 @@ -import{_ as e,c as s,o as a,R as o}from"./chunks/framework.bdd825cc.js";const f=JSON.parse('{"title":"SIP003: A simplified plugin design for shadowsocks","description":"","frontmatter":{},"headers":[],"relativePath":"doc/sip003.md","filePath":"doc/sip003.md","lastUpdated":1692058814000}'),n={name:"doc/sip003.md"},t=o(`

SIP003: A simplified plugin design for shadowsocks

Architecture Overview

The plugin of shadowsocks is very similar to the Pluggable Transport plugins from Tor project. Unlike the SOCKS5 proxy design in PT, every SIP003 plugin works as a tunnel (or called local port forwarding). This design aims to avoid per-connection arguments in PT, leading to much easier implementation.

+------------+                    +---------------------------+
+import{_ as e,c as s,o as a,R as o}from"./chunks/framework.bdd825cc.js";const f=JSON.parse('{"title":"SIP003: A simplified plugin design for shadowsocks","description":"","frontmatter":{},"headers":[],"relativePath":"doc/sip003.md","filePath":"doc/sip003.md","lastUpdated":1694046893000}'),n={name:"doc/sip003.md"},t=o(`

SIP003: A simplified plugin design for shadowsocks

Architecture Overview

The plugin of shadowsocks is very similar to the Pluggable Transport plugins from Tor project. Unlike the SOCKS5 proxy design in PT, every SIP003 plugin works as a tunnel (or called local port forwarding). This design aims to avoid per-connection arguments in PT, leading to much easier implementation.

+------------+                    +---------------------------+
 |  SS Client +-- Local Loopback --+  Plugin Client (Tunnel)   +--+
 +------------+                    +---------------------------+  |
                                                                  |
diff --git a/assets/doc_sip003.md.a00aec58.lean.js b/assets/doc_sip003.md.550330d8.lean.js
similarity index 85%
rename from assets/doc_sip003.md.a00aec58.lean.js
rename to assets/doc_sip003.md.550330d8.lean.js
index 5c8fdfe..29e7ea7 100644
--- a/assets/doc_sip003.md.a00aec58.lean.js
+++ b/assets/doc_sip003.md.550330d8.lean.js
@@ -1 +1 @@
-import{_ as e,c as s,o as a,R as o}from"./chunks/framework.bdd825cc.js";const f=JSON.parse('{"title":"SIP003: A simplified plugin design for shadowsocks","description":"","frontmatter":{},"headers":[],"relativePath":"doc/sip003.md","filePath":"doc/sip003.md","lastUpdated":1692058814000}'),n={name:"doc/sip003.md"},t=o("",27),l=[t];function r(i,p,c,h,d,u){return a(),s("div",null,l)}const m=e(n,[["render",r]]);export{f as __pageData,m as default};
+import{_ as e,c as s,o as a,R as o}from"./chunks/framework.bdd825cc.js";const f=JSON.parse('{"title":"SIP003: A simplified plugin design for shadowsocks","description":"","frontmatter":{},"headers":[],"relativePath":"doc/sip003.md","filePath":"doc/sip003.md","lastUpdated":1694046893000}'),n={name:"doc/sip003.md"},t=o("",27),l=[t];function r(i,p,c,h,d,u){return a(),s("div",null,l)}const m=e(n,[["render",r]]);export{f as __pageData,m as default};
diff --git a/assets/doc_sip008.md.f8790501.js b/assets/doc_sip008.md.9ab13a28.js
similarity index 99%
rename from assets/doc_sip008.md.f8790501.js
rename to assets/doc_sip008.md.9ab13a28.js
index 06ff8fb..093da18 100644
--- a/assets/doc_sip008.md.f8790501.js
+++ b/assets/doc_sip008.md.9ab13a28.js
@@ -1,4 +1,4 @@
-import{_ as e,c as s,o as n,R as a}from"./chunks/framework.bdd825cc.js";const f=JSON.parse('{"title":"SIP008 Online Configuration Delivery","description":"","frontmatter":{},"headers":[],"relativePath":"doc/sip008.md","filePath":"doc/sip008.md","lastUpdated":1692058814000}'),o={name:"doc/sip008.md"},t=a(`

SIP008 Online Configuration Delivery

This specification defines a standard JSON document format for online configuration sharing and delivery, along with guidelines and security considerations for the secure transport of server configurations.

JSON Document Format

An example of a standard SIP008 JSON document:

{
+import{_ as e,c as s,o as n,R as a}from"./chunks/framework.bdd825cc.js";const f=JSON.parse('{"title":"SIP008 Online Configuration Delivery","description":"","frontmatter":{},"headers":[],"relativePath":"doc/sip008.md","filePath":"doc/sip008.md","lastUpdated":1694046893000}'),o={name:"doc/sip008.md"},t=a(`

SIP008 Online Configuration Delivery

This specification defines a standard JSON document format for online configuration sharing and delivery, along with guidelines and security considerations for the secure transport of server configurations.

JSON Document Format

An example of a standard SIP008 JSON document:

{
     "version": 1,
     "servers": [
         {
diff --git a/assets/doc_sip008.md.f8790501.lean.js b/assets/doc_sip008.md.9ab13a28.lean.js
similarity index 85%
rename from assets/doc_sip008.md.f8790501.lean.js
rename to assets/doc_sip008.md.9ab13a28.lean.js
index b6792ec..cd972a6 100644
--- a/assets/doc_sip008.md.f8790501.lean.js
+++ b/assets/doc_sip008.md.9ab13a28.lean.js
@@ -1 +1 @@
-import{_ as e,c as s,o as n,R as a}from"./chunks/framework.bdd825cc.js";const f=JSON.parse('{"title":"SIP008 Online Configuration Delivery","description":"","frontmatter":{},"headers":[],"relativePath":"doc/sip008.md","filePath":"doc/sip008.md","lastUpdated":1692058814000}'),o={name:"doc/sip008.md"},t=a("",8),i=[t];function l(r,p,c,d,u,h){return n(),s("div",null,i)}const y=e(o,[["render",l]]);export{f as __pageData,y as default};
+import{_ as e,c as s,o as n,R as a}from"./chunks/framework.bdd825cc.js";const f=JSON.parse('{"title":"SIP008 Online Configuration Delivery","description":"","frontmatter":{},"headers":[],"relativePath":"doc/sip008.md","filePath":"doc/sip008.md","lastUpdated":1694046893000}'),o={name:"doc/sip008.md"},t=a("",8),i=[t];function l(r,p,c,d,u,h){return n(),s("div",null,i)}const y=e(o,[["render",l]]);export{f as __pageData,y as default};
diff --git a/assets/doc_sip022.md.10538951.js b/assets/doc_sip022.md.9fcd5786.js
similarity index 99%
rename from assets/doc_sip022.md.10538951.js
rename to assets/doc_sip022.md.9fcd5786.js
index fd0ce30..b3b2811 100644
--- a/assets/doc_sip022.md.10538951.js
+++ b/assets/doc_sip022.md.9fcd5786.js
@@ -1,4 +1,4 @@
-import{_ as e,c as s,o as a,R as n}from"./chunks/framework.bdd825cc.js";const A=JSON.parse('{"title":"SIP022 AEAD-2022 Ciphers","description":"","frontmatter":{},"headers":[],"relativePath":"doc/sip022.md","filePath":"doc/sip022.md","lastUpdated":1692058814000}'),t={name:"doc/sip022.md"},o=n(`

SIP022 AEAD-2022 Ciphers

Abstract

This document defines the 2022 Edition of the Shadowsocks protocol. Improving upon Shadowsocks AEAD (2017), Shadowsocks 2022 addresses well-known issues of the previous editions, drops usage of obsolete cryptography, optimizes for security and performance, and leaves room for future extensions.

1. Overview

Shadowsocks 2022 is a secure proxy protocol for TCP and UDP traffic. The protocol uses AEAD with a pre-shared symmetric key to protect payload integrity and confidentiality. The proxy traffic is indistinguishable from a random byte stream, and therefore can circumvent firewalls and Internet censors that rely on DPI (Deep Packet Inspection).

Compared to previous editions of the protocol family, Shadowsocks 2022 allows and mandates full replay protection. Each message has its unique type and cannot be used for unintended purposes. The session-based UDP proxying significantly reduces protocol overhead and improves reliability and efficiency. Obsolete cryptographic functions have been replaced by their modern counterparts.

As with previous editions, Shadowsocks 2022 does not provide forward secrecy. It is believed that using a pre-shared key without performing handshakes is best for its use cases.

A Shadowsocks 2022 implementation consists of a server, a client, and optionally a relay. This document specifies requirements that implementations must follow.

1.1. Document Structure

This document describes the Shadowsocks 2022 Edition and is structured as follows:

  • Section 2 describes requirements on the encryption key and how to derive session subkeys.
  • Section 3 defines the encoding details of the required AES-GCM methods and the process for handling requests and responses.
  • Section 4 defines the encoding details of the optional ChaCha-Poly1305 methods.

1.2. Terms and Definitions

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 RFC2119 RFC8174 when, and only when, they appear in all capitals, as shown here.

Commonly used terms in this document are described below.

  • Shadowsocks AEAD: The original AEAD construction of Shadowsocks, standardized in 2017.

2. Encryption/Decryption Keys

A pre-shared key is used to derive session subkeys, which are subsequently used to encrypt/decrypt traffic for the session. The pre-shared key is also used directly in some places.

2.1. PSK

Unlike previous editions, Shadowsocks 2022 requires that a cryptographically-secure fixed-length PSK to be directly provided by the user. Implementations MUST NOT use the old EVP_BytesToKey function or any other method to generate keys from passwords.

The PSK is encoded in base64 for convenience. Practically, it can be generated with openssl rand -base64 <key_size>. The key size depends on the chosen method. This change was inspired by WireGuard.

MethodKey BytesSalt Bytes
2022-blake3-aes-128-gcm1616
2022-blake3-aes-256-gcm3232

2.2. Subkey Derivation

Shadowsocks 2022's subkey derivation uses BLAKE3's key derivation mode, which replaces the obsolete HKDF_SHA1 function in previous editions. A randomly generated salt is appended to the PSK to be used as key material. The salt has the same length as the pre-shared key.

session_subkey := blake3::derive_key(context: "shadowsocks 2022 session subkey", key_material: key + salt)

3. Required Methods

Method 2022-blake3-aes-128-gcm and 2022-blake3-aes-256-gcm MUST be implemented by all implementations. 2022 reflects the fast-changing and flexible nature of the protocol.

3.1. TCP

TCP connections over a Shadowsocks 2022 tunnel maps 1:1 to proxy connections. Each proxy connection carries 2 proxy streams: request stream and response stream. A client initiates a proxy connection by starting a request stream, and the server sends back response over the response stream. These streams carry chunks of data encrypted by the session subkey.

For payload transfer, Shadowsocks 2022 inherits the length-chunk-payload-chunk model from Shadowsocks AEAD, with some minor tweaks to improve performance. Standalone header chunks are added to both request and response streams to improve security and protect against replay attacks.

3.1.1. Encryption and Decryption

Each proxy stream derives its own session subkey with a random salt for encryption and decryption. A 12-byte little-endian integer is used as nonce, and is incremented after each encryption or decryption operation.

u96le counter
+import{_ as e,c as s,o as a,R as n}from"./chunks/framework.bdd825cc.js";const A=JSON.parse('{"title":"SIP022 AEAD-2022 Ciphers","description":"","frontmatter":{},"headers":[],"relativePath":"doc/sip022.md","filePath":"doc/sip022.md","lastUpdated":1694046893000}'),t={name:"doc/sip022.md"},o=n(`

SIP022 AEAD-2022 Ciphers

Abstract

This document defines the 2022 Edition of the Shadowsocks protocol. Improving upon Shadowsocks AEAD (2017), Shadowsocks 2022 addresses well-known issues of the previous editions, drops usage of obsolete cryptography, optimizes for security and performance, and leaves room for future extensions.

1. Overview

Shadowsocks 2022 is a secure proxy protocol for TCP and UDP traffic. The protocol uses AEAD with a pre-shared symmetric key to protect payload integrity and confidentiality. The proxy traffic is indistinguishable from a random byte stream, and therefore can circumvent firewalls and Internet censors that rely on DPI (Deep Packet Inspection).

Compared to previous editions of the protocol family, Shadowsocks 2022 allows and mandates full replay protection. Each message has its unique type and cannot be used for unintended purposes. The session-based UDP proxying significantly reduces protocol overhead and improves reliability and efficiency. Obsolete cryptographic functions have been replaced by their modern counterparts.

As with previous editions, Shadowsocks 2022 does not provide forward secrecy. It is believed that using a pre-shared key without performing handshakes is best for its use cases.

A Shadowsocks 2022 implementation consists of a server, a client, and optionally a relay. This document specifies requirements that implementations must follow.

1.1. Document Structure

This document describes the Shadowsocks 2022 Edition and is structured as follows:

  • Section 2 describes requirements on the encryption key and how to derive session subkeys.
  • Section 3 defines the encoding details of the required AES-GCM methods and the process for handling requests and responses.
  • Section 4 defines the encoding details of the optional ChaCha-Poly1305 methods.

1.2. Terms and Definitions

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 RFC2119 RFC8174 when, and only when, they appear in all capitals, as shown here.

Commonly used terms in this document are described below.

  • Shadowsocks AEAD: The original AEAD construction of Shadowsocks, standardized in 2017.

2. Encryption/Decryption Keys

A pre-shared key is used to derive session subkeys, which are subsequently used to encrypt/decrypt traffic for the session. The pre-shared key is also used directly in some places.

2.1. PSK

Unlike previous editions, Shadowsocks 2022 requires that a cryptographically-secure fixed-length PSK to be directly provided by the user. Implementations MUST NOT use the old EVP_BytesToKey function or any other method to generate keys from passwords.

The PSK is encoded in base64 for convenience. Practically, it can be generated with openssl rand -base64 <key_size>. The key size depends on the chosen method. This change was inspired by WireGuard.

MethodKey BytesSalt Bytes
2022-blake3-aes-128-gcm1616
2022-blake3-aes-256-gcm3232

2.2. Subkey Derivation

Shadowsocks 2022's subkey derivation uses BLAKE3's key derivation mode, which replaces the obsolete HKDF_SHA1 function in previous editions. A randomly generated salt is appended to the PSK to be used as key material. The salt has the same length as the pre-shared key.

session_subkey := blake3::derive_key(context: "shadowsocks 2022 session subkey", key_material: key + salt)

3. Required Methods

Method 2022-blake3-aes-128-gcm and 2022-blake3-aes-256-gcm MUST be implemented by all implementations. 2022 reflects the fast-changing and flexible nature of the protocol.

3.1. TCP

TCP connections over a Shadowsocks 2022 tunnel maps 1:1 to proxy connections. Each proxy connection carries 2 proxy streams: request stream and response stream. A client initiates a proxy connection by starting a request stream, and the server sends back response over the response stream. These streams carry chunks of data encrypted by the session subkey.

For payload transfer, Shadowsocks 2022 inherits the length-chunk-payload-chunk model from Shadowsocks AEAD, with some minor tweaks to improve performance. Standalone header chunks are added to both request and response streams to improve security and protect against replay attacks.

3.1.1. Encryption and Decryption

Each proxy stream derives its own session subkey with a random salt for encryption and decryption. A 12-byte little-endian integer is used as nonce, and is incremented after each encryption or decryption operation.

u96le counter
 aead := aead_new(key: session_subkey)
 ciphertext := aead.seal(nonce: counter, plaintext)
 plaintext := aead.open(nonce: counter, ciphertext)

3.1.2. Format

A request stream starts with one random salt and two standalone header chunks, followed repeatedly by one length chunk and one payload chunk. Each chunk is independently encrypted/decrypted using the AEAD cipher.

A response stream also starts with a random salt, but only has one fixed-length header chunk, which also acts as the first length chunk.

A length chunk is a 16-bit big-endian unsigned integer that describes the payload length in the next payload chunk. Servers and clients rely on length chunks to know how many bytes to read for the next payload chunk.

A payload chunk can have up to 0xFFFF (65535) bytes of unencrypted payload. The 0x3FFF (16383) length cap in Shadowsocks AEAD does not apply to this edition.

+----------------+
diff --git a/assets/doc_sip022.md.10538951.lean.js b/assets/doc_sip022.md.9fcd5786.lean.js
similarity index 69%
rename from assets/doc_sip022.md.10538951.lean.js
rename to assets/doc_sip022.md.9fcd5786.lean.js
index a0825cc..3949704 100644
--- a/assets/doc_sip022.md.10538951.lean.js
+++ b/assets/doc_sip022.md.9fcd5786.lean.js
@@ -1 +1 @@
-import{_ as e,c as s,o as a,R as n}from"./chunks/framework.bdd825cc.js";const A=JSON.parse('{"title":"SIP022 AEAD-2022 Ciphers","description":"","frontmatter":{},"headers":[],"relativePath":"doc/sip022.md","filePath":"doc/sip022.md","lastUpdated":1692058814000}'),t={name:"doc/sip022.md"},o=n("",86),r=[o];function i(l,p,c,d,h,y){return a(),s("div",null,r)}const m=e(t,[["render",i]]);export{A as __pageData,m as default};
+import{_ as e,c as s,o as a,R as n}from"./chunks/framework.bdd825cc.js";const A=JSON.parse('{"title":"SIP022 AEAD-2022 Ciphers","description":"","frontmatter":{},"headers":[],"relativePath":"doc/sip022.md","filePath":"doc/sip022.md","lastUpdated":1694046893000}'),t={name:"doc/sip022.md"},o=n("",86),r=[o];function i(l,p,c,d,h,y){return a(),s("div",null,r)}const m=e(t,[["render",i]]);export{A as __pageData,m as default};
diff --git a/assets/doc_sip023.md.17c4bc0b.js b/assets/doc_sip023.md.7ad16cb4.js
similarity index 99%
rename from assets/doc_sip023.md.17c4bc0b.js
rename to assets/doc_sip023.md.7ad16cb4.js
index cd15097..a0e679d 100644
--- a/assets/doc_sip023.md.17c4bc0b.js
+++ b/assets/doc_sip023.md.7ad16cb4.js
@@ -1,4 +1,4 @@
-import{_ as e,c as t,o as s,R as a}from"./chunks/framework.bdd825cc.js";const P=JSON.parse('{"title":"SIP023 Shadowsocks 2022 Extensible Identity Headers","description":"","frontmatter":{},"headers":[],"relativePath":"doc/sip023.md","filePath":"doc/sip023.md","lastUpdated":1692058814000}'),n={name:"doc/sip023.md"},i=a(`

SIP023 Shadowsocks 2022 Extensible Identity Headers

Identity headers are one or more additional layers of headers, each consisting of the next layer's PSK hash. The next layer of an identity header is the next identity header, or the protocol header if it's the last identity header. Identity headers are encrypted with the current layer's identity PSK using an AES block cipher.

Identity headers are implemented in such a way that's fully backward compatible with current Shadowsocks 2022 implementations. Each identity processor is fully transparent to the next.

  • iPSKn: The nth identity PSK that identifies the current layer.
  • uPSKn: The nth user PSK that identifies a user on the server.

TCP

In TCP requests, identity headers are located between salt and AEAD chunks.

identity_subkey := blake3::derive_key(context: "shadowsocks 2022 identity subkey", key_material: iPSKn + salt)
+import{_ as e,c as t,o as s,R as a}from"./chunks/framework.bdd825cc.js";const P=JSON.parse('{"title":"SIP023 Shadowsocks 2022 Extensible Identity Headers","description":"","frontmatter":{},"headers":[],"relativePath":"doc/sip023.md","filePath":"doc/sip023.md","lastUpdated":1694046893000}'),n={name:"doc/sip023.md"},i=a(`

SIP023 Shadowsocks 2022 Extensible Identity Headers

Identity headers are one or more additional layers of headers, each consisting of the next layer's PSK hash. The next layer of an identity header is the next identity header, or the protocol header if it's the last identity header. Identity headers are encrypted with the current layer's identity PSK using an AES block cipher.

Identity headers are implemented in such a way that's fully backward compatible with current Shadowsocks 2022 implementations. Each identity processor is fully transparent to the next.

  • iPSKn: The nth identity PSK that identifies the current layer.
  • uPSKn: The nth user PSK that identifies a user on the server.

TCP

In TCP requests, identity headers are located between salt and AEAD chunks.

identity_subkey := blake3::derive_key(context: "shadowsocks 2022 identity subkey", key_material: iPSKn + salt)
 plaintext := blake3::hash(iPSKn+1)[0..16] // Take the first 16 bytes of the next iPSK's hash.
 identity_header := aes_encrypt(key: identity_subkey, plaintext: plaintext)

UDP

In UDP packets, identity headers are located between the separate header (session ID, packet ID) and AEAD ciphertext.

plaintext := blake3::hash(iPSKn+1)[0..16] ^ session_id_packet_id // XOR to make it different for each packet.
 identity_header := aes_encrypt(key: iPSKn, plaintext: plaintext)

When iPSKs are used, the separate header MUST be encrypted with the first iPSK. Each identity processor MUST decrypt and re-encrypt the separate header with the next layer's PSK.

Scenarios

client0       >---+
diff --git a/assets/doc_sip023.md.17c4bc0b.lean.js b/assets/doc_sip023.md.7ad16cb4.lean.js
similarity index 85%
rename from assets/doc_sip023.md.17c4bc0b.lean.js
rename to assets/doc_sip023.md.7ad16cb4.lean.js
index bd572b9..4868178 100644
--- a/assets/doc_sip023.md.17c4bc0b.lean.js
+++ b/assets/doc_sip023.md.7ad16cb4.lean.js
@@ -1 +1 @@
-import{_ as e,c as t,o as s,R as a}from"./chunks/framework.bdd825cc.js";const P=JSON.parse('{"title":"SIP023 Shadowsocks 2022 Extensible Identity Headers","description":"","frontmatter":{},"headers":[],"relativePath":"doc/sip023.md","filePath":"doc/sip023.md","lastUpdated":1692058814000}'),n={name:"doc/sip023.md"},i=a("",21),r=[i];function o(d,c,l,p,h,y){return s(),t("div",null,r)}const S=e(n,[["render",o]]);export{P as __pageData,S as default};
+import{_ as e,c as t,o as s,R as a}from"./chunks/framework.bdd825cc.js";const P=JSON.parse('{"title":"SIP023 Shadowsocks 2022 Extensible Identity Headers","description":"","frontmatter":{},"headers":[],"relativePath":"doc/sip023.md","filePath":"doc/sip023.md","lastUpdated":1694046893000}'),n={name:"doc/sip023.md"},i=a("",21),r=[i];function o(d,c,l,p,h,y){return s(),t("div",null,r)}const S=e(n,[["render",o]]);export{P as __pageData,S as default};
diff --git a/assets/doc_stream.md.ceb77f24.js b/assets/doc_stream.md.e8b38e2e.js
similarity index 98%
rename from assets/doc_stream.md.ceb77f24.js
rename to assets/doc_stream.md.e8b38e2e.js
index c6ad8c8..2be65d0 100644
--- a/assets/doc_stream.md.ceb77f24.js
+++ b/assets/doc_stream.md.e8b38e2e.js
@@ -1 +1 @@
-import{_ as t,c as e,o as a,R as r}from"./chunks/framework.bdd825cc.js";const u=JSON.parse('{"title":"Stream ciphers","description":"","frontmatter":{},"headers":[],"relativePath":"doc/stream.md","filePath":"doc/stream.md","lastUpdated":1692058814000}'),s={name:"doc/stream.md"},d=r('

Stream ciphers

Stream ciphers are completely broken and will be removed soon. New users must use AEAD ciphers.

This historic document is for educational purposes only.

Stream Encryption/Decryption

Stream_encrypt is a function that takes a secret key, an initialization vector, a message, and produces a ciphertext with the same length as the message.

Stream_encrypt(key, IV, message) => ciphertext

Stream_decrypt is a function that takes a secret key, an initializaiton vector, a ciphertext, and produces the original message.

Stream_decrypt(key, IV, ciphertext) => message

The key can be input directly from user or generated from a password. The key derivation is following EVP_BytesToKey(3) in OpenSSL. The detailed spec can be found here: https://wiki.openssl.org/index.php/Manual:EVP_BytesToKey(3)

TCP

A stream cipher encrypted TCP stream starts with a randomly generated initializaiton vector, followed by encrypted payload data.

[IV][encrypted payload]

UDP

A stream cipher encrypted UDP packet has the following structure

[IV][encrypted payload]

Each UDP packet is encrypted/decrypted independently with a randomly generated initialization vector.

Historic stream ciphers

NameKey SizeIV Length
aes-128-ctr1616
aes-192-ctr2416
aes-256-ctr3216
aes-128-cfb1616
aes-192-cfb2416
aes-256-cfb3216
camellia-128-cfb1616
camellia-192-cfb2416
camellia-256-cfb3216
chacha20-ietf3212
bf-cfb168
chacha20328
salsa20328
rc4-md51616
',18),i=[d];function c(n,o,p,l,h,m){return a(),e("div",null,i)}const g=t(s,[["render",c]]);export{u as __pageData,g as default}; +import{_ as t,c as e,o as a,R as r}from"./chunks/framework.bdd825cc.js";const u=JSON.parse('{"title":"Stream ciphers","description":"","frontmatter":{},"headers":[],"relativePath":"doc/stream.md","filePath":"doc/stream.md","lastUpdated":1694046893000}'),s={name:"doc/stream.md"},d=r('

Stream ciphers

Stream ciphers are completely broken and will be removed soon. New users must use AEAD ciphers.

This historic document is for educational purposes only.

Stream Encryption/Decryption

Stream_encrypt is a function that takes a secret key, an initialization vector, a message, and produces a ciphertext with the same length as the message.

Stream_encrypt(key, IV, message) => ciphertext

Stream_decrypt is a function that takes a secret key, an initializaiton vector, a ciphertext, and produces the original message.

Stream_decrypt(key, IV, ciphertext) => message

The key can be input directly from user or generated from a password. The key derivation is following EVP_BytesToKey(3) in OpenSSL. The detailed spec can be found here: https://wiki.openssl.org/index.php/Manual:EVP_BytesToKey(3)

TCP

A stream cipher encrypted TCP stream starts with a randomly generated initializaiton vector, followed by encrypted payload data.

[IV][encrypted payload]

UDP

A stream cipher encrypted UDP packet has the following structure

[IV][encrypted payload]

Each UDP packet is encrypted/decrypted independently with a randomly generated initialization vector.

Historic stream ciphers

NameKey SizeIV Length
aes-128-ctr1616
aes-192-ctr2416
aes-256-ctr3216
aes-128-cfb1616
aes-192-cfb2416
aes-256-cfb3216
camellia-128-cfb1616
camellia-192-cfb2416
camellia-256-cfb3216
chacha20-ietf3212
bf-cfb168
chacha20328
salsa20328
rc4-md51616
',18),i=[d];function c(n,o,p,l,h,m){return a(),e("div",null,i)}const g=t(s,[["render",c]]);export{u as __pageData,g as default}; diff --git a/assets/doc_stream.md.ceb77f24.lean.js b/assets/doc_stream.md.e8b38e2e.lean.js similarity index 84% rename from assets/doc_stream.md.ceb77f24.lean.js rename to assets/doc_stream.md.e8b38e2e.lean.js index 46fcd6e..77aaf3c 100644 --- a/assets/doc_stream.md.ceb77f24.lean.js +++ b/assets/doc_stream.md.e8b38e2e.lean.js @@ -1 +1 @@ -import{_ as t,c as e,o as a,R as r}from"./chunks/framework.bdd825cc.js";const u=JSON.parse('{"title":"Stream ciphers","description":"","frontmatter":{},"headers":[],"relativePath":"doc/stream.md","filePath":"doc/stream.md","lastUpdated":1692058814000}'),s={name:"doc/stream.md"},d=r("",18),i=[d];function c(n,o,p,l,h,m){return a(),e("div",null,i)}const g=t(s,[["render",c]]);export{u as __pageData,g as default}; +import{_ as t,c as e,o as a,R as r}from"./chunks/framework.bdd825cc.js";const u=JSON.parse('{"title":"Stream ciphers","description":"","frontmatter":{},"headers":[],"relativePath":"doc/stream.md","filePath":"doc/stream.md","lastUpdated":1694046893000}'),s={name:"doc/stream.md"},d=r("",18),i=[d];function c(n,o,p,l,h,m){return a(),e("div",null,i)}const g=t(s,[["render",c]]);export{u as __pageData,g as default}; diff --git a/assets/doc_what-is-shadowsocks.md.ac508dd6.js b/assets/doc_what-is-shadowsocks.md.d789ca84.js similarity index 98% rename from assets/doc_what-is-shadowsocks.md.ac508dd6.js rename to assets/doc_what-is-shadowsocks.md.d789ca84.js index 86d1406..cccb638 100644 --- a/assets/doc_what-is-shadowsocks.md.ac508dd6.js +++ b/assets/doc_what-is-shadowsocks.md.d789ca84.js @@ -1 +1 @@ -import{_ as e,c as a,o as s,R as t}from"./chunks/framework.bdd825cc.js";const m=JSON.parse('{"title":"What is Shadowsocks?","description":"","frontmatter":{},"headers":[],"relativePath":"doc/what-is-shadowsocks.md","filePath":"doc/what-is-shadowsocks.md","lastUpdated":1692058814000}'),o={name:"doc/what-is-shadowsocks.md"},n=t('

What is Shadowsocks?

Shadowsocks is a secure split proxy loosely based on SOCKS5.

client <---> ss-local <--[encrypted]--> ss-remote <---> target

The Shadowsocks local component (ss-local) acts like a traditional SOCKS5 server and provides proxy service to clients. It encrypts and forwards data streams and packets from the client to the Shadowsocks remote component (ss-remote), which decrypts and forwards to the target. Replies from target are similarly encrypted and relayed by ss-remote back to ss-local, which decrypts and eventually returns to the original client.

Addressing

Addresses used in Shadowsocks follow the SOCKS5 address format:

[1-byte type][variable-length host][2-byte port]

The following address types are defined:

  • 0x01: host is a 4-byte IPv4 address.
  • 0x03: host is a variable length string, starting with a 1-byte length, followed by up to 255-byte domain name.
  • 0x04: host is a 16-byte IPv6 address.

The port number is a 2-byte big-endian unsigned integer.

TCP

ss-local initiates a TCP connection to ss-remote by sending an encrypted data stream starting with the target address followed by payload data. The exact encryption scheme differs depending on the cipher used.

[target address][payload]

ss-remote receives the encrypted data stream, decrypts and parses the leading target address. It then establishes a new TCP connection to the target and forwards payload data to it. ss-remote receives reply from the target, encrypts and forwards it back to the ss-local, until ss-local disconnects.

For better obfuscation purposes, both local and remote SHOULD send the handshake data along with some payload in the first packet.

UDP

ss-local sends an encrypted data packet containing the target address and payload to ss-remote.

[target address][payload]

Upon receiving the encrypted packet, ss-remote decrypts and parses the target address. It then sends a new data packet containing only the payload to the target. ss-remote receives data packets back from target and prepends the target address to the payload in each packet, then sends encrypted copies back to ss-local.

[target address][payload]

Essentially, ss-remote is performing Network Address Translation for ss-local.

',21),r=[n];function d(l,c,i,p,h,g){return s(),a("div",null,r)}const b=e(o,[["render",d]]);export{m as __pageData,b as default}; +import{_ as e,c as a,o as s,R as t}from"./chunks/framework.bdd825cc.js";const m=JSON.parse('{"title":"What is Shadowsocks?","description":"","frontmatter":{},"headers":[],"relativePath":"doc/what-is-shadowsocks.md","filePath":"doc/what-is-shadowsocks.md","lastUpdated":1694046893000}'),o={name:"doc/what-is-shadowsocks.md"},n=t('

What is Shadowsocks?

Shadowsocks is a secure split proxy loosely based on SOCKS5.

client <---> ss-local <--[encrypted]--> ss-remote <---> target

The Shadowsocks local component (ss-local) acts like a traditional SOCKS5 server and provides proxy service to clients. It encrypts and forwards data streams and packets from the client to the Shadowsocks remote component (ss-remote), which decrypts and forwards to the target. Replies from target are similarly encrypted and relayed by ss-remote back to ss-local, which decrypts and eventually returns to the original client.

Addressing

Addresses used in Shadowsocks follow the SOCKS5 address format:

[1-byte type][variable-length host][2-byte port]

The following address types are defined:

  • 0x01: host is a 4-byte IPv4 address.
  • 0x03: host is a variable length string, starting with a 1-byte length, followed by up to 255-byte domain name.
  • 0x04: host is a 16-byte IPv6 address.

The port number is a 2-byte big-endian unsigned integer.

TCP

ss-local initiates a TCP connection to ss-remote by sending an encrypted data stream starting with the target address followed by payload data. The exact encryption scheme differs depending on the cipher used.

[target address][payload]

ss-remote receives the encrypted data stream, decrypts and parses the leading target address. It then establishes a new TCP connection to the target and forwards payload data to it. ss-remote receives reply from the target, encrypts and forwards it back to the ss-local, until ss-local disconnects.

For better obfuscation purposes, both local and remote SHOULD send the handshake data along with some payload in the first packet.

UDP

ss-local sends an encrypted data packet containing the target address and payload to ss-remote.

[target address][payload]

Upon receiving the encrypted packet, ss-remote decrypts and parses the target address. It then sends a new data packet containing only the payload to the target. ss-remote receives data packets back from target and prepends the target address to the payload in each packet, then sends encrypted copies back to ss-local.

[target address][payload]

Essentially, ss-remote is performing Network Address Translation for ss-local.

',21),r=[n];function d(l,c,i,p,h,g){return s(),a("div",null,r)}const b=e(o,[["render",d]]);export{m as __pageData,b as default}; diff --git a/assets/doc_what-is-shadowsocks.md.ac508dd6.lean.js b/assets/doc_what-is-shadowsocks.md.d789ca84.lean.js similarity index 86% rename from assets/doc_what-is-shadowsocks.md.ac508dd6.lean.js rename to assets/doc_what-is-shadowsocks.md.d789ca84.lean.js index 80a26dc..a451cfa 100644 --- a/assets/doc_what-is-shadowsocks.md.ac508dd6.lean.js +++ b/assets/doc_what-is-shadowsocks.md.d789ca84.lean.js @@ -1 +1 @@ -import{_ as e,c as a,o as s,R as t}from"./chunks/framework.bdd825cc.js";const m=JSON.parse('{"title":"What is Shadowsocks?","description":"","frontmatter":{},"headers":[],"relativePath":"doc/what-is-shadowsocks.md","filePath":"doc/what-is-shadowsocks.md","lastUpdated":1692058814000}'),o={name:"doc/what-is-shadowsocks.md"},n=t("",21),r=[n];function d(l,c,i,p,h,g){return s(),a("div",null,r)}const b=e(o,[["render",d]]);export{m as __pageData,b as default}; +import{_ as e,c as a,o as s,R as t}from"./chunks/framework.bdd825cc.js";const m=JSON.parse('{"title":"What is Shadowsocks?","description":"","frontmatter":{},"headers":[],"relativePath":"doc/what-is-shadowsocks.md","filePath":"doc/what-is-shadowsocks.md","lastUpdated":1694046893000}'),o={name:"doc/what-is-shadowsocks.md"},n=t("",21),r=[n];function d(l,c,i,p,h,g){return s(),a("div",null,r)}const b=e(o,[["render",d]]);export{m as __pageData,b as default}; diff --git a/assets/doc_what-is-sip.md.5b61a30c.js b/assets/doc_what-is-sip.md.46284b5c.js similarity index 96% rename from assets/doc_what-is-sip.md.5b61a30c.js rename to assets/doc_what-is-sip.md.46284b5c.js index 11879dd..65c5c66 100644 --- a/assets/doc_what-is-sip.md.5b61a30c.js +++ b/assets/doc_what-is-sip.md.46284b5c.js @@ -1 +1 @@ -import{_ as e,c as a,o as t,R as s}from"./chunks/framework.bdd825cc.js";const w=JSON.parse('{"title":"What is SIP","description":"","frontmatter":{},"headers":[],"relativePath":"doc/what-is-sip.md","filePath":"doc/what-is-sip.md","lastUpdated":1692058814000}'),o={name:"doc/what-is-sip.md"},i=s('

What is SIP

SIP stands for Shadowsocks Improvement Proposal. SIPs are standards specifying potential new features or processes for Shadowsocks.

How to submit a SIP?

Shadowsocks is a community open to any reasonable improvements. To submit a SIP, please open a SIP issue on our GitHub issue tracker.

When will a SIP be accepted?

If most of the core contributors agree to accept a SIP, a formal SIP document will be published on this site. All the developers will review and follow the SIP to implement their own implementations. The whole process usually takes months.

',6),r=[i];function n(h,c,l,p,d,m){return t(),a("div",null,r)}const _=e(o,[["render",n]]);export{w as __pageData,_ as default}; +import{_ as e,c as a,o as t,R as s}from"./chunks/framework.bdd825cc.js";const w=JSON.parse('{"title":"What is SIP","description":"","frontmatter":{},"headers":[],"relativePath":"doc/what-is-sip.md","filePath":"doc/what-is-sip.md","lastUpdated":1694046893000}'),o={name:"doc/what-is-sip.md"},i=s('

What is SIP

SIP stands for Shadowsocks Improvement Proposal. SIPs are standards specifying potential new features or processes for Shadowsocks.

How to submit a SIP?

Shadowsocks is a community open to any reasonable improvements. To submit a SIP, please open a SIP issue on our GitHub issue tracker.

When will a SIP be accepted?

If most of the core contributors agree to accept a SIP, a formal SIP document will be published on this site. All the developers will review and follow the SIP to implement their own implementations. The whole process usually takes months.

',6),r=[i];function n(h,c,l,p,d,m){return t(),a("div",null,r)}const _=e(o,[["render",n]]);export{w as __pageData,_ as default}; diff --git a/assets/doc_what-is-sip.md.5b61a30c.lean.js b/assets/doc_what-is-sip.md.46284b5c.lean.js similarity index 84% rename from assets/doc_what-is-sip.md.5b61a30c.lean.js rename to assets/doc_what-is-sip.md.46284b5c.lean.js index 1196e97..35b020c 100644 --- a/assets/doc_what-is-sip.md.5b61a30c.lean.js +++ b/assets/doc_what-is-sip.md.46284b5c.lean.js @@ -1 +1 @@ -import{_ as e,c as a,o as t,R as s}from"./chunks/framework.bdd825cc.js";const w=JSON.parse('{"title":"What is SIP","description":"","frontmatter":{},"headers":[],"relativePath":"doc/what-is-sip.md","filePath":"doc/what-is-sip.md","lastUpdated":1692058814000}'),o={name:"doc/what-is-sip.md"},i=s("",6),r=[i];function n(h,c,l,p,d,m){return t(),a("div",null,r)}const _=e(o,[["render",n]]);export{w as __pageData,_ as default}; +import{_ as e,c as a,o as t,R as s}from"./chunks/framework.bdd825cc.js";const w=JSON.parse('{"title":"What is SIP","description":"","frontmatter":{},"headers":[],"relativePath":"doc/what-is-sip.md","filePath":"doc/what-is-sip.md","lastUpdated":1694046893000}'),o={name:"doc/what-is-sip.md"},i=s("",6),r=[i];function n(h,c,l,p,d,m){return t(),a("div",null,r)}const _=e(o,[["render",n]]);export{w as __pageData,_ as default}; diff --git a/assets/index.md.2a8afa05.js b/assets/index.md.fcd24b3f.js similarity index 95% rename from assets/index.md.2a8afa05.js rename to assets/index.md.fcd24b3f.js index 5f16cdb..464095c 100644 --- a/assets/index.md.2a8afa05.js +++ b/assets/index.md.fcd24b3f.js @@ -1 +1 @@ -import{_ as e,c as t,o as a}from"./chunks/framework.bdd825cc.js";const h=JSON.parse('{"title":"Shadowsocks","titleTemplate":"A fast tunnel proxy that helps you bypass firewalls.","description":"","frontmatter":{"layout":"home","title":"Shadowsocks","titleTemplate":"A fast tunnel proxy that helps you bypass firewalls.","hero":{"name":"Shadowsocks","text":"A fast tunnel proxy that helps you bypass firewalls","actions":[{"theme":"brand","text":"Get Started","link":"/doc/what-is-shadowsocks"},{"theme":"alt","text":"View on GitHub","link":"https://github.com/shadowsocks"},{"theme":"alt","text":"Download from IPFS","link":"https://cloudflare-ipfs.com/ipfs/Qma38UCuXzFFvsPJYn1zA82Nb9yUrUn2mdpmQahw5SvHDB"}]},"features":[{"title":"Super Fast","details":"Bleeding edge techniques using Asynchronous I/O and Event-driven programming."},{"title":"Flexible Encryption","details":"Secured with industry level encryption algorithm. Flexible to support custom algorithms."},{"title":"Mobile Ready","details":"Optimized for mobile device and wireless network, with low CPU and bandwidth usage."},{"title":"Cross Platform","details":"Available on most platforms, including Windows, Linux, Mac, Android, iOS, and OpenWRT."}]},"headers":[],"relativePath":"index.md","filePath":"index.md","lastUpdated":1692058814000}'),s={name:"index.md"};function o(i,n,l,r,d,p){return a(),t("div")}const m=e(s,[["render",o]]);export{h as __pageData,m as default}; +import{_ as e,c as t,o as a}from"./chunks/framework.bdd825cc.js";const h=JSON.parse('{"title":"Shadowsocks","titleTemplate":"A fast tunnel proxy that helps you bypass firewalls.","description":"","frontmatter":{"layout":"home","title":"Shadowsocks","titleTemplate":"A fast tunnel proxy that helps you bypass firewalls.","hero":{"name":"Shadowsocks","text":"A fast tunnel proxy that helps you bypass firewalls","actions":[{"theme":"brand","text":"Get Started","link":"/doc/what-is-shadowsocks"},{"theme":"alt","text":"View on GitHub","link":"https://github.com/shadowsocks"},{"theme":"alt","text":"Download from IPFS","link":"https://cloudflare-ipfs.com/ipfs/Qma38UCuXzFFvsPJYn1zA82Nb9yUrUn2mdpmQahw5SvHDB"}]},"features":[{"title":"Super Fast","details":"Bleeding edge techniques using Asynchronous I/O and Event-driven programming."},{"title":"Flexible Encryption","details":"Secured with industry level encryption algorithm. Flexible to support custom algorithms."},{"title":"Mobile Ready","details":"Optimized for mobile device and wireless network, with low CPU and bandwidth usage."},{"title":"Cross Platform","details":"Available on most platforms, including Windows, Linux, Mac, Android, iOS, and OpenWRT."}]},"headers":[],"relativePath":"index.md","filePath":"index.md","lastUpdated":1694046893000}'),s={name:"index.md"};function o(i,n,l,r,d,p){return a(),t("div")}const m=e(s,[["render",o]]);export{h as __pageData,m as default}; diff --git a/assets/index.md.2a8afa05.lean.js b/assets/index.md.fcd24b3f.lean.js similarity index 95% rename from assets/index.md.2a8afa05.lean.js rename to assets/index.md.fcd24b3f.lean.js index 5f16cdb..464095c 100644 --- a/assets/index.md.2a8afa05.lean.js +++ b/assets/index.md.fcd24b3f.lean.js @@ -1 +1 @@ -import{_ as e,c as t,o as a}from"./chunks/framework.bdd825cc.js";const h=JSON.parse('{"title":"Shadowsocks","titleTemplate":"A fast tunnel proxy that helps you bypass firewalls.","description":"","frontmatter":{"layout":"home","title":"Shadowsocks","titleTemplate":"A fast tunnel proxy that helps you bypass firewalls.","hero":{"name":"Shadowsocks","text":"A fast tunnel proxy that helps you bypass firewalls","actions":[{"theme":"brand","text":"Get Started","link":"/doc/what-is-shadowsocks"},{"theme":"alt","text":"View on GitHub","link":"https://github.com/shadowsocks"},{"theme":"alt","text":"Download from IPFS","link":"https://cloudflare-ipfs.com/ipfs/Qma38UCuXzFFvsPJYn1zA82Nb9yUrUn2mdpmQahw5SvHDB"}]},"features":[{"title":"Super Fast","details":"Bleeding edge techniques using Asynchronous I/O and Event-driven programming."},{"title":"Flexible Encryption","details":"Secured with industry level encryption algorithm. Flexible to support custom algorithms."},{"title":"Mobile Ready","details":"Optimized for mobile device and wireless network, with low CPU and bandwidth usage."},{"title":"Cross Platform","details":"Available on most platforms, including Windows, Linux, Mac, Android, iOS, and OpenWRT."}]},"headers":[],"relativePath":"index.md","filePath":"index.md","lastUpdated":1692058814000}'),s={name:"index.md"};function o(i,n,l,r,d,p){return a(),t("div")}const m=e(s,[["render",o]]);export{h as __pageData,m as default}; +import{_ as e,c as t,o as a}from"./chunks/framework.bdd825cc.js";const h=JSON.parse('{"title":"Shadowsocks","titleTemplate":"A fast tunnel proxy that helps you bypass firewalls.","description":"","frontmatter":{"layout":"home","title":"Shadowsocks","titleTemplate":"A fast tunnel proxy that helps you bypass firewalls.","hero":{"name":"Shadowsocks","text":"A fast tunnel proxy that helps you bypass firewalls","actions":[{"theme":"brand","text":"Get Started","link":"/doc/what-is-shadowsocks"},{"theme":"alt","text":"View on GitHub","link":"https://github.com/shadowsocks"},{"theme":"alt","text":"Download from IPFS","link":"https://cloudflare-ipfs.com/ipfs/Qma38UCuXzFFvsPJYn1zA82Nb9yUrUn2mdpmQahw5SvHDB"}]},"features":[{"title":"Super Fast","details":"Bleeding edge techniques using Asynchronous I/O and Event-driven programming."},{"title":"Flexible Encryption","details":"Secured with industry level encryption algorithm. Flexible to support custom algorithms."},{"title":"Mobile Ready","details":"Optimized for mobile device and wireless network, with low CPU and bandwidth usage."},{"title":"Cross Platform","details":"Available on most platforms, including Windows, Linux, Mac, Android, iOS, and OpenWRT."}]},"headers":[],"relativePath":"index.md","filePath":"index.md","lastUpdated":1694046893000}'),s={name:"index.md"};function o(i,n,l,r,d,p){return a(),t("div")}const m=e(s,[["render",o]]);export{h as __pageData,m as default}; diff --git a/doc/advanced.html b/doc/advanced.html index fcd22ba..3874bf7 100644 --- a/doc/advanced.html +++ b/doc/advanced.html @@ -10,7 +10,7 @@ - + @@ -41,8 +41,8 @@ net.ipv4.tcp_rmem = 4096 87380 67108864 net.ipv4.tcp_wmem = 4096 65536 67108864 net.ipv4.tcp_mtu_probing = 1 -net.ipv4.tcp_congestion_control = hybla

Of course, remember to execute sysctl -p to reload the config at runtime.

This website is released under the MIT License.

- diff --git a/doc/aead.html b/doc/aead.html index 8726f5e..d32f98e 100644 --- a/doc/aead.html +++ b/doc/aead.html @@ -10,14 +10,14 @@ - + -
Skip to content
On this page

AEAD ciphers

AEAD stands for Authenticated Encryption with Associated Data. AEAD ciphers simultaneously provide confidentiality, integrity, and authenticity. They have excellent performance and power efficiency on modern hardware. Users should use AEAD ciphers whenever possible.

The following AEAD ciphers are recommended. Compliant Shadowsocks implementations must support AEAD_CHACHA20_POLY1305. Implementations for devices with hardware AES acceleration should also implement AEAD_AES_128_GCM and AEAD_AES_256_GCM.

NameAliasKey SizeSalt SizeNonce SizeTag Size
AEAD_CHACHA20_POLY1305chacha20-ietf-poly130532321216
AEAD_AES_256_GCMaes-256-gcm32321216
AEAD_AES_128_GCMaes-128-gcm16161216

Please refer to IANA AEAD registry for naming scheme and specification.

The way Shadowsocks using AEAD ciphers is specified in SIP004 and amended in SIP007. SIP004 was proposed by @Mygod with design inspirations from @wongsyrone, @Noisyfox and @breakwa11. SIP007 was proposed by @riobard with input from @madeye, @Mygod, @wongsyrone, and many others.

Key Derivation

The master key can be input directly from user or generated from a password. The key derivation is still following EVP_BytesToKey(3) in OpenSSL. The detailed spec can be found here

HKDF_SHA1 is a function that takes a secret key, a non-secret salt, an info string, and produces a subkey that is cryptographically strong even if the input secret key is weak.

HKDF_SHA1(key, salt, info) => subkey

The info string binds the generated subkey to a specific application context. In our case, it must be the string "ss-subkey" without quotes.

We derive a per-session subkey from a pre-shared master key using HKDF_SHA1. Salt must be unique through the entire life of the pre-shared master key.

Authenticated Encryption/Decryption

AE_encrypt is a function that takes a secret key, a non-secret nonce, a message, and produces ciphertext and authentication tag. Nonce must be unique for a given key in each invocation.

AE_encrypt(key, nonce, message) => (ciphertext, tag)

AE_decrypt is a function that takes a secret key, non-secret nonce, ciphertext, authentication tag, and produces original message. If any of the input is tampered with, decryption will fail.

AE_decrypt(key, nonce, ciphertext, tag) => message

TCP

An AEAD encrypted TCP stream starts with a randomly generated salt to derive the per-session subkey, followed by any number of encrypted chunks. Each chunk has the following structure:

[encrypted payload length][length tag][encrypted payload][payload tag]

Payload length is a 2-byte big-endian unsigned integer capped at 0x3FFF. The higher two bits are reserved and must be set to zero. Payload is therefore limited to 16*1024 - 1 bytes.

The first AEAD encrypt/decrypt operation uses a counting nonce starting from 0. After each encrypt/decrypt operation, the nonce is incremented by one as if it were an unsigned little-endian integer. Note that each TCP chunk involves two AEAD encrypt/decrypt operation: one for the payload length, and one for the payload. Therefore each chunk increases the nonce twice.

UDP

An AEAD encrypted UDP packet has the following structure

[salt][encrypted payload][tag]

The salt is used to derive the per-session subkey and must be generated randomly to ensure uniqueness. Each UDP packet is encrypted/decrypted independently, using the derived subkey and a nonce with all zero bytes.

This website is released under the MIT License.

- diff --git a/doc/configs.html b/doc/configs.html index f7eaa42..0757214 100644 --- a/doc/configs.html +++ b/doc/configs.html @@ -10,7 +10,7 @@ - + @@ -23,8 +23,8 @@ "password":"barfoo!", "method":"chacha20-ietf-poly1305" }

Explanation of each field:

  • server: your hostname or server IP (IPv4/IPv6).
  • server_port: server port number.
  • local_port: local port number.
  • password: a password used to encrypt transfer.
  • method: encryption method.

Encryption Method

The strongest option is an AEAD cipher. The recommended choice is "chacha20-ietf-poly1305" or "aes-256-gcm". Other stream ciphers are implemented but do not provide integrity and authenticity. Unless otherwise specified the encryption method defaults to "table", which is not secure.

URI and QR code

Shadowsocks for Android / iOS also accepts BASE64 encoded URI format configs:

ss://BASE64-ENCODED-STRING-WITHOUT-PADDING#TAG

Where the plain URI should be:

ss://method:password@hostname:port

Note that the above URI doesn't follow RFC3986. It means the password here should be plain text, not percent-encoded.

For example, we have a server at 192.168.100.1:8888 using bf-cfb encryption method and password test/!@#:. Then, with the plain URI ss://bf-cfb:test/!@#:@192.168.100.1:8888, we can generate the BASE64 encoded URI:

> console.log( "ss://" + btoa("bf-cfb:test/!@#:@192.168.100.1:8888") )
-ss://YmYtY2ZiOnRlc3QvIUAjOkAxOTIuMTY4LjEwMC4xOjg4ODg

To help organize and identify these URIs, you can append a tag after the BASE64 encoded string:

ss://YmYtY2ZiOnRlc3QvIUAjOkAxOTIuMTY4LjEwMC4xOjg4ODg#example-server

This URI can also be encoded to QR code. Then, just scan it with your Android / iOS devices:

SIP002

There is also a new URI scheme proposed in SIP002. Any client or server which supports SIP003 plugin should use SIP002 URI scheme instead.

- diff --git a/doc/contributors.html b/doc/contributors.html index ec1a7c9..0c50506 100644 --- a/doc/contributors.html +++ b/doc/contributors.html @@ -10,14 +10,14 @@ - + -
Skip to content
On this page

Contributors

Core Contributors

@clowwindy

The creator of shadowsocks and the maintainer of shadowsocks-python/nodejs/gui/iOS.

@zonyitoo

The maintainer of shadowsocks-rust.

@cyfdecyf

The maintainer of shadowsocks-go and cow.

@madeye

The maintainer of shadowsocks-libev/android and this project site.

@linusyang

The maintainer of shadowsocks-libev and MobileShadowSocks.

@Mygod

The maintainer of shadowsocks-android.

@aa65535

The maintainer of openwrt-shadowsocks.

@librehat

The maintainer of shadowsocks-qt5 and libQtShadowsocks

Other Contributors

@ohdarling

The maintainer of GoAgentX.

@fqrouter

The maintainer of fqrouter.

This website is released under the MIT License.

- diff --git a/doc/deploying.html b/doc/deploying.html index 4ed4713..ebec3a8 100644 --- a/doc/deploying.html +++ b/doc/deploying.html @@ -10,7 +10,7 @@ - + @@ -40,8 +40,8 @@ $ ./Build install

or

bash
$ perl Makefile.PL
 $ make
 $ make test
-$ make install

You might need to change make to dmake or nmake depending on the compiler toolchain used on Windows. If you have cpan, you can also install using this command:

bash
$ cpan Net::Shadowsocks

Running

There is a server.pl script under the eg directory. Put your config.json in the same directory as server.pl and run the server.pl script there.

Net::Shadowsocks is licensed under the [Artistic License (2.0)] (http://www.perlfoundation.org/artistic_license_2_0).

- diff --git a/doc/getting-started.html b/doc/getting-started.html index 84eadf2..2afb56c 100644 --- a/doc/getting-started.html +++ b/doc/getting-started.html @@ -10,14 +10,14 @@ - + -
Skip to content
On this page

Getting Started

First, you need to pick a shadowsocks server and client implementation. Any implementation below is compatible with each other.

CLI implementations

  • shadowsocks: The original Python implementation.
  • shadowsocks-libev: Lightweight C implementation for embedded devices and low end boxes. Very small footprint (several megabytes) for thousands of connections.
  • go-shadowsocks2: Go implementation focusing on core features and code reusability.
  • shadowsocks-rust: A rust port of shadowsocks.

Feature comparison

ssss-libevgo-ss2ss-rust
TCP Fast Open
Multiuser
Management API
Redirect mode
Tunnel mode
UDP Relay
MPTCP
AEAD ciphers
Plugin
Plugin UDP (Experimental)

GUI Clients

Feature comparison

ss-winssx-ngss-qt5ss-android
System Proxy
CHNRoutes
PAC Configuration
Profile Switching
QR Code Scan
QR Code Generation

This website is released under the MIT License.

- diff --git a/doc/sip002.html b/doc/sip002.html index 5ec7271..25a4ecd 100644 --- a/doc/sip002.html +++ b/doc/sip002.html @@ -10,7 +10,7 @@ - + @@ -18,8 +18,8 @@
Skip to content
On this page

SIP002 URI scheme

SIP002 purposed a new URI scheme, following RFC3986:

SS-URI = "ss://" userinfo "@" hostname ":" port [ "/" ] [ "?" plugin ] [ "#" tag ]
 userinfo = websafe-base64-encode-utf8(method  ":" password)
-           method ":" password

Note that encoding userinfo with Base64URL is recommended but optional for Stream and AEAD (SIP004). But for AEAD-2022 (SIP022), userinfo MUST NOT be encoded with Base64URL. When userinfo is not encoded, method and password MUST be percent encoded.

The last / should be appended if plugin is present, but is optional if only tag is present. Example: ss://YmYtY2ZiOnRlc3Q@192.168.100.1:8888/?plugin=url-encoded-plugin-argument-value&unsupported-arguments=should-be-ignored#Dummy+profile+name. This kind of URIs can be parsed by standard libraries provided by most languages.

For plugin argument, we use the similar format as TOR_PT_SERVER_TRANSPORT_OPTIONS, which have the format like simple-obfs;obfs=http;obfs-host=example.com where colons, semicolons, equal signs and backslashes MUST be escaped with a backslash.

Examples:

With user info encoded with Base64URL:

  • ss://YWVzLTEyOC1nY206dGVzdA@192.168.100.1:8888#Example1
  • ss://cmM0LW1kNTpwYXNzd2Q@192.168.100.1:8888/?plugin=obfs-local%3Bobfs%3Dhttp#Example2

Plain user info:

  • ss://2022-blake3-aes-256-gcm:YctPZ6U7xPPcU%2Bgp3u%2B0tx%2FtRizJN9K8y%2BuKlW2qjlI%3D@192.168.100.1:8888#Example3
  • ss://2022-blake3-aes-256-gcm:YctPZ6U7xPPcU%2Bgp3u%2B0tx%2FtRizJN9K8y%2BuKlW2qjlI%3D@192.168.100.1:8888/?plugin=v2ray-plugin%3Bserver#Example3

FAQ:

Q1: Why parse user info to Base64URL?

A1: To safely encode all the characters in the key string. Note that we never try to "encrypt" your key in the URI.

Q2: Why not parse host name and port number into Base64URL?

A2: As mentioned above, we never try to "encrypt" anything in the URI. Additional parsing of host name and port number is not necessary.

Q3: Why not every client supports SIP002 URI scheme?

A3: Currently, SIP002 is still an optional feature unless the client supports SIP003 plugin.

Q4: Why the tags with space is truncated?

A4: White space is not legal in URI. It should be escaped. For example, ss://...#shadowsocks server 1 (illegal URI) should be escaped into ss://...#shadowsocks%20server%201 (legal URI).

This website is released under the MIT License.

- diff --git a/doc/sip003.html b/doc/sip003.html index b237c10..e2c21d4 100644 --- a/doc/sip003.html +++ b/doc/sip003.html @@ -10,7 +10,7 @@ - + @@ -24,8 +24,8 @@ | +------------+ +---------------------------+ | | SS Server +-- Local Loopback --+ Plugin Server (Tunnel) +--+ -+------------+ +---------------------------+

Life cycle of a plugin

Very similar to PT, the plugin client/server is started as child process of shadowsocks client/server.

If any error happens, the child process of plugin should exit with a error code. Then, the parent process of shadowsocks stops as well (SIGCHLD).

When a shadowsocks client/server is stopped by user, the child process of plugin will also be terminated.

Passing arguments to a plugin

A plugin accepts arguments through environment variables.

a. Four MUST-HAVE environment variables are SS_REMOTE_HOST, SS_REMOTE_PORT, SS_LOCAL_HOST and SS_LOCAL_PORT. SS_REMOTE_HOST and SS_REMOTE_PORT are the hostname and port of the remote plugin service. SS_LOCAL_HOST and SS_LOCAL_PORT are the hostname and port of the local shadowsocks or plugin service.

b. One OPTIONAL environment variable is SS_PLUGIN_OPTIONS. If a plugin requires additional arguments, like path to a config file, these arguments can be passed as extra options in a formatted string. An example is 'obfs=http;obfs-host=www.baidu.com', where semicolons, equal signs and backslashes MUST be escaped with a backslash.

Compatibility with PT

For all the plugins from Tor projects, there are two possible ways to support them. 1) We can fork these plugins and modify them to support SIP003, e.g. obfs4-tunnel. 2) Implement a adapter of PT as SIP003 plugin.

Licenses of plugins

As all plugin services should run in a separate process, they can pick any license they like. There is no GPL restrictions for any plugin providers.

Restrictions

a. Plugin over plugin is NOT supported. Only one plugin can be enabled when a shadowsocks service is started. If you really need this feature, implement a plugin-over-plugin transport as a SIP003 plugin. b. Only TCP traffic is forwarded. For now, there is no plan to support UDP traffic forwarding.

Example projects

Command line example

On the server:

bash
ss-server --plugin obfs-server --plugin-opts "obfs=http"

On the client:

bash
ss-local -c config.json --plugin obfs-local --plugin-opts "obfs=http;obfs-host=www.baidu.com"

Known good SIP003 plugins

- diff --git a/doc/sip008.html b/doc/sip008.html index c9a4956..16820dd 100644 --- a/doc/sip008.html +++ b/doc/sip008.html @@ -10,7 +10,7 @@ - + @@ -47,8 +47,8 @@ "bytes_used": 274877906944, "bytes_remaining": 824633720832 // You may add other custom fields in the root object. -}

Transport and Delivery

- diff --git a/doc/sip022.html b/doc/sip022.html index 4077585..8586f66 100644 --- a/doc/sip022.html +++ b/doc/sip022.html @@ -10,7 +10,7 @@ - + @@ -120,8 +120,8 @@ | server session ID | server packet ID | type | timestamp | client session ID | padding length | padding | ATYP | address | port | +-------------------+------------------+------+------------------+-------------------+----------------+----------+------+----------+-------+ | 8B | u64be | 1B | u64be unix epoch | 8B | u16be | variable | 1B | variable | u16be | -+-------------------+------------------+------+------------------+-------------------+----------------+----------+------+----------+-------+

Acknowledgement

I would like to thank @zonyitoo, @xiaokangwang, and @nekohasekai for their input on the design of the protocol.

- diff --git a/doc/sip023.html b/doc/sip023.html index 5b8fc8f..26c9c65 100644 --- a/doc/sip023.html +++ b/doc/sip023.html @@ -10,7 +10,7 @@ - + @@ -29,8 +29,8 @@ (iPSK0:iPSK1:uPSK2) >------/ +---> server1 [uPSK3] / client3 / - (iPSK0:uPSK3) >---+

A set of PSKs, delimited by :, are assigned to each client. To send a request, a client MUST generate one identity header for each iPSK.

A relay decrypts the first identity header with its identity key, looks up the PSK hash table to find the target server, and relays the remainder of the request.

A single-port-multi-user-capable server decrypts the identity header with its identity key, looks up the user PSK hash table to find the cipher for the user PSK, and processes the remainder of the request.

In the above graph, client0, client1, client2 are users of server0, which is relayed through relay0. server1 is a simple server without identity header support. client3 connects to server1 via relay0.

To start a TCP session, client0 generates a random salt, encrypts iPSK1's hash with iPSK0-derived subkey as the 1st identity header, encrypts uPSK0's hash with iPSK1-derived subkey as the 2nd identity header, and finishes the remainder of the request following the original spec.

To process the TCP request, relay0 decrypts the 1st identity header with iPSK0-derived subkey, looks up the PSK hash table, and writes the salt and remainder of the request (without the processed identity header) to server0.

To send a UDP packet, client0 encrypts the separate header with iPSK0, encrypts (iPSK1's hash XOR session_id_packet_id) with iPSK0 as the 1st identity header, encrypts (uPSK0's hash XOR session_id_packet_id) with iPSK1 as the 2nd identity header, and finishes off following the original spec.

To process the UDP packet, relay0 decrypts the separate header in-place with iPSK0, decrypts the 1st identity header with iPSK0, looks up the PSK hash table, re-encrypt the separate header into the place of the first identity header, and sends the packet (starting at the re-encrypted separate header) to server0.

- diff --git a/doc/stream.html b/doc/stream.html index 2b3c4e8..043a023 100644 --- a/doc/stream.html +++ b/doc/stream.html @@ -10,14 +10,14 @@ - + -
Skip to content
On this page

Stream ciphers

Stream ciphers are completely broken and will be removed soon. New users must use AEAD ciphers.

This historic document is for educational purposes only.

Stream Encryption/Decryption

Stream_encrypt is a function that takes a secret key, an initialization vector, a message, and produces a ciphertext with the same length as the message.

Stream_encrypt(key, IV, message) => ciphertext

Stream_decrypt is a function that takes a secret key, an initializaiton vector, a ciphertext, and produces the original message.

Stream_decrypt(key, IV, ciphertext) => message

The key can be input directly from user or generated from a password. The key derivation is following EVP_BytesToKey(3) in OpenSSL. The detailed spec can be found here: https://wiki.openssl.org/index.php/Manual:EVP_BytesToKey(3)

TCP

A stream cipher encrypted TCP stream starts with a randomly generated initializaiton vector, followed by encrypted payload data.

[IV][encrypted payload]

UDP

A stream cipher encrypted UDP packet has the following structure

[IV][encrypted payload]

Each UDP packet is encrypted/decrypted independently with a randomly generated initialization vector.

Historic stream ciphers

NameKey SizeIV Length
aes-128-ctr1616
aes-192-ctr2416
aes-256-ctr3216
aes-128-cfb1616
aes-192-cfb2416
aes-256-cfb3216
camellia-128-cfb1616
camellia-192-cfb2416
camellia-256-cfb3216
chacha20-ietf3212
bf-cfb168
chacha20328
salsa20328
rc4-md51616

This website is released under the MIT License.

- diff --git a/doc/what-is-shadowsocks.html b/doc/what-is-shadowsocks.html index 74318f9..45e49fe 100644 --- a/doc/what-is-shadowsocks.html +++ b/doc/what-is-shadowsocks.html @@ -10,14 +10,14 @@ - + -
Skip to content
On this page

What is Shadowsocks?

Shadowsocks is a secure split proxy loosely based on SOCKS5.

client <---> ss-local <--[encrypted]--> ss-remote <---> target

The Shadowsocks local component (ss-local) acts like a traditional SOCKS5 server and provides proxy service to clients. It encrypts and forwards data streams and packets from the client to the Shadowsocks remote component (ss-remote), which decrypts and forwards to the target. Replies from target are similarly encrypted and relayed by ss-remote back to ss-local, which decrypts and eventually returns to the original client.

Addressing

Addresses used in Shadowsocks follow the SOCKS5 address format:

[1-byte type][variable-length host][2-byte port]

The following address types are defined:

  • 0x01: host is a 4-byte IPv4 address.
  • 0x03: host is a variable length string, starting with a 1-byte length, followed by up to 255-byte domain name.
  • 0x04: host is a 16-byte IPv6 address.

The port number is a 2-byte big-endian unsigned integer.

TCP

ss-local initiates a TCP connection to ss-remote by sending an encrypted data stream starting with the target address followed by payload data. The exact encryption scheme differs depending on the cipher used.

[target address][payload]

ss-remote receives the encrypted data stream, decrypts and parses the leading target address. It then establishes a new TCP connection to the target and forwards payload data to it. ss-remote receives reply from the target, encrypts and forwards it back to the ss-local, until ss-local disconnects.

For better obfuscation purposes, both local and remote SHOULD send the handshake data along with some payload in the first packet.

UDP

ss-local sends an encrypted data packet containing the target address and payload to ss-remote.

[target address][payload]

Upon receiving the encrypted packet, ss-remote decrypts and parses the target address. It then sends a new data packet containing only the payload to the target. ss-remote receives data packets back from target and prepends the target address to the payload in each packet, then sends encrypted copies back to ss-local.

[target address][payload]

Essentially, ss-remote is performing Network Address Translation for ss-local.

This website is released under the MIT License.

- diff --git a/doc/what-is-sip.html b/doc/what-is-sip.html index 5ad1bb8..cb49061 100644 --- a/doc/what-is-sip.html +++ b/doc/what-is-sip.html @@ -10,14 +10,14 @@ - + -
Skip to content
On this page

What is SIP

SIP stands for Shadowsocks Improvement Proposal. SIPs are standards specifying potential new features or processes for Shadowsocks.

How to submit a SIP?

Shadowsocks is a community open to any reasonable improvements. To submit a SIP, please open a SIP issue on our GitHub issue tracker.

When will a SIP be accepted?

If most of the core contributors agree to accept a SIP, a formal SIP document will be published on this site. All the developers will review and follow the SIP to implement their own implementations. The whole process usually takes months.

This website is released under the MIT License.

- diff --git a/hashmap.json b/hashmap.json index 85c58f1..d376d38 100644 --- a/hashmap.json +++ b/hashmap.json @@ -1 +1 @@ -{"doc_advanced.md":"aaab6733","doc_stream.md":"ceb77f24","doc_aead.md":"04b1947a","doc_what-is-sip.md":"5b61a30c","index.md":"2a8afa05","doc_sip022.md":"10538951","doc_deploying.md":"c558b9e9","doc_sip023.md":"17c4bc0b","doc_what-is-shadowsocks.md":"ac508dd6","doc_sip008.md":"f8790501","doc_contributors.md":"4e813132","doc_getting-started.md":"b448fa30","doc_sip002.md":"bdd62ea4","doc_configs.md":"ffd0c0d9","doc_sip003.md":"a00aec58"} +{"doc_sip008.md":"9ab13a28","doc_aead.md":"1a1aa961","doc_advanced.md":"dcc3bff7","doc_configs.md":"dea687f7","doc_contributors.md":"8415bd3e","doc_deploying.md":"09688cfb","doc_sip003.md":"550330d8","doc_sip002.md":"7d5727dc","doc_getting-started.md":"95c40c21","index.md":"fcd24b3f","doc_sip023.md":"7ad16cb4","doc_stream.md":"e8b38e2e","doc_sip022.md":"9fcd5786","doc_what-is-shadowsocks.md":"d789ca84","doc_what-is-sip.md":"46284b5c"} diff --git a/index.html b/index.html index d6b32ab..7bb4286 100644 --- a/index.html +++ b/index.html @@ -10,14 +10,14 @@ - +
Skip to content

Shadowsocks

A fast tunnel proxy that helps you bypass firewalls

Super Fast

Bleeding edge techniques using Asynchronous I/O and Event-driven programming.

Flexible Encryption

Secured with industry level encryption algorithm. Flexible to support custom algorithms.

Mobile Ready

Optimized for mobile device and wireless network, with low CPU and bandwidth usage.

Cross Platform

Available on most platforms, including Windows, Linux, Mac, Android, iOS, and OpenWRT.

This website is released under the MIT License.

-