diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 00000000..b0bc0d78 --- /dev/null +++ b/.cargo/config.toml @@ -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", +] \ No newline at end of file diff --git a/.circleci/config.yml b/.circleci/config.yml index 21be219e..b21c69e6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -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: | diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index dfefd754..579561fc 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -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 diff --git a/Cargo.lock b/Cargo.lock index 81408d51..4d704cf2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index 853d1c66..a5aba3ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" } \ No newline at end of file diff --git a/README.md b/README.md index f31a92ab..f40c75ff 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/src/bin/local.rs b/src/bin/local.rs index 9a4734db..9e29ee24 100644 --- a/src/bin/local.rs +++ b/src/bin/local.rs @@ -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::() + .parse::() .expect("encryption method"); let svr_addr = svr_addr.parse::().expect("server-addr"); diff --git a/src/bin/manager.rs b/src/bin/manager.rs index da2d4431..d5e85b10 100644 --- a/src/bin/manager.rs +++ b/src/bin/manager.rs @@ -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::().expect("encrypt-method")); + manager_config.method = Some(m.parse::().expect("encrypt-method")); } if let Some(t) = matches.value_of("TIMEOUT") { diff --git a/src/bin/server.rs b/src/bin/server.rs index a0570e66..59aa0c06 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -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::() + .parse::() .expect("encryption method"); let svr_addr = svr_addr.parse::().expect("server-addr"); let timeout = matches diff --git a/src/config.rs b/src/config.rs index 4e20cdaa..e94d729b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -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, #[serde(skip_serializing_if = "Option::is_none")] servers: Option>, + #[cfg(feature = "trust-dns")] #[serde(skip_serializing_if = "Option::is_none")] dns: Option, #[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, - /// Encryption key + // Encryption key enc_key: Bytes, + /// Plugin config plugin: Option, /// 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, plugin: Option, ) -> 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, + pub method: Option, /// Timeout for TCP connections, setting to manager's created servers pub timeout: Option, /// IP/Host for servers to bind (inbound) @@ -1341,7 +1353,7 @@ impl Config { }, }; - let method = match m.parse::() { + let method = match m.parse::() { Ok(m) => m, Err(..) => { let err = Error::new( @@ -1394,7 +1406,7 @@ impl Config { }, }; - let method = match svr.method.parse::() { + let method = match svr.method.parse::() { 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())); } diff --git a/src/crypto/aead.rs b/src/crypto/aead.rs deleted file mode 100644 index 9d72c566..00000000 --- a/src/crypto/aead.rs +++ /dev/null @@ -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; - -/// Variant `AeadEncryptor` -pub type BoxAeadEncryptor = Box; - -/// 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::::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]); - } -} diff --git a/src/crypto/cipher.rs b/src/crypto/cipher.rs deleted file mode 100644 index 63fdfab3..00000000 --- a/src/crypto/cipher.rs +++ /dev/null @@ -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 = Result; - -/// 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 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 { - 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 - ); - } -} diff --git a/src/crypto/dummy.rs b/src/crypto/dummy.rs deleted file mode 100644 index e380cb65..00000000 --- a/src/crypto/dummy.rs +++ /dev/null @@ -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() - } -} diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs deleted file mode 100644 index e0b4c9ef..00000000 --- a/src/crypto/mod.rs +++ /dev/null @@ -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 for symm::Mode { - fn from(m: CryptoMode) -> symm::Mode { - match m { - CryptoMode::Encrypt => symm::Mode::Encrypt, - CryptoMode::Decrypt => symm::Mode::Decrypt, - } - } -} diff --git a/src/crypto/openssl.rs b/src/crypto/openssl.rs deleted file mode 100644 index 0a2f0e70..00000000 --- a/src/crypto/openssl.rs +++ /dev/null @@ -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) - } -} diff --git a/src/crypto/rc4_md5.rs b/src/crypto/rc4_md5.rs deleted file mode 100644 index 8bc1bc94..00000000 --- a/src/crypto/rc4_md5.rs +++ /dev/null @@ -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[..]); - } -} diff --git a/src/crypto/ring.rs b/src/crypto/ring.rs deleted file mode 100644 index c2985f05..00000000 --- a/src/crypto/ring.rs +++ /dev/null @@ -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), - Open(OpeningKey), -} - -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 { - 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>(&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); - } -} diff --git a/src/crypto/siv.rs b/src/crypto/siv.rs deleted file mode 100644 index 766d7b42..00000000 --- a/src/crypto/siv.rs +++ /dev/null @@ -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); - } -} diff --git a/src/crypto/sodium.rs b/src/crypto/sodium.rs deleted file mode 100644 index c781eb3d..00000000 --- a/src/crypto/sodium.rs +++ /dev/null @@ -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, - iv: Vec, - 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( - 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); - } -} diff --git a/src/crypto/stream.rs b/src/crypto/stream.rs deleted file mode 100644 index b47555ba..00000000 --- a/src/crypto/stream.rs +++ /dev/null @@ -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; - -/// 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), - } -} diff --git a/src/crypto/table.rs b/src/crypto/table.rs deleted file mode 100644 index a01670ce..00000000 --- a/src/crypto/table.rs +++ /dev/null @@ -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::().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()); -} diff --git a/src/lib.rs b/src/lib.rs index d69473e6..86802ead 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; diff --git a/src/relay/loadbalancing/server.rs b/src/relay/loadbalancing/server.rs index 9fd39861..2f881f70 100644 --- a/src/relay/loadbalancing/server.rs +++ b/src/relay/loadbalancing/server.rs @@ -539,7 +539,7 @@ impl PingBalancer { 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?; diff --git a/src/relay/local.rs b/src/relay/local.rs index 51478e15..3dc71d9a 100644 --- a/src/relay/local.rs +++ b/src/relay/local.rs @@ -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. diff --git a/src/relay/manager.rs b/src/relay/manager.rs index deecd4a3..9d67757d 100644 --- a/src/relay/manager.rs +++ b/src/relay/manager.rs @@ -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::() { + .unwrap_or(CipherKind::NONE), + Some(method) => match method.parse::() { Ok(m) => m, Err(..) => { let err = Error::new(ErrorKind::Other, format!("unrecognized method \"{}\"", method)); diff --git a/src/relay/tcprelay/aead.rs b/src/relay/tcprelay/aead.rs index 9fa43182..478ddb28 100644 --- a/src/relay/tcprelay/aead.rs +++ b/src/relay/tcprelay/aead.rs @@ -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(&mut self, ctx: &mut Context<'_>, r: &mut R, size: usize) -> Poll> + fn poll_read_decrypted_data(&mut self, ctx: &mut Context<'_>, r: &mut R, plen: usize) -> Poll> 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 - } } diff --git a/src/relay/tcprelay/crypto_io.rs b/src/relay/tcprelay/crypto_io.rs index 19195fd1..13e5e3b8 100644 --- a/src/relay/tcprelay/crypto_io.rs +++ b/src/relay/tcprelay/crypto_io.rs @@ -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, CipherType, Bytes), + WaitIv(SharedContext, Limit, CipherKind, Bytes), /// Connection is established, DecryptedReader is initialized Established, @@ -65,21 +66,28 @@ impl Unpin for CryptoStream {} impl CryptoStream { /// Create a new CryptoStream with the underlying stream connection pub fn new(context: SharedContext, stream: S, svr_cfg: &ServerConfig) -> CryptoStream { - 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::::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 CryptoStream { } 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 CryptoStream { 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 CryptoStream { 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` pub fn split(self) -> (ReadHalf>, WriteHalf>) { - use tokio::io::split; - split(self) + tokio::io::split(self) } } diff --git a/src/relay/tcprelay/mod.rs b/src/relay/tcprelay/mod.rs index d53a6ba5..0401c827 100644 --- a/src/relay/tcprelay/mod.rs +++ b/src/relay/tcprelay/mod.rs @@ -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; diff --git a/src/relay/tcprelay/stream.rs b/src/relay/tcprelay/stream.rs index 5d2fe302..a6c795b6 100644 --- a/src/relay/tcprelay/stream.rs +++ b/src/relay/tcprelay/stream.rs @@ -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) - } } diff --git a/src/relay/udprelay/association.rs b/src/relay/udprelay/association.rs index 5ecf2939..d72e80f1 100644 --- a/src/relay/udprelay/association.rs +++ b/src/relay/udprelay/association.rs @@ -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"); diff --git a/src/relay/udprelay/client.rs b/src/relay/udprelay/client.rs index 0c514547..e2d5e334 100644 --- a/src/relay/udprelay/client.rs +++ b/src/relay/udprelay/client.rs @@ -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)> { diff --git a/src/relay/udprelay/crypto_io.rs b/src/relay/udprelay/crypto_io.rs index a6a1a413..5783a44f 100644 --- a/src/relay/udprelay/crypto_io.rs +++ b/src/relay/udprelay/crypto_io.rs @@ -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>> { - match t.category() { +pub fn decrypt_payload(context: &Context, method: CipherKind, key: &[u8], payload: &[u8]) -> io::Result>> { + 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>> { - 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>> { + 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>> { - 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>> { + 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)) } diff --git a/tests/socks4.rs b/tests/socks4.rs index c683e25f..623aee81 100644 --- a/tests/socks4.rs +++ b/tests/socks4.rs @@ -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(svr_addr: S, local_addr: L, pwd: &str, method: CipherType) -> Socks4TestServer + pub fn new(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; diff --git a/tests/socks5.rs b/tests/socks5.rs index 5ed086fc..25adc3d9 100644 --- a/tests/socks5.rs +++ b/tests/socks5.rs @@ -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(svr_addr: S, local_addr: L, pwd: &str, method: CipherType, enable_udp: bool) -> Socks5TestServer + pub fn new(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; diff --git a/tests/udp.rs b/tests/udp.rs index c79ab875..58996219 100644 --- a/tests/udp.rs +++ b/tests/udp.rs @@ -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);