mirror of
https://github.com/shadowsocks/shadowsocks-rust.git
synced 2026-11-06 08:14:07 +00:00
Add shadowsocks-crypto
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
[build]
|
||||
rustflags = [
|
||||
"--print", "native-static-libs",
|
||||
]
|
||||
|
||||
[target.'cfg(any(target_arch = "x86", target_arch = "x86_64"))']
|
||||
rustflags = [
|
||||
# "--print", "native-static-libs",
|
||||
"-C", "target-cpu=native",
|
||||
"-C", "target-feature=+aes,+pclmul,+sse,+sse2,+sse3,+ssse3,+sse4.1,+sse4.2,+avx,+avx2,+fma,adx",
|
||||
]
|
||||
|
||||
[target.'cfg(target_arch = "arm")']
|
||||
rustflags = [
|
||||
# "--print", "native-static-libs",
|
||||
"-C", "target-cpu=native",
|
||||
]
|
||||
|
||||
# NOTE:
|
||||
# Crypto: AES + PMULL + SHA1 + SHA2
|
||||
# https://github.com/rust-lang/stdarch/blob/master/crates/std_detect/src/detect/arch/aarch64.rs#L26
|
||||
[target.'cfg(target_arch = "aarch64")']
|
||||
rustflags = [
|
||||
# "--print", "native-static-libs",
|
||||
"-C", "target-cpu=native",
|
||||
"-C", "target-feature=+aes,+crypto,+neon",
|
||||
]
|
||||
@@ -87,12 +87,6 @@ jobs:
|
||||
- restore_cache:
|
||||
key: cargo-cache-{{ arch }}-{{ checksum "rust-version" }}-{{ checksum "Cargo.lock" }}
|
||||
# - run: ./build-host-release
|
||||
- run:
|
||||
name: "Build Release"
|
||||
command: |
|
||||
$env:Path += ";C:\Users\circleci\.cargo\bin"
|
||||
cargo build --release --features "aes-pmac-siv"
|
||||
no_output_timeout: 1h
|
||||
- run:
|
||||
name: "Package Release"
|
||||
command: |
|
||||
|
||||
@@ -24,10 +24,6 @@ jobs:
|
||||
~/.cargo/git
|
||||
target
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
- name: Build
|
||||
run: cargo build --features "aes-pmac-siv local-redir" --verbose
|
||||
- name: Run tests
|
||||
run: cargo test --features "aes-pmac-siv local-redir" --verbose
|
||||
|
||||
build-windows:
|
||||
runs-on: windows-latest
|
||||
@@ -41,10 +37,6 @@ jobs:
|
||||
~/.cargo/git
|
||||
target
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
- name: Build
|
||||
run: cargo build --features "aes-pmac-siv openssl-vendored" --verbose
|
||||
- name: Run tests
|
||||
run: cargo test --features "aes-pmac-siv openssl-vendored" --verbose
|
||||
|
||||
build-macos:
|
||||
runs-on: macos-latest
|
||||
@@ -63,7 +55,3 @@ jobs:
|
||||
~/.cargo/git
|
||||
target
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
- name: Build
|
||||
run: cargo build --features "aes-pmac-siv local-redir" --verbose
|
||||
- name: Run tests
|
||||
run: cargo test --features "aes-pmac-siv local-redir" --verbose
|
||||
|
||||
Generated
+159
-320
@@ -6,64 +6,6 @@ version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
|
||||
|
||||
[[package]]
|
||||
name = "aead"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4cf01b9b56e767bb57b94ebf91a58b338002963785cdd7013e21c0d4679471e4"
|
||||
dependencies = [
|
||||
"generic-array 0.12.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aes"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54eb1d8fe354e5fc611daf4f2ea97dd45a765f4f1e4512306ec183ae2e8f20c9"
|
||||
dependencies = [
|
||||
"aes-soft",
|
||||
"aesni",
|
||||
"block-cipher-trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aes-siv"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34eed13dc1be5d8c2a5a1f40e166bd9308b7cc41cc53a30f42d6c9ff21828cbb"
|
||||
dependencies = [
|
||||
"aead",
|
||||
"aes",
|
||||
"cmac",
|
||||
"crypto-mac 0.7.0",
|
||||
"ctr",
|
||||
"dbl",
|
||||
"pmac",
|
||||
"stream-cipher",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aes-soft"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d"
|
||||
dependencies = [
|
||||
"block-cipher-trait",
|
||||
"byteorder",
|
||||
"opaque-debug 0.2.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aesni"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100"
|
||||
dependencies = [
|
||||
"block-cipher-trait",
|
||||
"opaque-debug 0.2.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.15"
|
||||
@@ -79,7 +21,7 @@ version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -107,7 +49,7 @@ checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -149,25 +91,7 @@ dependencies = [
|
||||
"block-padding",
|
||||
"byte-tools",
|
||||
"byteorder",
|
||||
"generic-array 0.12.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
|
||||
dependencies = [
|
||||
"generic-array 0.14.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-cipher-trait"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774"
|
||||
dependencies = [
|
||||
"generic-array 0.12.3",
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -266,7 +190,7 @@ dependencies = [
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"time",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -294,22 +218,11 @@ dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cmac"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f4a435124bcc292eba031f1f725d7abacdaf13cbf9f935450e8c45aa9e96cad"
|
||||
dependencies = [
|
||||
"block-cipher-trait",
|
||||
"crypto-mac 0.7.0",
|
||||
"dbl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cmake"
|
||||
version = "0.1.44"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e56268c17a6248366d66d4a47a3381369d068cce8409bb1716ed77ea32163bb"
|
||||
checksum = "eb6210b637171dfba4cda12e579ac6dc73f5165ad56133e5d72ef3131f320855"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
@@ -346,12 +259,6 @@ version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b"
|
||||
|
||||
[[package]]
|
||||
name = "cpuid-bool"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.2.1"
|
||||
@@ -362,34 +269,10 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-mac"
|
||||
version = "0.7.0"
|
||||
name = "crypto2"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5"
|
||||
dependencies = [
|
||||
"generic-array 0.12.3",
|
||||
"subtle 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-mac"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4857fd85a0c34b3c3297875b747c1e02e06b6a0ea32dd892d8192b9ce0813ea6"
|
||||
dependencies = [
|
||||
"generic-array 0.14.4",
|
||||
"subtle 2.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctr"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "022cd691704491df67d25d006fe8eca083098253c4d43516c2206479c58c6736"
|
||||
dependencies = [
|
||||
"block-cipher-trait",
|
||||
"stream-cipher",
|
||||
]
|
||||
checksum = "01fbcb1373267875ae654300ee278f9d3e18aade3a391a52f4a74e04644c2610"
|
||||
|
||||
[[package]]
|
||||
name = "daemonize"
|
||||
@@ -407,31 +290,13 @@ version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "993a608597367c6377b258c25d7120740f00ed23a2252b729b1932dd7866f908"
|
||||
|
||||
[[package]]
|
||||
name = "dbl"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28dc203b75decc900220c4d9838e738d08413e663c26826ba92b669bed1d0795"
|
||||
dependencies = [
|
||||
"generic-array 0.12.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
|
||||
dependencies = [
|
||||
"generic-array 0.12.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
|
||||
dependencies = [
|
||||
"generic-array 0.14.4",
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -520,6 +385,22 @@ version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394"
|
||||
|
||||
[[package]]
|
||||
name = "fuchsia-zircon"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"fuchsia-zircon-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fuchsia-zircon-sys"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.8"
|
||||
@@ -624,16 +505,6 @@ dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.1.15"
|
||||
@@ -688,26 +559,6 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hkdf"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51ab2f639c231793c5f6114bdb9bbe50a7dbbfcd7c7c6bd8475dec2d991e964f"
|
||||
dependencies = [
|
||||
"digest 0.9.0",
|
||||
"hmac",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hmac"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15"
|
||||
dependencies = [
|
||||
"crypto-mac 0.10.0",
|
||||
"digest 0.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hostname"
|
||||
version = "0.3.1"
|
||||
@@ -716,7 +567,7 @@ checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"match_cfg",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -770,7 +621,7 @@ checksum = "3c1ad908cc71012b7bea4d0c53ba96a8cba9962f048fa68d143376143d863b7a"
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.14.0-dev"
|
||||
source = "git+https://github.com/hyperium/hyper.git#b3b8010e1c6eadc2769103e13f1c1d8cca521a5b"
|
||||
source = "git+https://github.com/hyperium/hyper.git#2f2ceb24265a7e63601cf1ffde7d586cd666a783"
|
||||
dependencies = [
|
||||
"bytes 0.5.6",
|
||||
"futures-channel",
|
||||
@@ -820,6 +671,15 @@ dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iovec"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipconfig"
|
||||
version = "0.2.2"
|
||||
@@ -828,7 +688,7 @@ checksum = "f7e2f18aece9709094573a9f24f483c4f65caa4298e2f7ae1b71cc65d853fad7"
|
||||
dependencies = [
|
||||
"socket2",
|
||||
"widestring",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
"winreg",
|
||||
]
|
||||
|
||||
@@ -894,6 +754,16 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kernel32-sys"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
dependencies = [
|
||||
"winapi 0.2.8",
|
||||
"winapi-build",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
@@ -915,17 +785,6 @@ dependencies = [
|
||||
"cmake",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libsodium-sys"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a685b64f837b339074115f2e7f7b431ac73681d08d75b389db7498b8892b8a58"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.5.3"
|
||||
@@ -979,7 +838,7 @@ dependencies = [
|
||||
"serde_yaml",
|
||||
"thread-id",
|
||||
"typemap",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1015,17 +874,6 @@ version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
|
||||
|
||||
[[package]]
|
||||
name = "md-5"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15"
|
||||
dependencies = [
|
||||
"block-buffer 0.9.0",
|
||||
"digest 0.9.0",
|
||||
"opaque-debug 0.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.3.4"
|
||||
@@ -1053,40 +901,67 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.7.5"
|
||||
version = "0.6.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8962c171f57fcfffa53f4df1bb15ec4c8cf26a7569459c9ceb62d94aab0d9584"
|
||||
checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"fuchsia-zircon",
|
||||
"fuchsia-zircon-sys",
|
||||
"iovec",
|
||||
"kernel32-sys",
|
||||
"libc",
|
||||
"log",
|
||||
"miow 0.2.1",
|
||||
"net2",
|
||||
"slab",
|
||||
"winapi 0.2.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f33bc887064ef1fd66020c9adfc45bb9f33d75a42096c81e7c56c65b75dd1a8b"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"miow",
|
||||
"miow 0.3.6",
|
||||
"ntapi",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio-uds"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0"
|
||||
dependencies = [
|
||||
"iovec",
|
||||
"libc",
|
||||
"mio 0.6.22",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miow"
|
||||
version = "0.3.5"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07b88fb9795d4d36d62a012dfbf49a8f5cf12751f36d31a9dbe66d528e58979e"
|
||||
checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
|
||||
dependencies = [
|
||||
"socket2",
|
||||
"winapi",
|
||||
"kernel32-sys",
|
||||
"net2",
|
||||
"winapi 0.2.8",
|
||||
"ws2_32-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miscreant"
|
||||
version = "0.5.2"
|
||||
name = "miow"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c174360ad70a6f4c707dee38ac3c9b1bc74c1cd9bc9bc66bc20b8868f6d2672"
|
||||
checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"aes-siv",
|
||||
"cmac",
|
||||
"crypto-mac 0.7.0",
|
||||
"ctr",
|
||||
"pmac",
|
||||
"stream-cipher",
|
||||
"socket2",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1107,6 +982,17 @@ dependencies = [
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "net2"
|
||||
version = "0.2.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ebc3ec692ed7c9a255596c67808dee269f64655d8baf7b4f0638e51ba1d6853"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"libc",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.19.0"
|
||||
@@ -1125,7 +1011,7 @@ version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1169,12 +1055,6 @@ version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.30"
|
||||
@@ -1195,15 +1075,6 @@ version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-src"
|
||||
version = "111.12.0+1.1.1h"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "858a4132194f8570a7ee9eb8629e85b23cbc4565f2d4a162e87556e5956abf61"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.58"
|
||||
@@ -1213,7 +1084,6 @@ dependencies = [
|
||||
"autocfg",
|
||||
"cc",
|
||||
"libc",
|
||||
"openssl-src",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
@@ -1250,7 +1120,7 @@ dependencies = [
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1299,7 +1169,7 @@ checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d"
|
||||
dependencies = [
|
||||
"maplit",
|
||||
"pest",
|
||||
"sha-1 0.8.2",
|
||||
"sha-1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1360,17 +1230,6 @@ version = "0.3.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
|
||||
|
||||
[[package]]
|
||||
name = "pmac"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd76d63e86aa67a4bd063079f0d93bb8730b036eea5697345f651e6874ea61e5"
|
||||
dependencies = [
|
||||
"block-cipher-trait",
|
||||
"crypto-mac 0.7.0",
|
||||
"dbl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.10"
|
||||
@@ -1493,7 +1352,7 @@ version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1518,7 +1377,7 @@ dependencies = [
|
||||
"spin 0.5.2",
|
||||
"untrusted",
|
||||
"web-sys",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1559,7 +1418,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1695,23 +1554,21 @@ version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
|
||||
dependencies = [
|
||||
"block-buffer 0.7.3",
|
||||
"digest 0.8.1",
|
||||
"block-buffer",
|
||||
"digest",
|
||||
"fake-simd",
|
||||
"opaque-debug 0.2.3",
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha-1"
|
||||
version = "0.9.2"
|
||||
name = "shadowsocks-crypto"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce3cdf1b5e620a498ee6f2a171885ac7e22f0e12089ec4b3d22b84921792507c"
|
||||
checksum = "84db25042349bccfed0c40513ba3ee3522aeebef4f6c398fcc56e123e6e5c665"
|
||||
dependencies = [
|
||||
"block-buffer 0.9.0",
|
||||
"cfg-if 1.0.0",
|
||||
"cpuid-bool",
|
||||
"digest 0.9.0",
|
||||
"opaque-debug 0.3.0",
|
||||
"crypto2",
|
||||
"rand",
|
||||
"ring",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1727,11 +1584,8 @@ dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"clap",
|
||||
"daemonize",
|
||||
"digest 0.9.0",
|
||||
"env_logger",
|
||||
"futures",
|
||||
"hkdf",
|
||||
"hmac",
|
||||
"http",
|
||||
"hyper",
|
||||
"ipnet",
|
||||
@@ -1740,17 +1594,14 @@ dependencies = [
|
||||
"json5",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"libsodium-sys",
|
||||
"log",
|
||||
"log4rs",
|
||||
"lru_time_cache",
|
||||
"md-5",
|
||||
"mimalloc",
|
||||
"mio",
|
||||
"miscreant",
|
||||
"mio 0.6.22",
|
||||
"mio-uds",
|
||||
"native-tls",
|
||||
"nix",
|
||||
"openssl",
|
||||
"pin-project 1.0.1",
|
||||
"qrcode",
|
||||
"rand",
|
||||
@@ -1760,7 +1611,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"sha-1 0.9.2",
|
||||
"shadowsocks-crypto",
|
||||
"socket2",
|
||||
"spin 0.7.0",
|
||||
"strum",
|
||||
@@ -1772,10 +1623,9 @@ dependencies = [
|
||||
"tower",
|
||||
"trust-dns-proto",
|
||||
"trust-dns-resolver",
|
||||
"typenum",
|
||||
"url",
|
||||
"webpki-roots",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1814,7 +1664,7 @@ dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1829,15 +1679,6 @@ version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "652ac3743312871a5fb703f0337e68ffa3cdc28c863efad0b8dc858fa10c991b"
|
||||
|
||||
[[package]]
|
||||
name = "stream-cipher"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8131256a5896cabcf5eb04f4d6dacbe1aefda854b0d9896e09cb58829ec5638c"
|
||||
dependencies = [
|
||||
"generic-array 0.12.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
@@ -1862,18 +1703,6 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee"
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "343f3f510c2915908f155e94f17220b19ccfacf2a64a2a5d8004f2c3e311e7fd"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.48"
|
||||
@@ -1911,7 +1740,7 @@ dependencies = [
|
||||
"rand",
|
||||
"redox_syscall",
|
||||
"remove_dir_all",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1921,7 +1750,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1971,7 +1800,7 @@ checksum = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1991,7 +1820,7 @@ checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi 0.10.0+wasi-snapshot-preview1",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2021,14 +1850,14 @@ dependencies = [
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"memchr",
|
||||
"mio",
|
||||
"mio 0.7.6",
|
||||
"num_cpus",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"slab",
|
||||
"tokio-macros",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2152,7 +1981,7 @@ checksum = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079"
|
||||
[[package]]
|
||||
name = "trust-dns-https"
|
||||
version = "0.20.0-alpha.3"
|
||||
source = "git+https://github.com/bluejekyll/trust-dns.git?branch=upgrade-to-tokio-0.3#751299675322fb0792a1345838119cf182cceaf0"
|
||||
source = "git+https://github.com/bluejekyll/trust-dns.git?branch=upgrade-to-tokio-0.3#a931624c527b0db0f38260fddf3afe294121c7be"
|
||||
dependencies = [
|
||||
"bytes 0.5.6",
|
||||
"cfg-if 1.0.0",
|
||||
@@ -2174,7 +2003,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "trust-dns-proto"
|
||||
version = "0.20.0-alpha.3"
|
||||
source = "git+https://github.com/bluejekyll/trust-dns.git?branch=upgrade-to-tokio-0.3#751299675322fb0792a1345838119cf182cceaf0"
|
||||
source = "git+https://github.com/bluejekyll/trust-dns.git?branch=upgrade-to-tokio-0.3#a931624c527b0db0f38260fddf3afe294121c7be"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"cfg-if 1.0.0",
|
||||
@@ -2198,7 +2027,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "trust-dns-resolver"
|
||||
version = "0.20.0-alpha.3"
|
||||
source = "git+https://github.com/bluejekyll/trust-dns.git?branch=upgrade-to-tokio-0.3#751299675322fb0792a1345838119cf182cceaf0"
|
||||
source = "git+https://github.com/bluejekyll/trust-dns.git?branch=upgrade-to-tokio-0.3#a931624c527b0db0f38260fddf3afe294121c7be"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"futures-util",
|
||||
@@ -2223,7 +2052,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "trust-dns-rustls"
|
||||
version = "0.20.0-alpha.3"
|
||||
source = "git+https://github.com/bluejekyll/trust-dns.git?branch=upgrade-to-tokio-0.3#751299675322fb0792a1345838119cf182cceaf0"
|
||||
source = "git+https://github.com/bluejekyll/trust-dns.git?branch=upgrade-to-tokio-0.3#a931624c527b0db0f38260fddf3afe294121c7be"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-io",
|
||||
@@ -2338,12 +2167,6 @@ version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
|
||||
|
||||
[[package]]
|
||||
name = "want"
|
||||
version = "0.3.0"
|
||||
@@ -2455,6 +2278,12 @@ version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
@@ -2465,6 +2294,12 @@ dependencies = [
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-build"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
@@ -2477,7 +2312,7 @@ version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2492,7 +2327,17 @@ version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ws2_32-sys"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
|
||||
dependencies = [
|
||||
"winapi 0.2.8",
|
||||
"winapi-build",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2503,9 +2348,3 @@ checksum = "39f0c922f1a334134dc2f7a8b67dc5d25f0735263feec974345ff706bcf20b0d"
|
||||
dependencies = [
|
||||
"linked-hash-map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f33972566adbd2d3588b0491eb94b98b43695c4ef897903470ede4f3f5a28a"
|
||||
|
||||
+58
-60
@@ -39,30 +39,19 @@ incremental = false
|
||||
panic = "abort"
|
||||
|
||||
[features]
|
||||
default = ["ring-aead-ciphers", "sodium", "rc4", "aes-cfb", "aes-ctr", "trust-dns", "local-http", "local-http-rustls", "local-tunnel", "local-socks4"]
|
||||
# Enables AEAD ciphers that depending on ring
|
||||
# aes-128-gcm, aes-256-gcm, chacha20-ietf-poly1305
|
||||
ring-aead-ciphers = ["ring"]
|
||||
# Enables ciphers that depending on libsodium
|
||||
# chacha20, xchacha20, chacha20-ietf
|
||||
# salsa20, xsalsa20
|
||||
# xchacha20-ietf-poly1305
|
||||
sodium = ["libsodium-sys"]
|
||||
rc4 = ["openssl"]
|
||||
aes-cfb = ["openssl"]
|
||||
aes-ctr = ["openssl"]
|
||||
aes-pmac-siv = ["miscreant"]
|
||||
camellia-cfb = ["openssl"]
|
||||
default = [
|
||||
"trust-dns",
|
||||
"local-http",
|
||||
"local-http-rustls",
|
||||
"local-tunnel",
|
||||
"local-socks4"
|
||||
]
|
||||
# Uses single-threaded scheduler instead of default multi-threaded
|
||||
single-threaded = []
|
||||
# Enables trust-dns for replacing tokio's builtin DNS resolver
|
||||
trust-dns = ["trust-dns-resolver"]
|
||||
dns-over-tls = ["trust-dns", "trust-dns-resolver/dns-over-rustls"]
|
||||
dns-over-https = ["trust-dns", "trust-dns-resolver/dns-over-https-rustls"]
|
||||
# Enable vendored OpenSSL
|
||||
# rust-openssl tries to find OpenSSL system-wide by default,
|
||||
# by enabling this feature will try to build or use prebuilt OpenSSL libraries
|
||||
openssl-vendored = ["native-tls/vendored", "openssl/vendored"]
|
||||
# Enable DNS-relay
|
||||
local-dns = ["trust-dns-proto"]
|
||||
# Backward compatibility, DO NOT USE
|
||||
@@ -86,65 +75,74 @@ jemalloc = ["jemallocator"]
|
||||
# Enable bundled tcmalloc
|
||||
tcmalloc-vendored = ["tcmalloc/bundled"]
|
||||
|
||||
|
||||
[target.'cfg(any(target_arch = "x86", target_arch = "x86_64"))'.dependencies]
|
||||
shadowsocks-crypto = { version = "0.1", features = [ "ring" ] }
|
||||
|
||||
[target.'cfg(any(target_arch = "arm", target_arch = "aarch64"))'.dependencies]
|
||||
shadowsocks-crypto = { version = "0.1", features = [ "ring" ] }
|
||||
|
||||
[target.'cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64")))'.dependencies]
|
||||
shadowsocks-crypto = { version = "0.1", features = [ ] }
|
||||
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
rand = "0.7"
|
||||
clap = { version = "2.33", features = ["suggestions", "wrap_help"] }
|
||||
log = "0.4"
|
||||
rand = "0.7"
|
||||
clap = { version = "2.33", features = ["wrap_help", "suggestions"] }
|
||||
log4rs = "0.13"
|
||||
openssl = { version = "0.10", optional = true }
|
||||
libc = "^0.2.68"
|
||||
tokio = { version = "^0.3", features = ["full"] }
|
||||
libc = "0.2"
|
||||
bytes = "0.5"
|
||||
url = "2.1"
|
||||
regex = "1"
|
||||
cfg-if = "1.0"
|
||||
base64 = "0.13"
|
||||
byteorder = "1"
|
||||
byte_string = "1.0"
|
||||
lazy_static = "1.4"
|
||||
|
||||
ring = "0.16"
|
||||
|
||||
spin = "0.7"
|
||||
mio = "0.6"
|
||||
mio-uds = "0.6"
|
||||
socket2 = "0.3"
|
||||
pin-project = "1.0"
|
||||
bloomfilter = "^1.0.2"
|
||||
|
||||
json5 = "0.2"
|
||||
futures = "0.3"
|
||||
async-trait = "0.1"
|
||||
|
||||
tokio = { version = "^0.3", features = [ "full" ] }
|
||||
tokio-native-tls = { version = "0.2", optional = true }
|
||||
native-tls = { version = "0.2", optional = true }
|
||||
tokio-rustls = { version = "0.20", optional = true }
|
||||
webpki-roots = { version = "0.20", optional = true }
|
||||
rustls-native-certs = { version = "0.4", optional = true }
|
||||
futures = "0.3"
|
||||
json5 = "0.2"
|
||||
base64 = "0.13"
|
||||
bytes = "0.5"
|
||||
byteorder = "1"
|
||||
ring = { version = "0.16", optional = true }
|
||||
md-5 = "0.9"
|
||||
digest = "0.9"
|
||||
typenum = "1.11"
|
||||
|
||||
qrcode = { version = "0.12", default-features = false }
|
||||
serde_urlencoded = "0.7"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
url = "2.1"
|
||||
byte_string = "1.0"
|
||||
libsodium-sys = { version = "0.2", optional = true }
|
||||
miscreant = { version = "0.5", default-features = false, features = ["std", "pmac"], optional = true }
|
||||
serde_json = "1.0"
|
||||
serde_urlencoded = "0.7"
|
||||
|
||||
lru_time_cache = "0.11"
|
||||
http = { version = "0.2", optional = true }
|
||||
# trust-dns-resolver = { version = "0.19", optional = true }
|
||||
# trust-dns-proto = { version = "0.19", optional = true }
|
||||
# trust-dns-proto = "0.19"
|
||||
trust-dns-resolver = { git = "https://github.com/bluejekyll/trust-dns.git", optional = true, branch = "upgrade-to-tokio-0.3", features = ["serde-config"] }
|
||||
trust-dns-proto = { git = "https://github.com/bluejekyll/trust-dns.git", optional = true, branch = "upgrade-to-tokio-0.3" }
|
||||
hkdf = "0.10"
|
||||
hmac = "0.10"
|
||||
sha-1 = "0.9"
|
||||
lru_time_cache = "0.11"
|
||||
# hyper = { version = "0.14", optional = true }
|
||||
hyper = { git = "https://github.com/hyperium/hyper.git", optional = true }
|
||||
http = { version = "0.2", optional = true }
|
||||
# tower = { version = "0.4", optional = true }
|
||||
tower = { git = "https://github.com/tower-rs/tower.git", optional = true }
|
||||
pin-project = "1.0"
|
||||
socket2 = "0.3"
|
||||
cfg-if = "1.0"
|
||||
bloomfilter = "^1.0.2"
|
||||
spin = "0.7"
|
||||
mio = { version = "0.7", features = ["udp", "uds"] }
|
||||
serde_json = "1.0"
|
||||
regex = "1"
|
||||
|
||||
strum = "0.19"
|
||||
strum_macros = "0.19"
|
||||
iprange = "^0.6.3"
|
||||
ipnet = "2.2"
|
||||
async-trait = "0.1"
|
||||
lazy_static = "1.4"
|
||||
iprange = "0.6"
|
||||
|
||||
mimalloc = { version = "0.1", optional = true }
|
||||
tcmalloc = { version = "0.3", optional = true }
|
||||
jemallocator = { version = "0.3", optional = true }
|
||||
mimalloc = { version = "0.1", optional = true }
|
||||
tcmalloc = { version = "0.3", optional = true }
|
||||
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3", features = ["mswsock", "winsock2"] }
|
||||
@@ -160,4 +158,4 @@ daemonize = "^0.4.1"
|
||||
env_logger = "0.8"
|
||||
|
||||
[patch.crates-io]
|
||||
h2 = { git = "https://github.com/hyperium/h2.git" }
|
||||
h2 = { git = "https://github.com/hyperium/h2.git" }
|
||||
@@ -10,31 +10,11 @@ This is a port of [shadowsocks](https://github.com/shadowsocks/shadowsocks).
|
||||
|
||||
shadowsocks is a fast tunnel proxy that helps you bypass firewalls.
|
||||
|
||||
## Dependencies
|
||||
|
||||
* libcrypto (OpenSSL) (Required for `aes-*`, `camellia-*` and `rc4` ciphers)
|
||||
* libsodium >= 1.0.7 (Required for ciphers that are provided by libsodium)
|
||||
|
||||
## Build & Install
|
||||
|
||||
### Optional Features
|
||||
|
||||
* `sodium` - Enabled linking to [`libsodium`](https://github.com/jedisct1/libsodium), which will also enable ciphers that depending on `libsodium`.
|
||||
|
||||
* `rc4` - Enabled `rc4` encryption algorithm. Some OpenSSL Crypto does not ship with `rc4`, because it was already deprecated in 2015.
|
||||
|
||||
* `aes-cfb` - Enabled `aes-*-cfb` encryption algorithm.
|
||||
|
||||
* `aes-ctr` - Enabled `aes-*-ctr` encryption algorithm.
|
||||
|
||||
* `camellia-cfb` - Enabled `camellia-*-cfb` encryption algorithm.
|
||||
|
||||
* `aes-pmac-siv` - Enabled `aes-*-pmac-siv` encryption algorithm. (Experimental)
|
||||
|
||||
* Supported by [`miscreant`](https://crates.io/crates/miscreant), enable hardware accerlation on x86/x86\_64 with `RUSTFLAGS=-Ctarget-feature=+aes,+ssse3`
|
||||
|
||||
* `single-threaded` - Let `sslocal` and `ssserver` run in single threaded mode (by using Tokio's `basic_scheduler`).
|
||||
|
||||
* `trust-dns` - Uses [`trust-dns-resolver`](https://crates.io/crates/trust-dns-resolver) as DNS resolver instead of `tokio`'s builtin.
|
||||
|
||||
* `local-http` - Allow using HTTP protocol for `sslocal`
|
||||
@@ -57,9 +37,7 @@ This project uses system (libc) memory allocator (Rust's default). But it also a
|
||||
* `mimalloc` - Uses [mi-malloc](https://microsoft.github.io/mimalloc/) as global memory allocator
|
||||
* `tcmalloc` - Uses [TCMalloc](https://google.github.io/tcmalloc/overview.html) as global memory allocator. It tries to link system-wide tcmalloc by default, use vendored from source with `tcmalloc-vendored`.
|
||||
|
||||
Default features: `["sodium", "rc4", "aes-cfb", "aes-ctr", "trust-dns", "local-http", "local-http-native-tls", "local-tunnel", "local-socks4"]`.
|
||||
|
||||
NOTE: To disable dependency of OpenSSL, just disable feature `rc4`, `aes-cfb`, `aes-ctr`, `camellia-cfb`, `local-http-native-tls`.
|
||||
Default features: `["trust-dns", "local-http", "local-http-native-tls", "local-tunnel", "local-socks4"]`.
|
||||
|
||||
### **crates.io**
|
||||
|
||||
@@ -94,21 +72,6 @@ Use cargo to build.
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
NOTE: [`libsodium-sys`](https://crates.io/crates/libsodium-sys) builds and links statically to the `libsodium` in its package. Here are some useful environment variables for customizing build processes:
|
||||
|
||||
* Find `libsodium` installed in customized path:
|
||||
|
||||
* `SODIUM_LIB_DIR` - Directory path to `libsodium.a` or `sodium.lib`
|
||||
* `SODIUM_SHARED` - Dynamic-link instead of default static-link
|
||||
|
||||
* Find `libsodium` with `pkg-config` (*nix), `vcpkg` (MSVC)
|
||||
|
||||
* `SODIUM_USE_PKG_CONFIG=1`
|
||||
|
||||
```bash
|
||||
SODIUM_USE_PKG_CONFIG=1 cargo build --release
|
||||
```
|
||||
|
||||
Then `sslocal` and `ssserver` will appear in `./target/(debug|release)/`, it works similarly as the two binaries in the official ShadowSocks' implementation.
|
||||
|
||||
```bash
|
||||
@@ -129,21 +92,13 @@ Requirements:
|
||||
./build/build-release
|
||||
```
|
||||
|
||||
Then `sslocal`, `ssserver`, `sstunnel` and `ssurl` will be packaged in
|
||||
Then `sslocal`, `ssserver`, `ssmanager` and `ssurl` will be packaged in
|
||||
|
||||
* `./build/shadowsocks-${VERSION}-stable.x86_64-unknown-linux-musl.tar.xz`
|
||||
* `./build/shadowsocks-${VERSION}-stable.x86_64-pc-windows-gnu.zip`
|
||||
|
||||
Read `Cargo.toml` for more details.
|
||||
|
||||
### Build OpenSSL from source
|
||||
|
||||
Specify feature `openssl-vendored` to let [openssl](https://crates.io/crates/openssl) build from source.
|
||||
|
||||
```bash
|
||||
cargo build --features "openssl-vendored"
|
||||
```
|
||||
|
||||
## Getting Started
|
||||
|
||||
Create a ShadowSocks' configuration file. Example
|
||||
@@ -407,14 +362,13 @@ Example configuration:
|
||||
* `camellia-192-cfb`, `camellia-192-cfb1`, `camellia-192-cfb8`, `camellia-192-cfb128`
|
||||
* `camellia-256-cfb`, `camellia-256-cfb1`, `camellia-256-cfb8`, `camellia-256-cfb128`
|
||||
* `rc4`, `rc4-md5`
|
||||
* `chacha20`, `salsa20`, `chacha20-ietf`
|
||||
* `chacha20-ietf`
|
||||
* `plain` (No encryption, just for debugging)
|
||||
|
||||
### AEAD Ciphers
|
||||
|
||||
* `aes-128-gcm`, `aes-256-gcm`
|
||||
* `chacha20-ietf-poly1305`, `xchacha20-ietf-poly1305`
|
||||
* `aes-128-pmac-siv`, `aes-256-pmac-siv` (experimental)
|
||||
* `chacha20-ietf-poly1305`
|
||||
|
||||
## ACL
|
||||
|
||||
|
||||
+9
-6
@@ -10,14 +10,13 @@ use clap::{clap_app, Arg};
|
||||
use futures::future::{self, Either};
|
||||
use log::info;
|
||||
use tokio::{self, runtime::Builder};
|
||||
use shadowsocks_crypto::v1::CipherKind;
|
||||
|
||||
#[cfg(feature = "local-redir")]
|
||||
use shadowsocks::config::RedirType;
|
||||
use shadowsocks::{
|
||||
acl::AccessControl,
|
||||
crypto::CipherType,
|
||||
plugin::PluginConfig,
|
||||
relay::socks5::Address,
|
||||
run_local,
|
||||
Config,
|
||||
ConfigType,
|
||||
@@ -26,6 +25,10 @@ use shadowsocks::{
|
||||
ServerConfig,
|
||||
};
|
||||
|
||||
#[cfg(any(feature = "local-dns", feature = "local-tunnel", ))]
|
||||
use shadowsocks::relay::socks5::Address;
|
||||
|
||||
|
||||
mod allocator;
|
||||
#[cfg(unix)]
|
||||
mod daemonize;
|
||||
@@ -53,7 +56,7 @@ const AVAILABLE_PROTOCOLS: &[&str] = &[
|
||||
];
|
||||
|
||||
fn main() {
|
||||
let available_ciphers = CipherType::available_ciphers();
|
||||
// let available_ciphers = CipherType::available_ciphers();
|
||||
|
||||
let mut app = clap_app!(shadowsocks =>
|
||||
(version: shadowsocks::VERSION)
|
||||
@@ -68,7 +71,7 @@ fn main() {
|
||||
|
||||
(@arg SERVER_ADDR: -s --("server-addr") +takes_value {validator::validate_server_addr} requires[PASSWORD ENCRYPT_METHOD] "Server address")
|
||||
(@arg PASSWORD: -k --password +takes_value requires[SERVER_ADDR] "Server's password")
|
||||
(@arg ENCRYPT_METHOD: -m --("encrypt-method") +takes_value possible_values(&available_ciphers) requires[SERVER_ADDR] +next_line_help "Server's encryption method")
|
||||
(@arg ENCRYPT_METHOD: -m --("encrypt-method") +takes_value requires[SERVER_ADDR] +next_line_help "Server's encryption method")
|
||||
(@arg TIMEOUT: --timeout +takes_value {validator::validate_u64} requires[SERVER_ADDR] "Server's timeout seconds for TCP relay")
|
||||
|
||||
(@arg PLUGIN: --plugin +takes_value requires[SERVER_ADDR] "SIP003 (https://shadowsocks.org/en/spec/Plugin.html) plugin")
|
||||
@@ -180,7 +183,7 @@ fn main() {
|
||||
}
|
||||
|
||||
let matches = app.get_matches();
|
||||
drop(available_ciphers);
|
||||
// drop(available_ciphers);
|
||||
|
||||
match matches.value_of("LOG_CONFIG") {
|
||||
Some(path) => {
|
||||
@@ -227,7 +230,7 @@ fn main() {
|
||||
let method = matches
|
||||
.value_of("ENCRYPT_METHOD")
|
||||
.expect("encrypt-method")
|
||||
.parse::<CipherType>()
|
||||
.parse::<CipherKind>()
|
||||
.expect("encryption method");
|
||||
let svr_addr = svr_addr.parse::<ServerAddr>().expect("server-addr");
|
||||
|
||||
|
||||
+5
-5
@@ -16,11 +16,11 @@ use clap::{clap_app, Arg};
|
||||
use futures::future::{self, Either};
|
||||
use log::info;
|
||||
use tokio::{self, runtime::Builder};
|
||||
use shadowsocks_crypto::v1::CipherKind;
|
||||
|
||||
use shadowsocks::{
|
||||
acl::AccessControl,
|
||||
config::ManagerServerHost,
|
||||
crypto::CipherType,
|
||||
run_manager,
|
||||
Config,
|
||||
ConfigType,
|
||||
@@ -38,7 +38,7 @@ mod monitor;
|
||||
mod validator;
|
||||
|
||||
fn main() {
|
||||
let available_ciphers = CipherType::available_ciphers();
|
||||
// let available_ciphers = CipherKind::available_ciphers();
|
||||
|
||||
#[allow(unused_mut)]
|
||||
let mut app = clap_app!(shadowsocks =>
|
||||
@@ -59,7 +59,7 @@ fn main() {
|
||||
(@arg NO_DELAY: --("no-delay") !takes_value "Set TCP_NODELAY option for socket")
|
||||
|
||||
(@arg MANAGER_ADDRESS: --("manager-address") +takes_value {validator::validate_manager_addr} "ShadowSocks Manager (ssmgr) address, could be ip:port, domain:port or /path/to/unix.sock")
|
||||
(@arg ENCRYPT_METHOD: -m --("encrypt-method") +takes_value possible_values(&available_ciphers) +next_line_help "Default encryption method")
|
||||
(@arg ENCRYPT_METHOD: -m --("encrypt-method") +takes_value +next_line_help "Default encryption method")
|
||||
(@arg TIMEOUT: --timeout +takes_value {validator::validate_u64} "Default timeout seconds for TCP relay")
|
||||
|
||||
(@arg NOFILE: -n --nofile +takes_value "Set RLIMIT_NOFILE with both soft and hard limit (only for *nix systems)")
|
||||
@@ -92,7 +92,7 @@ fn main() {
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
drop(available_ciphers);
|
||||
// drop(available_ciphers);
|
||||
|
||||
match matches.value_of("LOG_CONFIG") {
|
||||
Some(path) => {
|
||||
@@ -153,7 +153,7 @@ fn main() {
|
||||
|
||||
if let Some(ref mut manager_config) = config.manager {
|
||||
if let Some(m) = matches.value_of("ENCRYPT_METHOD") {
|
||||
manager_config.method = Some(m.parse::<CipherType>().expect("encrypt-method"));
|
||||
manager_config.method = Some(m.parse::<CipherKind>().expect("encrypt-method"));
|
||||
}
|
||||
|
||||
if let Some(t) = matches.value_of("TIMEOUT") {
|
||||
|
||||
+6
-5
@@ -16,10 +16,11 @@ use clap::{clap_app, Arg};
|
||||
use futures::future::{self, Either};
|
||||
use log::info;
|
||||
use tokio::{self, runtime::Builder};
|
||||
use shadowsocks_crypto::v1::CipherKind;
|
||||
|
||||
|
||||
use shadowsocks::{
|
||||
acl::AccessControl,
|
||||
crypto::CipherType,
|
||||
plugin::PluginConfig,
|
||||
run_server,
|
||||
Config,
|
||||
@@ -39,7 +40,7 @@ mod monitor;
|
||||
mod validator;
|
||||
|
||||
fn main() {
|
||||
let available_ciphers = CipherType::available_ciphers();
|
||||
// let available_ciphers = CipherType::available_ciphers();
|
||||
|
||||
#[allow(unused_mut)]
|
||||
let mut app = clap_app!(shadowsocks =>
|
||||
@@ -55,7 +56,7 @@ fn main() {
|
||||
|
||||
(@arg SERVER_ADDR: -s --("server-addr") +takes_value {validator::validate_server_addr} requires[PASSWORD ENCRYPT_METHOD] "Server address")
|
||||
(@arg PASSWORD: -k --password +takes_value requires[SERVER_ADDR] "Server's password")
|
||||
(@arg ENCRYPT_METHOD: -m --("encrypt-method") +takes_value possible_values(&available_ciphers) requires[SERVER_ADDR] "Server's encryption method")
|
||||
(@arg ENCRYPT_METHOD: -m --("encrypt-method") +takes_value requires[SERVER_ADDR] "Server's encryption method")
|
||||
(@arg TIMEOUT: --timeout +takes_value {validator::validate_u64} requires[SERVER_ADDR] "Server's timeout seconds for TCP relay")
|
||||
|
||||
(@arg PLUGIN: --plugin +takes_value requires[SERVER_ADDR] "SIP003 (https://shadowsocks.org/en/spec/Plugin.html) plugin")
|
||||
@@ -97,7 +98,7 @@ fn main() {
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
drop(available_ciphers);
|
||||
// drop(available_ciphers);
|
||||
|
||||
match matches.value_of("LOG_CONFIG") {
|
||||
Some(path) => {
|
||||
@@ -123,7 +124,7 @@ fn main() {
|
||||
let method = matches
|
||||
.value_of("ENCRYPT_METHOD")
|
||||
.expect("encrypt-method")
|
||||
.parse::<CipherType>()
|
||||
.parse::<CipherKind>()
|
||||
.expect("encryption method");
|
||||
let svr_addr = svr_addr.parse::<ServerAddr>().expect("server-addr");
|
||||
let timeout = matches
|
||||
|
||||
+30
-17
@@ -57,22 +57,25 @@ use std::{
|
||||
};
|
||||
|
||||
use base64::{decode_config, encode_config, URL_SAFE_NO_PAD};
|
||||
use bytes::Bytes;
|
||||
use cfg_if::cfg_if;
|
||||
use log::error;
|
||||
use serde::{Deserialize, Serialize};
|
||||
#[cfg(feature = "trust-dns")]
|
||||
use trust_dns_resolver::config::{NameServerConfig, Protocol, ResolverConfig};
|
||||
use url::{self, Url};
|
||||
use bytes::Bytes;
|
||||
use shadowsocks_crypto::v1::{CipherKind, openssl_bytes_to_key};
|
||||
|
||||
|
||||
use crate::{
|
||||
acl::AccessControl,
|
||||
context::Context,
|
||||
crypto::cipher::CipherType,
|
||||
plugin::PluginConfig,
|
||||
relay::{dns_resolver::resolve_bind_addr, socks5::Address},
|
||||
};
|
||||
|
||||
|
||||
#[cfg(feature = "trust-dns")]
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(untagged)]
|
||||
enum SSDnsConfig {
|
||||
@@ -112,6 +115,7 @@ struct SSConfig {
|
||||
udp_max_associations: Option<usize>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
servers: Option<Vec<SSServerExtConfig>>,
|
||||
#[cfg(feature = "trust-dns")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
dns: Option<SSDnsConfig>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
@@ -276,11 +280,12 @@ pub struct ServerConfig {
|
||||
/// Encryption password (key)
|
||||
password: String,
|
||||
/// Encryption type (method)
|
||||
method: CipherType,
|
||||
method: CipherKind,
|
||||
/// Connection timeout
|
||||
timeout: Option<Duration>,
|
||||
/// Encryption key
|
||||
// Encryption key
|
||||
enc_key: Bytes,
|
||||
|
||||
/// Plugin config
|
||||
plugin: Option<PluginConfig>,
|
||||
/// Plugin address
|
||||
@@ -293,15 +298,18 @@ impl ServerConfig {
|
||||
/// Creates a new ServerConfig
|
||||
pub fn new(
|
||||
addr: ServerAddr,
|
||||
pwd: String,
|
||||
method: CipherType,
|
||||
password: String,
|
||||
method: CipherKind,
|
||||
timeout: Option<Duration>,
|
||||
plugin: Option<PluginConfig>,
|
||||
) -> ServerConfig {
|
||||
let enc_key = method.bytes_to_key(pwd.as_bytes());
|
||||
let mut key = vec![0u8; method.key_len()];
|
||||
openssl_bytes_to_key(password.as_bytes(), &mut key);
|
||||
let enc_key = Bytes::copy_from_slice(&key);
|
||||
|
||||
ServerConfig {
|
||||
addr,
|
||||
password: pwd,
|
||||
password,
|
||||
method,
|
||||
timeout,
|
||||
enc_key,
|
||||
@@ -312,15 +320,19 @@ impl ServerConfig {
|
||||
}
|
||||
|
||||
/// Create a basic config
|
||||
pub fn basic(addr: SocketAddr, password: String, method: CipherType) -> ServerConfig {
|
||||
pub fn basic(addr: SocketAddr, password: String, method: CipherKind) -> ServerConfig {
|
||||
ServerConfig::new(ServerAddr::SocketAddr(addr), password, method, None, None)
|
||||
}
|
||||
|
||||
/// Set encryption method
|
||||
pub fn set_method(&mut self, t: CipherType, pwd: String) {
|
||||
self.password = pwd;
|
||||
self.method = t;
|
||||
self.enc_key = t.bytes_to_key(self.password.as_bytes());
|
||||
pub fn set_method(&mut self, method: CipherKind, password: String) {
|
||||
self.method = method;
|
||||
|
||||
let mut key = vec![0u8; method.key_len()];
|
||||
openssl_bytes_to_key(password.as_bytes(), &mut key);
|
||||
|
||||
self.password = password;
|
||||
self.enc_key = Bytes::copy_from_slice(&key);
|
||||
}
|
||||
|
||||
/// Set plugin
|
||||
@@ -354,7 +366,7 @@ impl ServerConfig {
|
||||
}
|
||||
|
||||
/// Get method
|
||||
pub fn method(&self) -> CipherType {
|
||||
pub fn method(&self) -> CipherKind {
|
||||
self.method
|
||||
}
|
||||
|
||||
@@ -1077,7 +1089,7 @@ pub struct ManagerConfig {
|
||||
/// Address of `ss-manager`. Send servers' statistic data to the manager server
|
||||
pub addr: ManagerAddr,
|
||||
/// Manager's default method
|
||||
pub method: Option<CipherType>,
|
||||
pub method: Option<CipherKind>,
|
||||
/// Timeout for TCP connections, setting to manager's created servers
|
||||
pub timeout: Option<Duration>,
|
||||
/// IP/Host for servers to bind (inbound)
|
||||
@@ -1341,7 +1353,7 @@ impl Config {
|
||||
},
|
||||
};
|
||||
|
||||
let method = match m.parse::<CipherType>() {
|
||||
let method = match m.parse::<CipherKind>() {
|
||||
Ok(m) => m,
|
||||
Err(..) => {
|
||||
let err = Error::new(
|
||||
@@ -1394,7 +1406,7 @@ impl Config {
|
||||
},
|
||||
};
|
||||
|
||||
let method = match svr.method.parse::<CipherType>() {
|
||||
let method = match svr.method.parse::<CipherKind>() {
|
||||
Ok(m) => m,
|
||||
Err(..) => {
|
||||
let err = Error::new(
|
||||
@@ -1755,6 +1767,7 @@ impl fmt::Display for Config {
|
||||
jconf.no_delay = Some(self.no_delay);
|
||||
}
|
||||
|
||||
#[cfg(feature = "trust-dns")]
|
||||
if let Some(ref dns) = self.dns {
|
||||
jconf.dns = Some(SSDnsConfig::TrustDns(dns.clone()));
|
||||
}
|
||||
|
||||
@@ -1,311 +0,0 @@
|
||||
//! Aead Ciphers
|
||||
|
||||
use crate::crypto::cipher::{CipherCategory, CipherResult, CipherType};
|
||||
|
||||
#[cfg(feature = "ring-aead-ciphers")]
|
||||
use crate::crypto::ring::RingAeadCipher;
|
||||
#[cfg(feature = "aes-pmac-siv")]
|
||||
use crate::crypto::siv::MiscreantCipher;
|
||||
#[cfg(feature = "sodium")]
|
||||
use crate::crypto::sodium::SodiumAeadCipher;
|
||||
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use cfg_if::cfg_if;
|
||||
use hkdf::Hkdf;
|
||||
use sha1::Sha1;
|
||||
|
||||
/// Encryptor API for AEAD ciphers
|
||||
pub trait AeadEncryptor {
|
||||
/// Encrypt `input` to `output` with `tag`. `output.len()` should equals to `input.len() + tag.len()`.
|
||||
/// ```plain
|
||||
/// +----------------------------------------+-----------------------+
|
||||
/// | ENCRYPTED TEXT (length = input.len()) | TAG |
|
||||
/// +----------------------------------------+-----------------------+
|
||||
/// ```
|
||||
fn encrypt(&mut self, input: &[u8], output: &mut [u8]);
|
||||
}
|
||||
|
||||
/// Decryptor API for AEAD ciphers
|
||||
pub trait AeadDecryptor {
|
||||
/// Decrypt `input` to `output` with `tag`. `output.len()` should equals to `input.len() - tag.len()`.
|
||||
/// ```plain
|
||||
/// +----------------------------------------+-----------------------+
|
||||
/// | ENCRYPTED TEXT (length = output.len()) | TAG |
|
||||
/// +----------------------------------------+-----------------------+
|
||||
/// ```
|
||||
fn decrypt(&mut self, input: &[u8], output: &mut [u8]) -> CipherResult<()>;
|
||||
}
|
||||
|
||||
/// Variant `AeadDecryptor`
|
||||
pub type BoxAeadDecryptor = Box<dyn AeadDecryptor + Send + 'static>;
|
||||
|
||||
/// Variant `AeadEncryptor`
|
||||
pub type BoxAeadEncryptor = Box<dyn AeadEncryptor + Send + 'static>;
|
||||
|
||||
/// Generate a specific AEAD cipher encryptor
|
||||
pub fn new_aead_encryptor(t: CipherType, key: &[u8], nonce: &[u8]) -> BoxAeadEncryptor {
|
||||
assert!(t.category() == CipherCategory::Aead);
|
||||
|
||||
match t {
|
||||
#[cfg(feature = "ring-aead-ciphers")]
|
||||
CipherType::Aes128Gcm | CipherType::Aes256Gcm | CipherType::ChaCha20IetfPoly1305 => {
|
||||
Box::new(RingAeadCipher::new(t, key, nonce, true))
|
||||
}
|
||||
|
||||
#[cfg(feature = "sodium")]
|
||||
CipherType::XChaCha20IetfPoly1305 => Box::new(SodiumAeadCipher::new(t, key, nonce)),
|
||||
|
||||
#[cfg(feature = "aes-pmac-siv")]
|
||||
CipherType::Aes128PmacSiv | CipherType::Aes256PmacSiv => Box::new(MiscreantCipher::new(t, key, nonce)),
|
||||
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a specific AEAD cipher decryptor
|
||||
pub fn new_aead_decryptor(t: CipherType, key: &[u8], nonce: &[u8]) -> BoxAeadDecryptor {
|
||||
assert!(t.category() == CipherCategory::Aead);
|
||||
|
||||
match t {
|
||||
#[cfg(feature = "ring-aead-ciphers")]
|
||||
CipherType::Aes128Gcm | CipherType::Aes256Gcm | CipherType::ChaCha20IetfPoly1305 => {
|
||||
Box::new(RingAeadCipher::new(t, key, nonce, false))
|
||||
}
|
||||
|
||||
#[cfg(feature = "sodium")]
|
||||
CipherType::XChaCha20IetfPoly1305 => Box::new(SodiumAeadCipher::new(t, key, nonce)),
|
||||
|
||||
#[cfg(feature = "aes-pmac-siv")]
|
||||
CipherType::Aes128PmacSiv | CipherType::Aes256PmacSiv => Box::new(MiscreantCipher::new(t, key, nonce)),
|
||||
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
const SUBKEY_INFO: &[u8] = b"ss-subkey";
|
||||
|
||||
/// Make Session key
|
||||
///
|
||||
/// ## Session key (SIP007)
|
||||
///
|
||||
/// AEAD ciphers require a per-session subkey derived from the pre-shared master key using HKDF, and use the subkey
|
||||
/// to encrypt/decrypt. Essentially it means we are moving from (M+N)-bit (PSK, nonce) pair to
|
||||
/// (M+N)-bit (HKDF(PSK, salt), nonce) pair. Because HKDF is a PRF, the new construction significantly expands the
|
||||
/// amount of randomness (from N to at least M where M is much greater than N), thus correcting the previously
|
||||
/// mentioned design flaw.
|
||||
///
|
||||
/// Assuming we already have a user-supplied pre-shared master key PSK.
|
||||
///
|
||||
/// Function HKDF_SHA1 is a HKDF constructed using SHA1 hash. Its signature is
|
||||
///
|
||||
/// ```plain
|
||||
/// HKDF_SHA1(secret_key, salt, info)
|
||||
/// ```
|
||||
///
|
||||
/// The "info" string argument allows us to bind the derived subkey to a specific application context.
|
||||
///
|
||||
/// For AEAD ciphers, the encryption scheme is:
|
||||
///
|
||||
/// 1. Pick a random R-bit salt (R = max(128, len(SK)))
|
||||
/// 2. Derive subkey SK = HKDF_SHA1(PSK, salt, "ss-subkey")
|
||||
/// 3. Send salt
|
||||
/// 4. For each chunk, encrypt and authenticate payload using SK with a counting nonce
|
||||
/// (starting from 0 and increment by 1 after each use)
|
||||
/// 5. Send encrypted chunk
|
||||
pub fn make_skey(t: CipherType, key: &[u8], salt: &[u8]) -> Bytes {
|
||||
assert!(t.category() == CipherCategory::Aead);
|
||||
|
||||
let hkdf = Hkdf::<Sha1>::new(Some(salt), key);
|
||||
|
||||
let mut skey = BytesMut::with_capacity(key.len());
|
||||
unsafe {
|
||||
skey.set_len(key.len());
|
||||
}
|
||||
|
||||
hkdf.expand(SUBKEY_INFO, &mut skey).unwrap();
|
||||
|
||||
skey.freeze()
|
||||
}
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "sodium")] {
|
||||
/// Increase nonce by 1
|
||||
///
|
||||
/// AEAD ciphers requires to increase nonce after encrypt/decrypt every chunk (Little-Endian)
|
||||
pub fn increase_nonce(nonce: &mut [u8]) {
|
||||
use libsodium_sys::sodium_increment;
|
||||
|
||||
unsafe {
|
||||
sodium_increment(nonce.as_mut_ptr(), nonce.len());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if #[cfg(target_endian = "little")] {
|
||||
if #[cfg(target_pointer_width = "32")] {
|
||||
/// Increase nonce by 1
|
||||
///
|
||||
/// AEAD ciphers requires to increase nonce after encrypt/decrypt every chunk (Little-Endian)
|
||||
#[inline]
|
||||
pub fn increase_nonce(nonce: &mut [u8]) {
|
||||
increase_nonce_32_le(nonce);
|
||||
}
|
||||
|
||||
} else if #[cfg(target_pointer_width = "64")] {
|
||||
/// Increase nonce by 1
|
||||
///
|
||||
/// AEAD ciphers requires to increase nonce after encrypt/decrypt every chunk (Little-Endian)
|
||||
#[inline]
|
||||
pub fn increase_nonce(nonce: &mut [u8]) {
|
||||
increase_nonce_64_le(nonce);
|
||||
}
|
||||
} else {
|
||||
/// Increase nonce by 1
|
||||
///
|
||||
/// AEAD ciphers requires to increase nonce after encrypt/decrypt every chunk (Little-Endian)
|
||||
#[inline]
|
||||
pub fn increase_nonce(nonce: &mut [u8]) {
|
||||
increase_nonce_classic_le(nonce);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
/// Increase nonce by 1
|
||||
///
|
||||
/// AEAD ciphers requires to increase nonce after encrypt/decrypt every chunk (Little-Endian)
|
||||
#[inline]
|
||||
pub fn increase_nonce(nonce: &mut [u8]) {
|
||||
// Since current arthitecture is BE, integer add operations must be called after bswap operation.
|
||||
// bswap operation in most platforms are combinations of many instructions.
|
||||
increase_nonce_classic_le(nonce);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn increase_nonce_classic_le(nonce: &mut [u8]) {
|
||||
for i in nonce {
|
||||
if u8::MAX == *i {
|
||||
*i = 0;
|
||||
} else {
|
||||
*i += 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn increase_nonce_32_le(nonce: &mut [u8]) {
|
||||
let data = nonce.as_mut_ptr();
|
||||
|
||||
let mut i = 0;
|
||||
while i + 4 < nonce.len() {
|
||||
let v = unsafe { &mut *(data.offset(i as isize) as *mut u32) };
|
||||
if *v == u32::MAX {
|
||||
*v = 0;
|
||||
} else {
|
||||
*v += 1;
|
||||
return;
|
||||
}
|
||||
i += 4;
|
||||
}
|
||||
|
||||
if i < nonce.len() {
|
||||
// unlikely (missing intrinsic)
|
||||
|
||||
increase_nonce_classic_le(&mut nonce[i..]);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn increase_nonce_64_le(nonce: &mut [u8]) {
|
||||
let data = nonce.as_mut_ptr();
|
||||
|
||||
let mut i = 0;
|
||||
while i + 8 < nonce.len() {
|
||||
let v = unsafe { &mut *(data.offset(i as isize) as *mut u64) };
|
||||
if *v == u64::MAX {
|
||||
*v = 0;
|
||||
} else {
|
||||
*v += 1;
|
||||
return;
|
||||
}
|
||||
i += 8;
|
||||
}
|
||||
|
||||
if i < nonce.len() {
|
||||
// unlinkely (missing intrinsic)
|
||||
|
||||
increase_nonce_32_le(&mut nonce[i..]);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
#[test]
|
||||
fn increase_nonce_classic_le() {
|
||||
let mut v1 = [0, 0, 0, 0];
|
||||
super::increase_nonce_classic_le(&mut v1);
|
||||
assert_eq!(v1, [1, 0, 0, 0]);
|
||||
|
||||
let mut v2 = [0xff, 0, 0, 0];
|
||||
super::increase_nonce_classic_le(&mut v2);
|
||||
assert_eq!(v2, [0, 1, 0, 0]);
|
||||
|
||||
let mut v3 = [0xff, 0xff, 0xff, 0xff];
|
||||
super::increase_nonce_classic_le(&mut v3);
|
||||
assert_eq!(v3, [0, 0, 0, 0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_endian = "little")]
|
||||
fn increase_nonce_32_le() {
|
||||
let mut v1 = [0, 0, 0, 0];
|
||||
super::increase_nonce_32_le(&mut v1);
|
||||
assert_eq!(v1, [1, 0, 0, 0]);
|
||||
|
||||
let mut v2 = [0xff, 0, 0, 0];
|
||||
super::increase_nonce_32_le(&mut v2);
|
||||
assert_eq!(v2, [0, 1, 0, 0]);
|
||||
|
||||
let mut v3 = [0xff, 0xff, 0xff, 0xff];
|
||||
super::increase_nonce_32_le(&mut v3);
|
||||
assert_eq!(v3, [0, 0, 0, 0]);
|
||||
|
||||
let mut v4 = [0, 0, 0, 0, 0, 0];
|
||||
super::increase_nonce_32_le(&mut v4);
|
||||
assert_eq!(v4, [1, 0, 0, 0, 0, 0]);
|
||||
|
||||
let mut v5 = [0xff, 0xff, 0xff, 0xff, 0, 0];
|
||||
super::increase_nonce_32_le(&mut v5);
|
||||
assert_eq!(v5, [0, 0, 0, 0, 1, 0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_endian = "little")]
|
||||
fn increase_nonce_64_le() {
|
||||
let mut v1 = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
super::increase_nonce_64_le(&mut v1);
|
||||
assert_eq!(v1, [1, 0, 0, 0, 0, 0, 0, 0]);
|
||||
|
||||
let mut v2 = [0xff, 0, 0, 0, 0, 0, 0, 0];
|
||||
super::increase_nonce_64_le(&mut v2);
|
||||
assert_eq!(v2, [0, 1, 0, 0, 0, 0, 0, 0]);
|
||||
|
||||
let mut v3 = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
|
||||
super::increase_nonce_64_le(&mut v3);
|
||||
assert_eq!(v3, [0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
|
||||
let mut v4 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
super::increase_nonce_64_le(&mut v4);
|
||||
assert_eq!(v4, [1, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
|
||||
let mut v5 = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0];
|
||||
super::increase_nonce_64_le(&mut v5);
|
||||
assert_eq!(v5, [0, 0, 0, 0, 0, 0, 0, 0, 1, 0]);
|
||||
|
||||
let mut v6 = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0];
|
||||
super::increase_nonce_64_le(&mut v6);
|
||||
assert_eq!(v6, [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]);
|
||||
}
|
||||
}
|
||||
@@ -1,858 +0,0 @@
|
||||
//! Ciphers
|
||||
|
||||
use std::{
|
||||
convert::From,
|
||||
fmt::{self, Debug, Display},
|
||||
io,
|
||||
str::{self, FromStr},
|
||||
};
|
||||
|
||||
use bytes::{BufMut, Bytes, BytesMut};
|
||||
use digest::Digest;
|
||||
use md5::Md5;
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
use openssl::nid::Nid;
|
||||
#[cfg(feature = "openssl")]
|
||||
use openssl::symm;
|
||||
use rand::{self, RngCore};
|
||||
#[cfg(feature = "ring-aead-ciphers")]
|
||||
use ring::aead::{AES_128_GCM, AES_256_GCM, CHACHA20_POLY1305};
|
||||
use strum::IntoEnumIterator;
|
||||
use strum_macros::EnumIter;
|
||||
|
||||
/// Cipher result
|
||||
pub type CipherResult<T> = Result<T, Error>;
|
||||
|
||||
/// Cipher error
|
||||
pub enum Error {
|
||||
UnknownCipherType,
|
||||
#[cfg(feature = "openssl")]
|
||||
OpenSSLError(::openssl::error::ErrorStack),
|
||||
IoError(io::Error),
|
||||
AeadDecryptFailed,
|
||||
SodiumError,
|
||||
}
|
||||
|
||||
impl Debug for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Error::UnknownCipherType => write!(f, "UnknownCipherType"),
|
||||
#[cfg(feature = "openssl")]
|
||||
Error::OpenSSLError(ref err) => write!(f, "{:?}", err),
|
||||
Error::IoError(ref err) => write!(f, "{:?}", err),
|
||||
Error::AeadDecryptFailed => write!(f, "AEAD decrypt failed"),
|
||||
Error::SodiumError => write!(f, "Sodium error"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Error::UnknownCipherType => write!(f, "UnknownCipherType"),
|
||||
#[cfg(feature = "openssl")]
|
||||
Error::OpenSSLError(ref err) => write!(f, "{}", err),
|
||||
Error::IoError(ref err) => write!(f, "{}", err),
|
||||
Error::AeadDecryptFailed => write!(f, "AeadDecryptFailed"),
|
||||
Error::SodiumError => write!(f, "sodium error"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for io::Error {
|
||||
fn from(e: Error) -> io::Error {
|
||||
match e {
|
||||
Error::UnknownCipherType => io::Error::new(io::ErrorKind::Other, "unknown cipher type"),
|
||||
#[cfg(feature = "openssl")]
|
||||
Error::OpenSSLError(err) => From::from(err),
|
||||
Error::IoError(err) => err,
|
||||
Error::AeadDecryptFailed => io::Error::new(io::ErrorKind::Other, "AEAD decrypt error"),
|
||||
Error::SodiumError => io::Error::new(io::ErrorKind::Other, "sodium error"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "openssl")]
|
||||
impl From<::openssl::error::ErrorStack> for Error {
|
||||
fn from(e: ::openssl::error::ErrorStack) -> Error {
|
||||
Error::OpenSSLError(e)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
const CIPHER_AES_128_CFB: &str = "aes-128-cfb";
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
const CIPHER_AES_128_CFB_1: &str = "aes-128-cfb1";
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
const CIPHER_AES_128_CFB_8: &str = "aes-128-cfb8";
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
const CIPHER_AES_128_CFB_128: &str = "aes-128-cfb128";
|
||||
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
const CIPHER_AES_192_CFB: &str = "aes-192-cfb";
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
const CIPHER_AES_192_CFB_1: &str = "aes-192-cfb1";
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
const CIPHER_AES_192_CFB_8: &str = "aes-192-cfb8";
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
const CIPHER_AES_192_CFB_128: &str = "aes-192-cfb128";
|
||||
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
const CIPHER_AES_256_CFB: &str = "aes-256-cfb";
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
const CIPHER_AES_256_CFB_1: &str = "aes-256-cfb1";
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
const CIPHER_AES_256_CFB_8: &str = "aes-256-cfb8";
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
const CIPHER_AES_256_CFB_128: &str = "aes-256-cfb128";
|
||||
|
||||
#[cfg(feature = "aes-ctr")]
|
||||
const CIPHER_AES_128_CTR: &str = "aes-128-ctr";
|
||||
#[cfg(feature = "aes-ctr")]
|
||||
const CIPHER_AES_192_CTR: &str = "aes-192-ctr";
|
||||
#[cfg(feature = "aes-ctr")]
|
||||
const CIPHER_AES_256_CTR: &str = "aes-256-ctr";
|
||||
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
const CIPHER_CAMELLIA_128_CFB: &str = "camellia-128-cfb";
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
const CIPHER_CAMELLIA_192_CFB: &str = "camellia-192-cfb";
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
const CIPHER_CAMELLIA_256_CFB: &str = "camellia-256-cfb";
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
const CIPHER_CAMELLIA_128_CFB_1: &str = "camellia-128-cfb1";
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
const CIPHER_CAMELLIA_192_CFB_1: &str = "camellia-192-cfb1";
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
const CIPHER_CAMELLIA_256_CFB_1: &str = "camellia-256-cfb1";
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
const CIPHER_CAMELLIA_128_CFB_8: &str = "camellia-128-cfb8";
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
const CIPHER_CAMELLIA_192_CFB_8: &str = "camellia-192-cfb8";
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
const CIPHER_CAMELLIA_256_CFB_8: &str = "camellia-256-cfb8";
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
const CIPHER_CAMELLIA_128_CFB_128: &str = "camellia-128-cfb128";
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
const CIPHER_CAMELLIA_192_CFB_128: &str = "camellia-192-cfb128";
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
const CIPHER_CAMELLIA_256_CFB_128: &str = "camellia-256-cfb128";
|
||||
|
||||
#[cfg(feature = "rc4")]
|
||||
const CIPHER_RC4: &str = "rc4";
|
||||
#[cfg(feature = "rc4")]
|
||||
const CIPHER_RC4_MD5: &str = "rc4-md5";
|
||||
|
||||
const CIPHER_TABLE: &str = "table";
|
||||
|
||||
#[cfg(feature = "sodium")]
|
||||
const CIPHER_CHACHA20: &str = "chacha20";
|
||||
#[cfg(feature = "sodium")]
|
||||
const CIPHER_SALSA20: &str = "salsa20";
|
||||
#[cfg(feature = "sodium")]
|
||||
const CIPHER_XSALSA20: &str = "xsalsa20";
|
||||
#[cfg(feature = "sodium")]
|
||||
const CIPHER_CHACHA20_IETF: &str = "chacha20-ietf";
|
||||
|
||||
#[cfg(feature = "aes-pmac-siv")]
|
||||
const CIPHER_AES_128_PMAC_SIV: &str = "aes-128-pmac-siv";
|
||||
#[cfg(feature = "aes-pmac-siv")]
|
||||
const CIPHER_AES_256_PMAC_SIV: &str = "aes-256-pmac-siv";
|
||||
|
||||
const CIPHER_PLAIN: &str = "plain";
|
||||
const CIPHER_NONE: &str = "none";
|
||||
|
||||
#[cfg(feature = "ring-aead-ciphers")]
|
||||
const CIPHER_AES_128_GCM: &str = "aes-128-gcm";
|
||||
#[cfg(feature = "ring-aead-ciphers")]
|
||||
const CIPHER_AES_256_GCM: &str = "aes-256-gcm";
|
||||
#[cfg(feature = "ring-aead-ciphers")]
|
||||
const CIPHER_CHACHA20_IETF_POLY1305: &str = "chacha20-ietf-poly1305";
|
||||
#[cfg(feature = "sodium")]
|
||||
const CIPHER_XCHACHA20_IETF_POLY1305: &str = "xchacha20-ietf-poly1305";
|
||||
|
||||
/// ShadowSocks cipher type
|
||||
#[derive(Clone, Debug, Copy, EnumIter)]
|
||||
pub enum CipherType {
|
||||
Table,
|
||||
Plain,
|
||||
None,
|
||||
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
Aes128Cfb,
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
Aes128Cfb1,
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
Aes128Cfb8,
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
Aes128Cfb128,
|
||||
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
Aes192Cfb,
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
Aes192Cfb1,
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
Aes192Cfb8,
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
Aes192Cfb128,
|
||||
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
Aes256Cfb,
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
Aes256Cfb1,
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
Aes256Cfb8,
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
Aes256Cfb128,
|
||||
|
||||
#[cfg(feature = "aes-ctr")]
|
||||
Aes128Ctr,
|
||||
#[cfg(feature = "aes-ctr")]
|
||||
Aes192Ctr,
|
||||
#[cfg(feature = "aes-ctr")]
|
||||
Aes256Ctr,
|
||||
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
Camellia128Cfb,
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
Camellia192Cfb,
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
Camellia256Cfb,
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
Camellia128Cfb1,
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
Camellia192Cfb1,
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
Camellia256Cfb1,
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
Camellia128Cfb8,
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
Camellia192Cfb8,
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
Camellia256Cfb8,
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
Camellia128Cfb128,
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
Camellia192Cfb128,
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
Camellia256Cfb128,
|
||||
|
||||
#[cfg(feature = "rc4")]
|
||||
Rc4,
|
||||
#[cfg(feature = "rc4")]
|
||||
Rc4Md5,
|
||||
|
||||
#[cfg(feature = "sodium")]
|
||||
ChaCha20,
|
||||
#[cfg(feature = "sodium")]
|
||||
Salsa20,
|
||||
#[cfg(feature = "sodium")]
|
||||
XSalsa20,
|
||||
#[cfg(feature = "sodium")]
|
||||
ChaCha20Ietf,
|
||||
|
||||
#[cfg(feature = "ring-aead-ciphers")]
|
||||
Aes128Gcm,
|
||||
#[cfg(feature = "ring-aead-ciphers")]
|
||||
Aes256Gcm,
|
||||
|
||||
#[cfg(feature = "ring-aead-ciphers")]
|
||||
ChaCha20IetfPoly1305,
|
||||
#[cfg(feature = "sodium")]
|
||||
XChaCha20IetfPoly1305,
|
||||
|
||||
#[cfg(feature = "aes-pmac-siv")]
|
||||
Aes128PmacSiv,
|
||||
#[cfg(feature = "aes-pmac-siv")]
|
||||
Aes256PmacSiv,
|
||||
}
|
||||
|
||||
/// Category of ciphers
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum CipherCategory {
|
||||
/// No encryption
|
||||
None,
|
||||
/// Stream ciphers is used for OLD ShadowSocks protocol, which uses stream ciphers to encrypt data payloads
|
||||
Stream,
|
||||
/// AEAD ciphers is used in modern ShadowSocks protocol, which sends data in separate packets
|
||||
Aead,
|
||||
}
|
||||
|
||||
impl CipherType {
|
||||
/// Symmetric crypto key size
|
||||
pub fn key_size(self) -> usize {
|
||||
match self {
|
||||
CipherType::Table | CipherType::Plain | CipherType::None => 0,
|
||||
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes128Cfb1 => symm::Cipher::aes_128_cfb1().key_len(),
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes128Cfb8 => symm::Cipher::aes_128_cfb8().key_len(),
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes128Cfb | CipherType::Aes128Cfb128 => symm::Cipher::aes_128_cfb128().key_len(),
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes192Cfb1 => symm::Cipher::aes_192_cfb1().key_len(),
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes192Cfb8 => symm::Cipher::aes_192_cfb8().key_len(),
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes192Cfb | CipherType::Aes192Cfb128 => symm::Cipher::aes_192_cfb128().key_len(),
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes256Cfb1 => symm::Cipher::aes_256_cfb1().key_len(),
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes256Cfb8 => symm::Cipher::aes_256_cfb8().key_len(),
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes256Cfb | CipherType::Aes256Cfb128 => symm::Cipher::aes_256_cfb128().key_len(),
|
||||
|
||||
#[cfg(feature = "aes-ctr")]
|
||||
CipherType::Aes128Ctr => symm::Cipher::aes_128_ctr().key_len(),
|
||||
#[cfg(feature = "aes-ctr")]
|
||||
CipherType::Aes192Ctr => symm::Cipher::aes_192_ctr().key_len(),
|
||||
#[cfg(feature = "aes-ctr")]
|
||||
CipherType::Aes256Ctr => symm::Cipher::aes_256_ctr().key_len(),
|
||||
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia128Cfb => symm::Cipher::from_nid(Nid::CAMELLIA_128_CFB128)
|
||||
.expect("openssl doesn't support camellia-128-cfb")
|
||||
.key_len(),
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia128Cfb1 => symm::Cipher::from_nid(Nid::CAMELLIA_128_CFB1)
|
||||
.expect("openssl doesn't support camellia-128-cfb1")
|
||||
.key_len(),
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia128Cfb8 => symm::Cipher::from_nid(Nid::CAMELLIA_128_CFB8)
|
||||
.expect("openssl doesn't support camellia-128-cfb8")
|
||||
.key_len(),
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia128Cfb128 => symm::Cipher::from_nid(Nid::CAMELLIA_128_CFB128)
|
||||
.expect("openssl doesn't support camellia-128-cfb128")
|
||||
.key_len(),
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia192Cfb => symm::Cipher::from_nid(Nid::CAMELLIA_192_CFB128)
|
||||
.expect("openssl doesn't support camellia-192-cfb")
|
||||
.key_len(),
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia192Cfb1 => symm::Cipher::from_nid(Nid::CAMELLIA_192_CFB1)
|
||||
.expect("openssl doesn't support camellia-192-cfb1")
|
||||
.key_len(),
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia192Cfb8 => symm::Cipher::from_nid(Nid::CAMELLIA_192_CFB8)
|
||||
.expect("openssl doesn't support camellia-192-cfb8")
|
||||
.key_len(),
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia192Cfb128 => symm::Cipher::from_nid(Nid::CAMELLIA_192_CFB128)
|
||||
.expect("openssl doesn't support camellia-192-cfb128")
|
||||
.key_len(),
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia256Cfb => symm::Cipher::from_nid(Nid::CAMELLIA_256_CFB128)
|
||||
.expect("openssl doesn't support camellia-256-cfb")
|
||||
.key_len(),
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia256Cfb1 => symm::Cipher::from_nid(Nid::CAMELLIA_256_CFB1)
|
||||
.expect("openssl doesn't support camellia-256-cfb1")
|
||||
.key_len(),
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia256Cfb8 => symm::Cipher::from_nid(Nid::CAMELLIA_256_CFB8)
|
||||
.expect("openssl doesn't support camellia-256-cfb8")
|
||||
.key_len(),
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia256Cfb128 => symm::Cipher::from_nid(Nid::CAMELLIA_256_CFB128)
|
||||
.expect("openssl doesn't support camellia-256-cfb128")
|
||||
.key_len(),
|
||||
|
||||
#[cfg(feature = "rc4")]
|
||||
CipherType::Rc4 | CipherType::Rc4Md5 => symm::Cipher::rc4().key_len(),
|
||||
|
||||
#[cfg(feature = "sodium")]
|
||||
CipherType::ChaCha20 | CipherType::Salsa20 | CipherType::XSalsa20 | CipherType::ChaCha20Ietf => 32,
|
||||
|
||||
#[cfg(feature = "ring-aead-ciphers")]
|
||||
CipherType::Aes128Gcm => AES_128_GCM.key_len(),
|
||||
#[cfg(feature = "ring-aead-ciphers")]
|
||||
CipherType::Aes256Gcm => AES_256_GCM.key_len(),
|
||||
|
||||
#[cfg(feature = "ring-aead-ciphers")]
|
||||
CipherType::ChaCha20IetfPoly1305 => CHACHA20_POLY1305.key_len(),
|
||||
|
||||
#[cfg(feature = "sodium")]
|
||||
CipherType::XChaCha20IetfPoly1305 => 32,
|
||||
|
||||
#[cfg(feature = "aes-pmac-siv")]
|
||||
CipherType::Aes128PmacSiv => 32,
|
||||
#[cfg(feature = "aes-pmac-siv")]
|
||||
CipherType::Aes256PmacSiv => 64,
|
||||
}
|
||||
}
|
||||
|
||||
fn classic_bytes_to_key(self, key: &[u8]) -> Bytes {
|
||||
// key derivation by MD5 hash
|
||||
|
||||
let key_len = self.key_size();
|
||||
let digest_len = Md5::output_size();
|
||||
|
||||
let mut result = BytesMut::with_capacity((key_len + digest_len - 1) / digest_len);
|
||||
let mut m = None;
|
||||
|
||||
let mut d = Md5::new();
|
||||
while result.len() < key_len {
|
||||
if let Some(ref rm) = m {
|
||||
d.update(rm);
|
||||
}
|
||||
d.update(key);
|
||||
let digest = d.finalize_reset();
|
||||
result.put(&*digest);
|
||||
|
||||
m = Some(digest);
|
||||
}
|
||||
|
||||
result.truncate(key_len);
|
||||
result.freeze()
|
||||
}
|
||||
|
||||
/// Extends key to match the required key length
|
||||
pub fn bytes_to_key(self, key: &[u8]) -> Bytes {
|
||||
self.classic_bytes_to_key(key)
|
||||
}
|
||||
|
||||
/// Symmetric crypto initialize vector size
|
||||
pub fn iv_size(self) -> usize {
|
||||
match self {
|
||||
CipherType::Table | CipherType::Plain | CipherType::None => 0,
|
||||
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes128Cfb1 => symm::Cipher::aes_128_cfb1().iv_len().unwrap_or(0),
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes128Cfb8 => symm::Cipher::aes_128_cfb8().iv_len().unwrap_or(0),
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes128Cfb | CipherType::Aes128Cfb128 => symm::Cipher::aes_128_cfb128().iv_len().unwrap_or(0),
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes192Cfb1 => symm::Cipher::aes_192_cfb1().iv_len().unwrap_or(0),
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes192Cfb8 => symm::Cipher::aes_192_cfb8().iv_len().unwrap_or(0),
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes192Cfb | CipherType::Aes192Cfb128 => symm::Cipher::aes_192_cfb128().iv_len().unwrap_or(0),
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes256Cfb1 => symm::Cipher::aes_256_cfb1().iv_len().unwrap_or(0),
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes256Cfb8 => symm::Cipher::aes_256_cfb8().iv_len().unwrap_or(0),
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes256Cfb | CipherType::Aes256Cfb128 => symm::Cipher::aes_256_cfb128().iv_len().unwrap_or(0),
|
||||
|
||||
#[cfg(feature = "aes-ctr")]
|
||||
CipherType::Aes128Ctr => symm::Cipher::aes_128_ctr().iv_len().unwrap_or(0),
|
||||
#[cfg(feature = "aes-ctr")]
|
||||
CipherType::Aes192Ctr => symm::Cipher::aes_192_ctr().iv_len().unwrap_or(0),
|
||||
#[cfg(feature = "aes-ctr")]
|
||||
CipherType::Aes256Ctr => symm::Cipher::aes_256_ctr().iv_len().unwrap_or(0),
|
||||
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia128Cfb => symm::Cipher::from_nid(Nid::CAMELLIA_128_CFB128)
|
||||
.expect("openssl doesn't support camellia-128-cfb")
|
||||
.iv_len()
|
||||
.unwrap_or(0),
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia128Cfb1 => symm::Cipher::from_nid(Nid::CAMELLIA_128_CFB1)
|
||||
.expect("openssl doesn't support camellia-128-cfb1")
|
||||
.iv_len()
|
||||
.unwrap_or(0),
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia128Cfb8 => symm::Cipher::from_nid(Nid::CAMELLIA_128_CFB8)
|
||||
.expect("openssl doesn't support camellia-128-cfb8")
|
||||
.iv_len()
|
||||
.unwrap_or(0),
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia128Cfb128 => symm::Cipher::from_nid(Nid::CAMELLIA_128_CFB128)
|
||||
.expect("openssl doesn't support camellia-128-cfb128")
|
||||
.iv_len()
|
||||
.unwrap_or(0),
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia192Cfb => symm::Cipher::from_nid(Nid::CAMELLIA_192_CFB128)
|
||||
.expect("openssl doesn't support camellia-192-cfb")
|
||||
.iv_len()
|
||||
.unwrap_or(0),
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia192Cfb1 => symm::Cipher::from_nid(Nid::CAMELLIA_192_CFB1)
|
||||
.expect("openssl doesn't support camellia-192-cfb1")
|
||||
.iv_len()
|
||||
.unwrap_or(0),
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia192Cfb8 => symm::Cipher::from_nid(Nid::CAMELLIA_192_CFB8)
|
||||
.expect("openssl doesn't support camellia-192-cfb8")
|
||||
.iv_len()
|
||||
.unwrap_or(0),
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia192Cfb128 => symm::Cipher::from_nid(Nid::CAMELLIA_192_CFB128)
|
||||
.expect("openssl doesn't support camellia-192-cfb128")
|
||||
.iv_len()
|
||||
.unwrap_or(0),
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia256Cfb => symm::Cipher::from_nid(Nid::CAMELLIA_256_CFB128)
|
||||
.expect("openssl doesn't support camellia-256-cfb")
|
||||
.iv_len()
|
||||
.unwrap_or(0),
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia256Cfb1 => symm::Cipher::from_nid(Nid::CAMELLIA_256_CFB1)
|
||||
.expect("openssl doesn't support camellia-256-cfb1")
|
||||
.iv_len()
|
||||
.unwrap_or(0),
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia256Cfb8 => symm::Cipher::from_nid(Nid::CAMELLIA_256_CFB8)
|
||||
.expect("openssl doesn't support camellia-256-cfb8")
|
||||
.iv_len()
|
||||
.unwrap_or(0),
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia256Cfb128 => symm::Cipher::from_nid(Nid::CAMELLIA_256_CFB128)
|
||||
.expect("openssl doesn't support camellia-256-cfb128")
|
||||
.iv_len()
|
||||
.unwrap_or(0),
|
||||
|
||||
#[cfg(feature = "rc4")]
|
||||
CipherType::Rc4 => symm::Cipher::rc4().iv_len().unwrap_or(0),
|
||||
#[cfg(feature = "rc4")]
|
||||
CipherType::Rc4Md5 => 16,
|
||||
|
||||
#[cfg(feature = "sodium")]
|
||||
CipherType::ChaCha20 | CipherType::Salsa20 => 8,
|
||||
#[cfg(feature = "sodium")]
|
||||
CipherType::XSalsa20 => 24,
|
||||
#[cfg(feature = "sodium")]
|
||||
CipherType::ChaCha20Ietf => 12,
|
||||
|
||||
#[cfg(feature = "ring-aead-ciphers")]
|
||||
CipherType::Aes128Gcm => AES_128_GCM.nonce_len(),
|
||||
#[cfg(feature = "ring-aead-ciphers")]
|
||||
CipherType::Aes256Gcm => AES_256_GCM.nonce_len(),
|
||||
#[cfg(feature = "ring-aead-ciphers")]
|
||||
CipherType::ChaCha20IetfPoly1305 => CHACHA20_POLY1305.nonce_len(),
|
||||
#[cfg(feature = "sodium")]
|
||||
CipherType::XChaCha20IetfPoly1305 => 24,
|
||||
|
||||
#[cfg(feature = "aes-pmac-siv")]
|
||||
CipherType::Aes128PmacSiv => 8,
|
||||
#[cfg(feature = "aes-pmac-siv")]
|
||||
CipherType::Aes256PmacSiv => 8,
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_random_bytes(len: usize) -> Bytes {
|
||||
if len == 0 {
|
||||
return Bytes::new();
|
||||
}
|
||||
|
||||
let mut iv = BytesMut::with_capacity(len);
|
||||
unsafe {
|
||||
iv.set_len(len);
|
||||
}
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
rng.fill_bytes(&mut iv);
|
||||
iv.freeze()
|
||||
}
|
||||
|
||||
/// Generate a random initialize vector for this cipher
|
||||
pub fn gen_init_vec(self) -> Bytes {
|
||||
let iv_len = self.iv_size();
|
||||
CipherType::gen_random_bytes(iv_len)
|
||||
}
|
||||
|
||||
/// Get category of cipher
|
||||
pub fn category(self) -> CipherCategory {
|
||||
match self {
|
||||
CipherType::None | CipherType::Plain => CipherCategory::None,
|
||||
|
||||
#[cfg(feature = "ring-aead-ciphers")]
|
||||
CipherType::Aes128Gcm | CipherType::Aes256Gcm | CipherType::ChaCha20IetfPoly1305 => CipherCategory::Aead,
|
||||
|
||||
#[cfg(feature = "sodium")]
|
||||
CipherType::XChaCha20IetfPoly1305 => CipherCategory::Aead,
|
||||
|
||||
#[cfg(feature = "aes-pmac-siv")]
|
||||
CipherType::Aes128PmacSiv | CipherType::Aes256PmacSiv => CipherCategory::Aead,
|
||||
|
||||
_ => CipherCategory::Stream,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get tag size for AEAD Ciphers
|
||||
pub fn tag_size(self) -> usize {
|
||||
assert!(self.category() == CipherCategory::Aead);
|
||||
|
||||
match self {
|
||||
#[cfg(feature = "ring-aead-ciphers")]
|
||||
CipherType::Aes128Gcm => AES_128_GCM.tag_len(),
|
||||
#[cfg(feature = "ring-aead-ciphers")]
|
||||
CipherType::Aes256Gcm => AES_256_GCM.tag_len(),
|
||||
#[cfg(feature = "ring-aead-ciphers")]
|
||||
CipherType::ChaCha20IetfPoly1305 => CHACHA20_POLY1305.tag_len(),
|
||||
#[cfg(feature = "sodium")]
|
||||
CipherType::XChaCha20IetfPoly1305 => 16,
|
||||
|
||||
#[cfg(feature = "aes-pmac-siv")]
|
||||
CipherType::Aes128PmacSiv | CipherType::Aes256PmacSiv => 16,
|
||||
|
||||
_ => panic!("only support AEAD ciphers, found {:?}", self),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get nonce size for AEAD ciphers
|
||||
pub fn salt_size(self) -> usize {
|
||||
assert!(self.category() == CipherCategory::Aead);
|
||||
self.key_size()
|
||||
}
|
||||
|
||||
/// Get salt for AEAD ciphers
|
||||
pub fn gen_salt(self) -> Bytes {
|
||||
CipherType::gen_random_bytes(self.salt_size())
|
||||
}
|
||||
|
||||
/// Get all available ciphers in String representation
|
||||
pub fn available_ciphers() -> Vec<&'static str> {
|
||||
let mut v = Vec::new();
|
||||
for e in Self::iter() {
|
||||
v.push(e.name());
|
||||
}
|
||||
v
|
||||
}
|
||||
|
||||
/// Name of cipher
|
||||
pub fn name(self) -> &'static str {
|
||||
match self {
|
||||
CipherType::Table => CIPHER_TABLE,
|
||||
CipherType::Plain => CIPHER_PLAIN,
|
||||
CipherType::None => CIPHER_NONE,
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes128Cfb => CIPHER_AES_128_CFB,
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes128Cfb1 => CIPHER_AES_128_CFB_1,
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes128Cfb8 => CIPHER_AES_128_CFB_8,
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes128Cfb128 => CIPHER_AES_128_CFB_128,
|
||||
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes192Cfb => CIPHER_AES_192_CFB,
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes192Cfb1 => CIPHER_AES_192_CFB_1,
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes192Cfb8 => CIPHER_AES_192_CFB_8,
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes192Cfb128 => CIPHER_AES_192_CFB_128,
|
||||
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes256Cfb => CIPHER_AES_256_CFB,
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes256Cfb1 => CIPHER_AES_256_CFB_1,
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes256Cfb8 => CIPHER_AES_256_CFB_8,
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes256Cfb128 => CIPHER_AES_256_CFB_128,
|
||||
|
||||
#[cfg(feature = "aes-ctr")]
|
||||
CipherType::Aes128Ctr => CIPHER_AES_128_CTR,
|
||||
#[cfg(feature = "aes-ctr")]
|
||||
CipherType::Aes192Ctr => CIPHER_AES_192_CTR,
|
||||
#[cfg(feature = "aes-ctr")]
|
||||
CipherType::Aes256Ctr => CIPHER_AES_256_CTR,
|
||||
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia128Cfb => CIPHER_CAMELLIA_128_CFB,
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia128Cfb1 => CIPHER_CAMELLIA_128_CFB_1,
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia128Cfb8 => CIPHER_CAMELLIA_128_CFB_8,
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia128Cfb128 => CIPHER_CAMELLIA_128_CFB_128,
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia192Cfb => CIPHER_CAMELLIA_192_CFB,
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia192Cfb1 => CIPHER_CAMELLIA_192_CFB_1,
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia192Cfb8 => CIPHER_CAMELLIA_192_CFB_8,
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia192Cfb128 => CIPHER_CAMELLIA_192_CFB_128,
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia256Cfb => CIPHER_CAMELLIA_256_CFB,
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia256Cfb1 => CIPHER_CAMELLIA_256_CFB_1,
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia256Cfb8 => CIPHER_CAMELLIA_256_CFB_8,
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia256Cfb128 => CIPHER_CAMELLIA_256_CFB_128,
|
||||
|
||||
#[cfg(feature = "rc4")]
|
||||
CipherType::Rc4 => CIPHER_RC4,
|
||||
#[cfg(feature = "rc4")]
|
||||
CipherType::Rc4Md5 => CIPHER_RC4_MD5,
|
||||
|
||||
#[cfg(feature = "sodium")]
|
||||
CipherType::ChaCha20 => CIPHER_CHACHA20,
|
||||
#[cfg(feature = "sodium")]
|
||||
CipherType::Salsa20 => CIPHER_SALSA20,
|
||||
#[cfg(feature = "sodium")]
|
||||
CipherType::XSalsa20 => CIPHER_XSALSA20,
|
||||
#[cfg(feature = "sodium")]
|
||||
CipherType::ChaCha20Ietf => CIPHER_CHACHA20_IETF,
|
||||
|
||||
#[cfg(feature = "ring-aead-ciphers")]
|
||||
CipherType::Aes128Gcm => CIPHER_AES_128_GCM,
|
||||
#[cfg(feature = "ring-aead-ciphers")]
|
||||
CipherType::Aes256Gcm => CIPHER_AES_256_GCM,
|
||||
#[cfg(feature = "ring-aead-ciphers")]
|
||||
CipherType::ChaCha20IetfPoly1305 => CIPHER_CHACHA20_IETF_POLY1305,
|
||||
#[cfg(feature = "sodium")]
|
||||
CipherType::XChaCha20IetfPoly1305 => CIPHER_XCHACHA20_IETF_POLY1305,
|
||||
|
||||
#[cfg(feature = "aes-pmac-siv")]
|
||||
CipherType::Aes128PmacSiv => CIPHER_AES_128_PMAC_SIV,
|
||||
#[cfg(feature = "aes-pmac-siv")]
|
||||
CipherType::Aes256PmacSiv => CIPHER_AES_256_PMAC_SIV,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for CipherType {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<CipherType, Error> {
|
||||
match s {
|
||||
CIPHER_TABLE | "" => Ok(CipherType::Table),
|
||||
CIPHER_PLAIN => Ok(CipherType::Plain),
|
||||
CIPHER_NONE => Ok(CipherType::None),
|
||||
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CIPHER_AES_128_CFB => Ok(CipherType::Aes128Cfb),
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CIPHER_AES_128_CFB_1 => Ok(CipherType::Aes128Cfb1),
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CIPHER_AES_128_CFB_8 => Ok(CipherType::Aes128Cfb8),
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CIPHER_AES_128_CFB_128 => Ok(CipherType::Aes128Cfb128),
|
||||
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CIPHER_AES_192_CFB => Ok(CipherType::Aes192Cfb),
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CIPHER_AES_192_CFB_1 => Ok(CipherType::Aes192Cfb1),
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CIPHER_AES_192_CFB_8 => Ok(CipherType::Aes192Cfb8),
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CIPHER_AES_192_CFB_128 => Ok(CipherType::Aes192Cfb128),
|
||||
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CIPHER_AES_256_CFB => Ok(CipherType::Aes256Cfb),
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CIPHER_AES_256_CFB_1 => Ok(CipherType::Aes256Cfb1),
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CIPHER_AES_256_CFB_8 => Ok(CipherType::Aes256Cfb8),
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CIPHER_AES_256_CFB_128 => Ok(CipherType::Aes256Cfb128),
|
||||
|
||||
#[cfg(feature = "aes-ctr")]
|
||||
CIPHER_AES_128_CTR => Ok(CipherType::Aes128Ctr),
|
||||
#[cfg(feature = "aes-ctr")]
|
||||
CIPHER_AES_192_CTR => Ok(CipherType::Aes192Ctr),
|
||||
#[cfg(feature = "aes-ctr")]
|
||||
CIPHER_AES_256_CTR => Ok(CipherType::Aes256Ctr),
|
||||
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CIPHER_CAMELLIA_128_CFB => Ok(CipherType::Camellia128Cfb),
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CIPHER_CAMELLIA_128_CFB_1 => Ok(CipherType::Camellia128Cfb1),
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CIPHER_CAMELLIA_128_CFB_8 => Ok(CipherType::Camellia128Cfb8),
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CIPHER_CAMELLIA_128_CFB_128 => Ok(CipherType::Camellia128Cfb128),
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CIPHER_CAMELLIA_192_CFB => Ok(CipherType::Camellia192Cfb),
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CIPHER_CAMELLIA_192_CFB_1 => Ok(CipherType::Camellia192Cfb1),
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CIPHER_CAMELLIA_192_CFB_8 => Ok(CipherType::Camellia192Cfb8),
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CIPHER_CAMELLIA_192_CFB_128 => Ok(CipherType::Camellia192Cfb128),
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CIPHER_CAMELLIA_256_CFB => Ok(CipherType::Camellia256Cfb),
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CIPHER_CAMELLIA_256_CFB_1 => Ok(CipherType::Camellia256Cfb1),
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CIPHER_CAMELLIA_256_CFB_8 => Ok(CipherType::Camellia256Cfb8),
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CIPHER_CAMELLIA_256_CFB_128 => Ok(CipherType::Camellia256Cfb128),
|
||||
|
||||
#[cfg(feature = "rc4")]
|
||||
CIPHER_RC4 => Ok(CipherType::Rc4),
|
||||
#[cfg(feature = "rc4")]
|
||||
CIPHER_RC4_MD5 => Ok(CipherType::Rc4Md5),
|
||||
|
||||
#[cfg(feature = "sodium")]
|
||||
CIPHER_CHACHA20 => Ok(CipherType::ChaCha20),
|
||||
#[cfg(feature = "sodium")]
|
||||
CIPHER_SALSA20 => Ok(CipherType::Salsa20),
|
||||
#[cfg(feature = "sodium")]
|
||||
CIPHER_XSALSA20 => Ok(CipherType::XSalsa20),
|
||||
#[cfg(feature = "sodium")]
|
||||
CIPHER_CHACHA20_IETF => Ok(CipherType::ChaCha20Ietf),
|
||||
|
||||
#[cfg(feature = "ring-aead-ciphers")]
|
||||
CIPHER_AES_128_GCM => Ok(CipherType::Aes128Gcm),
|
||||
#[cfg(feature = "ring-aead-ciphers")]
|
||||
CIPHER_AES_256_GCM => Ok(CipherType::Aes256Gcm),
|
||||
|
||||
#[cfg(feature = "ring-aead-ciphers")]
|
||||
CIPHER_CHACHA20_IETF_POLY1305 => Ok(CipherType::ChaCha20IetfPoly1305),
|
||||
#[cfg(feature = "sodium")]
|
||||
CIPHER_XCHACHA20_IETF_POLY1305 => Ok(CipherType::XChaCha20IetfPoly1305),
|
||||
|
||||
#[cfg(feature = "aes-pmac-siv")]
|
||||
CIPHER_AES_128_PMAC_SIV => Ok(CipherType::Aes128PmacSiv),
|
||||
#[cfg(feature = "aes-pmac-siv")]
|
||||
CIPHER_AES_256_PMAC_SIV => Ok(CipherType::Aes256PmacSiv),
|
||||
|
||||
_ => Err(Error::UnknownCipherType),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for CipherType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str(self.name())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_cipher {
|
||||
use crate::crypto::{new_stream, CipherType, CryptoMode};
|
||||
|
||||
#[test]
|
||||
fn get_cipher() {
|
||||
let key = CipherType::Table.bytes_to_key(b"PassWORD");
|
||||
let iv = CipherType::Table.gen_init_vec();
|
||||
let mut encryptor = new_stream(CipherType::Table, &key[0..], &iv[0..], CryptoMode::Encrypt);
|
||||
let mut decryptor = new_stream(CipherType::Table, &key[0..], &iv[0..], CryptoMode::Decrypt);
|
||||
let message = "HELLO WORLD";
|
||||
|
||||
let mut encrypted_msg = Vec::new();
|
||||
encryptor.update(message.as_bytes(), &mut encrypted_msg).unwrap();
|
||||
let mut decrypted_msg = Vec::new();
|
||||
decryptor.update(&encrypted_msg[..], &mut decrypted_msg).unwrap();
|
||||
|
||||
assert!(message.as_bytes() == &decrypted_msg[..]);
|
||||
}
|
||||
|
||||
#[cfg(feature = "rc4")]
|
||||
#[test]
|
||||
fn rc4_md5_key_iv() {
|
||||
let ty = CipherType::Rc4Md5;
|
||||
assert_eq!(ty.key_size(), 16);
|
||||
assert_eq!(ty.iv_size(), 16);
|
||||
}
|
||||
|
||||
#[cfg(feature = "ring-aead-ciphers")]
|
||||
#[test]
|
||||
fn classic_bytes_to_key() {
|
||||
let ty = CipherType::Aes256Gcm;
|
||||
let vkey = ty.classic_bytes_to_key(b"abc");
|
||||
assert_eq!(
|
||||
&b"\x90\x01P\x98<\xd2O\xb0\xd6\x96?}(\xe1\x7fr\xea\x0b1\xe1\x08z\"\xbcS\x94\xa6cnn\xd3K"[..],
|
||||
&vkey
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
//! Dummy cipher, encrypt and decrypt nothing
|
||||
|
||||
use super::{CipherResult, StreamCipher};
|
||||
|
||||
use bytes::BufMut;
|
||||
|
||||
/// Dummy cipher
|
||||
///
|
||||
/// Copies data directly to output, very dummy
|
||||
pub struct DummyCipher;
|
||||
|
||||
impl StreamCipher for DummyCipher {
|
||||
fn update(&mut self, data: &[u8], out: &mut dyn BufMut) -> CipherResult<()> {
|
||||
out.put_slice(data);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn finalize(&mut self, _: &mut dyn BufMut) -> CipherResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn buffer_size(&self, data: &[u8]) -> usize {
|
||||
data.len()
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
//! Crypto methods for shadowsocks
|
||||
|
||||
pub use self::{
|
||||
aead::{new_aead_decryptor, new_aead_encryptor, AeadDecryptor, AeadEncryptor, BoxAeadDecryptor, BoxAeadEncryptor},
|
||||
cipher::{CipherCategory, CipherResult, CipherType},
|
||||
stream::{new_stream, BoxStreamCipher, StreamCipher},
|
||||
};
|
||||
#[cfg(feature = "openssl")]
|
||||
use ::openssl::symm;
|
||||
|
||||
pub mod aead;
|
||||
pub mod cipher;
|
||||
pub mod dummy;
|
||||
#[cfg(feature = "openssl")]
|
||||
pub mod openssl;
|
||||
#[cfg(feature = "rc4")]
|
||||
pub mod rc4_md5;
|
||||
#[cfg(feature = "ring-aead-ciphers")]
|
||||
pub mod ring;
|
||||
#[cfg(feature = "aes-pmac-siv")]
|
||||
pub mod siv;
|
||||
#[cfg(feature = "sodium")]
|
||||
pub mod sodium;
|
||||
pub mod stream;
|
||||
pub mod table;
|
||||
|
||||
/// Crypto mode, encrypt or decrypt
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
|
||||
pub enum CryptoMode {
|
||||
Encrypt,
|
||||
Decrypt,
|
||||
}
|
||||
|
||||
#[cfg(feature = "openssl")]
|
||||
impl std::convert::From<CryptoMode> for symm::Mode {
|
||||
fn from(m: CryptoMode) -> symm::Mode {
|
||||
match m {
|
||||
CryptoMode::Encrypt => symm::Mode::Encrypt,
|
||||
CryptoMode::Decrypt => symm::Mode::Decrypt,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,199 +0,0 @@
|
||||
//! Cipher defined with Rust binding for libcrypto (OpenSSL)
|
||||
|
||||
use std::convert::From;
|
||||
|
||||
use crate::crypto::{cipher, CipherResult, CipherType, StreamCipher};
|
||||
|
||||
use crate::crypto::CryptoMode;
|
||||
|
||||
use bytes::{BufMut, BytesMut};
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
use openssl::nid::Nid;
|
||||
use openssl::symm;
|
||||
|
||||
/// Core cipher of OpenSSL
|
||||
pub struct OpenSSLCrypto {
|
||||
cipher: symm::Cipher,
|
||||
inner: symm::Crypter,
|
||||
}
|
||||
|
||||
impl OpenSSLCrypto {
|
||||
/// Creates by type
|
||||
pub fn new(cipher_type: cipher::CipherType, key: &[u8], iv: &[u8], mode: CryptoMode) -> OpenSSLCrypto {
|
||||
let t =
|
||||
match cipher_type {
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes128Cfb => symm::Cipher::aes_128_cfb128(),
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes128Cfb1 => symm::Cipher::aes_128_cfb1(),
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes128Cfb128 => symm::Cipher::aes_128_cfb128(),
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes192Cfb => symm::Cipher::aes_192_cfb128(),
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes192Cfb1 => symm::Cipher::aes_192_cfb1(),
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes192Cfb128 => symm::Cipher::aes_192_cfb128(),
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes256Cfb => symm::Cipher::aes_256_cfb128(),
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes256Cfb1 => symm::Cipher::aes_256_cfb1(),
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes256Cfb128 => symm::Cipher::aes_256_cfb128(),
|
||||
|
||||
#[cfg(feature = "aes-ctr")]
|
||||
CipherType::Aes128Ctr => symm::Cipher::aes_128_ctr(),
|
||||
#[cfg(feature = "aes-ctr")]
|
||||
CipherType::Aes192Ctr => symm::Cipher::aes_192_ctr(),
|
||||
#[cfg(feature = "aes-ctr")]
|
||||
CipherType::Aes256Ctr => symm::Cipher::aes_256_ctr(),
|
||||
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia128Cfb => {
|
||||
symm::Cipher::from_nid(Nid::CAMELLIA_128_CFB128).expect("openssl doesn't support camellia-128-cfb")
|
||||
}
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia128Cfb1 => {
|
||||
symm::Cipher::from_nid(Nid::CAMELLIA_128_CFB1).expect("openssl doesn't support camellia-128-cfb1")
|
||||
}
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia128Cfb8 => {
|
||||
symm::Cipher::from_nid(Nid::CAMELLIA_128_CFB8).expect("openssl doesn't support camellia-128-cfb8")
|
||||
}
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia128Cfb128 => symm::Cipher::from_nid(Nid::CAMELLIA_128_CFB128)
|
||||
.expect("openssl doesn't support camellia-128-cfb128"),
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia192Cfb => {
|
||||
symm::Cipher::from_nid(Nid::CAMELLIA_192_CFB128).expect("openssl doesn't support camellia-192-cfb")
|
||||
}
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia192Cfb1 => {
|
||||
symm::Cipher::from_nid(Nid::CAMELLIA_192_CFB1).expect("openssl doesn't support camellia-192-cfb1")
|
||||
}
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia192Cfb8 => {
|
||||
symm::Cipher::from_nid(Nid::CAMELLIA_192_CFB8).expect("openssl doesn't support camellia-192-cfb8")
|
||||
}
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia192Cfb128 => symm::Cipher::from_nid(Nid::CAMELLIA_192_CFB128)
|
||||
.expect("openssl doesn't support camellia-192-cfb128"),
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia256Cfb => {
|
||||
symm::Cipher::from_nid(Nid::CAMELLIA_256_CFB128).expect("openssl doesn't support camellia-256-cfb")
|
||||
}
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia256Cfb1 => {
|
||||
symm::Cipher::from_nid(Nid::CAMELLIA_256_CFB1).expect("openssl doesn't support camellia-256-cfb1")
|
||||
}
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia256Cfb8 => {
|
||||
symm::Cipher::from_nid(Nid::CAMELLIA_256_CFB8).expect("openssl doesn't support camellia-256-cfb8")
|
||||
}
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia256Cfb128 => symm::Cipher::from_nid(Nid::CAMELLIA_256_CFB128)
|
||||
.expect("openssl doesn't support camellia-256-cfb128"),
|
||||
|
||||
#[cfg(feature = "rc4")]
|
||||
CipherType::Rc4 => symm::Cipher::rc4(),
|
||||
_ => panic!("cipher type {:?} is not supported by OpenSSLCrypt yet", cipher_type),
|
||||
};
|
||||
|
||||
// Panic if error occurs
|
||||
let cipher = symm::Crypter::new(t, From::from(mode), key, Some(iv)).unwrap();
|
||||
|
||||
OpenSSLCrypto {
|
||||
cipher: t,
|
||||
inner: cipher,
|
||||
}
|
||||
}
|
||||
|
||||
/// Update data
|
||||
pub fn update(&mut self, data: &[u8], out: &mut dyn BufMut) -> CipherResult<()> {
|
||||
let least_reserved = data.len() + self.cipher.block_size();
|
||||
let mut buf = BytesMut::with_capacity(least_reserved); // NOTE: len() is 0 now!
|
||||
unsafe {
|
||||
buf.set_len(least_reserved);
|
||||
}
|
||||
let length = self.inner.update(data, &mut *buf)?;
|
||||
buf.truncate(length);
|
||||
out.put_slice(&buf);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Generate the final block
|
||||
pub fn finalize(&mut self, out: &mut dyn BufMut) -> CipherResult<()> {
|
||||
let least_reserved = self.cipher.block_size();
|
||||
let mut buf = BytesMut::with_capacity(least_reserved); // NOTE: len() is 0 now!
|
||||
unsafe {
|
||||
buf.set_len(least_reserved);
|
||||
}
|
||||
|
||||
let length = self.inner.finalize(&mut *buf)?;
|
||||
buf.truncate(length);
|
||||
out.put_slice(&buf);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Gets output buffer size based on data
|
||||
pub fn buffer_size(&self, data: &[u8]) -> usize {
|
||||
self.cipher.block_size() + data.len()
|
||||
}
|
||||
}
|
||||
|
||||
/// The Cipher binding for OpenSSL's `libcrypto`.
|
||||
///
|
||||
/// It should be noticed that the decipher needs to read the iv (initialization vector)
|
||||
/// from the first call of `decrypt`. So the cipher will have to insert the iv into
|
||||
/// the front of the encrypted data.
|
||||
///
|
||||
/// *Note: This behavior works just the same as the official version of shadowsocks.*
|
||||
///
|
||||
/// ```rust
|
||||
/// use shadowsocks::crypto::{openssl::OpenSSLCipher, CipherType, CryptoMode, StreamCipher};
|
||||
///
|
||||
/// let method = CipherType::Aes128Cfb;
|
||||
///
|
||||
/// let key = method.bytes_to_key(b"password");
|
||||
/// let iv = method.gen_init_vec();
|
||||
///
|
||||
/// let mut enc = OpenSSLCipher::new(method, &key[0..], &iv[0..], CryptoMode::Encrypt);
|
||||
/// let mut dec = OpenSSLCipher::new(method, &key[0..], &iv[0..], CryptoMode::Decrypt);
|
||||
///
|
||||
/// let message = "hello world";
|
||||
/// let mut encrypted_message = Vec::new();
|
||||
/// enc.update(message.as_bytes(), &mut encrypted_message).unwrap();
|
||||
///
|
||||
/// let mut decrypted_message = Vec::new();
|
||||
/// dec.update(&encrypted_message[..], &mut decrypted_message).unwrap();
|
||||
///
|
||||
/// assert!(&decrypted_message[..] == message.as_bytes());
|
||||
/// ```
|
||||
pub struct OpenSSLCipher {
|
||||
worker: OpenSSLCrypto,
|
||||
}
|
||||
|
||||
impl OpenSSLCipher {
|
||||
/// Creates by type
|
||||
pub fn new(cipher_type: cipher::CipherType, key: &[u8], iv: &[u8], mode: CryptoMode) -> OpenSSLCipher {
|
||||
OpenSSLCipher {
|
||||
worker: OpenSSLCrypto::new(cipher_type, &key[..], &iv[..], mode),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for OpenSSLCipher {}
|
||||
|
||||
impl StreamCipher for OpenSSLCipher {
|
||||
fn update(&mut self, data: &[u8], out: &mut dyn BufMut) -> CipherResult<()> {
|
||||
self.worker.update(data, out)
|
||||
}
|
||||
|
||||
fn finalize(&mut self, out: &mut dyn BufMut) -> CipherResult<()> {
|
||||
self.worker.finalize(out)
|
||||
}
|
||||
|
||||
fn buffer_size(&self, data: &[u8]) -> usize {
|
||||
self.worker.buffer_size(data)
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
//! Rc4Md5 cipher definition
|
||||
|
||||
use crate::crypto::{openssl::OpenSSLCrypto, CipherResult, CipherType, CryptoMode, StreamCipher};
|
||||
|
||||
use bytes::BufMut;
|
||||
use digest::Digest;
|
||||
use md5::Md5;
|
||||
|
||||
/// Rc4Md5 Cipher
|
||||
pub struct Rc4Md5Cipher {
|
||||
crypto: OpenSSLCrypto,
|
||||
}
|
||||
|
||||
impl Rc4Md5Cipher {
|
||||
pub fn new(key: &[u8], iv: &[u8], mode: CryptoMode) -> Rc4Md5Cipher {
|
||||
let mut md5_digest = Md5::new();
|
||||
md5_digest.update(key);
|
||||
md5_digest.update(iv);
|
||||
|
||||
let key = md5_digest.finalize();
|
||||
|
||||
Rc4Md5Cipher {
|
||||
crypto: OpenSSLCrypto::new(CipherType::Rc4, &key, b"", mode),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StreamCipher for Rc4Md5Cipher {
|
||||
fn update(&mut self, data: &[u8], out: &mut dyn BufMut) -> CipherResult<()> {
|
||||
self.crypto.update(data, out)
|
||||
}
|
||||
|
||||
fn finalize(&mut self, out: &mut dyn BufMut) -> CipherResult<()> {
|
||||
self.crypto.finalize(out)
|
||||
}
|
||||
|
||||
fn buffer_size(&self, data: &[u8]) -> usize {
|
||||
self.crypto.buffer_size(data)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Rc4Md5Cipher {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::crypto::{CipherType, CryptoMode, StreamCipher};
|
||||
|
||||
#[test]
|
||||
fn test_rc4_md5_cipher() {
|
||||
let msg = b"abcd1234";
|
||||
let key = b"key";
|
||||
|
||||
let t = CipherType::Rc4Md5;
|
||||
let iv = t.gen_init_vec();
|
||||
|
||||
let mut enc = Rc4Md5Cipher::new(key, &iv[..], CryptoMode::Encrypt);
|
||||
let mut encrypted_msg = Vec::new();
|
||||
enc.update(msg, &mut encrypted_msg)
|
||||
.and_then(|_| enc.finalize(&mut encrypted_msg))
|
||||
.unwrap();
|
||||
|
||||
let mut dec = Rc4Md5Cipher::new(key, &iv[..], CryptoMode::Decrypt);
|
||||
let mut decrypted_msg = Vec::new();
|
||||
dec.update(&encrypted_msg[..], &mut decrypted_msg)
|
||||
.and_then(|_| dec.finalize(&mut decrypted_msg))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(msg, &decrypted_msg[..]);
|
||||
}
|
||||
}
|
||||
@@ -1,218 +0,0 @@
|
||||
//! Cipher defined with Ring
|
||||
|
||||
use ring::{
|
||||
aead::{
|
||||
Aad,
|
||||
Algorithm,
|
||||
Nonce,
|
||||
NonceSequence,
|
||||
OpeningKey,
|
||||
SealingKey,
|
||||
UnboundKey,
|
||||
AES_128_GCM,
|
||||
AES_256_GCM,
|
||||
CHACHA20_POLY1305,
|
||||
NONCE_LEN,
|
||||
},
|
||||
error::Unspecified,
|
||||
};
|
||||
|
||||
use crate::crypto::{
|
||||
aead::{increase_nonce, make_skey},
|
||||
cipher::Error,
|
||||
AeadDecryptor,
|
||||
AeadEncryptor,
|
||||
CipherResult,
|
||||
CipherType,
|
||||
};
|
||||
|
||||
use byte_string::ByteStr;
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use log::error;
|
||||
|
||||
/// AEAD ciphers provided by Ring
|
||||
pub enum RingAeadCryptoVariant {
|
||||
Seal(SealingKey<RingAeadNonceSequence>),
|
||||
Open(OpeningKey<RingAeadNonceSequence>),
|
||||
}
|
||||
|
||||
pub struct RingAeadNonceSequence {
|
||||
nonce: [u8; NONCE_LEN],
|
||||
}
|
||||
|
||||
impl RingAeadNonceSequence {
|
||||
fn new() -> RingAeadNonceSequence {
|
||||
RingAeadNonceSequence {
|
||||
nonce: [0u8; NONCE_LEN],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NonceSequence for RingAeadNonceSequence {
|
||||
fn advance(&mut self) -> Result<Nonce, Unspecified> {
|
||||
let nonce = Nonce::assume_unique_for_key(self.nonce);
|
||||
increase_nonce(&mut self.nonce);
|
||||
Ok(nonce)
|
||||
}
|
||||
}
|
||||
|
||||
/// AEAD Cipher context
|
||||
///
|
||||
/// According to SIP004, the `nonce` has to incr 1 after each encrypt/decrypt.
|
||||
pub struct RingAeadCipher {
|
||||
cipher: RingAeadCryptoVariant,
|
||||
cipher_type: CipherType,
|
||||
}
|
||||
|
||||
impl RingAeadCipher {
|
||||
/// Initialize context
|
||||
pub fn new(t: CipherType, key: &[u8], salt: &[u8], is_seal: bool) -> RingAeadCipher {
|
||||
// TODO: Check if salt is duplicated
|
||||
|
||||
// Nonce is 12 bytes
|
||||
assert_eq!(t.iv_size(), NONCE_LEN);
|
||||
|
||||
let skey = make_skey(t, key, salt);
|
||||
let cipher = RingAeadCipher::new_variant(t, &skey, is_seal);
|
||||
RingAeadCipher { cipher, cipher_type: t }
|
||||
}
|
||||
|
||||
fn new_variant(t: CipherType, key: &[u8], is_seal: bool) -> RingAeadCryptoVariant {
|
||||
match t {
|
||||
CipherType::Aes128Gcm => RingAeadCipher::new_crypt(&AES_128_GCM, key, is_seal),
|
||||
CipherType::Aes256Gcm => RingAeadCipher::new_crypt(&AES_256_GCM, key, is_seal),
|
||||
CipherType::ChaCha20IetfPoly1305 => RingAeadCipher::new_crypt(&CHACHA20_POLY1305, key, is_seal),
|
||||
_ => panic!("unsupported cipher in ring {:?}", t),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn new_crypt(algorithm: &'static Algorithm, key: &[u8], is_seal: bool) -> RingAeadCryptoVariant {
|
||||
use ring::aead::BoundKey;
|
||||
|
||||
let unbound_key = UnboundKey::new(algorithm, key).unwrap();
|
||||
|
||||
if is_seal {
|
||||
RingAeadCryptoVariant::Seal(SealingKey::new(unbound_key, RingAeadNonceSequence::new()))
|
||||
} else {
|
||||
RingAeadCryptoVariant::Open(OpeningKey::new(unbound_key, RingAeadNonceSequence::new()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SealBuffer<'a> {
|
||||
buffer: &'a mut [u8],
|
||||
input_len: usize,
|
||||
}
|
||||
|
||||
impl<'a> AsRef<[u8]> for SealBuffer<'a> {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.buffer[..self.input_len]
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsMut<[u8]> for SealBuffer<'a> {
|
||||
fn as_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.buffer[..self.input_len]
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'in_out> Extend<&'in_out u8> for SealBuffer<'a> {
|
||||
fn extend<T: IntoIterator<Item = &'in_out u8>>(&mut self, iter: T) {
|
||||
for (idx, b) in iter.into_iter().enumerate() {
|
||||
self.buffer[self.input_len + idx] = *b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AeadEncryptor for RingAeadCipher {
|
||||
fn encrypt(&mut self, input: &[u8], output: &mut [u8]) {
|
||||
let tag_len = self.cipher_type.tag_size();
|
||||
let buf_len = input.len() + tag_len;
|
||||
assert!(output.len() >= buf_len);
|
||||
|
||||
output[..input.len()].copy_from_slice(input);
|
||||
|
||||
let mut buf = SealBuffer {
|
||||
buffer: output,
|
||||
input_len: input.len(),
|
||||
};
|
||||
|
||||
if let RingAeadCryptoVariant::Seal(ref mut key) = self.cipher {
|
||||
key.seal_in_place_append_tag(Aad::empty(), &mut buf).unwrap();
|
||||
} else {
|
||||
unreachable!("encrypt is called on a non-seal cipher");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AeadDecryptor for RingAeadCipher {
|
||||
fn decrypt(&mut self, input: &[u8], output: &mut [u8]) -> CipherResult<()> {
|
||||
let tag_len = self.cipher_type.tag_size();
|
||||
assert!(output.len() + tag_len >= input.len());
|
||||
|
||||
let mut buf = BytesMut::with_capacity(input.len());
|
||||
buf.put_slice(input);
|
||||
|
||||
if let RingAeadCryptoVariant::Open(ref mut key) = self.cipher {
|
||||
match key.open_in_place(Aad::empty(), &mut buf) {
|
||||
Ok(obuf) => {
|
||||
output.copy_from_slice(obuf);
|
||||
Ok(())
|
||||
}
|
||||
Err(..) => {
|
||||
error!(
|
||||
"AEAD decrypt failed, input={:?}, tag={:?}, opening: {:?}",
|
||||
ByteStr::new(&input[..output.len()]),
|
||||
ByteStr::new(&input[output.len()..]),
|
||||
key,
|
||||
);
|
||||
Err(Error::AeadDecryptFailed)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
unreachable!("decrypt is called on a non-open cipher");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::crypto::CipherType;
|
||||
|
||||
fn test_ring_aead(ct: CipherType) {
|
||||
let key = ct.bytes_to_key(b"PassWORD");
|
||||
let message = b"message";
|
||||
|
||||
let iv = ct.gen_init_vec();
|
||||
|
||||
let mut enc = RingAeadCipher::new(ct, &key[..], &iv[..], true);
|
||||
|
||||
let mut encrypted_msg = vec![0u8; message.len() + ct.tag_size()];
|
||||
enc.encrypt(message, &mut encrypted_msg);
|
||||
|
||||
assert_ne!(message, &encrypted_msg[..]);
|
||||
|
||||
let mut dec = RingAeadCipher::new(ct, &key[..], &iv[..], false);
|
||||
let mut decrypted_msg = vec![0u8; message.len()];
|
||||
dec.decrypt(&encrypted_msg[..], &mut decrypted_msg).unwrap();
|
||||
|
||||
assert_eq!(&decrypted_msg[..], message);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ring_aes128gcm() {
|
||||
test_ring_aead(CipherType::Aes128Gcm);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ring_aes256gcm() {
|
||||
test_ring_aead(CipherType::Aes256Gcm);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ring_chacha20poly1305() {
|
||||
test_ring_aead(CipherType::ChaCha20IetfPoly1305);
|
||||
}
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
//! Cipher defined with Miscreant
|
||||
|
||||
use std::ptr;
|
||||
|
||||
use miscreant::{Aead, Aes128PmacSivAead, Aes256PmacSivAead};
|
||||
|
||||
use crate::crypto::{
|
||||
aead::{increase_nonce, make_skey},
|
||||
cipher::Error,
|
||||
AeadDecryptor,
|
||||
AeadEncryptor,
|
||||
CipherResult,
|
||||
CipherType,
|
||||
};
|
||||
|
||||
use byte_string::ByteStr;
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use log::error;
|
||||
|
||||
/// AEAD ciphers provided by Miscreant
|
||||
pub enum MiscreantCryptoVariant {
|
||||
Aes128(Aes128PmacSivAead),
|
||||
Aes256(Aes256PmacSivAead),
|
||||
}
|
||||
|
||||
/// AEAD Cipher context
|
||||
///
|
||||
/// According to SIP004, the `nonce` has to incr 1 after each encrypt/decrypt.
|
||||
pub struct MiscreantCipher {
|
||||
cipher_type: CipherType,
|
||||
cipher: MiscreantCryptoVariant,
|
||||
nonce: BytesMut,
|
||||
}
|
||||
|
||||
impl MiscreantCipher {
|
||||
/// Initialize context
|
||||
pub fn new(t: CipherType, key: &[u8], salt: &[u8]) -> Self {
|
||||
// NOTE: Don't need check salt is duplicated. :)
|
||||
|
||||
let nonce_size = t.iv_size();
|
||||
let mut nonce = BytesMut::with_capacity(nonce_size);
|
||||
unsafe {
|
||||
nonce.set_len(nonce_size);
|
||||
ptr::write_bytes(nonce.as_mut_ptr(), 0, nonce_size);
|
||||
}
|
||||
|
||||
let skey = make_skey(t, key, salt);
|
||||
let cipher = Self::new_variant(t, &skey);
|
||||
MiscreantCipher {
|
||||
cipher_type: t,
|
||||
cipher: cipher,
|
||||
nonce: nonce,
|
||||
}
|
||||
}
|
||||
|
||||
fn new_variant(t: CipherType, key: &[u8]) -> MiscreantCryptoVariant {
|
||||
match t {
|
||||
CipherType::Aes128PmacSiv => {
|
||||
let mut skey = [0; 32];
|
||||
skey.copy_from_slice(key);
|
||||
MiscreantCryptoVariant::Aes128(Aes128PmacSivAead::new(&skey))
|
||||
}
|
||||
CipherType::Aes256PmacSiv => {
|
||||
let mut skey = [0; 64];
|
||||
skey.copy_from_slice(key);
|
||||
MiscreantCryptoVariant::Aes256(Aes256PmacSivAead::new(&skey))
|
||||
}
|
||||
_ => panic!("unsupported cipher in miscreant {:?}", t),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AeadEncryptor for MiscreantCipher {
|
||||
fn encrypt(&mut self, input: &[u8], output: &mut [u8]) {
|
||||
let tag_len = self.cipher_type.tag_size();
|
||||
let buf_len = input.len() + tag_len;
|
||||
|
||||
// Miscreant requires tag to be in front of text
|
||||
let mut buf = BytesMut::with_capacity(buf_len);
|
||||
unsafe {
|
||||
buf.set_len(buf_len);
|
||||
}
|
||||
buf[tag_len..].copy_from_slice(input);
|
||||
|
||||
// NOTE: Must swap tag and encrypted text to output
|
||||
match self.cipher {
|
||||
MiscreantCryptoVariant::Aes128(ref mut cipher) => {
|
||||
cipher.encrypt_in_place(&self.nonce, b"", &mut buf);
|
||||
output[..input.len()].copy_from_slice(&buf[tag_len..]);
|
||||
output[input.len()..].copy_from_slice(&buf[..tag_len]);
|
||||
}
|
||||
MiscreantCryptoVariant::Aes256(ref mut cipher) => {
|
||||
cipher.encrypt_in_place(&self.nonce, b"", &mut buf);
|
||||
output[..input.len()].copy_from_slice(&buf[tag_len..]);
|
||||
output[input.len()..].copy_from_slice(&buf[..tag_len]);
|
||||
}
|
||||
}
|
||||
|
||||
increase_nonce(&mut self.nonce);
|
||||
}
|
||||
}
|
||||
|
||||
impl AeadDecryptor for MiscreantCipher {
|
||||
fn decrypt(&mut self, input: &[u8], output: &mut [u8]) -> CipherResult<()> {
|
||||
let tag_size = self.cipher_type.tag_size();
|
||||
|
||||
// Swap encrypted text and tag
|
||||
// Miscreant requires tag in front of encrypted text
|
||||
let mut buf = BytesMut::with_capacity(input.len() + tag_size);
|
||||
buf.put_slice(&input[input.len() - tag_size..]);
|
||||
buf.put_slice(&input[..input.len() - tag_size]);
|
||||
|
||||
let result = match self.cipher {
|
||||
MiscreantCryptoVariant::Aes128(ref mut cipher) => cipher.decrypt_in_place(&self.nonce, b"", &mut buf),
|
||||
MiscreantCryptoVariant::Aes256(ref mut cipher) => cipher.decrypt_in_place(&self.nonce, b"", &mut buf),
|
||||
};
|
||||
|
||||
result
|
||||
.map(|buf| {
|
||||
output.copy_from_slice(buf);
|
||||
increase_nonce(&mut self.nonce);
|
||||
})
|
||||
.map_err(|_| {
|
||||
error!(
|
||||
"AEAD decrypt failed, nonce={:?}, input={:?}, tag={:?}, err: decrypt failure",
|
||||
ByteStr::new(&self.nonce),
|
||||
ByteStr::new(&input[..input.len() - tag_size]),
|
||||
ByteStr::new(&input[input.len() - tag_size..])
|
||||
);
|
||||
Error::AeadDecryptFailed
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
fn test_miscreant(ct: CipherType) {
|
||||
let key = ct.bytes_to_key(b"PassWORD");
|
||||
let message = b"message";
|
||||
|
||||
let iv = ct.gen_init_vec();
|
||||
|
||||
let mut enc = MiscreantCipher::new(ct, &key[..], &iv[..]);
|
||||
|
||||
let mut encrypted_msg = vec![0u8; message.len() + ct.tag_size()];
|
||||
enc.encrypt(message, &mut encrypted_msg);
|
||||
|
||||
assert_ne!(message, &encrypted_msg[..]);
|
||||
|
||||
let mut dec = MiscreantCipher::new(ct, &key[..], &iv[..]);
|
||||
let mut decrypted_msg = vec![0u8; message.len()];
|
||||
dec.decrypt(&encrypted_msg[..], &mut decrypted_msg).unwrap();
|
||||
|
||||
assert_eq!(&decrypted_msg[..], message);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rust_crypto_cipher_aes_128_pmac_siv() {
|
||||
test_miscreant(CipherType::Aes128PmacSiv);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rust_crypto_cipher_aes_256_pmac_siv() {
|
||||
test_miscreant(CipherType::Aes256PmacSiv);
|
||||
}
|
||||
}
|
||||
@@ -1,331 +0,0 @@
|
||||
//! Cipher defined with libsodium
|
||||
|
||||
use std::{ptr, sync::Once};
|
||||
|
||||
use bytes::{BufMut, Bytes, BytesMut};
|
||||
|
||||
use libc::c_ulonglong;
|
||||
use libsodium_sys::{
|
||||
crypto_aead_xchacha20poly1305_ietf_decrypt,
|
||||
crypto_aead_xchacha20poly1305_ietf_encrypt,
|
||||
crypto_stream_chacha20_ietf_xor_ic,
|
||||
crypto_stream_chacha20_xor_ic,
|
||||
crypto_stream_salsa20_xor_ic,
|
||||
crypto_stream_xsalsa20_xor_ic,
|
||||
sodium_init,
|
||||
};
|
||||
|
||||
use crate::crypto::{
|
||||
aead::{increase_nonce, make_skey},
|
||||
cipher::Error,
|
||||
AeadDecryptor,
|
||||
AeadEncryptor,
|
||||
CipherResult,
|
||||
CipherType,
|
||||
StreamCipher,
|
||||
};
|
||||
|
||||
static SODIUM_INIT_FLAG: Once = Once::new();
|
||||
|
||||
/// Cipher provided by `libsodium`
|
||||
pub struct SodiumStreamCipher {
|
||||
cipher_type: CipherType,
|
||||
key: Vec<u8>,
|
||||
iv: Vec<u8>,
|
||||
counter: usize,
|
||||
}
|
||||
|
||||
const SODIUM_BLOCK_SIZE: usize = 64;
|
||||
|
||||
impl SodiumStreamCipher {
|
||||
/// Creates an instance
|
||||
pub fn new(t: CipherType, key: &[u8], iv: &[u8]) -> SodiumStreamCipher {
|
||||
match t {
|
||||
CipherType::ChaCha20 | CipherType::Salsa20 | CipherType::XSalsa20 | CipherType::ChaCha20Ietf => {}
|
||||
_ => panic!("sodium cipher does not support {:?} cipher", t),
|
||||
}
|
||||
|
||||
SODIUM_INIT_FLAG.call_once(|| unsafe {
|
||||
assert_eq!(sodium_init(), 0);
|
||||
});
|
||||
|
||||
SodiumStreamCipher {
|
||||
cipher_type: t,
|
||||
key: key.to_owned(),
|
||||
iv: iv.to_owned(),
|
||||
counter: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn padding_len(&self) -> usize {
|
||||
self.counter % SODIUM_BLOCK_SIZE
|
||||
}
|
||||
|
||||
fn process(&mut self, data: &[u8], out: &mut dyn BufMut) -> CipherResult<()> {
|
||||
let padding = self.padding_len();
|
||||
|
||||
let mut plain_text = vec![0u8; data.len() + padding];
|
||||
(&mut plain_text[padding..]).copy_from_slice(&data);
|
||||
|
||||
let mut out_buf = BytesMut::with_capacity((data.len() + padding) * 2);
|
||||
|
||||
crypto_stream_xor_ic(
|
||||
self.cipher_type,
|
||||
self.counter / SODIUM_BLOCK_SIZE,
|
||||
&self.iv,
|
||||
&self.key,
|
||||
&plain_text,
|
||||
&mut out_buf,
|
||||
)?;
|
||||
|
||||
out.put_slice(&out_buf[padding..padding + data.len()]);
|
||||
|
||||
self.counter += data.len();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn crypto_stream_xor_ic<B: BufMut>(
|
||||
t: CipherType,
|
||||
ic: usize,
|
||||
iv: &[u8],
|
||||
key: &[u8],
|
||||
data: &[u8],
|
||||
out: &mut B,
|
||||
) -> CipherResult<()> {
|
||||
assert!(data.len() <= out.bytes_mut().len());
|
||||
|
||||
let ret = unsafe {
|
||||
match t {
|
||||
CipherType::ChaCha20 => crypto_stream_chacha20_xor_ic(
|
||||
out.bytes_mut().as_mut_ptr() as *mut u8,
|
||||
data.as_ptr(),
|
||||
data.len() as c_ulonglong,
|
||||
iv.as_ptr(),
|
||||
ic as c_ulonglong,
|
||||
key.as_ptr(),
|
||||
),
|
||||
CipherType::ChaCha20Ietf => crypto_stream_chacha20_ietf_xor_ic(
|
||||
out.bytes_mut().as_mut_ptr() as *mut u8,
|
||||
data.as_ptr(),
|
||||
data.len() as c_ulonglong,
|
||||
iv.as_ptr(),
|
||||
ic as u32,
|
||||
key.as_ptr(),
|
||||
),
|
||||
CipherType::Salsa20 => crypto_stream_salsa20_xor_ic(
|
||||
out.bytes_mut().as_mut_ptr() as *mut u8,
|
||||
data.as_ptr(),
|
||||
data.len() as c_ulonglong,
|
||||
iv.as_ptr(),
|
||||
ic as c_ulonglong,
|
||||
key.as_ptr(),
|
||||
),
|
||||
CipherType::XSalsa20 => crypto_stream_xsalsa20_xor_ic(
|
||||
out.bytes_mut().as_mut_ptr() as *mut u8,
|
||||
data.as_ptr(),
|
||||
data.len() as c_ulonglong,
|
||||
iv.as_ptr(),
|
||||
ic as c_ulonglong,
|
||||
key.as_ptr(),
|
||||
),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
};
|
||||
|
||||
if ret != 0 {
|
||||
Err(Error::SodiumError)
|
||||
} else {
|
||||
unsafe {
|
||||
out.advance_mut(data.len());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl StreamCipher for SodiumStreamCipher {
|
||||
fn update(&mut self, data: &[u8], out: &mut dyn BufMut) -> CipherResult<()> {
|
||||
self.process(data, out)
|
||||
}
|
||||
|
||||
fn finalize(&mut self, _: &mut dyn BufMut) -> CipherResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn buffer_size(&self, data: &[u8]) -> usize {
|
||||
data.len()
|
||||
}
|
||||
}
|
||||
|
||||
/// Cipher provided by `libsodium`
|
||||
pub struct SodiumAeadCipher {
|
||||
cipher_type: CipherType,
|
||||
key: Bytes,
|
||||
nonce: BytesMut,
|
||||
}
|
||||
|
||||
impl SodiumAeadCipher {
|
||||
pub fn new(t: CipherType, key: &[u8], salt: &[u8]) -> SodiumAeadCipher {
|
||||
// TODO: Check if salt is duplicated
|
||||
|
||||
let nonce_size = t.iv_size();
|
||||
let mut nonce = BytesMut::with_capacity(nonce_size);
|
||||
unsafe {
|
||||
nonce.set_len(nonce_size);
|
||||
ptr::write_bytes(nonce.as_mut_ptr(), 0, nonce_size);
|
||||
}
|
||||
|
||||
let skey = make_skey(t, key, salt);
|
||||
|
||||
SodiumAeadCipher {
|
||||
cipher_type: t,
|
||||
key: skey,
|
||||
nonce,
|
||||
}
|
||||
}
|
||||
|
||||
fn increase_nonce(&mut self) {
|
||||
increase_nonce(&mut self.nonce);
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
self.increase_nonce();
|
||||
}
|
||||
}
|
||||
|
||||
impl AeadEncryptor for SodiumAeadCipher {
|
||||
fn encrypt(&mut self, input: &[u8], output: &mut [u8]) {
|
||||
let tag_len = self.cipher_type.tag_size();
|
||||
let buf_len = input.len() + tag_len;
|
||||
assert!(output.len() >= buf_len);
|
||||
|
||||
let mut len: c_ulonglong = output.len() as c_ulonglong;
|
||||
|
||||
let ret = match self.cipher_type {
|
||||
CipherType::XChaCha20IetfPoly1305 => unsafe {
|
||||
crypto_aead_xchacha20poly1305_ietf_encrypt(
|
||||
output.as_mut_ptr(),
|
||||
&mut len,
|
||||
input.as_ptr(),
|
||||
input.len() as c_ulonglong,
|
||||
ptr::null(),
|
||||
0,
|
||||
ptr::null(),
|
||||
self.nonce.as_ref().as_ptr(),
|
||||
self.key.as_ref().as_ptr(),
|
||||
)
|
||||
},
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
self.reset();
|
||||
|
||||
assert_eq!(ret, 0);
|
||||
assert_eq!(len, output.len() as c_ulonglong);
|
||||
}
|
||||
}
|
||||
|
||||
impl AeadDecryptor for SodiumAeadCipher {
|
||||
fn decrypt(&mut self, input: &[u8], output: &mut [u8]) -> CipherResult<()> {
|
||||
let tag_len = self.cipher_type.tag_size();
|
||||
assert!(output.len() + tag_len >= input.len());
|
||||
|
||||
let mut len: c_ulonglong = output.len() as c_ulonglong;
|
||||
|
||||
let ret = match self.cipher_type {
|
||||
CipherType::XChaCha20IetfPoly1305 => unsafe {
|
||||
crypto_aead_xchacha20poly1305_ietf_decrypt(
|
||||
output.as_mut_ptr(),
|
||||
&mut len,
|
||||
ptr::null_mut(),
|
||||
input.as_ptr(),
|
||||
input.len() as c_ulonglong,
|
||||
ptr::null(),
|
||||
0,
|
||||
self.nonce.as_ref().as_ptr(),
|
||||
self.key.as_ref().as_ptr(),
|
||||
)
|
||||
},
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
self.reset();
|
||||
|
||||
if ret != 0 || len != output.len() as c_ulonglong {
|
||||
return Err(Error::SodiumError);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::crypto::{CipherType, StreamCipher};
|
||||
|
||||
fn test_sodium(ct: CipherType) {
|
||||
let key = ct.bytes_to_key(b"PassWORD");
|
||||
let message = b"message";
|
||||
|
||||
let iv = ct.gen_init_vec();
|
||||
|
||||
let mut enc = SodiumStreamCipher::new(ct, &key[..], &iv[..]);
|
||||
let mut encrypted_msg = Vec::with_capacity(enc.buffer_size(&message[..]));
|
||||
enc.update(message, &mut encrypted_msg).unwrap();
|
||||
|
||||
assert_ne!(message, &encrypted_msg[..]);
|
||||
|
||||
let mut dec = SodiumStreamCipher::new(ct, &key[..], &iv[..]);
|
||||
let mut decrypted_msg = Vec::with_capacity(dec.buffer_size(&encrypted_msg));
|
||||
dec.update(&encrypted_msg[..], &mut decrypted_msg).unwrap();
|
||||
|
||||
assert_eq!(&decrypted_msg[..], message);
|
||||
}
|
||||
|
||||
fn test_sodium_aead(ct: CipherType) {
|
||||
let key = ct.bytes_to_key(b"PassWORD");
|
||||
let message = b"message";
|
||||
|
||||
let iv = ct.gen_init_vec();
|
||||
|
||||
let mut enc = SodiumAeadCipher::new(ct, &key[..], &iv[..]);
|
||||
|
||||
let mut encrypted_msg = vec![0u8; message.len() + ct.tag_size()];
|
||||
enc.encrypt(message, &mut encrypted_msg);
|
||||
|
||||
assert_ne!(message, &encrypted_msg[..]);
|
||||
|
||||
let mut dec = SodiumAeadCipher::new(ct, &key[..], &iv[..]);
|
||||
let mut decrypted_msg = vec![0u8; message.len()];
|
||||
dec.decrypt(&encrypted_msg[..], &mut decrypted_msg).unwrap();
|
||||
|
||||
assert_eq!(&decrypted_msg[..], message);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rust_crypto_cipher_chacha20() {
|
||||
test_sodium(CipherType::ChaCha20);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rust_crypto_cipher_salsa20() {
|
||||
test_sodium(CipherType::Salsa20);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rust_crypto_cipher_xsalsa20() {
|
||||
test_sodium(CipherType::XSalsa20);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rust_crypto_cipher_chacha20_ietf() {
|
||||
test_sodium(CipherType::ChaCha20Ietf);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rust_crypto_cipher_xchacha20_ietf_poly1305() {
|
||||
test_sodium_aead(CipherType::XChaCha20IetfPoly1305);
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
//! Stream ciphers
|
||||
|
||||
#[cfg(feature = "openssl")]
|
||||
use crate::crypto::openssl;
|
||||
#[cfg(feature = "rc4")]
|
||||
use crate::crypto::rc4_md5;
|
||||
#[cfg(feature = "sodium")]
|
||||
use crate::crypto::sodium;
|
||||
use crate::crypto::{
|
||||
cipher::{CipherCategory, CipherResult, CipherType},
|
||||
dummy,
|
||||
table,
|
||||
CryptoMode,
|
||||
};
|
||||
|
||||
use bytes::BufMut;
|
||||
|
||||
/// Basic operation of Cipher, which is a Symmetric Cipher.
|
||||
///
|
||||
/// The `update` method could be called multiple times, and the `finalize` method will
|
||||
/// encrypt the last block
|
||||
pub trait StreamCipher {
|
||||
fn update(&mut self, data: &[u8], out: &mut dyn BufMut) -> CipherResult<()>;
|
||||
fn finalize(&mut self, out: &mut dyn BufMut) -> CipherResult<()>;
|
||||
fn buffer_size(&self, data: &[u8]) -> usize;
|
||||
}
|
||||
|
||||
/// Variant cipher which contains all possible stream ciphers
|
||||
pub type BoxStreamCipher = Box<dyn StreamCipher + Send + 'static>;
|
||||
|
||||
/// Generate a specific Cipher with key and initialize vector
|
||||
#[allow(unused_variables)]
|
||||
pub fn new_stream(t: CipherType, key: &[u8], iv: &[u8], mode: CryptoMode) -> BoxStreamCipher {
|
||||
assert!(
|
||||
t.category() == CipherCategory::Stream,
|
||||
"only allow initializing with stream cipher"
|
||||
);
|
||||
|
||||
match t {
|
||||
CipherType::Table => Box::new(table::TableCipher::new(key, mode)),
|
||||
CipherType::Plain | CipherType::None => Box::new(dummy::DummyCipher),
|
||||
|
||||
#[cfg(feature = "sodium")]
|
||||
CipherType::ChaCha20 | CipherType::Salsa20 | CipherType::XSalsa20 | CipherType::ChaCha20Ietf => {
|
||||
Box::new(sodium::SodiumStreamCipher::new(t, key, iv))
|
||||
}
|
||||
|
||||
#[cfg(feature = "rc4")]
|
||||
CipherType::Rc4 => Box::new(openssl::OpenSSLCipher::new(t, key, iv, mode)),
|
||||
|
||||
#[cfg(feature = "rc4")]
|
||||
CipherType::Rc4Md5 => Box::new(rc4_md5::Rc4Md5Cipher::new(key, iv, mode)),
|
||||
|
||||
#[cfg(feature = "aes-cfb")]
|
||||
CipherType::Aes128Cfb
|
||||
| CipherType::Aes128Cfb1
|
||||
| CipherType::Aes128Cfb8
|
||||
| CipherType::Aes128Cfb128
|
||||
| CipherType::Aes192Cfb
|
||||
| CipherType::Aes192Cfb1
|
||||
| CipherType::Aes192Cfb8
|
||||
| CipherType::Aes192Cfb128
|
||||
| CipherType::Aes256Cfb
|
||||
| CipherType::Aes256Cfb1
|
||||
| CipherType::Aes256Cfb8
|
||||
| CipherType::Aes256Cfb128 => Box::new(openssl::OpenSSLCipher::new(t, key, iv, mode)),
|
||||
|
||||
#[cfg(feature = "aes-ctr")]
|
||||
CipherType::Aes128Ctr | CipherType::Aes192Ctr | CipherType::Aes256Ctr => {
|
||||
Box::new(openssl::OpenSSLCipher::new(t, key, iv, mode))
|
||||
}
|
||||
|
||||
#[cfg(feature = "camellia-cfb")]
|
||||
CipherType::Camellia128Cfb
|
||||
| CipherType::Camellia128Cfb1
|
||||
| CipherType::Camellia128Cfb8
|
||||
| CipherType::Camellia128Cfb128
|
||||
| CipherType::Camellia192Cfb
|
||||
| CipherType::Camellia192Cfb1
|
||||
| CipherType::Camellia192Cfb8
|
||||
| CipherType::Camellia192Cfb128
|
||||
| CipherType::Camellia256Cfb
|
||||
| CipherType::Camellia256Cfb1
|
||||
| CipherType::Camellia256Cfb8
|
||||
| CipherType::Camellia256Cfb128 => Box::new(openssl::OpenSSLCipher::new(t, key, iv, mode)),
|
||||
|
||||
_ => unreachable!("{} is not a stream cipher", t),
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
//! This module implements the `table` cipher for fallback compatibility
|
||||
|
||||
use std::io::Cursor;
|
||||
|
||||
use crate::crypto::{CipherResult, CryptoMode, StreamCipher};
|
||||
|
||||
use byteorder::{LittleEndian, ReadBytesExt};
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use digest::Digest;
|
||||
use md5::Md5;
|
||||
|
||||
const TABLE_SIZE: usize = 256usize;
|
||||
|
||||
/// Table cipher
|
||||
pub struct TableCipher {
|
||||
table: [u8; TABLE_SIZE],
|
||||
}
|
||||
|
||||
impl TableCipher {
|
||||
pub fn new(key: &[u8], mode: CryptoMode) -> TableCipher {
|
||||
let key_digest = Md5::digest(key);
|
||||
|
||||
let mut bufr = Cursor::new(&key_digest);
|
||||
let a = bufr.read_u64::<LittleEndian>().unwrap();
|
||||
|
||||
let mut table = [0u64; TABLE_SIZE];
|
||||
for (i, element) in table.iter_mut().enumerate() {
|
||||
*element = i as u64;
|
||||
}
|
||||
|
||||
for i in 1..1024 {
|
||||
table.sort_by(|x, y| (a % (*x + i)).cmp(&(a % (*y + i))))
|
||||
}
|
||||
|
||||
TableCipher {
|
||||
table: match mode {
|
||||
CryptoMode::Encrypt => {
|
||||
let mut t = [0u8; TABLE_SIZE];
|
||||
for i in 0..TABLE_SIZE {
|
||||
t[i] = table[i] as u8;
|
||||
}
|
||||
t
|
||||
}
|
||||
CryptoMode::Decrypt => {
|
||||
let mut t = [0u8; TABLE_SIZE];
|
||||
for (idx, &item) in table.iter().enumerate() {
|
||||
t[item as usize] = idx as u8;
|
||||
}
|
||||
t
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn process(&mut self, data: &[u8], out: &mut dyn BufMut) -> CipherResult<()> {
|
||||
let mut buf = BytesMut::with_capacity(self.buffer_size(data));
|
||||
unsafe {
|
||||
buf.set_len(self.buffer_size(data)); // Set length
|
||||
}
|
||||
for (idx, d) in data.iter().enumerate() {
|
||||
buf[idx] = self.table[*d as usize];
|
||||
}
|
||||
out.put_slice(&buf);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl StreamCipher for TableCipher {
|
||||
fn update(&mut self, data: &[u8], out: &mut dyn BufMut) -> CipherResult<()> {
|
||||
self.process(data, out)
|
||||
}
|
||||
|
||||
fn finalize(&mut self, _: &mut dyn BufMut) -> CipherResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn buffer_size(&self, data: &[u8]) -> usize {
|
||||
data.len()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_table_cipher() {
|
||||
let message = "hello world";
|
||||
let key = "keykeykk";
|
||||
|
||||
let mut enc = TableCipher::new(key.as_bytes(), CryptoMode::Encrypt);
|
||||
let mut dec = TableCipher::new(key.as_bytes(), CryptoMode::Decrypt);
|
||||
let mut encrypted_msg = Vec::new();
|
||||
enc.update(message.as_bytes(), &mut encrypted_msg).unwrap();
|
||||
let mut decrypted_msg = Vec::new();
|
||||
dec.update(&encrypted_msg[..], &mut decrypted_msg).unwrap();
|
||||
|
||||
assert_eq!(&decrypted_msg[..], message.as_bytes());
|
||||
}
|
||||
+1
-1
@@ -94,7 +94,7 @@ pub use self::{
|
||||
pub mod acl;
|
||||
pub mod config;
|
||||
pub mod context;
|
||||
pub mod crypto;
|
||||
// pub mod crypto;
|
||||
pub mod plugin;
|
||||
pub mod relay;
|
||||
|
||||
|
||||
@@ -539,7 +539,7 @@ impl<S: ServerData + 'static> PingBalancer<S> {
|
||||
b"\x12\x34\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07firefox\x03com\x00\x00\x01\x00\x01";
|
||||
|
||||
let addr = Address::SocketAddress(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)), 53));
|
||||
|
||||
|
||||
let client = UdpServerClient::new(stat.context(), stat.server_config()).await?;
|
||||
client.send_to(stat.context(), &addr, DNS_QUERY).await?;
|
||||
|
||||
|
||||
+5
-1
@@ -11,7 +11,11 @@ use crate::{
|
||||
config::{Config, ConfigType},
|
||||
context::{Context, ServerState},
|
||||
plugin::{PluginMode, Plugins},
|
||||
relay::{tcprelay::local::run as run_tcp, udprelay::local::run as run_udp, utils::set_nofile},
|
||||
relay::{
|
||||
tcprelay::local::run as run_tcp,
|
||||
udprelay::local::run as run_udp,
|
||||
utils::set_nofile,
|
||||
},
|
||||
};
|
||||
|
||||
/// Relay server running under local environment.
|
||||
|
||||
@@ -16,11 +16,12 @@ use log::{debug, error, info, trace, warn};
|
||||
#[cfg(unix)]
|
||||
use tokio::net::{unix::SocketAddr as UnixSocketAddr, UnixDatagram};
|
||||
use tokio::{self, net::UdpSocket};
|
||||
use shadowsocks_crypto::v1::CipherKind;
|
||||
|
||||
|
||||
use crate::{
|
||||
config::{Config, ConfigType, ManagerAddr, Mode, ServerAddr, ServerConfig},
|
||||
context::{Context, ServerState, SharedContext, SharedServerState},
|
||||
crypto::CipherType,
|
||||
plugin::PluginConfig,
|
||||
relay::{
|
||||
flow::{MultiServerFlowStatistic, SharedServerFlowStatistic},
|
||||
@@ -423,8 +424,8 @@ impl ManagerService {
|
||||
None => manager_config.method
|
||||
// Default method as shadowsocks-libev's ss-server
|
||||
// Just for compatiblity, some shadowsocks manager relies on this default method
|
||||
.unwrap_or(CipherType::None),
|
||||
Some(method) => match method.parse::<CipherType>() {
|
||||
.unwrap_or(CipherKind::NONE),
|
||||
Some(method) => match method.parse::<CipherKind>() {
|
||||
Ok(m) => m,
|
||||
Err(..) => {
|
||||
let err = Error::new(ErrorKind::Other, format!("unrecognized method \"{}\"", method));
|
||||
|
||||
+60
-54
@@ -31,6 +31,11 @@
|
||||
//! | 2 | Fixed | Variable | Fixed |
|
||||
//! +--------------+---------------+--------------+------------+
|
||||
//! ```
|
||||
use futures::ready;
|
||||
use bytes::{Buf, BufMut, BytesMut};
|
||||
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
use shadowsocks_crypto::v1::{CipherKind, Cipher};
|
||||
|
||||
|
||||
use std::{
|
||||
cmp,
|
||||
@@ -42,12 +47,6 @@ use std::{
|
||||
u16,
|
||||
};
|
||||
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
use bytes::{Buf, BufMut, BytesMut};
|
||||
use futures::ready;
|
||||
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
|
||||
use crate::crypto::{self, BoxAeadDecryptor, BoxAeadEncryptor, CipherType};
|
||||
|
||||
/// AEAD packet payload must be smaller than 0x3FFF
|
||||
const MAX_PACKET_SIZE: usize = 0x3FFF;
|
||||
@@ -62,7 +61,7 @@ enum DecryptReadStep {
|
||||
pub struct DecryptedReader {
|
||||
buffer: BytesMut,
|
||||
data: BytesMut,
|
||||
cipher: BoxAeadDecryptor,
|
||||
cipher: Cipher,
|
||||
pos: usize,
|
||||
tag_size: usize,
|
||||
steps: DecryptReadStep,
|
||||
@@ -70,14 +69,14 @@ pub struct DecryptedReader {
|
||||
}
|
||||
|
||||
impl DecryptedReader {
|
||||
pub fn new(t: CipherType, key: &[u8], nonce: &[u8]) -> DecryptedReader {
|
||||
pub fn new(method: CipherKind, key: &[u8], nonce: &[u8]) -> DecryptedReader {
|
||||
DecryptedReader {
|
||||
// Initialize for the first length block
|
||||
buffer: BytesMut::with_capacity(2 + t.tag_size()),
|
||||
buffer: BytesMut::with_capacity(2 + method.tag_len()),
|
||||
data: BytesMut::new(),
|
||||
cipher: crypto::new_aead_decryptor(t, key, nonce),
|
||||
cipher: Cipher::new(method, key, nonce),
|
||||
pos: 0,
|
||||
tag_size: t.tag_size(),
|
||||
tag_size: method.tag_len(),
|
||||
steps: DecryptReadStep::Length,
|
||||
got_final: false,
|
||||
}
|
||||
@@ -101,7 +100,7 @@ impl DecryptedReader {
|
||||
// Refill buffer
|
||||
match self.steps {
|
||||
DecryptReadStep::Length => ready!(self.poll_read_decrypted_length(ctx, r))?,
|
||||
DecryptReadStep::Data(len) => ready!(self.poll_read_decrypted_data(ctx, r, len))?,
|
||||
DecryptReadStep::Data(plen) => ready!(self.poll_read_decrypted_data(ctx, r, plen))?,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,29 +115,33 @@ impl DecryptedReader {
|
||||
where
|
||||
R: AsyncRead + Unpin,
|
||||
{
|
||||
let buf_len = 2 + self.tag_size;
|
||||
ready!(self.poll_read_exact(ctx, r, buf_len, true))?;
|
||||
let mlen = 2 + self.tag_size;
|
||||
ready!(self.poll_read_exact(ctx, r, mlen, true))?;
|
||||
|
||||
if self.got_final {
|
||||
return Poll::Ready(Ok(()));
|
||||
}
|
||||
|
||||
// Done reading, decrypt it
|
||||
let len = {
|
||||
let mut len_buf = [0u8; 2];
|
||||
self.cipher.decrypt(&self.buffer[..], &mut len_buf)?;
|
||||
BigEndian::read_u16(&len_buf) as usize
|
||||
let plen = {
|
||||
let m = &mut self.buffer[..mlen];
|
||||
|
||||
if !self.cipher.decrypt_packet(m) {
|
||||
return Poll::Ready(Err(io::Error::new(io::ErrorKind::Other, "invalid tag-in")));
|
||||
}
|
||||
|
||||
u16::from_be_bytes([self.buffer[0], self.buffer[1]]) as usize
|
||||
};
|
||||
|
||||
if len > MAX_PACKET_SIZE {
|
||||
if plen > MAX_PACKET_SIZE {
|
||||
// https://shadowsocks.org/en/spec/AEAD-Ciphers.html
|
||||
//
|
||||
// AEAD TCP protocol have reserved the higher two bits for future use
|
||||
|
||||
let err = io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
format!(
|
||||
"buffer size too large ({:#x}), AEAD encryption protocol requires buffer to be smaller than 0x3FFF, the higher two bits must be set to zero",
|
||||
len
|
||||
plen
|
||||
),
|
||||
);
|
||||
return Poll::Ready(Err(err));
|
||||
@@ -150,28 +153,35 @@ impl DecryptedReader {
|
||||
self.pos = 0;
|
||||
|
||||
// Next step, read data
|
||||
self.steps = DecryptReadStep::Data(len);
|
||||
self.buffer.reserve(len + self.tag_size);
|
||||
self.data.reserve(len);
|
||||
self.steps = DecryptReadStep::Data(plen);
|
||||
self.buffer.reserve(plen + self.tag_size);
|
||||
self.data.reserve(plen);
|
||||
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn poll_read_decrypted_data<R>(&mut self, ctx: &mut Context<'_>, r: &mut R, size: usize) -> Poll<io::Result<()>>
|
||||
fn poll_read_decrypted_data<R>(&mut self, ctx: &mut Context<'_>, r: &mut R, plen: usize) -> Poll<io::Result<()>>
|
||||
where
|
||||
R: AsyncRead + Unpin,
|
||||
{
|
||||
let buf_len = size + self.tag_size;
|
||||
ready!(self.poll_read_exact(ctx, r, buf_len, false))?;
|
||||
let mlen = plen + self.tag_size;
|
||||
ready!(self.poll_read_exact(ctx, r, mlen, false))?;
|
||||
|
||||
// Done reading data, decrypt it
|
||||
unsafe {
|
||||
let m: &mut [u8] = self.buffer.as_mut();
|
||||
assert_eq!(m.len(), mlen);
|
||||
|
||||
if !self.cipher.decrypt_packet(m) {
|
||||
return Poll::Ready(Err(io::Error::new(io::ErrorKind::Other, "invalid tag-in")));
|
||||
}
|
||||
|
||||
// It has enough space, I am sure about that
|
||||
let buffer = slice::from_raw_parts_mut(self.data.bytes_mut().as_mut_ptr() as *mut u8, size);
|
||||
self.cipher.decrypt(&self.buffer[..], buffer)?;
|
||||
let data = slice::from_raw_parts_mut(self.data.bytes_mut().as_mut_ptr() as *mut u8, plen);
|
||||
data[..].copy_from_slice(&self.buffer[..plen]);
|
||||
|
||||
// Move forward the pointer
|
||||
self.data.advance_mut(size);
|
||||
self.data.advance_mut(plen);
|
||||
}
|
||||
|
||||
// Clear buffer before overwriting it
|
||||
@@ -214,8 +224,7 @@ impl DecryptedReader {
|
||||
self.got_final = true;
|
||||
return Poll::Ready(Ok(()));
|
||||
} else {
|
||||
use std::io::ErrorKind;
|
||||
return Poll::Ready(Err(ErrorKind::UnexpectedEof.into()));
|
||||
return Poll::Ready(Err(io::ErrorKind::UnexpectedEof.into()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,7 +242,7 @@ enum EncryptWriteStep {
|
||||
|
||||
/// Writer wrapper that will encrypt data automatically
|
||||
pub struct EncryptedWriter {
|
||||
cipher: BoxAeadEncryptor,
|
||||
cipher: Cipher,
|
||||
tag_size: usize,
|
||||
steps: EncryptWriteStep,
|
||||
buf: BytesMut,
|
||||
@@ -241,14 +250,14 @@ pub struct EncryptedWriter {
|
||||
|
||||
impl EncryptedWriter {
|
||||
/// Creates a new EncryptedWriter
|
||||
pub fn new(t: CipherType, key: &[u8], nonce: &[u8]) -> EncryptedWriter {
|
||||
pub fn new(method: CipherKind, key: &[u8], nonce: &[u8]) -> EncryptedWriter {
|
||||
// nonce should be sent with the first packet
|
||||
let mut buf = BytesMut::with_capacity(nonce.len());
|
||||
buf.put(nonce);
|
||||
|
||||
EncryptedWriter {
|
||||
cipher: crypto::new_aead_encryptor(t, key, nonce),
|
||||
tag_size: t.tag_size(),
|
||||
cipher: Cipher::new(method, key, nonce),
|
||||
tag_size: method.tag_len(),
|
||||
steps: EncryptWriteStep::Nothing,
|
||||
buf,
|
||||
}
|
||||
@@ -284,22 +293,25 @@ impl EncryptedWriter {
|
||||
loop {
|
||||
match self.steps {
|
||||
EncryptWriteStep::Nothing => {
|
||||
let output_length = self.buffer_size(data);
|
||||
let data_length = data.len() as u16;
|
||||
let plen = data.len();
|
||||
let mlen = 2 + self.tag_size + plen + self.tag_size;
|
||||
|
||||
self.buf.reserve(output_length);
|
||||
|
||||
let mut data_len_buf = [0u8; 2];
|
||||
BigEndian::write_u16(&mut data_len_buf, data_length);
|
||||
self.buf.reserve(mlen);
|
||||
|
||||
unsafe {
|
||||
let b = slice::from_raw_parts_mut(self.buf.bytes_mut().as_mut_ptr() as *mut u8, output_length);
|
||||
let len_octets = (plen as u16).to_be_bytes();
|
||||
let m = slice::from_raw_parts_mut(self.buf.bytes_mut().as_mut_ptr() as *mut u8, mlen);
|
||||
m[0] = len_octets[0];
|
||||
m[1] = len_octets[1];
|
||||
|
||||
let output_length_size = 2 + self.tag_size;
|
||||
self.cipher.encrypt(&data_len_buf, &mut b[..output_length_size]);
|
||||
self.cipher.encrypt(data, &mut b[output_length_size..output_length]);
|
||||
let hlen = 2 + self.tag_size;
|
||||
|
||||
self.buf.advance_mut(output_length);
|
||||
m[hlen..mlen - self.tag_size].copy_from_slice(data);
|
||||
|
||||
self.cipher.encrypt_packet(&mut m[..hlen]);
|
||||
self.cipher.encrypt_packet(&mut m[hlen..mlen]);
|
||||
|
||||
self.buf.advance_mut(mlen);
|
||||
}
|
||||
|
||||
self.steps = EncryptWriteStep::Writing;
|
||||
@@ -309,8 +321,7 @@ impl EncryptedWriter {
|
||||
let n = ready!(Pin::new(&mut *w).poll_write(ctx, self.buf.bytes()))?;
|
||||
self.buf.advance(n);
|
||||
if n == 0 {
|
||||
use std::io::ErrorKind;
|
||||
return Poll::Ready(Err(ErrorKind::UnexpectedEof.into()));
|
||||
return Poll::Ready(Err(io::ErrorKind::UnexpectedEof.into()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,9 +334,4 @@ impl EncryptedWriter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn buffer_size(&self, data: &[u8]) -> usize {
|
||||
2 + self.tag_size // len and len_tag
|
||||
+ data.len() + self.tag_size // data and data_tag
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,19 +15,20 @@ use bytes::{
|
||||
BytesMut,
|
||||
};
|
||||
use futures::ready;
|
||||
use log::{debug, trace};
|
||||
use log::trace;
|
||||
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf, ReadHalf, WriteHalf};
|
||||
|
||||
use crate::{
|
||||
config::ServerConfig,
|
||||
context::SharedContext,
|
||||
crypto::{CipherCategory, CipherType},
|
||||
};
|
||||
|
||||
use super::{
|
||||
aead::{DecryptedReader as AeadDecryptedReader, EncryptedWriter as AeadEncryptedWriter},
|
||||
stream::{DecryptedReader as StreamDecryptedReader, EncryptedWriter as StreamEncryptedWriter},
|
||||
};
|
||||
use shadowsocks_crypto::v1::{CipherCategory, CipherKind, random_iv_or_salt, };
|
||||
|
||||
|
||||
enum DecryptedReader {
|
||||
None,
|
||||
@@ -46,7 +47,7 @@ enum ReadStatus {
|
||||
/// Waiting for initializing vector (or nonce for AEAD ciphers)
|
||||
///
|
||||
/// (context, Buffer, already_read_bytes, method, key)
|
||||
WaitIv(SharedContext, Limit<BytesMut>, CipherType, Bytes),
|
||||
WaitIv(SharedContext, Limit<BytesMut>, CipherKind, Bytes),
|
||||
|
||||
/// Connection is established, DecryptedReader is initialized
|
||||
Established,
|
||||
@@ -65,21 +66,28 @@ impl<S: Unpin> Unpin for CryptoStream<S> {}
|
||||
impl<S> CryptoStream<S> {
|
||||
/// Create a new CryptoStream with the underlying stream connection
|
||||
pub fn new(context: SharedContext, stream: S, svr_cfg: &ServerConfig) -> CryptoStream<S> {
|
||||
let method = svr_cfg.method();
|
||||
if method.category() == CipherCategory::None {
|
||||
let method = svr_cfg.method();
|
||||
let category = method.category();
|
||||
let key = svr_cfg.clone_key();
|
||||
|
||||
if category == CipherCategory::None {
|
||||
return CryptoStream::<S>::new_none(stream);
|
||||
}
|
||||
|
||||
let prev_len = match method.category() {
|
||||
CipherCategory::Stream => method.iv_size(),
|
||||
CipherCategory::Aead => method.salt_size(),
|
||||
let prev_len = match category {
|
||||
CipherCategory::Stream => method.iv_len(),
|
||||
CipherCategory::Aead => method.salt_len(),
|
||||
CipherCategory::None => 0,
|
||||
};
|
||||
|
||||
let iv = match method.category() {
|
||||
let iv = match category {
|
||||
CipherCategory::Stream => {
|
||||
let local_iv = loop {
|
||||
let iv = method.gen_init_vec();
|
||||
let mut iv = vec![0u8; prev_len];
|
||||
if prev_len > 0 {
|
||||
random_iv_or_salt(&mut iv);
|
||||
}
|
||||
|
||||
if context.check_nonce_and_set(&iv) {
|
||||
// IV exist, generate another one
|
||||
continue;
|
||||
@@ -91,7 +99,11 @@ impl<S> CryptoStream<S> {
|
||||
}
|
||||
CipherCategory::Aead => {
|
||||
let local_salt = loop {
|
||||
let salt = method.gen_salt();
|
||||
let mut salt = vec![0u8; prev_len];
|
||||
if prev_len > 0 {
|
||||
random_iv_or_salt(&mut salt);
|
||||
}
|
||||
|
||||
if context.check_nonce_and_set(&salt) {
|
||||
// Salt exist, generate another one
|
||||
continue;
|
||||
@@ -101,16 +113,15 @@ impl<S> CryptoStream<S> {
|
||||
trace!("generated AEAD cipher salt {:?}", local_salt);
|
||||
local_salt
|
||||
}
|
||||
CipherCategory::None => Bytes::new(),
|
||||
CipherCategory::None => Vec::new(),
|
||||
};
|
||||
|
||||
let method = svr_cfg.method();
|
||||
let enc = match method.category() {
|
||||
CipherCategory::Stream => EncryptedWriter::Stream(StreamEncryptedWriter::new(method, svr_cfg.key(), &iv)),
|
||||
CipherCategory::Aead => EncryptedWriter::Aead(AeadEncryptedWriter::new(method, svr_cfg.key(), &iv)),
|
||||
let enc = match category {
|
||||
CipherCategory::Stream => EncryptedWriter::Stream(StreamEncryptedWriter::new(method, &key, &iv)),
|
||||
CipherCategory::Aead => EncryptedWriter::Aead(AeadEncryptedWriter::new(method, &key, &iv)),
|
||||
CipherCategory::None => EncryptedWriter::None,
|
||||
};
|
||||
|
||||
|
||||
CryptoStream {
|
||||
stream,
|
||||
dec: None,
|
||||
@@ -119,7 +130,7 @@ impl<S> CryptoStream<S> {
|
||||
context,
|
||||
BytesMut::with_capacity(prev_len).limit(prev_len),
|
||||
method,
|
||||
svr_cfg.clone_key(),
|
||||
key,
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -158,8 +169,7 @@ where
|
||||
buf.advance_mut(n);
|
||||
}
|
||||
if n == 0 {
|
||||
use std::io::ErrorKind;
|
||||
return Poll::Ready(Err(ErrorKind::UnexpectedEof.into()));
|
||||
return Poll::Ready(Err(io::ErrorKind::UnexpectedEof.into()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,7 +179,7 @@ where
|
||||
if ctx.check_nonce_and_set(nonce) {
|
||||
use std::io::{Error, ErrorKind};
|
||||
|
||||
debug!("detected repeated iv/salt {:?}", ByteStr::new(nonce));
|
||||
trace!("detected repeated iv/salt {:?}", ByteStr::new(nonce));
|
||||
|
||||
let err = Error::new(ErrorKind::Other, "detected repeated iv/salt");
|
||||
return Poll::Ready(Err(err));
|
||||
@@ -236,8 +246,7 @@ where
|
||||
///
|
||||
/// The two halfs share the same `CryptoStream<S>`
|
||||
pub fn split(self) -> (ReadHalf<CryptoStream<S>>, WriteHalf<CryptoStream<S>>) {
|
||||
use tokio::io::split;
|
||||
split(self)
|
||||
tokio::io::split(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,9 +14,12 @@ use tokio::{
|
||||
};
|
||||
|
||||
mod aead;
|
||||
mod stream;
|
||||
mod crypto_io;
|
||||
|
||||
pub mod client;
|
||||
mod connection;
|
||||
mod crypto_io;
|
||||
|
||||
#[cfg(feature = "local-http")]
|
||||
mod http_local;
|
||||
#[cfg(all(
|
||||
@@ -35,7 +38,7 @@ pub mod server;
|
||||
#[cfg(feature = "local-socks4")]
|
||||
mod socks4_local;
|
||||
mod socks5_local;
|
||||
mod stream;
|
||||
|
||||
#[cfg(feature = "local-tunnel")]
|
||||
mod tunnel_local;
|
||||
mod utils;
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
//! Stream protocol implementation
|
||||
use futures::ready;
|
||||
use bytes::{Buf, BufMut, BytesMut};
|
||||
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
use shadowsocks_crypto::v1::{CipherKind, Cipher};
|
||||
|
||||
use std::{
|
||||
cmp,
|
||||
@@ -8,31 +12,26 @@ use std::{
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use crate::crypto::{new_stream, BoxStreamCipher, CipherType, CryptoMode};
|
||||
use bytes::{Buf, BufMut, BytesMut};
|
||||
use futures::ready;
|
||||
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
|
||||
use super::BUFFER_SIZE;
|
||||
// use super::BUFFER_SIZE;
|
||||
|
||||
/// Reader wrapper that will decrypt data automatically
|
||||
pub struct DecryptedReader {
|
||||
buffer: BytesMut,
|
||||
cipher: BoxStreamCipher,
|
||||
cipher: Cipher,
|
||||
pos: usize,
|
||||
got_final: bool,
|
||||
incoming_buffer: Box<[u8]>,
|
||||
// incoming_buffer: Box<[u8]>,
|
||||
}
|
||||
|
||||
impl DecryptedReader {
|
||||
pub fn new(t: CipherType, key: &[u8], iv: &[u8]) -> DecryptedReader {
|
||||
let cipher = new_stream(t, key, iv, CryptoMode::Decrypt);
|
||||
pub fn new(method: CipherKind, key: &[u8], iv: &[u8]) -> DecryptedReader {
|
||||
let cipher = Cipher::new(method, key, iv);
|
||||
DecryptedReader {
|
||||
buffer: BytesMut::new(),
|
||||
cipher,
|
||||
pos: 0,
|
||||
got_final: false,
|
||||
incoming_buffer: vec![0u8; BUFFER_SIZE].into_boxed_slice(),
|
||||
// incoming_buffer: vec![0u8; BUFFER_SIZE].into_boxed_slice(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,31 +44,33 @@ impl DecryptedReader {
|
||||
where
|
||||
R: AsyncRead + Unpin,
|
||||
{
|
||||
let mut buf = [0u8; 1 << 14];
|
||||
while self.pos >= self.buffer.len() {
|
||||
if self.got_final {
|
||||
return Poll::Ready(Ok(()));
|
||||
}
|
||||
|
||||
let mut buf = ReadBuf::new(&mut self.incoming_buffer);
|
||||
ready!(Pin::new(&mut *r).poll_read(ctx, &mut buf))?;
|
||||
let data = buf.filled();
|
||||
let mut buffer = ReadBuf::new(&mut buf);
|
||||
// let mut buf = ReadBuf::new(&mut self.incoming_buffer);
|
||||
ready!(Pin::new(&mut *r).poll_read(ctx, &mut buffer))?;
|
||||
let amt = buffer.filled().len();
|
||||
|
||||
if amt == 0 {
|
||||
self.got_final = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
let m = buffer.filled_mut();
|
||||
|
||||
assert_eq!(self.cipher.decrypt_packet(m), true);
|
||||
|
||||
// Reset pointers
|
||||
// So the outer loop will break if data.len() != 0
|
||||
self.buffer.clear();
|
||||
self.pos = 0;
|
||||
|
||||
if data.len() == 0 {
|
||||
// Finialize block
|
||||
self.buffer.reserve(self.cipher.buffer_size(&[]));
|
||||
self.cipher.finalize(&mut self.buffer)?;
|
||||
self.got_final = true;
|
||||
} else {
|
||||
// Ensure we have enough space
|
||||
let buffer_len = self.cipher.buffer_size(data);
|
||||
self.buffer.reserve(buffer_len);
|
||||
self.cipher.update(data, &mut self.buffer)?;
|
||||
}
|
||||
// Ensure we have enough space
|
||||
self.buffer.put_slice(m);
|
||||
}
|
||||
|
||||
let remaining_len = self.buffer.len() - self.pos;
|
||||
@@ -87,20 +88,20 @@ enum EncryptWriteStep {
|
||||
|
||||
/// Writer wrapper that will encrypt data automatically
|
||||
pub struct EncryptedWriter {
|
||||
cipher: BoxStreamCipher,
|
||||
cipher: Cipher,
|
||||
steps: EncryptWriteStep,
|
||||
buf: BytesMut,
|
||||
}
|
||||
|
||||
impl EncryptedWriter {
|
||||
/// Creates a new EncryptedWriter
|
||||
pub fn new(t: CipherType, key: &[u8], iv: &[u8]) -> EncryptedWriter {
|
||||
pub fn new(method: CipherKind, key: &[u8], iv: &[u8]) -> EncryptedWriter {
|
||||
// iv should be sent with the first packet
|
||||
let mut buf = BytesMut::with_capacity(iv.len());
|
||||
buf.put(iv);
|
||||
|
||||
EncryptedWriter {
|
||||
cipher: new_stream(t, key, &iv, CryptoMode::Encrypt),
|
||||
cipher: Cipher::new(method, key, &iv),
|
||||
steps: EncryptWriteStep::Nothing,
|
||||
buf,
|
||||
}
|
||||
@@ -119,12 +120,13 @@ impl EncryptedWriter {
|
||||
W: AsyncWrite + Unpin,
|
||||
{
|
||||
// FIXME: How about finalize?
|
||||
|
||||
loop {
|
||||
match self.steps {
|
||||
EncryptWriteStep::Nothing => {
|
||||
self.buf.reserve(self.buffer_size(data));
|
||||
self.cipher.update(data, &mut self.buf)?;
|
||||
let mut payload = data.to_vec();
|
||||
self.cipher.encrypt_packet(&mut payload);
|
||||
self.buf.put_slice(&payload);
|
||||
|
||||
self.steps = EncryptWriteStep::Writing;
|
||||
}
|
||||
EncryptWriteStep::Writing => {
|
||||
@@ -143,8 +145,4 @@ impl EncryptedWriter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn buffer_size(&self, data: &[u8]) -> usize {
|
||||
self.cipher.buffer_size(data)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,11 +24,11 @@ use tokio::{
|
||||
sync::{mpsc, Mutex},
|
||||
time,
|
||||
};
|
||||
use shadowsocks_crypto::v1::CipherCategory;
|
||||
|
||||
use crate::{
|
||||
config::{Config, ServerAddr, ServerConfig},
|
||||
context::{Context, SharedContext},
|
||||
crypto::CipherCategory,
|
||||
relay::{
|
||||
flow::SharedServerFlowStatistic,
|
||||
loadbalancing::server::{ServerData, SharedServerStatistic},
|
||||
@@ -573,7 +573,7 @@ impl ProxyAssociation {
|
||||
recv_buf.truncate(recv_n);
|
||||
Cursor::new(recv_buf)
|
||||
} else {
|
||||
let decrypt_buf = match decrypt_payload(context, svr_cfg.method(), svr_cfg.key(), &recv_buf[..recv_n])? {
|
||||
let decrypt_buf = match decrypt_payload(context, svr_cfg.method(), &svr_cfg.key(), &recv_buf[..recv_n])? {
|
||||
None => {
|
||||
error!("UDP packet too short, received length {}", recv_n);
|
||||
let err = io::Error::new(io::ErrorKind::InvalidData, "packet too short");
|
||||
|
||||
@@ -6,14 +6,16 @@ use std::{
|
||||
net::{IpAddr, Ipv4Addr, SocketAddr},
|
||||
};
|
||||
|
||||
|
||||
use bytes::{BufMut, Bytes, BytesMut};
|
||||
use log::{error, trace, warn};
|
||||
use tokio::net::UdpSocket;
|
||||
use shadowsocks_crypto::v1::{CipherCategory, CipherKind};
|
||||
|
||||
|
||||
use crate::{
|
||||
config::{ServerAddr, ServerConfig},
|
||||
context::Context,
|
||||
crypto::{CipherCategory, CipherType},
|
||||
relay::{
|
||||
socks5::{Address, UdpAssociateHeader},
|
||||
sys::{create_outbound_udp_socket, create_udp_socket},
|
||||
@@ -31,7 +33,6 @@ use super::{
|
||||
/// Socks5 proxy client
|
||||
pub struct Socks5Client {
|
||||
socket: UdpSocket,
|
||||
|
||||
// Socks5 protocol requires to keep this TCP connection alive
|
||||
// Theoretically if this connection is broken, the association is broken too, but the UDP Socks5 server in this crate doesn't behave like that
|
||||
#[allow(dead_code)]
|
||||
@@ -95,7 +96,7 @@ impl Socks5Client {
|
||||
/// UDP client for communicating with ShadowSocks' server
|
||||
pub struct ServerClient {
|
||||
socket: UdpSocket,
|
||||
method: CipherType,
|
||||
method: CipherKind,
|
||||
key: Bytes,
|
||||
}
|
||||
|
||||
@@ -110,15 +111,15 @@ impl ServerClient {
|
||||
lookup_then!(context, dname, *port, |addr| { socket.connect(&addr).await })?;
|
||||
}
|
||||
};
|
||||
Ok(ServerClient {
|
||||
socket,
|
||||
method: svr_cfg.method(),
|
||||
key: svr_cfg.clone_key(),
|
||||
})
|
||||
|
||||
let key = svr_cfg.clone_key();
|
||||
let method = svr_cfg.method();
|
||||
|
||||
Ok(ServerClient { socket, method, key, })
|
||||
}
|
||||
|
||||
async fn pack_req(
|
||||
method: CipherType,
|
||||
method: CipherKind,
|
||||
key: &Bytes,
|
||||
context: &Context,
|
||||
addr: &Address,
|
||||
@@ -163,7 +164,7 @@ impl ServerClient {
|
||||
|
||||
async fn parse_resp(
|
||||
context: &Context,
|
||||
method: CipherType,
|
||||
method: CipherKind,
|
||||
key: &Bytes,
|
||||
recv_buf: &[u8],
|
||||
) -> io::Result<(Address, Vec<u8>)> {
|
||||
|
||||
+112
-86
@@ -19,174 +19,200 @@
|
||||
//! | Fixed | Variable | Fixed |
|
||||
//! +--------+-----------+-----------+
|
||||
//! ```
|
||||
use crate::context::Context;
|
||||
|
||||
use std::{io, slice};
|
||||
|
||||
use log::{debug, trace};
|
||||
use byte_string::ByteStr;
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use log::{debug, trace};
|
||||
use shadowsocks_crypto::v1::{CipherCategory, CipherKind, Cipher, random_iv_or_salt};
|
||||
|
||||
use std::io;
|
||||
|
||||
use crate::{
|
||||
context::Context,
|
||||
crypto::{self, CipherCategory, CipherType, CryptoMode},
|
||||
};
|
||||
|
||||
/// Encrypt payload into ShadowSocks UDP encrypted packet
|
||||
pub fn encrypt_payload(
|
||||
context: &Context,
|
||||
t: CipherType,
|
||||
method: CipherKind,
|
||||
key: &[u8],
|
||||
payload: &[u8],
|
||||
dst: &mut BytesMut,
|
||||
) -> io::Result<()> {
|
||||
match t.category() {
|
||||
match method.category() {
|
||||
CipherCategory::None => {
|
||||
// FIXME: Is there a better way to prevent copying?
|
||||
dst.put_slice(payload);
|
||||
Ok(())
|
||||
}
|
||||
CipherCategory::Stream => encrypt_payload_stream(context, t, key, payload, dst),
|
||||
CipherCategory::Aead => encrypt_payload_aead(context, t, key, payload, dst),
|
||||
},
|
||||
CipherCategory::Stream => {
|
||||
encrypt_payload_stream(context, method, key, payload, dst)
|
||||
},
|
||||
CipherCategory::Aead => {
|
||||
encrypt_payload_aead(context, method, key, payload, dst)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn encrypt_payload_stream(
|
||||
context: &Context,
|
||||
t: CipherType,
|
||||
method: CipherKind,
|
||||
key: &[u8],
|
||||
payload: &[u8],
|
||||
dst: &mut BytesMut,
|
||||
) -> io::Result<()> {
|
||||
let iv = loop {
|
||||
let iv = t.gen_init_vec();
|
||||
if context.check_nonce_and_set(&iv) {
|
||||
continue;
|
||||
}
|
||||
break iv;
|
||||
};
|
||||
let mut cipher = crypto::new_stream(t, key, &iv, CryptoMode::Encrypt);
|
||||
let plen = payload.len();
|
||||
let iv_len = method.iv_len();
|
||||
|
||||
let mut iv = [0u8; 32];
|
||||
let iv = &mut iv[..iv_len];
|
||||
if iv_len > 0 {
|
||||
loop {
|
||||
random_iv_or_salt(iv);
|
||||
if !context.check_nonce_and_set(&iv) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
context.check_nonce_and_set(&iv);
|
||||
}
|
||||
|
||||
|
||||
let mut cipher = Cipher::new(method, &key, &iv);
|
||||
trace!("UDP packet generated stream iv {:?}", ByteStr::new(&iv));
|
||||
|
||||
dst.reserve(iv.len() + payload.len());
|
||||
dst.reserve(iv_len + plen);
|
||||
|
||||
// First of all, IV
|
||||
dst.put_slice(&iv);
|
||||
dst.extend_from_slice(&iv);
|
||||
dst.extend_from_slice(payload);
|
||||
|
||||
// Encrypted data
|
||||
cipher.update(&payload[..], dst)?;
|
||||
cipher.finalize(dst)?;
|
||||
let data: &mut [u8] = dst.as_mut();
|
||||
cipher.encrypt_packet(&mut data[iv_len..]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn encrypt_payload_aead(
|
||||
context: &Context,
|
||||
t: CipherType,
|
||||
method: CipherKind,
|
||||
key: &[u8],
|
||||
payload: &[u8],
|
||||
dst: &mut BytesMut,
|
||||
) -> io::Result<()> {
|
||||
let salt = loop {
|
||||
let salt = t.gen_salt();
|
||||
if context.check_nonce_and_set(&salt) {
|
||||
continue;
|
||||
let plen = payload.len();
|
||||
let salt_len = method.salt_len();
|
||||
|
||||
let mut salt = [0u8; 32];
|
||||
let salt = &mut salt[..salt_len];
|
||||
|
||||
if salt_len > 0 {
|
||||
loop {
|
||||
random_iv_or_salt(salt);
|
||||
if !context.check_nonce_and_set(&salt) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
break salt;
|
||||
};
|
||||
let tag_size = t.tag_size();
|
||||
let mut cipher = crypto::new_aead_encryptor(t, key, &salt);
|
||||
} else {
|
||||
context.check_nonce_and_set(&salt);
|
||||
}
|
||||
|
||||
let mut cipher = Cipher::new(method, &key, &salt);
|
||||
|
||||
let tag_len = cipher.tag_len();
|
||||
|
||||
trace!("UDP packet generated AEAD salt {:?}", ByteStr::new(&salt));
|
||||
|
||||
dst.reserve(salt.len() + payload.len() + tag_size);
|
||||
dst.reserve(salt_len + plen + tag_len);
|
||||
|
||||
// First of all, salt
|
||||
dst.put_slice(&salt);
|
||||
dst.extend_from_slice(&salt);
|
||||
dst.extend_from_slice(payload);
|
||||
dst.resize(dst.len() + tag_len, 0);
|
||||
|
||||
// Encrypted data
|
||||
unsafe {
|
||||
let remaining = dst.bytes_mut();
|
||||
let b = slice::from_raw_parts_mut(remaining.as_mut_ptr() as *mut u8, remaining.len());
|
||||
|
||||
cipher.encrypt(payload, b);
|
||||
dst.advance_mut(payload.len() + tag_size);
|
||||
}
|
||||
let data: &mut [u8] = dst.as_mut();
|
||||
cipher.encrypt_packet(&mut data[salt_len..]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
/// Decrypt payload from ShadowSocks UDP encrypted packet
|
||||
pub fn decrypt_payload(context: &Context, t: CipherType, key: &[u8], payload: &[u8]) -> io::Result<Option<Vec<u8>>> {
|
||||
match t.category() {
|
||||
pub fn decrypt_payload(context: &Context, method: CipherKind, key: &[u8], payload: &[u8]) -> io::Result<Option<Vec<u8>>> {
|
||||
match method.category() {
|
||||
CipherCategory::None => {
|
||||
// FIXME: Is there a better way to prevent copying?
|
||||
let mut buf = Vec::with_capacity(payload.len());
|
||||
buf.extend(payload);
|
||||
buf.extend_from_slice(payload);
|
||||
Ok(Some(buf))
|
||||
}
|
||||
CipherCategory::Stream => decrypt_payload_stream(context, t, key, payload),
|
||||
CipherCategory::Aead => decrypt_payload_aead(context, t, key, payload),
|
||||
},
|
||||
CipherCategory::Stream => {
|
||||
decrypt_payload_stream(context, method, key, payload)
|
||||
},
|
||||
CipherCategory::Aead => {
|
||||
decrypt_payload_aead(context, method, key, payload)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn decrypt_payload_stream(context: &Context, t: CipherType, key: &[u8], payload: &[u8]) -> io::Result<Option<Vec<u8>>> {
|
||||
let iv_size = t.iv_size();
|
||||
if payload.len() < iv_size {
|
||||
fn decrypt_payload_stream(context: &Context, method: CipherKind, key: &[u8], payload: &[u8]) -> io::Result<Option<Vec<u8>>> {
|
||||
let plen = payload.len();
|
||||
let iv_len = method.iv_len();
|
||||
|
||||
if plen < iv_len {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let iv = &payload[..iv_size];
|
||||
let iv = &payload[..iv_len];
|
||||
|
||||
if context.check_nonce_and_set(iv) {
|
||||
use std::io::{Error, ErrorKind};
|
||||
|
||||
debug!("detected repeated iv {:?}", ByteStr::new(iv));
|
||||
|
||||
let err = Error::new(ErrorKind::Other, "detected repeated iv");
|
||||
return Err(err);
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "detected repeated iv"));
|
||||
}
|
||||
|
||||
let data = &payload[iv_size..];
|
||||
|
||||
let data = &payload[iv_len..];
|
||||
|
||||
trace!("UDP packet got stream IV {:?}", ByteStr::new(iv));
|
||||
let mut cipher = Cipher::new(method, key, iv);
|
||||
|
||||
let mut cipher = crypto::new_stream(t, key, iv, CryptoMode::Decrypt);
|
||||
|
||||
let mut recv_payload = Vec::with_capacity(data.len());
|
||||
cipher.update(data, &mut recv_payload)?;
|
||||
cipher.finalize(&mut recv_payload)?;
|
||||
|
||||
Ok(Some(recv_payload))
|
||||
let mut buf = vec![0u8; data.len()];
|
||||
buf[..data.len()].copy_from_slice(data);
|
||||
|
||||
assert_eq!(cipher.decrypt_packet(&mut buf), true);
|
||||
|
||||
Ok(Some(buf))
|
||||
}
|
||||
|
||||
fn decrypt_payload_aead(context: &Context, t: CipherType, key: &[u8], payload: &[u8]) -> io::Result<Option<Vec<u8>>> {
|
||||
let tag_size = t.tag_size();
|
||||
let salt_size = t.salt_size();
|
||||
|
||||
if payload.len() < tag_size + salt_size {
|
||||
fn decrypt_payload_aead(context: &Context, method: CipherKind, key: &[u8], payload: &[u8]) -> io::Result<Option<Vec<u8>>> {
|
||||
let salt_len = method.salt_len();
|
||||
if payload.len() < salt_len {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let salt = &payload[..salt_size];
|
||||
let (salt, payload) = payload.split_at(salt_len);
|
||||
if context.check_nonce_and_set(salt) {
|
||||
use std::io::{Error, ErrorKind};
|
||||
|
||||
debug!("detected repeated salt {:?}", ByteStr::new(salt));
|
||||
|
||||
let err = Error::new(ErrorKind::Other, "detected repeated salt");
|
||||
return Err(err);
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "detected repeated salt"));
|
||||
}
|
||||
|
||||
let data = &payload[salt_size..];
|
||||
|
||||
trace!("UDP packet got AEAD salt {:?}", ByteStr::new(salt));
|
||||
|
||||
let data_length = payload.len() - tag_size - salt_size;
|
||||
let mut cipher = Cipher::new(method, &key, &salt);
|
||||
let tag_len = cipher.tag_len();
|
||||
|
||||
let mut cipher = crypto::new_aead_decryptor(t, key, salt);
|
||||
if payload.len() < tag_len {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let mut recv_payload = vec![0u8; data_length];
|
||||
cipher.decrypt(data, &mut recv_payload)?;
|
||||
let mut buf = vec![0u8; payload.len()];
|
||||
buf[..payload.len()].copy_from_slice(payload);
|
||||
|
||||
Ok(Some(recv_payload))
|
||||
|
||||
if !cipher.decrypt_packet(&mut buf) {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "invalid tag-in"));
|
||||
}
|
||||
|
||||
// NOTE: 移除 TAG 数据。
|
||||
buf.truncate(buf.len() - tag_len);
|
||||
|
||||
Ok(Some(buf))
|
||||
}
|
||||
|
||||
+3
-3
@@ -10,10 +10,10 @@ use tokio::{
|
||||
prelude::*,
|
||||
time::{self, Duration},
|
||||
};
|
||||
use shadowsocks_crypto::v1::CipherKind;
|
||||
|
||||
use shadowsocks::{
|
||||
config::{Config, ConfigType, ServerAddr, ServerConfig},
|
||||
crypto::CipherType,
|
||||
relay::socks4::{Address, Command, HandshakeRequest, HandshakeResponse, ResultCode},
|
||||
run_local,
|
||||
run_server,
|
||||
@@ -26,7 +26,7 @@ pub struct Socks4TestServer {
|
||||
}
|
||||
|
||||
impl Socks4TestServer {
|
||||
pub fn new<S, L>(svr_addr: S, local_addr: L, pwd: &str, method: CipherType) -> Socks4TestServer
|
||||
pub fn new<S, L>(svr_addr: S, local_addr: L, pwd: &str, method: CipherKind) -> Socks4TestServer
|
||||
where
|
||||
S: ToSocketAddrs,
|
||||
L: ToSocketAddrs,
|
||||
@@ -73,7 +73,7 @@ async fn socks4_relay_connect() {
|
||||
const LOCAL_ADDR: &str = "127.0.0.1:7200";
|
||||
|
||||
const PASSWORD: &str = "test-password";
|
||||
const METHOD: CipherType = CipherType::Aes128Gcm;
|
||||
const METHOD: CipherKind = CipherKind::AES_128_GCM;
|
||||
|
||||
let svr = Socks4TestServer::new(SERVER_ADDR, LOCAL_ADDR, PASSWORD, METHOD);
|
||||
svr.run().await;
|
||||
|
||||
+4
-4
@@ -7,10 +7,10 @@ use tokio::{
|
||||
prelude::*,
|
||||
time::{self, Duration},
|
||||
};
|
||||
use shadowsocks_crypto::v1::CipherKind;
|
||||
|
||||
use shadowsocks::{
|
||||
config::{Config, ConfigType, Mode, ServerAddr, ServerConfig},
|
||||
crypto::CipherType,
|
||||
relay::{socks5::Address, tcprelay::client::Socks5Client},
|
||||
run_local,
|
||||
run_server,
|
||||
@@ -23,7 +23,7 @@ pub struct Socks5TestServer {
|
||||
}
|
||||
|
||||
impl Socks5TestServer {
|
||||
pub fn new<S, L>(svr_addr: S, local_addr: L, pwd: &str, method: CipherType, enable_udp: bool) -> Socks5TestServer
|
||||
pub fn new<S, L>(svr_addr: S, local_addr: L, pwd: &str, method: CipherKind, enable_udp: bool) -> Socks5TestServer
|
||||
where
|
||||
S: ToSocketAddrs,
|
||||
L: ToSocketAddrs,
|
||||
@@ -72,7 +72,7 @@ async fn socks5_relay_stream() {
|
||||
const LOCAL_ADDR: &str = "127.0.0.1:8200";
|
||||
|
||||
const PASSWORD: &str = "test-password";
|
||||
const METHOD: CipherType = CipherType::Aes128Cfb;
|
||||
const METHOD: CipherKind = CipherKind::AES_128_CFB128;
|
||||
|
||||
let svr = Socks5TestServer::new(SERVER_ADDR, LOCAL_ADDR, PASSWORD, METHOD, false);
|
||||
svr.run().await;
|
||||
@@ -105,7 +105,7 @@ async fn socks5_relay_aead() {
|
||||
const LOCAL_ADDR: &str = "127.0.0.1:8210";
|
||||
|
||||
const PASSWORD: &str = "test-password";
|
||||
const METHOD: CipherType = CipherType::Aes256Gcm;
|
||||
const METHOD: CipherKind = CipherKind::AES_256_GCM;
|
||||
|
||||
let svr = Socks5TestServer::new(SERVER_ADDR, LOCAL_ADDR, PASSWORD, METHOD, false);
|
||||
svr.run().await;
|
||||
|
||||
+2
-2
@@ -4,10 +4,10 @@ use std::net::SocketAddr;
|
||||
|
||||
use log::debug;
|
||||
use tokio::time::{self, Duration};
|
||||
use shadowsocks_crypto::v1::CipherKind;
|
||||
|
||||
use shadowsocks::{
|
||||
config::{Config, ConfigType, Mode, ServerConfig},
|
||||
crypto::CipherType,
|
||||
relay::{socks5::Address, udprelay::client::Socks5Client},
|
||||
run_local,
|
||||
run_server,
|
||||
@@ -19,7 +19,7 @@ const LOCAL_ADDR: &str = "127.0.0.1:8291";
|
||||
const UDP_ECHO_SERVER_ADDR: &str = "127.0.0.1:50403";
|
||||
|
||||
const PASSWORD: &str = "test-password";
|
||||
const METHOD: CipherType = CipherType::Aes128Gcm;
|
||||
const METHOD: CipherKind = CipherKind::AES_128_GCM;
|
||||
|
||||
fn get_svr_config() -> Config {
|
||||
let mut cfg = Config::new(ConfigType::Server);
|
||||
|
||||
Reference in New Issue
Block a user