diff --git a/configure.ac b/configure.ac index 4c4b04c0..567c1357 100755 --- a/configure.ac +++ b/configure.ac @@ -3,7 +3,7 @@ dnl Process this file with autoconf to produce a configure script. AC_PREREQ([2.67]) AC_INIT([shadowsocks-libev], [2.6.3], [max.c.lv@gmail.com]) -AC_CONFIG_SRCDIR([src/encrypt.c]) +AC_CONFIG_SRCDIR([src/crypto.c]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_AUX_DIR(auto) AC_CONFIG_MACRO_DIR([m4]) @@ -38,19 +38,6 @@ AC_ARG_ENABLE( esac], [enable_system_shared_lib=false]) AM_CONDITIONAL([USE_SYSTEM_SHARED_LIB], [test x$enable_system_shared_lib = xtrue]) -dnl Checks for crypto library -AC_ARG_WITH( - [crypto-library], - [AS_HELP_STRING([--with-crypto-library=library], [build with the given crypto library, TYPE=openssl|polarssl|mbedtls @<:@default=openssl@:>@])], - [ - case "${withval}" in - openssl|polarssl|mbedtls) ;; - *) AC_MSG_ERROR([bad value ${withval} for --with-crypto-library]) ;; - esac - ], - [with_crypto_library="openssl"] -) - AC_ARG_ENABLE([documentation], AS_HELP_STRING([--disable-documentation], [do not build documentation]), [disable_documentation=true], @@ -92,22 +79,8 @@ esac dnl Checks for TLS AX_TLS([:], [:]) -dnl Checks for crypto library -case "${with_crypto_library}" in - openssl) - ss_ZLIB - ss_OPENSSL - AC_DEFINE([USE_CRYPTO_OPENSSL], [1], [Use OpenSSL library]) - ;; - polarssl) - ss_POLARSSL - AC_DEFINE([USE_CRYPTO_POLARSSL], [1], [Use PolarSSL library]) - ;; - mbedtls) - ss_MBEDTLS - AC_DEFINE([USE_CRYPTO_MBEDTLS], [1], [Use mbed TLS library]) - ;; -esac +dnl Checks for crypto libraries +ss_MBEDTLS ss_SODIUM dnl Checks for Apple CommonCrypto API diff --git a/debian/config.json b/debian/config.json index aba1bf28..c21e3e44 100644 --- a/debian/config.json +++ b/debian/config.json @@ -4,5 +4,5 @@ "local_port":1080, "password":"barfoo!", "timeout":60, - "method":null + "method":"rc4-md5" } diff --git a/doc/shadowsocks-libev.asciidoc b/doc/shadowsocks-libev.asciidoc index d8f944c4..a5823ad7 100644 --- a/doc/shadowsocks-libev.asciidoc +++ b/doc/shadowsocks-libev.asciidoc @@ -108,9 +108,6 @@ Enable UDP relay and disable TCP relay. + Not available in local mode. --A:: -Enable onetime authentication. - -L :: Specify destination server address and port for local port forwarding. + @@ -170,7 +167,6 @@ The config file equivalent of command line options is listed as example below. | --plugin "obfs-server" | "plugin": "obfs-server" | --plugin-opts "obfs=http" | "plugin_opts": "obfs=http" | -6 | "ipv6_first": true -| -A | "auth": true | -n "/etc/nofile" | "nofile": "/etc/nofile" | -d "8.8.8.8" | "nameserver": "8.8.8.8" | -L "somedns.net:53" | "tunnel_address": "somedns.net:53" diff --git a/doc/ss-local.asciidoc b/doc/ss-local.asciidoc index a1f2b0f2..56d1cf4a 100644 --- a/doc/ss-local.asciidoc +++ b/doc/ss-local.asciidoc @@ -8,7 +8,7 @@ ss-local - shadowsocks client as socks5 proxy, libev port SYNOPSIS -------- *ss-local* - [-Auv6] [-h|--help] + [-uv6] [-h|--help] [-s ] [-p ] [-l ] [-k ] [-m ] [-f ] [-t ] [-c ] [-i ] @@ -94,9 +94,6 @@ Enable UDP relay. -U:: Enable UDP relay and disable TCP relay. --A:: -Enable onetime authentication. - -6:: Resovle hostname to IPv6 address first. diff --git a/doc/ss-redir.asciidoc b/doc/ss-redir.asciidoc index 84b9c8bd..e0ac7f79 100644 --- a/doc/ss-redir.asciidoc +++ b/doc/ss-redir.asciidoc @@ -8,7 +8,7 @@ ss-redir - shadowsocks client as transparent proxy, libev port SYNOPSIS -------- *ss-redir* - [-AuUv6] [-h|--help] + [-uUv6] [-h|--help] [-s ] [-p ] [-l ] [-k ] [-m ] [-f ] [-t ] [-c ] [-b ] @@ -88,9 +88,6 @@ TPROXY is required in redir mode. You may need root permission. -U:: Enable UDP relay and disable TCP relay. --A:: -Enable onetime authentication. - -6:: Resovle hostname to IPv6 address first. diff --git a/doc/ss-server.asciidoc b/doc/ss-server.asciidoc index 8c6a2464..4bc1a689 100644 --- a/doc/ss-server.asciidoc +++ b/doc/ss-server.asciidoc @@ -8,7 +8,7 @@ ss-server - shadowsocks server, libev port SYNOPSIS -------- *ss-server* - [-AuUv] [-h|--help] + [-uUv] [-h|--help] [-s ] [-p ] [-l ] [-k ] [-m ] [-f ] [-t ] [-c ] [-i ] @@ -92,9 +92,6 @@ Enable UDP relay. -U:: Enable UDP relay and disable TCP relay. --A:: -Enable onetime authentication. - -6:: Resovle hostname to IPv6 address first. diff --git a/doc/ss-tunnel.asciidoc b/doc/ss-tunnel.asciidoc index ffd6ed88..eaffaa2a 100644 --- a/doc/ss-tunnel.asciidoc +++ b/doc/ss-tunnel.asciidoc @@ -8,7 +8,7 @@ ss-tunnel - shadowsocks tools for local port forwarding, libev port SYNOPSIS -------- *ss-tunnel* - [-AuUv6] [-h|--help] + [-uUv6] [-h|--help] [-s ] [-p ] [-l ] [-k ] [-m ] [-f ] [-t ] [-c ] [-i ] @@ -94,9 +94,6 @@ Enable UDP relay. -U:: Enable UDP relay and disable TCP relay. --A:: -Enable onetime authentication. - -6:: Resovle hostname to IPv6 address first. diff --git a/m4/openssl.m4 b/m4/openssl.m4 deleted file mode 100644 index c47e94b0..00000000 --- a/m4/openssl.m4 +++ /dev/null @@ -1,56 +0,0 @@ -dnl Check to find the OpenSSL headers/libraries - -AC_DEFUN([ss_OPENSSL], -[ - case $host_os in - *mingw*) - ;; - *) - AC_CHECK_FUNC(dlopen, - [], - [AC_CHECK_LIB(dl, dlopen, - [LIBS="$LIBS -ldl"], - [AC_MSG_ERROR([OpenSSL depends on libdl.]); break] - )] - ) - ;; - esac - - AC_ARG_WITH(openssl, - AS_HELP_STRING([--with-openssl=DIR], [OpenSSL base directory, or:]), - [openssl="$withval" - CFLAGS="$CFLAGS -I$withval/include" - LDFLAGS="$LDFLAGS -L$withval/lib"] - ) - - AC_ARG_WITH(openssl-include, - AS_HELP_STRING([--with-openssl-include=DIR], [OpenSSL headers directory (without trailing /openssl)]), - [openssl_include="$withval" - CFLAGS="$CFLAGS -I$withval"] - ) - - AC_ARG_WITH(openssl-lib, - AS_HELP_STRING([--with-openssl-lib=DIR], [OpenSSL library directory]), - [openssl_lib="$withval" - LDFLAGS="$LDFLAGS -L$withval"] - ) - - AC_CHECK_HEADERS(openssl/evp.h openssl/rsa.h openssl/rand.h openssl/err.h openssl/sha.h openssl/pem.h openssl/engine.h, - [], - [AC_MSG_ERROR([OpenSSL header files not found.]); break] - ) - - AC_CHECK_LIB(crypto, EVP_EncryptInit_ex, - [LIBS="-lcrypto $LIBS"], - [AC_MSG_ERROR([OpenSSL libraries not found.])] - ) - - AC_CHECK_FUNCS([RAND_pseudo_bytes EVP_EncryptInit_ex], , - [AC_MSG_ERROR([Missing OpenSSL functionality, make sure you have installed the latest version.]); break], - ) - - AC_CHECK_DECL([OpenSSL_add_all_algorithms], , - [AC_MSG_ERROR([Missing OpenSSL functionality, make sure you have installed the latest version.]); break], - [#include ] - ) -]) diff --git a/m4/polarssl.m4 b/m4/polarssl.m4 deleted file mode 100644 index 6507efb6..00000000 --- a/m4/polarssl.m4 +++ /dev/null @@ -1,45 +0,0 @@ -dnl Check to find the PolarSSL headers/libraries - -AC_DEFUN([ss_POLARSSL], -[ - - AC_ARG_WITH(polarssl, - AS_HELP_STRING([--with-polarssl=DIR], [PolarSSL base directory, or:]), - [polarssl="$withval" - CFLAGS="$CFLAGS -I$withval/include" - LDFLAGS="$LDFLAGS -L$withval/lib"] - ) - - AC_ARG_WITH(polarssl-include, - AS_HELP_STRING([--with-polarssl-include=DIR], [PolarSSL headers directory (without trailing /polarssl)]), - [polarssl_include="$withval" - CFLAGS="$CFLAGS -I$withval"] - ) - - AC_ARG_WITH(polarssl-lib, - AS_HELP_STRING([--with-polarssl-lib=DIR], [PolarSSL library directory]), - [polarssl_lib="$withval" - LDFLAGS="$LDFLAGS -L$withval"] - ) - - AC_CHECK_LIB(polarssl, cipher_init_ctx, - [LIBS="-lpolarssl $LIBS"], - [AC_MSG_ERROR([PolarSSL libraries not found.])] - ) - - AC_MSG_CHECKING([polarssl version]) - AC_COMPILE_IFELSE( - [AC_LANG_PROGRAM( - [[ -#include - ]], - [[ -#if POLARSSL_VERSION_NUMBER < 0x01020500 -#error invalid version -#endif - ]] - )], - [AC_MSG_RESULT([ok])], - [AC_MSG_ERROR([PolarSSL 1.2.5 or newer required])] - ) -]) diff --git a/m4/zlib.m4 b/m4/zlib.m4 deleted file mode 100644 index 13082862..00000000 --- a/m4/zlib.m4 +++ /dev/null @@ -1,38 +0,0 @@ -dnl Check to find the zlib headers/libraries - -AC_DEFUN([ss_ZLIB], -[ - AC_ARG_ENABLE([zlib], - AS_HELP_STRING([--disable-zlib], [disable zlib compression support])) - AS_IF([test "x$enable_zlib" != "xno"], [ - AC_DEFINE(HAVE_ZLIB, 1, [have zlib compression support]) - AC_ARG_WITH(zlib, - AS_HELP_STRING([--with-zlib=DIR], [zlib base directory, or:]), - [zlib="$withval" - CPPFLAGS="$CPPFLAGS -I$withval/include" - LDFLAGS="$LDFLAGS -L$withval/lib"] - ) - - AC_ARG_WITH(zlib-include, - AS_HELP_STRING([--with-zlib-include=DIR], [zlib headers directory]), - [zlib_include="$withval" - CPPFLAGS="$CPPFLAGS -I$withval"] - ) - - AC_ARG_WITH(zlib-lib, - AS_HELP_STRING([--with-zlib-lib=DIR], [zlib library directory]), - [zlib_lib="$withval" - LDFLAGS="$LDFLAGS -L$withval"] - ) - - AC_CHECK_HEADERS(zlib.h, - [], - [AC_MSG_ERROR("zlib header files not found."); break] - ) - - AC_CHECK_LIB(z, compress2, - [LIBS="$LIBS -lz"], - [AC_MSG_ERROR("zlib libraries not found.")] - ) - ]) -]) diff --git a/src/Makefile.am b/src/Makefile.am index bf627e27..2d386782 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -17,40 +17,44 @@ sni_src = http.c \ tls.c \ rule.c +crypto_src = crypto.c \ + aead.c \ + stream.c + plugin_src = plugin.c ss_local_SOURCES = utils.c \ jconf.c \ json.c \ - encrypt.c \ udprelay.c \ cache.c \ acl.c \ netutils.c \ local.c \ + $(crypto_src) \ $(plugin_src) \ $(sni_src) ss_tunnel_SOURCES = utils.c \ jconf.c \ json.c \ - encrypt.c \ udprelay.c \ cache.c \ netutils.c \ - $(plugin_src) \ - tunnel.c + tunnel.c \ + $(crypto_src) \ + $(plugin_src) ss_server_SOURCES = utils.c \ netutils.c \ jconf.c \ json.c \ - encrypt.c \ udprelay.c \ cache.c \ acl.c \ resolv.c \ server.c \ + $(crypto_src) \ $(plugin_src) \ $(sni_src) @@ -80,11 +84,11 @@ bin_PROGRAMS += ss-redir ss_redir_SOURCES = utils.c \ jconf.c \ json.c \ - encrypt.c \ netutils.c \ cache.c \ udprelay.c \ redir.c \ + $(crypto_src) \ $(plugin_src) \ $(sni_src) @@ -100,7 +104,7 @@ libshadowsocks_libev_la_LDFLAGS = -version-info $(VERSION_INFO) libshadowsocks_libev_la_LIBADD = $(ss_local_LDADD) include_HEADERS = shadowsocks.h -noinst_HEADERS = acl.h encrypt.h json.h netutils.h redir.h server.h tls.h uthash.h \ +noinst_HEADERS = acl.h crypto.h stream.h json.h netutils.h redir.h server.h tls.h uthash.h \ cache.h http.h local.h plugin.h resolv.h tunnel.h utils.h \ common.h jconf.h manager.h protocol.h rule.h socks5.h udprelay.h EXTRA_DIST = ss-nat diff --git a/src/acl.c b/src/acl.c index 84ec0365..0851c03f 100644 --- a/src/acl.c +++ b/src/acl.c @@ -1,7 +1,7 @@ /* * acl.c - Manage the ACL (Access Control List) * - * Copyright (C) 2013 - 2016, Max Lv + * Copyright (C) 2013 - 2017, Max Lv * * This file is part of the shadowsocks-libev. * diff --git a/src/acl.h b/src/acl.h index 6e404175..ba487173 100644 --- a/src/acl.h +++ b/src/acl.h @@ -1,7 +1,7 @@ /* * acl.h - Define the ACL interface * - * Copyright (C) 2013 - 2016, Max Lv + * Copyright (C) 2013 - 2017, Max Lv * * This file is part of the shadowsocks-libev. * diff --git a/src/aead.c b/src/aead.c new file mode 100644 index 00000000..a68bf7cc --- /dev/null +++ b/src/aead.c @@ -0,0 +1,768 @@ +/* + * aead.c - Manage AEAD ciphers + * + * Copyright (C) 2013 - 2017, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#define CIPHER_UNSUPPORTED "unsupported" +#include +#include +#include + +#include +#include + +#include "cache.h" +#include "aead.h" +#include "utils.h" + +#define NONE (-1) +#define AES128GCM 0 +#define AES192GCM 1 +#define AES256GCM 2 +/* + * methods above requires gcm context + * methods below doesn't require it, + * then we need to fake one + */ +#define CHACHA20POLY1305 3 +#define CHACHA20POLY1305IETF 4 + +#ifdef FS_HAVE_XCHACHA20IETF +#define XCHACHA20POLY1305IETF 5 +#endif + +#define CHUNK_SIZE_LEN 2 + +/* + * This is SIP004 proposed by @Mygod, the design of TCP chunk is from @breakwa11 and + * @Noisyfox. This first version of this file is written by @wongsyrone. + * + * The first nonce is either from client or server side, it is generated randomly, and + * the sequent nonces are increased by 1. + * + * Data.Len is used to separate general ciphertext and Auth tag. We can start decryption + * if and only if the verification is passed. + * Firstly, we do length check, then decrypt it, separate ciphertext and attached data tag + * based on the verified length, verify data tag and decrypt the corresponding data. + * Finally, do what you supposed to do, e.g. forward user data. + * + * For UDP, nonces are generated randomly without the incrementation. + * + * TCP request (before encryption) + * +------+---------------------+------------------+ + * | ATYP | Destination Address | Destination Port | + * +------+---------------------+------------------+ + * | 1 | Variable | 2 | + * +------+---------------------+------------------+ + * + * TCP request (after encryption, *ciphertext*) + * +--------+--------------+------------------+--------------+---------------+ + * | NONCE | *HeaderLen* | HeaderLen_TAG | *Header* | Header_TAG | + * +--------+--------------+------------------+--------------+---------------+ + * | Fixed | 2 | Fixed | Variable | Fixed | + * +--------+--------------+------------------+--------------+---------------+ + * + * Header input: atyp + dst.addr + dst.port + * HeaderLen is length(atyp + dst.addr + dst.port) + * Header_TAG and Header_TAG are in plaintext + * + * TCP Chunk (before encryption) + * +----------+ + * | DATA | + * +----------+ + * | Variable | + * +----------+ + * + * Data.Len is a 16-bit big-endian integer indicating the length of DATA. + * + * TCP Chunk (after encryption, *ciphertext*) + * +--------------+---------------+--------------+------------+ + * | *DataLen* | DataLen_TAG | *Data* | Data_TAG | + * +--------------+---------------+--------------+------------+ + * | 2 | Fixed | Variable | Fixed | + * +--------------+---------------+--------------+------------+ + * + * Len_TAG and DATA_TAG have the same length, they are in plaintext. + * After encryption, DATA -> DATA* + * + * UDP (before encryption) + * +------+---------------------+------------------+----------+ + * | ATYP | Destination Address | Destination Port | DATA | + * +------+---------------------+------------------+----------+ + * | 1 | Variable | 2 | Variable | + * +------+---------------------+------------------+----------+ + * + * UDP (after encryption, *ciphertext*) + * +--------+-----------+-----------+ + * | NONCE | *Data* | Data_TAG | + * +--------+-----------+-----------+ + * | Fixed | Variable | Fixed | + * +--------+-----------+-----------+ + * + * *Data* is Encrypt(atyp + dst.addr + dst.port + payload) + * RSV and FRAG are dropped + * Since UDP packet is either received completely or missed, + * we don't have to keep a field to track its length. + * + */ + +#ifdef DEBUG +static void +dump(char *tag, char *text, int len) +{ + int i; + printf("%s: ", tag); + for (i = 0; i < len; i++) + printf("0x%02x ", (uint8_t)text[i]); + printf("\n"); +} + +#endif + +const char *supported_aead_ciphers[AEAD_CIPHER_NUM] = { + "aes-128-gcm", + "aes-192-gcm", + "aes-256-gcm", + "chacha20-poly1305", + "chacha20-ietf-poly1305", +#ifdef FS_HAVE_XCHACHA20IETF + "xchacha20-ietf-poly1305" +#endif +}; + +/* + * use mbed TLS cipher wrapper to unify handling + */ +static const char *supported_aead_ciphers_mbedtls[AEAD_CIPHER_NUM] = { + "AES-128-GCM", + "AES-192-GCM", + "AES-256-GCM", + CIPHER_UNSUPPORTED, + CIPHER_UNSUPPORTED, +#ifdef FS_HAVE_XCHACHA20IETF + CIPHER_UNSUPPORTED +#endif +}; + +static const int supported_aead_ciphers_nonce_size[AEAD_CIPHER_NUM] = { + 12, 12, 12, 8, 12, +#ifdef FS_HAVE_XCHACHA20IETF + 24 +#endif +}; + +static const int supported_aead_ciphers_key_size[AEAD_CIPHER_NUM] = { + 16, 24, 32, 32, 32, +#ifdef FS_HAVE_XCHACHA20IETF + 32 +#endif +}; + +static const int supported_aead_ciphers_tag_size[AEAD_CIPHER_NUM] = { + 16, 16, 16, 16, 16, +#ifdef FS_HAVE_XCHACHA20IETF + 16 +#endif +}; + +static int +cipher_aead_encrypt(cipher_ctx_t *cipher_ctx, + uint8_t *c, + size_t *clen, + uint8_t *m, + size_t mlen, + uint8_t *ad, + size_t adlen, + uint8_t *n, + uint8_t *k, + size_t nlen, + size_t tlen) +{ + int err = CRYPTO_OK; + unsigned long long long_clen = 0; + + switch (cipher_ctx->cipher->method) { + case AES128GCM: + case AES192GCM: + case AES256GCM: + err = mbedtls_cipher_auth_encrypt(cipher_ctx->evp, n, nlen, ad, adlen, + m, mlen, c, clen, c + mlen, tlen); + *clen += tlen; + break; + case CHACHA20POLY1305: + err = crypto_aead_chacha20poly1305_encrypt(c, &long_clen, m, mlen, + ad, adlen, NULL, n, k); + *clen = (size_t)long_clen; + break; + case CHACHA20POLY1305IETF: + err = crypto_aead_chacha20poly1305_ietf_encrypt(c, &long_clen, m, mlen, + ad, adlen, NULL, n, k); + *clen = (size_t)long_clen; + break; +#ifdef FS_HAVE_XCHACHA20IETF + case XCHACHA20POLY1305IETF: + err = crypto_aead_xchacha20poly1305_ietf_encrypt(c, &long_clen, m, mlen, + ad, adlen, NULL, n, k); + *clen = (size_t)long_clen; + break; +#endif + default: + return CRYPTO_ERROR; + } + + return err; +} + +static int +cipher_aead_decrypt(cipher_ctx_t *cipher_ctx, + uint8_t *p, + size_t *plen, + uint8_t *m, + size_t mlen, + uint8_t *ad, + size_t adlen, + uint8_t *n, + uint8_t *k, + size_t nlen, + size_t tlen) +{ + int err = CRYPTO_ERROR; + unsigned long long long_plen = 0; + + switch (cipher_ctx->cipher->method) { + case AES128GCM: + case AES192GCM: + case AES256GCM: + err = mbedtls_cipher_auth_decrypt(cipher_ctx->evp, n, nlen, ad, adlen, + m, mlen - tlen, p, plen, m + mlen - tlen, tlen); + break; + case CHACHA20POLY1305: + err = crypto_aead_chacha20poly1305_decrypt(p, &long_plen, NULL, m, mlen, + ad, adlen, n, k); + *plen = (size_t)long_plen; // it's safe to cast 64bit to 32bit length here + break; + case CHACHA20POLY1305IETF: + err = crypto_aead_chacha20poly1305_ietf_decrypt(p, &long_plen, NULL, m, mlen, + ad, adlen, n, k); + *plen = (size_t)long_plen; // it's safe to cast 64bit to 32bit length here + break; +#ifdef FS_HAVE_XCHACHA20IETF + case XCHACHA20POLY1305IETF: + err = crypto_aead_xchacha20poly1305_ietf_decrypt(p, &long_plen, NULL, m, mlen, + ad, adlen, n, k); + *plen = (size_t)long_plen; // it's safe to cast 64bit to 32bit length here + break; +#endif + default: + return CRYPTO_ERROR; + } + + return err; +} + +/* + * get basic cipher info structure + * it's a wrapper offered by crypto library + */ +const cipher_kt_t * +aead_get_cipher_type(int method) +{ + if (method < AES128GCM || method >= AEAD_CIPHER_NUM) { + LOGE("aead_get_cipher_type(): Illegal method"); + return NULL; + } + + /* cipher that don't use mbed TLS, just return */ + if (method >= CHACHA20POLY1305) { + return NULL; + } + + const char *ciphername = supported_aead_ciphers[method]; + const char *mbedtlsname = supported_aead_ciphers_mbedtls[method]; + if (strcmp(mbedtlsname, CIPHER_UNSUPPORTED) == 0) { + LOGE("Cipher %s currently is not supported by mbed TLS library", + ciphername); + return NULL; + } + return mbedtls_cipher_info_from_string(mbedtlsname); +} + +static void +aead_cipher_ctx_init(cipher_ctx_t *cipher_ctx, int method, int enc) +{ + if (method < AES128GCM || method >= AEAD_CIPHER_NUM) { + LOGE("cipher_context_init(): Illegal method"); + return; + } + + if (method >= CHACHA20POLY1305) { + return; + } + + const char *ciphername = supported_aead_ciphers[method]; + + const cipher_kt_t *cipher = aead_get_cipher_type(method); + + cipher_ctx->evp = ss_malloc(sizeof(cipher_evp_t)); + memset(cipher_ctx->evp, 0, sizeof(cipher_evp_t)); + cipher_evp_t *evp = cipher_ctx->evp; + + if (cipher == NULL) { + LOGE("Cipher %s not found in mbed TLS library", ciphername); + FATAL("Cannot initialize mbed TLS cipher"); + } + mbedtls_cipher_init(evp); + if (mbedtls_cipher_setup(evp, cipher) != 0) { + FATAL("Cannot initialize mbed TLS cipher context"); + } + if (mbedtls_cipher_setkey(evp, cipher_ctx->cipher->key, + cipher_ctx->cipher->key_len * 8, enc) != 0) { + mbedtls_cipher_free(evp); + FATAL("Cannot set mbed TLS cipher key"); + } + if (mbedtls_cipher_reset(evp) != 0) { + mbedtls_cipher_free(evp); + FATAL("Cannot finish preparation of mbed TLS cipher context"); + } + +#ifdef DEBUG + dump("KEY", (char *)cipher_ctx->cipher->key, cipher_ctx->cipher->key_len); +#endif +} + +void +aead_ctx_init(cipher_t *cipher, cipher_ctx_t *cipher_ctx, int enc) +{ + sodium_memzero(cipher_ctx, sizeof(cipher_ctx_t)); + cipher_ctx->cipher = cipher; + + aead_cipher_ctx_init(cipher_ctx, cipher->method, enc); + + if (enc) { + rand_bytes(cipher_ctx->nonce, cipher->nonce_len); + } +} + +void +aead_ctx_release(cipher_ctx_t *cipher_ctx) +{ + if (cipher_ctx->cipher->method >= CHACHA20POLY1305) { + return; + } + + if (cipher_ctx->chunk != NULL) { + bfree(cipher_ctx->chunk); + ss_free(cipher_ctx->chunk); + cipher_ctx->chunk = NULL; + } + + mbedtls_cipher_free(cipher_ctx->evp); + ss_free(cipher_ctx->evp); +} + +int +aead_encrypt_all(buffer_t *plaintext, cipher_t *cipher, size_t capacity) +{ + cipher_ctx_t cipher_ctx; + aead_ctx_init(cipher, &cipher_ctx, 1); + + size_t nonce_len = cipher->nonce_len; + size_t tag_len = cipher->tag_len; + int err = CRYPTO_OK; + + static buffer_t tmp = { 0, 0, 0, NULL }; + brealloc(&tmp, nonce_len + tag_len + plaintext->len, capacity); + buffer_t *ciphertext = &tmp; + ciphertext->len = tag_len + plaintext->len; + + // generate nonce + uint8_t nonce[MAX_NONCE_LENGTH]; + rand_bytes(nonce, nonce_len); + + /* copy nonce to first pos */ + memcpy(ciphertext->data, nonce, nonce_len); + + size_t clen = ciphertext->len; + err = cipher_aead_encrypt(&cipher_ctx, + (uint8_t *)ciphertext->data + nonce_len, + &clen, + (uint8_t *)plaintext->data, + plaintext->len, + NULL, + 0, + nonce, + cipher->key, + nonce_len, + tag_len); + + if (err) { + bfree(plaintext); + aead_ctx_release(&cipher_ctx); + return CRYPTO_ERROR; + } + +#ifdef DEBUG + dump("PLAIN", plaintext->data, plaintext->len); + dump("CIPHER", ciphertext->data + nonce_len, ciphertext->len); +#endif + + aead_ctx_release(&cipher_ctx); + + assert(ciphertext->len == clen); + + brealloc(plaintext, nonce_len + ciphertext->len, capacity); + memcpy(plaintext->data, ciphertext->data, nonce_len + ciphertext->len); + plaintext->len = nonce_len + ciphertext->len; + + return CRYPTO_OK; +} + +int +aead_decrypt_all(buffer_t *ciphertext, cipher_t *cipher, size_t capacity) +{ + size_t nonce_len = cipher->nonce_len; + size_t tag_len = cipher->tag_len; + int err = CRYPTO_OK; + + if (ciphertext->len <= nonce_len + tag_len) { + return CRYPTO_ERROR; + } + + cipher_ctx_t cipher_ctx; + aead_ctx_init(cipher, &cipher_ctx, 0); + + static buffer_t tmp = { 0, 0, 0, NULL }; + brealloc(&tmp, ciphertext->len, capacity); + buffer_t *plaintext = &tmp; + plaintext->len = ciphertext->len - nonce_len - tag_len; + + /* get nonce */ + uint8_t nonce[MAX_NONCE_LENGTH]; + memcpy(nonce, ciphertext->data, nonce_len); + + size_t plen = plaintext->len; + err = cipher_aead_decrypt(&cipher_ctx, + (uint8_t *)plaintext->data, + &plen, + (uint8_t *)ciphertext->data + nonce_len, + ciphertext->len - nonce_len, + NULL, + 0, + nonce, + cipher->key, + nonce_len, + tag_len); + + if (err) { + bfree(ciphertext); + aead_ctx_release(&cipher_ctx); + return CRYPTO_ERROR; + } + +#ifdef DEBUG + dump("PLAIN", plaintext->data, plaintext->len); + dump("CIPHER", ciphertext->data + nonce_len, ciphertext->len - nonce_len); +#endif + + aead_ctx_release(&cipher_ctx); + + brealloc(ciphertext, plaintext->len, capacity); + memcpy(ciphertext->data, plaintext->data, plaintext->len); + ciphertext->len = plaintext->len; + + return CRYPTO_OK; +} + +static int +aead_chunk_encrypt(cipher_ctx_t *ctx, uint8_t *p, uint8_t *c, uint8_t *n, + uint16_t plen, size_t nlen, size_t tlen) +{ + int err; + size_t clen; + uint8_t len_buf[CHUNK_SIZE_LEN]; + uint16_t t = ntohs(plen + tlen); + memcpy(len_buf, &t, CHUNK_SIZE_LEN); + + clen = CHUNK_SIZE_LEN + tlen; + err = cipher_aead_encrypt(ctx, c, &clen, len_buf, CHUNK_SIZE_LEN, + NULL, 0, n, ctx->cipher->key, nlen, tlen); + if (err) + return CRYPTO_ERROR; + assert(clen == CHUNK_SIZE_LEN + tlen); + + sodium_increment(n, nlen); + + clen = plen + tlen; + err = cipher_aead_encrypt(ctx, c + CHUNK_SIZE_LEN + tlen, &clen, p, plen, + NULL, 0, n, ctx->cipher->key, nlen, tlen); + if (err) + return CRYPTO_ERROR; + assert(clen == plen + tlen); + + sodium_increment(n, nlen); + + return CRYPTO_OK; +} + +/* TCP */ +int +aead_encrypt(buffer_t *plaintext, cipher_ctx_t *cipher_ctx, size_t capacity) +{ + if (cipher_ctx == NULL) + return CRYPTO_ERROR; + + if (plaintext->len == 0) { + return CRYPTO_OK; + } + + static buffer_t tmp = { 0, 0, 0, NULL }; + buffer_t *ciphertext; + + cipher_t *cipher = cipher_ctx->cipher; + int err = CRYPTO_ERROR; + size_t nonce_ofst = 0; + size_t nonce_len = cipher->nonce_len; + size_t tag_len = cipher->tag_len; + + if (!cipher_ctx->init) { + nonce_ofst = nonce_len; + } + + size_t out_len = nonce_ofst + 2 * tag_len + plaintext->len + CHUNK_SIZE_LEN; + brealloc(&tmp, out_len, capacity); + ciphertext = &tmp; + ciphertext->len = out_len; + + if (!cipher_ctx->init) { + memcpy(ciphertext->data, cipher_ctx->nonce, nonce_len); + cipher_ctx->init = 1; + } + + err = aead_chunk_encrypt(cipher_ctx, + (uint8_t *)plaintext->data, + (uint8_t *)ciphertext->data + nonce_ofst, + cipher_ctx->nonce, plaintext->len, nonce_len, tag_len); + if (err) + return err; + +#ifdef DEBUG + dump("PLAIN", plaintext->data, plaintext->len); + dump("CIPHER", ciphertext->data + nonce_ofst, ciphertext->len); +#endif + + brealloc(plaintext, ciphertext->len, capacity); + memcpy(plaintext->data, ciphertext->data, ciphertext->len); + plaintext->len = ciphertext->len; + + return 0; +} + +static int +aead_chunk_decrypt(cipher_ctx_t *ctx, uint8_t *p, uint8_t *c, uint8_t *n, + size_t *plen, size_t *clen, size_t nlen, size_t tlen) +{ + int err; + size_t mlen; + + if (*clen <= 2 * tlen + CHUNK_SIZE_LEN) + return CRYPTO_NEED_MORE; + + uint8_t len_buf[2]; + err = cipher_aead_decrypt(ctx, len_buf, plen, c, CHUNK_SIZE_LEN + tlen, + NULL, 0, n, ctx->cipher->key, nlen, tlen); + if (err) + return CRYPTO_ERROR; + assert(*plen == CHUNK_SIZE_LEN); + + mlen = htons(*(uint16_t *)len_buf); + + size_t chunk_len = tlen + CHUNK_SIZE_LEN + mlen; + + if (*clen < chunk_len) + return CRYPTO_NEED_MORE; + + sodium_increment(n, nlen); + + err = cipher_aead_decrypt(ctx, p, plen, c + CHUNK_SIZE_LEN + tlen, mlen, + NULL, 0, n, ctx->cipher->key, nlen, tlen); + if (err) + return CRYPTO_ERROR; + assert(*plen == mlen - tlen); + + sodium_increment(n, nlen); + + if (*clen > chunk_len) + memmove(c, c + chunk_len, *clen - chunk_len); + + *clen = *clen - chunk_len; + + return CRYPTO_OK; +} + +int +aead_decrypt(buffer_t *ciphertext, cipher_ctx_t *cipher_ctx, size_t capacity) +{ + int err = CRYPTO_OK; + static buffer_t tmp = { 0, 0, 0, NULL }; + + cipher_t *cipher = cipher_ctx->cipher; + + size_t nonce_len = cipher->nonce_len; + size_t tag_len = cipher->tag_len; + + if (cipher_ctx->chunk == NULL) { + cipher_ctx->chunk = (buffer_t *)ss_malloc(sizeof(buffer_t)); + memset(cipher_ctx->chunk, 0, sizeof(buffer_t)); + balloc(cipher_ctx->chunk, capacity); + } + + brealloc(cipher_ctx->chunk, + cipher_ctx->chunk->len + ciphertext->len, capacity); + memcpy(cipher_ctx->chunk->data + cipher_ctx->chunk->len, + ciphertext->data, ciphertext->len); + cipher_ctx->chunk->len += ciphertext->len; + + brealloc(&tmp, cipher_ctx->chunk->len, capacity); + buffer_t *plaintext = &tmp; + + if (!cipher_ctx->init) { + if (cipher_ctx->chunk->len <= nonce_len) + return CRYPTO_NEED_MORE; + memcpy(cipher_ctx->nonce, cipher_ctx->chunk->data, nonce_len); + + if (cache_key_exist(nonce_cache, (char *)cipher_ctx->nonce, nonce_len)) { + bfree(ciphertext); + return CRYPTO_ERROR; + } else { + cache_insert(nonce_cache, (char *)cipher_ctx->nonce, nonce_len, NULL); + } + + memmove(cipher_ctx->chunk->data, cipher_ctx->chunk->data + nonce_len, + cipher_ctx->chunk->len - nonce_len); + cipher_ctx->chunk->len -= nonce_len; + + cipher_ctx->init = 1; + } + + size_t plen = 0; + while (cipher_ctx->chunk->len > 0) { + size_t chunk_clen = cipher_ctx->chunk->len; + size_t chunk_plen = 0; + err = aead_chunk_decrypt(cipher_ctx, + (uint8_t *)plaintext->data + plen, + (uint8_t *)cipher_ctx->chunk->data, + cipher_ctx->nonce, + &chunk_plen, &chunk_clen, + nonce_len, tag_len); + if (err == CRYPTO_ERROR) { + return err; + } else if (err == CRYPTO_NEED_MORE) { + if (plen == 0) + return err; + else + break; + } + cipher_ctx->chunk->len = chunk_clen; + plen += chunk_plen; + } + plaintext->len = plen; + +#ifdef DEBUG + dump("PLAIN", plaintext->data, plaintext->len); + dump("CIPHER", ciphertext->data + nonce_len, ciphertext->len - nonce_len); +#endif + + brealloc(ciphertext, plaintext->len, capacity); + memcpy(ciphertext->data, plaintext->data, plaintext->len); + ciphertext->len = plaintext->len; + + return CRYPTO_OK; +} + +cipher_t * +aead_key_init(int method, const char *pass) +{ + if (method < AES128GCM || method >= AEAD_CIPHER_NUM) { + LOGE("aead_key_init(): Illegal method"); + return NULL; + } + + // Initialize cache + cache_create(&nonce_cache, 1024, NULL); + + cipher_t *cipher = (cipher_t *)ss_malloc(sizeof(cipher_t)); + memset(cipher, 0, sizeof(cipher_t)); + + // Initialize sodium for random generator + if (sodium_init() == -1) { + FATAL("Failed to initialize sodium"); + } + + if (method >= CHACHA20POLY1305) { + cipher_kt_t *cipher_info = (cipher_kt_t *)ss_malloc(sizeof(cipher_kt_t)); + cipher->info = cipher_info; + cipher->info->base = NULL; + cipher->info->key_bitlen = supported_aead_ciphers_key_size[method] * 8; + cipher->info->iv_size = supported_aead_ciphers_nonce_size[method]; + } else { + cipher->info = (cipher_kt_t *)aead_get_cipher_type(method); + } + + if (cipher->info == NULL && cipher->key_len == 0) { + LOGE("Cipher %s not found in crypto library", supported_aead_ciphers[method]); + FATAL("Cannot initialize cipher"); + } + + cipher->key_len = crypto_derive_key(cipher, (const uint8_t *)pass, + cipher->key, supported_aead_ciphers_key_size[cipher->method]); + + if (cipher->key_len == 0) { + FATAL("Cannot generate key and nonce"); + } + + cipher->nonce_len = supported_aead_ciphers_nonce_size[method]; + cipher->tag_len = supported_aead_ciphers_tag_size[method]; + cipher->method = method; + + return cipher; +} + +cipher_t * +aead_init(const char *pass, const char *method) +{ + int m = AES128GCM; + if (method != NULL) { + /* check method validity */ + for (m = AES128GCM; m < AEAD_CIPHER_NUM; m++) + if (strcmp(method, supported_aead_ciphers[m]) == 0) { + break; + } + if (m >= AEAD_CIPHER_NUM) { + LOGE("Invalid cipher name: %s, use aes-256-gcm instead", method); + m = AES256GCM; + } + } + return aead_key_init(m, pass); +} diff --git a/src/aead.h b/src/aead.h new file mode 100644 index 00000000..a0461149 --- /dev/null +++ b/src/aead.h @@ -0,0 +1,46 @@ +/* + * aead.h - Define the AEAD interface + * + * Copyright (C) 2013 - 2017, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#ifndef _AEAD_H +#define _AEAD_H + +#include "crypto.h" + +// currently, XCHACHA20POLY1305IETF is not released yet +// XCHACHA20POLY1305 is removed in upstream +#ifdef FS_HAVE_XCHACHA20IETF +#define AEAD_CIPHER_NUM 6 +#else +#define AEAD_CIPHER_NUM 5 +#endif + +int aead_encrypt_all(buffer_t *, cipher_t *, size_t); +int aead_decrypt_all(buffer_t *, cipher_t *, size_t); + +int aead_encrypt(buffer_t *, cipher_ctx_t *, size_t); +int aead_decrypt(buffer_t *, cipher_ctx_t *, size_t); + +void aead_ctx_init(cipher_t *, cipher_ctx_t *, int); +void aead_ctx_release(cipher_ctx_t *); +cipher_t *aead_init(const char *pass, const char *method); + +#endif // _AEAD_H diff --git a/src/android.c b/src/android.c index 666c21bf..360bf377 100644 --- a/src/android.c +++ b/src/android.c @@ -1,7 +1,7 @@ /* * android.c - Setup IPC for shadowsocks-android * - * Copyright (C) 2013 - 2016, Max Lv + * Copyright (C) 2013 - 2017, Max Lv * * This file is part of the shadowsocks-libev. * diff --git a/src/cache.c b/src/cache.c index cb409131..c7754e50 100644 --- a/src/cache.c +++ b/src/cache.c @@ -1,7 +1,7 @@ /* * cache.c - Manage the connection cache for UDPRELAY * - * Copyright (C) 2013 - 2016, Max Lv + * Copyright (C) 2013 - 2017, Max Lv * * This file is part of the shadowsocks-libev. * diff --git a/src/cache.h b/src/cache.h index 0ec98f55..9fa27486 100644 --- a/src/cache.h +++ b/src/cache.h @@ -1,7 +1,7 @@ /* * cache.h - Define the cache manager interface * - * Copyright (C) 2013 - 2016, Max Lv + * Copyright (C) 2013 - 2017, Max Lv * * This file is part of the shadowsocks-libev. * diff --git a/src/common.h b/src/common.h index 25e7317b..f983a879 100644 --- a/src/common.h +++ b/src/common.h @@ -1,7 +1,7 @@ /* * common.h - Provide global definitions * - * Copyright (C) 2013 - 2016, Max Lv + * Copyright (C) 2013 - 2017, Max Lv * * This file is part of the shadowsocks-libev. * shadowsocks-libev is free software; you can redistribute it and/or modify @@ -32,6 +32,8 @@ #define MODULE_LOCAL #endif +#include "crypto.h" + int init_udprelay(const char *server_host, const char *server_port, #ifdef MODULE_LOCAL const struct sockaddr *remote_addr, const int remote_addr_len, @@ -39,7 +41,7 @@ int init_udprelay(const char *server_host, const char *server_port, const ss_addr_t tunnel_addr, #endif #endif - int mtu, int method, int auth, int timeout, const char *iface); + int mtu, crypto_t *crypto, int timeout, const char *iface); void free_udprelay(void); diff --git a/src/crypto.c b/src/crypto.c new file mode 100644 index 00000000..e24ff153 --- /dev/null +++ b/src/crypto.c @@ -0,0 +1,212 @@ +/* + * crypto.c - Manage the global crypto + * + * Copyright (C) 2013 - 2017, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "cache.h" +#include "crypto.h" +#include "stream.h" +#include "aead.h" +#include "utils.h" + +struct cache *nonce_cache; + +int +balloc(buffer_t *ptr, size_t capacity) +{ + sodium_memzero(ptr, sizeof(buffer_t)); + ptr->data = ss_malloc(capacity); + ptr->capacity = capacity; + return capacity; +} + +int +brealloc(buffer_t *ptr, size_t len, size_t capacity) +{ + if (ptr == NULL) + return -1; + size_t real_capacity = max(len, capacity); + if (ptr->capacity < real_capacity) { + ptr->data = ss_realloc(ptr->data, real_capacity); + ptr->capacity = real_capacity; + } + return real_capacity; +} + +void +bfree(buffer_t *ptr) +{ + if (ptr == NULL) + return; + ptr->idx = 0; + ptr->len = 0; + ptr->capacity = 0; + if (ptr->data != NULL) { + ss_free(ptr->data); + } +} + +int +bprepend(buffer_t *dst, buffer_t *src, size_t capacity) +{ + brealloc(dst, dst->len + src->len, capacity); + memmove(dst->data + src->len, dst->data, dst->len); + memcpy(dst->data, src->data, src->len); + dst->len = dst->len + src->len; + return dst->len; +} + +int +rand_bytes(void *output, int len) +{ + randombytes_buf(output, len); + // always return success + return 0; +} + +unsigned char * +crypto_md5(const unsigned char *d, size_t n, unsigned char *md) +{ + static unsigned char m[16]; + if (md == NULL) { + md = m; + } + mbedtls_md5(d, n, md); + return md; +} + +int +crypto_derive_key(const cipher_t *cipher, const uint8_t *pass, + uint8_t *key, size_t nkey) +{ + size_t datal; + datal = strlen((const char *)pass); + + const digest_type_t *md = mbedtls_md_info_from_string("MD5"); + if (md == NULL) { + FATAL("MD5 Digest not found in crypto library"); + } + + mbedtls_md_context_t c; + unsigned char md_buf[MAX_MD_SIZE]; + int addmd; + unsigned int i, j, mds; + + mds = mbedtls_md_get_size(md); + memset(&c, 0, sizeof(mbedtls_md_context_t)); + + if (pass == NULL) + return nkey; + if (mbedtls_md_setup(&c, md, 1)) + return 0; + + for (j = 0, addmd = 0; j < nkey; addmd++) { + mbedtls_md_starts(&c); + if (addmd) { + mbedtls_md_update(&c, md_buf, mds); + } + mbedtls_md_update(&c, pass, datal); + mbedtls_md_finish(&c, &(md_buf[0])); + + for (i = 0; i < mds; i++, j++) { + if (j >= nkey) + break; + key[j] = md_buf[i]; + } + } + + mbedtls_md_free(&c); + return nkey; +} + +crypto_t * +crypto_init(const char *password, const char *method) +{ + int i, m = -1; + + // Initialize sodium for random generator + if (sodium_init() == -1) { + FATAL("Failed to initialize sodium"); + } + + // Initialize NONCE cache + cache_create(&nonce_cache, 1024, NULL); + + if (method != NULL) { + for (i = 0; i < STREAM_CIPHER_NUM; i++) { + if (strcmp(method, supported_stream_ciphers[i]) == 0) { + m = i; + break; + } + } + if (m != -1) { + cipher_t *cipher = stream_init(password, method); + if (cipher == NULL) + return NULL; + crypto_t *crypto = (crypto_t *)malloc(sizeof(crypto_t)); + crypto_t tmp = { + .cipher = cipher, + .encrypt_all = &stream_encrypt_all, + .decrypt_all = &stream_decrypt_all, + .encrypt = &stream_encrypt, + .decrypt = &stream_decrypt, + .ctx_init = &stream_ctx_init, + .ctx_release = &stream_ctx_release + }; + memcpy(crypto, &tmp, sizeof(crypto_t)); + return crypto; + } + + for (i = 0; i < AEAD_CIPHER_NUM; i++) { + if (strcmp(method, supported_aead_ciphers[i]) == 0) { + m = i; + break; + } + } + if (m != -1) { + cipher_t *cipher = aead_init(password, method); + if (cipher == NULL) + return NULL; + crypto_t *crypto = (crypto_t *)ss_malloc(sizeof(crypto_t)); + crypto_t tmp = { + .cipher = cipher, + .encrypt_all = &aead_encrypt_all, + .decrypt_all = &aead_decrypt_all, + .encrypt = &aead_encrypt, + .decrypt = &aead_decrypt, + .ctx_init = &aead_ctx_init, + .ctx_release = &aead_ctx_release + }; + memcpy(crypto, &tmp, sizeof(crypto_t)); + return crypto; + } + } + + LOGE("invalid cipher name: %s", method); + return NULL; +} diff --git a/src/crypto.h b/src/crypto.h new file mode 100644 index 00000000..0adef50a --- /dev/null +++ b/src/crypto.h @@ -0,0 +1,119 @@ +/* + * crypto.h - Define the enryptor's interface + * + * Copyright (C) 2013 - 2017, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have recenonceed a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#ifndef _CRYPTO_H +#define _CRYPTO_H + +#include +#include +#include +#include + +#ifdef HAVE_STDINT_H +#include +#elif HAVE_INTTYPES_H +#include +#endif + +/* Definations for mbedTLS */ +#include +#include +typedef mbedtls_cipher_info_t cipher_kt_t; +typedef mbedtls_cipher_context_t cipher_evp_t; +typedef mbedtls_md_info_t digest_type_t; +#define MAX_KEY_LENGTH 64 +#define MAX_NONCE_LENGTH 32 +#define MAX_MD_SIZE MBEDTLS_MD_MAX_SIZE +/* we must have MBEDTLS_CIPHER_MODE_CFB defined */ +#if !defined(MBEDTLS_CIPHER_MODE_CFB) +#error Cipher Feedback mode a.k.a CFB not supported by your mbed TLS. +#endif +#ifndef MBEDTLS_GCM_C +#error No GCM support detected +#endif +#ifdef crypto_aead_xchacha20poly1305_ietf_ABYTES +#define FS_HAVE_XCHACHA20IETF +#endif + +#define ADDRTYPE_MASK 0xF + +#define CRYPTO_ERROR -2 +#define CRYPTO_NEED_MORE -1 +#define CRYPTO_OK 0 + +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#define max(a, b) (((a) > (b)) ? (a) : (b)) + +typedef struct buffer { + size_t idx; + size_t len; + size_t capacity; + char *data; +} buffer_t; + +typedef struct { + int method; + cipher_kt_t *info; + size_t nonce_len; + size_t key_len; + size_t tag_len; + uint8_t key[MAX_KEY_LENGTH]; +} cipher_t; + +typedef struct { + uint8_t init; + uint64_t counter; + cipher_evp_t *evp; + cipher_t *cipher; + buffer_t *chunk; + uint8_t nonce[MAX_NONCE_LENGTH]; +} cipher_ctx_t; + +typedef struct crypto { + cipher_t *cipher; + + int (*const encrypt_all)(buffer_t*, cipher_t*, size_t); + int (*const decrypt_all)(buffer_t*, cipher_t*, size_t); + int (*const encrypt)(buffer_t*, cipher_ctx_t*, size_t); + int (*const decrypt)(buffer_t*, cipher_ctx_t*, size_t); + + void (*const ctx_init)(cipher_t*, cipher_ctx_t*, int); + void (*const ctx_release)(cipher_ctx_t*); +} crypto_t; + +int balloc(buffer_t *ptr, size_t capacity); +int brealloc(buffer_t *ptr, size_t len, size_t capacity); +int bprepend(buffer_t *dst, buffer_t *src, size_t capacity); +void bfree(buffer_t *ptr); +int rand_bytes(void *output, int len); + +crypto_t *crypto_init(const char *password, const char *method); +unsigned char * crypto_md5(const unsigned char *d, size_t n, + unsigned char *md); +int crypto_derive_key(const cipher_t *cipher, + const uint8_t *pass, uint8_t *key, size_t nkey); + +extern struct cache *nonce_cache; +extern const char *supported_stream_ciphers[]; +extern const char *supported_aead_ciphers[]; + +#endif // _CRYPTO_H diff --git a/src/encrypt.c b/src/encrypt.c deleted file mode 100644 index f04d8efe..00000000 --- a/src/encrypt.c +++ /dev/null @@ -1,1491 +0,0 @@ -/* - * encrypt.c - Manage the global encryptor - * - * Copyright (C) 2013 - 2016, Max Lv - * - * This file is part of the shadowsocks-libev. - * - * shadowsocks-libev is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * shadowsocks-libev is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with shadowsocks-libev; see the file COPYING. If not, see - * . - */ - -#include - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#if defined(USE_CRYPTO_OPENSSL) - -#include -#include -#include - -#elif defined(USE_CRYPTO_POLARSSL) - -#include -#include -#include -#include -#include -#define CIPHER_UNSUPPORTED "unsupported" -#include -#include - -#elif defined(USE_CRYPTO_MBEDTLS) - -#include -#include -#include -#include -#define CIPHER_UNSUPPORTED "unsupported" -#include -#include - -#endif - -#include - -#include - -#include "cache.h" -#include "encrypt.h" -#include "utils.h" - -#if SODIUM_LIBRARY_VERSION_MAJOR >= 8 -#define CIPHER_NUM 21 -#else -#define CIPHER_NUM 20 -#endif - -#define OFFSET_ROL(p, o) ((uint64_t)(*(p + o)) << (8 * o)) - -static uint8_t *enc_table; -static uint8_t *dec_table; -static uint8_t enc_key[MAX_KEY_LENGTH]; -static int enc_key_len; -static int enc_iv_len; -static int enc_method; - -static struct cache *iv_cache; - -#ifdef DEBUG -static void -dump(char *tag, char *text, int len) -{ - int i; - printf("%s: ", tag); - for (i = 0; i < len; i++) - printf("0x%02x ", (uint8_t)text[i]); - printf("\n"); -} - -#endif - -static const char *supported_ciphers[CIPHER_NUM] = { - "table", - "rc4", - "rc4-md5", - "aes-128-cfb", - "aes-192-cfb", - "aes-256-cfb", - "aes-128-ctr", - "aes-192-ctr", - "aes-256-ctr", - "bf-cfb", - "camellia-128-cfb", - "camellia-192-cfb", - "camellia-256-cfb", - "cast5-cfb", - "des-cfb", - "idea-cfb", - "rc2-cfb", - "seed-cfb", - "salsa20", - "chacha20", -#if SODIUM_LIBRARY_VERSION_MAJOR >= 8 - "chacha20-ietf" -#endif -}; - -#ifdef USE_CRYPTO_POLARSSL -static const char *supported_ciphers_polarssl[CIPHER_NUM] = { - "table", - "ARC4-128", - "ARC4-128", - "AES-128-CFB128", - "AES-192-CFB128", - "AES-256-CFB128", - "AES-128-CTR", - "AES-192-CTR", - "AES-256-CTR", - "BLOWFISH-CFB64", - "CAMELLIA-128-CFB128", - "CAMELLIA-192-CFB128", - "CAMELLIA-256-CFB128", - CIPHER_UNSUPPORTED, - CIPHER_UNSUPPORTED, - CIPHER_UNSUPPORTED, - CIPHER_UNSUPPORTED, - CIPHER_UNSUPPORTED, - "salsa20", - "chacha20", -#if SODIUM_LIBRARY_VERSION_MAJOR >= 8 - "chacha20-ietf" -#endif -}; -#endif - -#ifdef USE_CRYPTO_MBEDTLS -static const char *supported_ciphers_mbedtls[CIPHER_NUM] = { - "table", - "ARC4-128", - "ARC4-128", - "AES-128-CFB128", - "AES-192-CFB128", - "AES-256-CFB128", - "AES-128-CTR", - "AES-192-CTR", - "AES-256-CTR", - "BLOWFISH-CFB64", - "CAMELLIA-128-CFB128", - "CAMELLIA-192-CFB128", - "CAMELLIA-256-CFB128", - CIPHER_UNSUPPORTED, - CIPHER_UNSUPPORTED, - CIPHER_UNSUPPORTED, - CIPHER_UNSUPPORTED, - CIPHER_UNSUPPORTED, - "salsa20", - "chacha20", -#if SODIUM_LIBRARY_VERSION_MAJOR >= 8 - "chacha20-ietf" -#endif -}; -#endif - -#ifdef USE_CRYPTO_APPLECC -static const CCAlgorithm supported_ciphers_applecc[CIPHER_NUM] = { - kCCAlgorithmInvalid, - kCCAlgorithmRC4, - kCCAlgorithmRC4, - kCCAlgorithmAES, - kCCAlgorithmAES, - kCCAlgorithmAES, - kCCAlgorithmAES, - kCCAlgorithmAES, - kCCAlgorithmAES, - kCCAlgorithmBlowfish, - kCCAlgorithmInvalid, - kCCAlgorithmInvalid, - kCCAlgorithmInvalid, - kCCAlgorithmCAST, - kCCAlgorithmDES, - kCCAlgorithmInvalid, - kCCAlgorithmRC2, - kCCAlgorithmInvalid, - kCCAlgorithmInvalid, - kCCAlgorithmInvalid, -#if SODIUM_LIBRARY_VERSION_MAJOR >= 8 - kCCAlgorithmInvalid -#endif -}; - -static const CCMode supported_modes_applecc[CIPHER_NUM] = { - kCCAlgorithmInvalid, - kCCAlgorithmInvalid, - kCCModeRC4, - kCCModeCFB, - kCCModeCFB, - kCCModeCFB, - kCCModeCTR, - kCCModeCTR, - kCCModeCTR, - kCCModeCFB, - kCCAlgorithmInvalid, - kCCAlgorithmInvalid, - kCCAlgorithmInvalid, - kCCModeCFB, - kCCModeCFB, - kCCModeCFB, - kCCModeCFB, - kCCAlgorithmInvalid, - kCCAlgorithmInvalid, - kCCAlgorithmInvalid, -#if SODIUM_LIBRARY_VERSION_MAJOR >= 8 - kCCAlgorithmInvalid -#endif -}; -#endif - -static const int supported_ciphers_iv_size[CIPHER_NUM] = { - 0, 0, 16, 16, 16, 16, 16, 16, 16, 8, 16, 16, 16, 8, 8, 8, 8, 16, 8, 8 -#if SODIUM_LIBRARY_VERSION_MAJOR >= 8 - , 12 -#endif -}; - -static const int supported_ciphers_key_size[CIPHER_NUM] = { - 0, 16, 16, 16, 24, 32, 16, 24, 32, 16, 16, 24, 32, 16, 8, 16, 16, 16, 32, 32 -#if SODIUM_LIBRARY_VERSION_MAJOR >= 8 - , 32 -#endif -}; - -static int -safe_memcmp(const void *s1, const void *s2, size_t n) -{ - const unsigned char *_s1 = (const unsigned char *)s1; - const unsigned char *_s2 = (const unsigned char *)s2; - int ret = 0; - size_t i; - for (i = 0; i < n; i++) - ret |= _s1[i] ^ _s2[i]; - return !!ret; -} - -int -balloc(buffer_t *ptr, size_t capacity) -{ - sodium_memzero(ptr, sizeof(buffer_t)); - ptr->data = ss_malloc(capacity); - ptr->capacity = capacity; - return capacity; -} - -int -brealloc(buffer_t *ptr, size_t len, size_t capacity) -{ - if (ptr == NULL) - return -1; - size_t real_capacity = max(len, capacity); - if (ptr->capacity < real_capacity) { - ptr->data = ss_realloc(ptr->data, real_capacity); - ptr->capacity = real_capacity; - } - return real_capacity; -} - -void -bfree(buffer_t *ptr) -{ - if (ptr == NULL) - return; - ptr->idx = 0; - ptr->len = 0; - ptr->capacity = 0; - if (ptr->data != NULL) { - ss_free(ptr->data); - } -} - -static int -crypto_stream_xor_ic(uint8_t *c, const uint8_t *m, uint64_t mlen, - const uint8_t *n, uint64_t ic, const uint8_t *k, - int method) -{ - switch (method) { - case SALSA20: - return crypto_stream_salsa20_xor_ic(c, m, mlen, n, ic, k); - case CHACHA20: - return crypto_stream_chacha20_xor_ic(c, m, mlen, n, ic, k); -#if SODIUM_LIBRARY_VERSION_MAJOR >= 8 - case CHACHA20IETF: - return crypto_stream_chacha20_ietf_xor_ic(c, m, mlen, n, (uint32_t)ic, k); -#endif - } - // always return 0 - return 0; -} - -static int -random_compare(const void *_x, const void *_y, uint32_t i, - uint64_t a) -{ - uint8_t x = *((uint8_t *)_x); - uint8_t y = *((uint8_t *)_y); - return a % (x + i) - a % (y + i); -} - -static void -merge(uint8_t *left, int llength, uint8_t *right, - int rlength, uint32_t salt, uint64_t key) -{ - uint8_t *ltmp = (uint8_t *)malloc(llength * sizeof(uint8_t)); - uint8_t *rtmp = (uint8_t *)malloc(rlength * sizeof(uint8_t)); - - uint8_t *ll = ltmp; - uint8_t *rr = rtmp; - - uint8_t *result = left; - - memcpy(ltmp, left, llength * sizeof(uint8_t)); - memcpy(rtmp, right, rlength * sizeof(uint8_t)); - - while (llength > 0 && rlength > 0) { - if (random_compare(ll, rr, salt, key) <= 0) { - *result = *ll; - ++ll; - --llength; - } else { - *result = *rr; - ++rr; - --rlength; - } - ++result; - } - - if (llength > 0) { - while (llength > 0) { - *result = *ll; - ++result; - ++ll; - --llength; - } - } else { - while (rlength > 0) { - *result = *rr; - ++result; - ++rr; - --rlength; - } - } - - ss_free(ltmp); - ss_free(rtmp); -} - -static void -merge_sort(uint8_t data[], int length, - uint32_t salt, uint64_t key) -{ - uint8_t middle; - uint8_t *left, *right; - int llength; - - if (length <= 1) { - return; - } - - middle = length / 2; - - llength = length - middle; - - left = data; - right = data + llength; - - merge_sort(left, llength, salt, key); - merge_sort(right, middle, salt, key); - merge(left, llength, right, middle, salt, key); -} - -int -enc_get_iv_len() -{ - return enc_iv_len; -} - -unsigned char * -enc_md5(const unsigned char *d, size_t n, unsigned char *md) -{ -#if defined(USE_CRYPTO_OPENSSL) - return MD5(d, n, md); -#elif defined(USE_CRYPTO_POLARSSL) - static unsigned char m[16]; - if (md == NULL) { - md = m; - } - md5(d, n, md); - return md; -#elif defined(USE_CRYPTO_MBEDTLS) - static unsigned char m[16]; - if (md == NULL) { - md = m; - } - mbedtls_md5(d, n, md); - return md; -#endif -} - -void -enc_table_init(const char *pass) -{ - uint32_t i; - uint64_t key = 0; - uint8_t *digest; - - enc_table = ss_malloc(256); - dec_table = ss_malloc(256); - - digest = enc_md5((const uint8_t *)pass, strlen(pass), NULL); - - for (i = 0; i < 8; i++) - key += OFFSET_ROL(digest, i); - - for (i = 0; i < 256; ++i) - enc_table[i] = i; - for (i = 1; i < 1024; ++i) - merge_sort(enc_table, 256, i, key); - for (i = 0; i < 256; ++i) - // gen decrypt table from encrypt table - dec_table[enc_table[i]] = i; -} - -int -cipher_iv_size(const cipher_t *cipher) -{ -#if defined(USE_CRYPTO_OPENSSL) - if (cipher->info == NULL) - return cipher->iv_len; - else - return EVP_CIPHER_iv_length(cipher->info); -#elif defined(USE_CRYPTO_POLARSSL) || defined(USE_CRYPTO_MBEDTLS) - if (cipher == NULL) { - return 0; - } - return cipher->info->iv_size; -#endif -} - -int -cipher_key_size(const cipher_t *cipher) -{ -#if defined(USE_CRYPTO_OPENSSL) - if (cipher->info == NULL) - return cipher->key_len; - else - return EVP_CIPHER_key_length(cipher->info); -#elif defined(USE_CRYPTO_POLARSSL) - if (cipher == NULL) { - return 0; - } - /* Override PolarSSL 32 bit default key size with sane 128 bit default */ - if (cipher->info->base != NULL && POLARSSL_CIPHER_ID_BLOWFISH == - cipher->info->base->cipher) { - return 128 / 8; - } - return cipher->info->key_length / 8; -#elif defined(USE_CRYPTO_MBEDTLS) - /* - * Semi-API changes (technically public, morally private) - * Renamed a few headers to include _internal in the name. Those headers are - * not supposed to be included by users. - * Changed md_info_t into an opaque structure (use md_get_xxx() accessors). - * Changed pk_info_t into an opaque structure. - * Changed cipher_base_t into an opaque structure. - */ - if (cipher == NULL) { - return 0; - } - /* From Version 1.2.7 released 2013-04-13 Default Blowfish keysize is now 128-bits */ - return cipher->info->key_bitlen / 8; -#endif -} - -int -bytes_to_key(const cipher_t *cipher, const digest_type_t *md, - const uint8_t *pass, uint8_t *key) -{ - size_t datal; - datal = strlen((const char *)pass); - -#if defined(USE_CRYPTO_OPENSSL) - - MD5_CTX c; - unsigned char md_buf[MAX_MD_SIZE]; - int nkey; - int addmd; - unsigned int i, j, mds; - - mds = 16; - nkey = cipher_key_size(cipher); - if (pass == NULL) - return nkey; - memset(&c, 0, sizeof(MD5_CTX)); - - for (j = 0, addmd = 0; j < nkey; addmd++) { - MD5_Init(&c); - if (addmd) { - MD5_Update(&c, md_buf, mds); - } - MD5_Update(&c, pass, datal); - MD5_Final(md_buf, &c); - - for (i = 0; i < mds; i++, j++) { - if (j >= nkey) - break; - key[j] = md_buf[i]; - } - } - - return nkey; - -#elif defined(USE_CRYPTO_POLARSSL) - md_context_t c; - unsigned char md_buf[MAX_MD_SIZE]; - int nkey; - int addmd; - unsigned int i, j, mds; - - nkey = cipher_key_size(cipher); - mds = md_get_size(md); - memset(&c, 0, sizeof(md_context_t)); - - if (pass == NULL) - return nkey; - if (md_init_ctx(&c, md)) - return 0; - - for (j = 0, addmd = 0; j < nkey; addmd++) { - md_starts(&c); - if (addmd) { - md_update(&c, md_buf, mds); - } - md_update(&c, pass, datal); - md_finish(&c, md_buf); - - for (i = 0; i < mds; i++, j++) { - if (j >= nkey) - break; - key[j] = md_buf[i]; - } - } - - md_free_ctx(&c); - return nkey; - -#elif defined(USE_CRYPTO_MBEDTLS) - - mbedtls_md_context_t c; - unsigned char md_buf[MAX_MD_SIZE]; - int nkey; - int addmd; - unsigned int i, j, mds; - - nkey = cipher_key_size(cipher); - mds = mbedtls_md_get_size(md); - memset(&c, 0, sizeof(mbedtls_md_context_t)); - - if (pass == NULL) - return nkey; - if (mbedtls_md_setup(&c, md, 1)) - return 0; - - for (j = 0, addmd = 0; j < nkey; addmd++) { - mbedtls_md_starts(&c); - if (addmd) { - mbedtls_md_update(&c, md_buf, mds); - } - mbedtls_md_update(&c, pass, datal); - mbedtls_md_finish(&c, &(md_buf[0])); - - for (i = 0; i < mds; i++, j++) { - if (j >= nkey) - break; - key[j] = md_buf[i]; - } - } - - mbedtls_md_free(&c); - return nkey; -#endif -} - -int -rand_bytes(void *output, int len) -{ - randombytes_buf(output, len); - // always return success - return 0; -} - -const cipher_kt_t * -get_cipher_type(int method) -{ - if (method <= TABLE || method >= CIPHER_NUM) { - LOGE("get_cipher_type(): Illegal method"); - return NULL; - } - - if (method == RC4_MD5) { - method = RC4; - } - - if (method >= SALSA20) { - return NULL; - } - - const char *ciphername = supported_ciphers[method]; -#if defined(USE_CRYPTO_OPENSSL) - return EVP_get_cipherbyname(ciphername); -#elif defined(USE_CRYPTO_POLARSSL) - const char *polarname = supported_ciphers_polarssl[method]; - if (strcmp(polarname, CIPHER_UNSUPPORTED) == 0) { - LOGE("Cipher %s currently is not supported by PolarSSL library", - ciphername); - return NULL; - } - return cipher_info_from_string(polarname); -#elif defined(USE_CRYPTO_MBEDTLS) - const char *mbedtlsname = supported_ciphers_mbedtls[method]; - if (strcmp(mbedtlsname, CIPHER_UNSUPPORTED) == 0) { - LOGE("Cipher %s currently is not supported by mbed TLS library", - ciphername); - return NULL; - } - return mbedtls_cipher_info_from_string(mbedtlsname); -#endif -} - -const digest_type_t * -get_digest_type(const char *digest) -{ - if (digest == NULL) { - LOGE("get_digest_type(): Digest name is null"); - return NULL; - } - -#if defined(USE_CRYPTO_OPENSSL) - return EVP_get_digestbyname(digest); -#elif defined(USE_CRYPTO_POLARSSL) - return md_info_from_string(digest); -#elif defined(USE_CRYPTO_MBEDTLS) - return mbedtls_md_info_from_string(digest); -#endif -} - -void -cipher_context_init(cipher_ctx_t *ctx, int method, int enc) -{ - if (method <= TABLE || method >= CIPHER_NUM) { - LOGE("cipher_context_init(): Illegal method"); - return; - } - - if (method >= SALSA20) { - enc_iv_len = supported_ciphers_iv_size[method]; - return; - } - - const char *ciphername = supported_ciphers[method]; -#if defined(USE_CRYPTO_APPLECC) - cipher_cc_t *cc = &ctx->cc; - cc->cryptor = NULL; - cc->cipher = supported_ciphers_applecc[method]; - if (cc->cipher == kCCAlgorithmInvalid) { - cc->valid = kCCContextInvalid; - } else { - cc->valid = kCCContextValid; - if (cc->cipher == kCCAlgorithmRC4) { - cc->mode = supported_modes_applecc[method]; - cc->padding = ccNoPadding; - } else { - cc->mode = supported_modes_applecc[method]; - if (cc->mode == kCCModeCTR) { - cc->padding = ccNoPadding; - } else { - cc->padding = ccPKCS7Padding; - } - } - return; - } -#endif - - const cipher_kt_t *cipher = get_cipher_type(method); - -#if defined(USE_CRYPTO_OPENSSL) - ctx->evp = EVP_CIPHER_CTX_new(); - cipher_evp_t *evp = ctx->evp; - - if (cipher == NULL) { - LOGE("Cipher %s not found in OpenSSL library", ciphername); - FATAL("Cannot initialize cipher"); - } - if (!EVP_CipherInit_ex(evp, cipher, NULL, NULL, NULL, enc)) { - LOGE("Cannot initialize cipher %s", ciphername); - exit(EXIT_FAILURE); - } - if (!EVP_CIPHER_CTX_set_key_length(evp, enc_key_len)) { - EVP_CIPHER_CTX_cleanup(evp); - LOGE("Invalid key length: %d", enc_key_len); - exit(EXIT_FAILURE); - } - if (method > RC4_MD5) { - EVP_CIPHER_CTX_set_padding(evp, 1); - } -#elif defined(USE_CRYPTO_POLARSSL) - ctx->evp = (cipher_evp_t *)ss_malloc(sizeof(cipher_evp_t)); - cipher_evp_t *evp = ctx->evp; - - if (cipher == NULL) { - LOGE("Cipher %s not found in PolarSSL library", ciphername); - FATAL("Cannot initialize PolarSSL cipher"); - } - if (cipher_init_ctx(evp, cipher) != 0) { - FATAL("Cannot initialize PolarSSL cipher context"); - } -#elif defined(USE_CRYPTO_MBEDTLS) - ctx->evp = ss_malloc(sizeof(cipher_evp_t)); - memset(ctx->evp, 0, sizeof(cipher_evp_t)); - cipher_evp_t *evp = ctx->evp; - - if (cipher == NULL) { - LOGE("Cipher %s not found in mbed TLS library", ciphername); - FATAL("Cannot initialize mbed TLS cipher"); - } - mbedtls_cipher_init(evp); - if (mbedtls_cipher_setup(evp, cipher) != 0) { - FATAL("Cannot initialize mbed TLS cipher context"); - } -#endif -} - -void -cipher_context_set_iv(cipher_ctx_t *ctx, uint8_t *iv, size_t iv_len, - int enc) -{ - const unsigned char *true_key; - - if (iv == NULL) { - LOGE("cipher_context_set_iv(): IV is null"); - return; - } - - if (!enc) { - memcpy(ctx->iv, iv, iv_len); - } - - if (enc_method >= SALSA20) { - return; - } - - if (enc_method == RC4_MD5) { - unsigned char key_iv[32]; - memcpy(key_iv, enc_key, 16); - memcpy(key_iv + 16, iv, 16); - true_key = enc_md5(key_iv, 32, NULL); - iv_len = 0; - } else { - true_key = enc_key; - } - -#ifdef USE_CRYPTO_APPLECC - cipher_cc_t *cc = &ctx->cc; - if (cc->valid == kCCContextValid) { - memcpy(cc->iv, iv, iv_len); - memcpy(cc->key, true_key, enc_key_len); - cc->iv_len = iv_len; - cc->key_len = enc_key_len; - cc->encrypt = enc ? kCCEncrypt : kCCDecrypt; - if (cc->cryptor != NULL) { - CCCryptorRelease(cc->cryptor); - cc->cryptor = NULL; - } - - CCCryptorStatus ret; - ret = CCCryptorCreateWithMode( - cc->encrypt, - cc->mode, - cc->cipher, - cc->padding, - cc->iv, cc->key, cc->key_len, - NULL, 0, 0, kCCModeOptionCTR_BE, - &cc->cryptor); - if (ret != kCCSuccess) { - if (cc->cryptor != NULL) { - CCCryptorRelease(cc->cryptor); - cc->cryptor = NULL; - } - FATAL("Cannot set CommonCrypto key and IV"); - } - return; - } -#endif - - cipher_evp_t *evp = ctx->evp; - if (evp == NULL) { - LOGE("cipher_context_set_iv(): Cipher context is null"); - return; - } -#if defined(USE_CRYPTO_OPENSSL) - if (!EVP_CipherInit_ex(evp, NULL, NULL, true_key, iv, enc)) { - EVP_CIPHER_CTX_cleanup(evp); - FATAL("Cannot set key and IV"); - } -#elif defined(USE_CRYPTO_POLARSSL) - // XXX: PolarSSL 1.3.11: cipher_free_ctx deprecated, Use cipher_free() instead. - if (cipher_setkey(evp, true_key, enc_key_len * 8, enc) != 0) { - cipher_free_ctx(evp); - FATAL("Cannot set PolarSSL cipher key"); - } -#if POLARSSL_VERSION_NUMBER >= 0x01030000 - if (cipher_set_iv(evp, iv, iv_len) != 0) { - cipher_free_ctx(evp); - FATAL("Cannot set PolarSSL cipher IV"); - } - if (cipher_reset(evp) != 0) { - cipher_free_ctx(evp); - FATAL("Cannot finalize PolarSSL cipher context"); - } -#else - if (cipher_reset(evp, iv) != 0) { - cipher_free_ctx(evp); - FATAL("Cannot set PolarSSL cipher IV"); - } -#endif -#elif defined(USE_CRYPTO_MBEDTLS) - if (mbedtls_cipher_setkey(evp, true_key, enc_key_len * 8, enc) != 0) { - mbedtls_cipher_free(evp); - FATAL("Cannot set mbed TLS cipher key"); - } - - if (mbedtls_cipher_set_iv(evp, iv, iv_len) != 0) { - mbedtls_cipher_free(evp); - FATAL("Cannot set mbed TLS cipher IV"); - } - if (mbedtls_cipher_reset(evp) != 0) { - mbedtls_cipher_free(evp); - FATAL("Cannot finalize mbed TLS cipher context"); - } -#endif - -#ifdef DEBUG - dump("IV", (char *)iv, iv_len); -#endif -} - -void -cipher_context_release(cipher_ctx_t *ctx) -{ - if (enc_method >= SALSA20) { - return; - } - -#ifdef USE_CRYPTO_APPLECC - cipher_cc_t *cc = &ctx->cc; - if (cc->cryptor != NULL) { - CCCryptorRelease(cc->cryptor); - cc->cryptor = NULL; - } - if (cc->valid == kCCContextValid) { - return; - } -#endif - -#if defined(USE_CRYPTO_OPENSSL) - EVP_CIPHER_CTX_free(ctx->evp); -#elif defined(USE_CRYPTO_POLARSSL) -// NOTE: cipher_free_ctx deprecated in PolarSSL 1.3.11 - cipher_free_ctx(ctx->evp); - ss_free(ctx->evp); -#elif defined(USE_CRYPTO_MBEDTLS) -// NOTE: cipher_free_ctx deprecated - mbedtls_cipher_free(ctx->evp); - ss_free(ctx->evp); -#endif -} - -static int -cipher_context_update(cipher_ctx_t *ctx, uint8_t *output, size_t *olen, - const uint8_t *input, size_t ilen) -{ -#ifdef USE_CRYPTO_APPLECC - cipher_cc_t *cc = &ctx->cc; - if (cc->valid == kCCContextValid) { - CCCryptorStatus ret; - ret = CCCryptorUpdate(cc->cryptor, input, ilen, output, - ilen, olen); - return (ret == kCCSuccess) ? 1 : 0; - } -#endif - cipher_evp_t *evp = ctx->evp; -#if defined(USE_CRYPTO_OPENSSL) - int err = 0, tlen = *olen; - err = EVP_CipherUpdate(evp, (uint8_t *)output, &tlen, - (const uint8_t *)input, ilen); - *olen = tlen; - return err; -#elif defined(USE_CRYPTO_POLARSSL) - return !cipher_update(evp, (const uint8_t *)input, ilen, - (uint8_t *)output, olen); -#elif defined(USE_CRYPTO_MBEDTLS) - return !mbedtls_cipher_update(evp, (const uint8_t *)input, ilen, - (uint8_t *)output, olen); -#endif -} - -int -ss_onetimeauth(buffer_t *buf, uint8_t *iv, size_t capacity) -{ - uint8_t hash[ONETIMEAUTH_BYTES * 2]; - uint8_t auth_key[MAX_IV_LENGTH + MAX_KEY_LENGTH]; - memcpy(auth_key, iv, enc_iv_len); - memcpy(auth_key + enc_iv_len, enc_key, enc_key_len); - - brealloc(buf, ONETIMEAUTH_BYTES + buf->len, capacity); - -#if defined(USE_CRYPTO_OPENSSL) - HMAC(EVP_sha1(), auth_key, enc_iv_len + enc_key_len, (uint8_t *)buf->data, buf->len, (uint8_t *)hash, NULL); -#elif defined(USE_CRYPTO_MBEDTLS) - mbedtls_md_hmac(mbedtls_md_info_from_type( - MBEDTLS_MD_SHA1), auth_key, enc_iv_len + enc_key_len, (uint8_t *)buf->data, buf->len, - (uint8_t *)hash); -#else - sha1_hmac(auth_key, enc_iv_len + enc_key_len, (uint8_t *)buf->data, buf->len, (uint8_t *)hash); -#endif - - memcpy(buf->data + buf->len, hash, ONETIMEAUTH_BYTES); - buf->len += ONETIMEAUTH_BYTES; - - return 0; -} - -int -ss_onetimeauth_verify(buffer_t *buf, uint8_t *iv) -{ - uint8_t hash[ONETIMEAUTH_BYTES * 2]; - uint8_t auth_key[MAX_IV_LENGTH + MAX_KEY_LENGTH]; - memcpy(auth_key, iv, enc_iv_len); - memcpy(auth_key + enc_iv_len, enc_key, enc_key_len); - size_t len = buf->len - ONETIMEAUTH_BYTES; - -#if defined(USE_CRYPTO_OPENSSL) - HMAC(EVP_sha1(), auth_key, enc_iv_len + enc_key_len, (uint8_t *)buf->data, len, hash, NULL); -#elif defined(USE_CRYPTO_MBEDTLS) - mbedtls_md_hmac(mbedtls_md_info_from_type( - MBEDTLS_MD_SHA1), auth_key, enc_iv_len + enc_key_len, (uint8_t *)buf->data, len, hash); -#else - sha1_hmac(auth_key, enc_iv_len + enc_key_len, (uint8_t *)buf->data, len, hash); -#endif - - return safe_memcmp(buf->data + len, hash, ONETIMEAUTH_BYTES); -} - -int -ss_encrypt_all(buffer_t *plain, int method, int auth, size_t capacity) -{ - if (method > TABLE) { - cipher_ctx_t evp; - cipher_context_init(&evp, method, 1); - - size_t iv_len = enc_iv_len; - int err = 1; - - static buffer_t tmp = { 0, 0, 0, NULL }; - brealloc(&tmp, iv_len + plain->len, capacity); - buffer_t *cipher = &tmp; - cipher->len = plain->len; - - uint8_t iv[MAX_IV_LENGTH]; - - rand_bytes(iv, iv_len); - cipher_context_set_iv(&evp, iv, iv_len, 1); - memcpy(cipher->data, iv, iv_len); - - if (auth) { - ss_onetimeauth(plain, iv, capacity); - cipher->len = plain->len; - } - - if (method >= SALSA20) { - crypto_stream_xor_ic((uint8_t *)(cipher->data + iv_len), - (const uint8_t *)plain->data, (uint64_t)(plain->len), - (const uint8_t *)iv, - 0, enc_key, method); - } else { - err = cipher_context_update(&evp, (uint8_t *)(cipher->data + iv_len), - &cipher->len, (const uint8_t *)plain->data, - plain->len); - } - - if (!err) { - bfree(plain); - cipher_context_release(&evp); - return -1; - } - -#ifdef DEBUG - dump("PLAIN", plain->data, plain->len); - dump("CIPHER", cipher->data + iv_len, cipher->len); -#endif - - cipher_context_release(&evp); - - brealloc(plain, iv_len + cipher->len, capacity); - memcpy(plain->data, cipher->data, iv_len + cipher->len); - plain->len = iv_len + cipher->len; - - return 0; - } else { - char *begin = plain->data; - char *ptr = plain->data; - while (ptr < begin + plain->len) { - *ptr = (char)enc_table[(uint8_t)*ptr]; - ptr++; - } - return 0; - } -} - -int -ss_encrypt(buffer_t *plain, enc_ctx_t *ctx, size_t capacity) -{ - if (ctx != NULL) { - static buffer_t tmp = { 0, 0, 0, NULL }; - - int err = 1; - size_t iv_len = 0; - if (!ctx->init) { - iv_len = enc_iv_len; - } - - brealloc(&tmp, iv_len + plain->len, capacity); - buffer_t *cipher = &tmp; - cipher->len = plain->len; - - if (!ctx->init) { - cipher_context_set_iv(&ctx->evp, ctx->evp.iv, iv_len, 1); - memcpy(cipher->data, ctx->evp.iv, iv_len); - ctx->counter = 0; - ctx->init = 1; - } - - if (enc_method >= SALSA20) { - int padding = ctx->counter % SODIUM_BLOCK_SIZE; - brealloc(cipher, iv_len + (padding + cipher->len) * 2, capacity); - if (padding) { - brealloc(plain, plain->len + padding, capacity); - memmove(plain->data + padding, plain->data, plain->len); - sodium_memzero(plain->data, padding); - } - crypto_stream_xor_ic((uint8_t *)(cipher->data + iv_len), - (const uint8_t *)plain->data, - (uint64_t)(plain->len + padding), - (const uint8_t *)ctx->evp.iv, - ctx->counter / SODIUM_BLOCK_SIZE, enc_key, - enc_method); - ctx->counter += plain->len; - if (padding) { - memmove(cipher->data + iv_len, - cipher->data + iv_len + padding, cipher->len); - } - } else { - err = - cipher_context_update(&ctx->evp, - (uint8_t *)(cipher->data + iv_len), - &cipher->len, (const uint8_t *)plain->data, - plain->len); - if (!err) { - return -1; - } - } - -#ifdef DEBUG - dump("PLAIN", plain->data, plain->len); - dump("CIPHER", cipher->data + iv_len, cipher->len); -#endif - - brealloc(plain, iv_len + cipher->len, capacity); - memcpy(plain->data, cipher->data, iv_len + cipher->len); - plain->len = iv_len + cipher->len; - - return 0; - } else { - char *begin = plain->data; - char *ptr = plain->data; - while (ptr < begin + plain->len) { - *ptr = (char)enc_table[(uint8_t)*ptr]; - ptr++; - } - return 0; - } -} - -int -ss_decrypt_all(buffer_t *cipher, int method, int auth, size_t capacity) -{ - if (method > TABLE) { - size_t iv_len = enc_iv_len; - int ret = 1; - - if (cipher->len <= iv_len) { - return -1; - } - - cipher_ctx_t evp; - cipher_context_init(&evp, method, 0); - - static buffer_t tmp = { 0, 0, 0, NULL }; - brealloc(&tmp, cipher->len, capacity); - buffer_t *plain = &tmp; - plain->len = cipher->len - iv_len; - - uint8_t iv[MAX_IV_LENGTH]; - memcpy(iv, cipher->data, iv_len); - cipher_context_set_iv(&evp, iv, iv_len, 0); - - if (method >= SALSA20) { - crypto_stream_xor_ic((uint8_t *)plain->data, - (const uint8_t *)(cipher->data + iv_len), - (uint64_t)(cipher->len - iv_len), - (const uint8_t *)iv, 0, enc_key, method); - } else { - ret = cipher_context_update(&evp, (uint8_t *)plain->data, &plain->len, - (const uint8_t *)(cipher->data + iv_len), - cipher->len - iv_len); - } - - if (auth || (plain->data[0] & ONETIMEAUTH_FLAG)) { - if (plain->len > ONETIMEAUTH_BYTES) { - ret = !ss_onetimeauth_verify(plain, iv); - if (ret) { - plain->len -= ONETIMEAUTH_BYTES; - } - } else { - ret = 0; - } - } - - if (!ret) { - bfree(cipher); - cipher_context_release(&evp); - return -1; - } - -#ifdef DEBUG - dump("PLAIN", plain->data, plain->len); - dump("CIPHER", cipher->data + iv_len, cipher->len - iv_len); -#endif - - cipher_context_release(&evp); - - brealloc(cipher, plain->len, capacity); - memcpy(cipher->data, plain->data, plain->len); - cipher->len = plain->len; - - return 0; - } else { - char *begin = cipher->data; - char *ptr = cipher->data; - while (ptr < begin + cipher->len) { - *ptr = (char)dec_table[(uint8_t)*ptr]; - ptr++; - } - return 0; - } -} - -int -ss_decrypt(buffer_t *cipher, enc_ctx_t *ctx, size_t capacity) -{ - if (ctx != NULL) { - static buffer_t tmp = { 0, 0, 0, NULL }; - - size_t iv_len = 0; - int err = 1; - - brealloc(&tmp, cipher->len, capacity); - buffer_t *plain = &tmp; - plain->len = cipher->len; - - if (!ctx->init) { - uint8_t iv[MAX_IV_LENGTH]; - iv_len = enc_iv_len; - plain->len -= iv_len; - - memcpy(iv, cipher->data, iv_len); - cipher_context_set_iv(&ctx->evp, iv, iv_len, 0); - ctx->counter = 0; - ctx->init = 1; - - if (enc_method >= RC4_MD5) { - if (cache_key_exist(iv_cache, (char *)iv, iv_len)) { - bfree(cipher); - return -1; - } else { - cache_insert(iv_cache, (char *)iv, iv_len, NULL); - } - } - } - - if (enc_method >= SALSA20) { - int padding = ctx->counter % SODIUM_BLOCK_SIZE; - brealloc(plain, (plain->len + padding) * 2, capacity); - - if (padding) { - brealloc(cipher, cipher->len + padding, capacity); - memmove(cipher->data + iv_len + padding, cipher->data + iv_len, - cipher->len - iv_len); - sodium_memzero(cipher->data + iv_len, padding); - } - crypto_stream_xor_ic((uint8_t *)plain->data, - (const uint8_t *)(cipher->data + iv_len), - (uint64_t)(cipher->len - iv_len + padding), - (const uint8_t *)ctx->evp.iv, - ctx->counter / SODIUM_BLOCK_SIZE, enc_key, - enc_method); - ctx->counter += cipher->len - iv_len; - if (padding) { - memmove(plain->data, plain->data + padding, plain->len); - } - } else { - err = cipher_context_update(&ctx->evp, (uint8_t *)plain->data, &plain->len, - (const uint8_t *)(cipher->data + iv_len), - cipher->len - iv_len); - } - - if (!err) { - bfree(cipher); - return -1; - } - -#ifdef DEBUG - dump("PLAIN", plain->data, plain->len); - dump("CIPHER", cipher->data + iv_len, cipher->len - iv_len); -#endif - - brealloc(cipher, plain->len, capacity); - memcpy(cipher->data, plain->data, plain->len); - cipher->len = plain->len; - - return 0; - } else { - char *begin = cipher->data; - char *ptr = cipher->data; - while (ptr < begin + cipher->len) { - *ptr = (char)dec_table[(uint8_t)*ptr]; - ptr++; - } - return 0; - } -} - -void -enc_ctx_init(int method, enc_ctx_t *ctx, int enc) -{ - sodium_memzero(ctx, sizeof(enc_ctx_t)); - cipher_context_init(&ctx->evp, method, enc); - - if (enc) { - rand_bytes(ctx->evp.iv, enc_iv_len); - } -} - -void -enc_key_init(int method, const char *pass) -{ - if (method <= TABLE || method >= CIPHER_NUM) { - LOGE("enc_key_init(): Illegal method"); - return; - } - - // Initialize cache - cache_create(&iv_cache, 1024, NULL); - -#if defined(USE_CRYPTO_OPENSSL) - OpenSSL_add_all_algorithms(); -#else - cipher_kt_t cipher_info; -#endif - - cipher_t cipher; - memset(&cipher, 0, sizeof(cipher_t)); - - // Initialize sodium for random generator - if (sodium_init() == -1) { - FATAL("Failed to initialize sodium"); - } - -#if SODIUM_LIBRARY_VERSION_MAJOR >= 8 - if (method == SALSA20 || method == CHACHA20 || method == CHACHA20IETF) { -#else - if (method == SALSA20 || method == CHACHA20) { -#endif -#if defined(USE_CRYPTO_OPENSSL) - cipher.info = NULL; - cipher.key_len = supported_ciphers_key_size[method]; - cipher.iv_len = supported_ciphers_iv_size[method]; -#endif -#if defined(USE_CRYPTO_POLARSSL) - cipher.info = &cipher_info; - cipher.info->base = NULL; - cipher.info->key_length = supported_ciphers_key_size[method] * 8; - cipher.info->iv_size = supported_ciphers_iv_size[method]; -#endif -#if defined(USE_CRYPTO_MBEDTLS) - // XXX: key_length changed to key_bitlen in mbed TLS 2.0.0 - cipher.info = &cipher_info; - cipher.info->base = NULL; - cipher.info->key_bitlen = supported_ciphers_key_size[method] * 8; - cipher.info->iv_size = supported_ciphers_iv_size[method]; -#endif - } else { - cipher.info = (cipher_kt_t *)get_cipher_type(method); - } - - if (cipher.info == NULL && cipher.key_len == 0) { - do { -#if defined(USE_CRYPTO_POLARSSL) && defined(USE_CRYPTO_APPLECC) - if (supported_ciphers_applecc[method] != kCCAlgorithmInvalid) { - cipher_info.base = NULL; - cipher_info.key_length = supported_ciphers_key_size[method] * 8; - cipher_info.iv_size = supported_ciphers_iv_size[method]; - cipher.info = (cipher_kt_t *)&cipher_info; - break; - } -#endif -#if defined(USE_CRYPTO_MBEDTLS) && defined(USE_CRYPTO_APPLECC) - // XXX: key_length changed to key_bitlen in mbed TLS 2.0.0 - if (supported_ciphers_applecc[method] != kCCAlgorithmInvalid) { - cipher_info.base = NULL; - cipher_info.key_bitlen = supported_ciphers_key_size[method] * 8; - cipher_info.iv_size = supported_ciphers_iv_size[method]; - cipher.info = (cipher_kt_t *)&cipher_info; - break; - } -#endif - LOGE("Cipher %s not found in crypto library", supported_ciphers[method]); - FATAL("Cannot initialize cipher"); - } while (0); - } - - const digest_type_t *md = get_digest_type("MD5"); - if (md == NULL) { - FATAL("MD5 Digest not found in crypto library"); - } - - enc_key_len = bytes_to_key(&cipher, md, (const uint8_t *)pass, enc_key); - - if (enc_key_len == 0) { - FATAL("Cannot generate key and IV"); - } - if (method == RC4_MD5) { - enc_iv_len = 16; - } else { - enc_iv_len = cipher_iv_size(&cipher); - } - enc_method = method; -} - -int -enc_init(const char *pass, const char *method) -{ - int m = TABLE; - if (method != NULL) { - for (m = TABLE; m < CIPHER_NUM; m++) - if (strcmp(method, supported_ciphers[m]) == 0) { - break; - } - if (m >= CIPHER_NUM) { - LOGE("Invalid cipher name: %s, use rc4-md5 instead", method); - m = RC4_MD5; - } - } - if (m == TABLE) { - enc_table_init(pass); - } else { - enc_key_init(m, pass); - } - return m; -} - -int -ss_check_hash(buffer_t *buf, chunk_t *chunk, enc_ctx_t *ctx, size_t capacity) -{ - int i, j, k; - ssize_t blen = buf->len; - uint32_t cidx = chunk->idx; - - brealloc(chunk->buf, chunk->len + blen, capacity); - brealloc(buf, chunk->len + blen, capacity); - - for (i = 0, j = 0, k = 0; i < blen; i++) { - chunk->buf->data[cidx++] = buf->data[k++]; - - if (cidx == CLEN_BYTES) { - uint16_t clen = ntohs(*((uint16_t *)chunk->buf->data)); - brealloc(chunk->buf, clen + AUTH_BYTES, capacity); - chunk->len = clen; - } - - if (cidx == chunk->len + AUTH_BYTES) { - // Compare hash - uint8_t hash[ONETIMEAUTH_BYTES * 2]; - uint8_t key[MAX_IV_LENGTH + sizeof(uint32_t)]; - - uint32_t c = htonl(chunk->counter); - memcpy(key, ctx->evp.iv, enc_iv_len); - memcpy(key + enc_iv_len, &c, sizeof(uint32_t)); -#if defined(USE_CRYPTO_OPENSSL) - HMAC(EVP_sha1(), key, enc_iv_len + sizeof(uint32_t), - (uint8_t *)chunk->buf->data + AUTH_BYTES, chunk->len, hash, NULL); -#elif defined(USE_CRYPTO_MBEDTLS) - mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), key, enc_iv_len + sizeof(uint32_t), - (uint8_t *)chunk->buf->data + AUTH_BYTES, chunk->len, hash); -#else - sha1_hmac(key, enc_iv_len + sizeof(uint32_t), - (uint8_t *)chunk->buf->data + AUTH_BYTES, chunk->len, hash); -#endif - - if (safe_memcmp(hash, chunk->buf->data + CLEN_BYTES, ONETIMEAUTH_BYTES) != 0) { - return 0; - } - - // Copy chunk back to buffer - memmove(buf->data + j + chunk->len, buf->data + k, blen - i - 1); - memcpy(buf->data + j, chunk->buf->data + AUTH_BYTES, chunk->len); - - // Reset the base offset - j += chunk->len; - k = j; - cidx = 0; - chunk->counter++; - } - } - - buf->len = j; - chunk->idx = cidx; - return 1; -} - -int -ss_gen_hash(buffer_t *buf, uint32_t *counter, enc_ctx_t *ctx, size_t capacity) -{ - ssize_t blen = buf->len; - uint16_t chunk_len = htons((uint16_t)blen); - uint8_t hash[ONETIMEAUTH_BYTES * 2]; - uint8_t key[MAX_IV_LENGTH + sizeof(uint32_t)]; - uint32_t c = htonl(*counter); - - brealloc(buf, AUTH_BYTES + blen, capacity); - memcpy(key, ctx->evp.iv, enc_iv_len); - memcpy(key + enc_iv_len, &c, sizeof(uint32_t)); -#if defined(USE_CRYPTO_OPENSSL) - HMAC(EVP_sha1(), key, enc_iv_len + sizeof(uint32_t), (uint8_t *)buf->data, blen, hash, NULL); -#elif defined(USE_CRYPTO_MBEDTLS) - mbedtls_md_hmac(mbedtls_md_info_from_type( - MBEDTLS_MD_SHA1), key, enc_iv_len + sizeof(uint32_t), (uint8_t *)buf->data, blen, hash); -#else - sha1_hmac(key, enc_iv_len + sizeof(uint32_t), (uint8_t *)buf->data, blen, hash); -#endif - - memmove(buf->data + AUTH_BYTES, buf->data, blen); - memcpy(buf->data + CLEN_BYTES, hash, ONETIMEAUTH_BYTES); - memcpy(buf->data, &chunk_len, CLEN_BYTES); - - *counter = *counter + 1; - buf->len = blen + AUTH_BYTES; - - return 0; -} diff --git a/src/encrypt.h b/src/encrypt.h deleted file mode 100644 index 9bf3b03c..00000000 --- a/src/encrypt.h +++ /dev/null @@ -1,194 +0,0 @@ -/* - * encrypt.h - Define the enryptor's interface - * - * Copyright (C) 2013 - 2016, Max Lv - * - * This file is part of the shadowsocks-libev. - * - * shadowsocks-libev is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * shadowsocks-libev is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with shadowsocks-libev; see the file COPYING. If not, see - * . - */ - -#ifndef _ENCRYPT_H -#define _ENCRYPT_H - -#include -#include -#include -#include -#include - -#if defined(USE_CRYPTO_OPENSSL) - -#include -typedef EVP_CIPHER cipher_kt_t; -typedef EVP_CIPHER_CTX cipher_evp_t; -typedef EVP_MD digest_type_t; -#define MAX_KEY_LENGTH EVP_MAX_KEY_LENGTH -#define MAX_IV_LENGTH EVP_MAX_IV_LENGTH -#define MAX_MD_SIZE EVP_MAX_MD_SIZE - -#elif defined(USE_CRYPTO_POLARSSL) - -#include -#include -typedef cipher_info_t cipher_kt_t; -typedef cipher_context_t cipher_evp_t; -typedef md_info_t digest_type_t; -#define MAX_KEY_LENGTH 64 -#define MAX_IV_LENGTH POLARSSL_MAX_IV_LENGTH -#define MAX_MD_SIZE POLARSSL_MD_MAX_SIZE - -#elif defined(USE_CRYPTO_MBEDTLS) - -#include -#include -typedef mbedtls_cipher_info_t cipher_kt_t; -typedef mbedtls_cipher_context_t cipher_evp_t; -typedef mbedtls_md_info_t digest_type_t; -#define MAX_KEY_LENGTH 64 -#define MAX_IV_LENGTH MBEDTLS_MAX_IV_LENGTH -#define MAX_MD_SIZE MBEDTLS_MD_MAX_SIZE - -/* we must have MBEDTLS_CIPHER_MODE_CFB defined */ -#if !defined(MBEDTLS_CIPHER_MODE_CFB) -#error Cipher Feedback mode a.k.a CFB not supported by your mbed TLS. -#endif - -#endif - -#ifdef USE_CRYPTO_APPLECC - -#include - -#define kCCAlgorithmInvalid UINT32_MAX -#define kCCContextValid 0 -#define kCCContextInvalid -1 - -typedef struct { - CCCryptorRef cryptor; - int valid; - CCOperation encrypt; - CCAlgorithm cipher; - CCMode mode; - CCPadding padding; - uint8_t iv[MAX_IV_LENGTH]; - uint8_t key[MAX_KEY_LENGTH]; - size_t iv_len; - size_t key_len; -} cipher_cc_t; - -#endif - -typedef struct { - cipher_evp_t *evp; -#ifdef USE_CRYPTO_APPLECC - cipher_cc_t cc; -#endif - uint8_t iv[MAX_IV_LENGTH]; -} cipher_ctx_t; - -typedef struct { - cipher_kt_t *info; - size_t iv_len; - size_t key_len; -} cipher_t; - -#ifdef HAVE_STDINT_H -#include -#elif HAVE_INTTYPES_H -#include -#endif - -#define SODIUM_BLOCK_SIZE 64 - -#define NONE -1 -#define TABLE 0 -#define RC4 1 -#define RC4_MD5 2 -#define AES_128_CFB 3 -#define AES_192_CFB 4 -#define AES_256_CFB 5 -#define AES_128_CTR 6 -#define AES_192_CTR 7 -#define AES_256_CTR 8 -#define BF_CFB 9 -#define CAMELLIA_128_CFB 10 -#define CAMELLIA_192_CFB 11 -#define CAMELLIA_256_CFB 12 -#define CAST5_CFB 13 -#define DES_CFB 14 -#define IDEA_CFB 15 -#define RC2_CFB 16 -#define SEED_CFB 17 -#define SALSA20 18 -#define CHACHA20 19 -#if SODIUM_LIBRARY_VERSION_MAJOR >= 8 -#define CHACHA20IETF 20 -#endif - -#define ONETIMEAUTH_FLAG 0x10 -#define ADDRTYPE_MASK 0xEF - -#define ONETIMEAUTH_BYTES 10U -#define CLEN_BYTES 2U -#define AUTH_BYTES (ONETIMEAUTH_BYTES + CLEN_BYTES) - -#define min(a, b) (((a) < (b)) ? (a) : (b)) -#define max(a, b) (((a) > (b)) ? (a) : (b)) - -typedef struct buffer { - size_t idx; - size_t len; - size_t capacity; - char *data; -} buffer_t; - -typedef struct chunk { - uint32_t idx; - uint32_t len; - uint32_t counter; - buffer_t *buf; -} chunk_t; - -typedef struct enc_ctx { - uint8_t init; - uint64_t counter; - cipher_ctx_t evp; -} enc_ctx_t; - -int ss_encrypt_all(buffer_t *plaintext, int method, int auth, size_t capacity); -int ss_decrypt_all(buffer_t *ciphertext, int method, int auth, size_t capacity); -int ss_encrypt(buffer_t *plaintext, enc_ctx_t *ctx, size_t capacity); -int ss_decrypt(buffer_t *ciphertext, enc_ctx_t *ctx, size_t capacity); - -void enc_ctx_init(int method, enc_ctx_t *ctx, int enc); -int enc_init(const char *pass, const char *method); -int enc_get_iv_len(void); -void cipher_context_release(cipher_ctx_t *evp); -unsigned char *enc_md5(const unsigned char *d, size_t n, unsigned char *md); - -int ss_onetimeauth(buffer_t *buf, uint8_t *iv, size_t capacity); -int ss_onetimeauth_verify(buffer_t *buf, uint8_t *iv); - -int ss_check_hash(buffer_t *buf, chunk_t *chunk, enc_ctx_t *ctx, size_t capacity); -int ss_gen_hash(buffer_t *buf, uint32_t *counter, enc_ctx_t *ctx, size_t capacity); - -int balloc(buffer_t *ptr, size_t capacity); -int brealloc(buffer_t *ptr, size_t len, size_t capacity); -void bfree(buffer_t *ptr); - -int rand_bytes(void *output, int len); - -#endif // _ENCRYPT_H diff --git a/src/jconf.c b/src/jconf.c index 2ad0b3d4..72b34641 100644 --- a/src/jconf.c +++ b/src/jconf.c @@ -1,7 +1,7 @@ /* * jconf.c - Parse the JSON format config file * - * Copyright (C) 2013 - 2016, Max Lv + * Copyright (C) 2013 - 2017, Max Lv * * This file is part of the shadowsocks-libev. * shadowsocks-libev is free software; you can redistribute it and/or modify diff --git a/src/jconf.h b/src/jconf.h index 98a4357b..0f660f2e 100644 --- a/src/jconf.h +++ b/src/jconf.h @@ -1,7 +1,7 @@ /* * jconf.h - Define the config data structure * - * Copyright (C) 2013 - 2016, Max Lv + * Copyright (C) 2013 - 2017, Max Lv * * This file is part of the shadowsocks-libev. * shadowsocks-libev is free software; you can redistribute it and/or modify diff --git a/src/local.c b/src/local.c index 1f200cee..bf02c93f 100644 --- a/src/local.c +++ b/src/local.c @@ -1,7 +1,7 @@ /* * local.c - Setup a socks5 proxy through remote shadowsocks server * - * Copyright (C) 2013 - 2016, Max Lv + * Copyright (C) 2013 - 2017, Max Lv * * This file is part of the shadowsocks-libev. * @@ -95,8 +95,9 @@ uint64_t rx = 0; ev_tstamp last = 0; #endif +static crypto_t *crypto; + static int acl = 0; -static int auth = 0; static int mode = TCP_ONLY; static int ipv6first = 0; static int fast_open = 0; @@ -130,7 +131,7 @@ static void free_server(server_t *server); static void close_and_free_server(EV_P_ server_t *server); static remote_t *new_remote(int fd, int timeout); -static server_t *new_server(int fd, int method); +static server_t *new_server(int fd); static struct cork_dllist connections; @@ -287,16 +288,12 @@ server_recv_cb(EV_P_ ev_io *w, int revents) return; } - if (!remote->direct && remote->send_ctx->connected && auth) { - ss_gen_hash(remote->buf, &remote->counter, server->e_ctx, BUF_SIZE); - } - // insert shadowsocks header if (!remote->direct) { #ifdef ANDROID tx += remote->buf->len; #endif - int err = ss_encrypt(remote->buf, server->e_ctx, BUF_SIZE); + int err = crypto->encrypt(remote->buf, server->e_ctx, BUF_SIZE); if (err) { LOGE("invalid password or cipher"); @@ -304,6 +301,13 @@ server_recv_cb(EV_P_ ev_io *w, int revents) close_and_free_server(EV_A_ server); return; } + + if (server->abuf) { + bprepend(remote->buf, server->abuf, BUF_SIZE); + bfree(server->abuf); + ss_free(server->abuf); + server->abuf = NULL; + } } if (!remote->send_ctx->connected) { @@ -527,9 +531,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents) char host[257], ip[INET6_ADDRSTRLEN], port[16]; - buffer_t ss_addr_to_send; - buffer_t *abuf = &ss_addr_to_send; - balloc(abuf, BUF_SIZE); + buffer_t *abuf = server->abuf; abuf->data[abuf->len++] = request->atyp; int atyp = request->atyp; @@ -574,7 +576,6 @@ server_recv_cb(EV_P_ ev_io *w, int revents) sprintf(port, "%d", p); } } else { - bfree(abuf); LOGE("unsupported addrtype: %d", request->atyp); close_and_free_remote(EV_A_ remote); close_and_free_server(EV_A_ server); @@ -596,7 +597,6 @@ server_recv_cb(EV_P_ ev_io *w, int revents) buf->len - 3 - abuf->len, &hostname); if (ret == -1 && buf->len < BUF_SIZE) { server->stage = STAGE_PARSE; - bfree(abuf); return; } else if (ret > 0) { sni_detected = 1; @@ -684,40 +684,28 @@ server_recv_cb(EV_P_ ev_io *w, int revents) } if (remote == NULL) { - bfree(abuf); LOGE("invalid remote addr"); close_and_free_server(EV_A_ server); return; } if (!remote->direct) { - if (auth) { - abuf->data[0] |= ONETIMEAUTH_FLAG; - ss_onetimeauth(abuf, server->e_ctx->evp.iv, BUF_SIZE); + int err = crypto->encrypt(abuf, server->e_ctx, BUF_SIZE); + if (err) { + LOGE("invalid password or cipher"); + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + return; } + } - if (buf->len > 0 && auth) { - ss_gen_hash(buf, &remote->counter, server->e_ctx, BUF_SIZE); - } - - brealloc(remote->buf, buf->len + abuf->len, BUF_SIZE); - memcpy(remote->buf->data, abuf->data, abuf->len); - remote->buf->len = buf->len + abuf->len; - - if (buf->len > 0) { - memcpy(remote->buf->data + abuf->len, buf->data, buf->len); - } - } else { - if (buf->len > 0) { - memcpy(remote->buf->data, buf->data, buf->len); - remote->buf->len = buf->len; - } + if (buf->len > 0) { + memcpy(remote->buf->data, buf->data, buf->len); + remote->buf->len = buf->len; } server->remote = remote; remote->server = server; - - bfree(abuf); } } } @@ -829,12 +817,14 @@ remote_recv_cb(EV_P_ ev_io *w, int revents) #ifdef ANDROID rx += server->buf->len; #endif - int err = ss_decrypt(server->buf, server->d_ctx, BUF_SIZE); - if (err) { + int err = crypto->decrypt(server->buf, server->d_ctx, BUF_SIZE); + if (err == CRYPTO_ERROR) { LOGE("invalid password or cipher"); close_and_free_remote(EV_A_ remote); close_and_free_server(EV_A_ server); return; + } else if (err == CRYPTO_NEED_MORE) { + return; // Wait for more } } @@ -991,7 +981,7 @@ close_and_free_remote(EV_P_ remote_t *remote) } static server_t * -new_server(int fd, int method) +new_server(int fd) { server_t *server; server = ss_malloc(sizeof(server_t)); @@ -1001,7 +991,9 @@ new_server(int fd, int method) server->recv_ctx = ss_malloc(sizeof(server_ctx_t)); server->send_ctx = ss_malloc(sizeof(server_ctx_t)); server->buf = ss_malloc(sizeof(buffer_t)); + server->abuf = ss_malloc(sizeof(buffer_t)); balloc(server->buf, BUF_SIZE); + balloc(server->abuf, BUF_SIZE); memset(server->recv_ctx, 0, sizeof(server_ctx_t)); memset(server->send_ctx, 0, sizeof(server_ctx_t)); server->stage = STAGE_INIT; @@ -1011,15 +1003,10 @@ new_server(int fd, int method) server->recv_ctx->server = server; server->send_ctx->server = server; - if (method) { - server->e_ctx = ss_malloc(sizeof(struct enc_ctx)); - server->d_ctx = ss_malloc(sizeof(struct enc_ctx)); - enc_ctx_init(method, server->e_ctx, 1); - enc_ctx_init(method, server->d_ctx, 0); - } else { - server->e_ctx = NULL; - server->d_ctx = NULL; - } + server->e_ctx = ss_malloc(sizeof(cipher_ctx_t)); + server->d_ctx = ss_malloc(sizeof(cipher_ctx_t)); + crypto->ctx_init(crypto->cipher, server->e_ctx, 1); + crypto->ctx_init(crypto->cipher, server->d_ctx, 0); ev_io_init(&server->recv_ctx->io, server_recv_cb, fd, EV_READ); ev_io_init(&server->send_ctx->io, server_send_cb, fd, EV_WRITE); @@ -1038,17 +1025,21 @@ free_server(server_t *server) server->remote->server = NULL; } if (server->e_ctx != NULL) { - cipher_context_release(&server->e_ctx->evp); + crypto->ctx_release(server->e_ctx); ss_free(server->e_ctx); } if (server->d_ctx != NULL) { - cipher_context_release(&server->d_ctx->evp); + crypto->ctx_release(server->d_ctx); ss_free(server->d_ctx); } if (server->buf != NULL) { bfree(server->buf); ss_free(server->buf); } + if (server->abuf != NULL) { + bfree(server->abuf); + ss_free(server->abuf); + } ss_free(server->recv_ctx); ss_free(server->send_ctx); ss_free(server); @@ -1153,7 +1144,7 @@ accept_cb(EV_P_ ev_io *w, int revents) setsockopt(serverfd, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt)); #endif - server_t *server = new_server(serverfd, listener->method); + server_t *server = new_server(serverfd); server->listener = listener; ev_io_start(EV_A_ & server->recv_ctx->io); @@ -1206,10 +1197,10 @@ main(int argc, char **argv) USE_TTY(); #ifdef ANDROID - while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:i:c:b:a:n:huUvVA6", + while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:i:c:b:a:n:huUvV6", long_options, &option_index)) != -1) { #else - while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:i:c:b:a:n:huUvA6", + while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:i:c:b:a:n:huUv6", long_options, &option_index)) != -1) { #endif switch (c) { @@ -1288,9 +1279,6 @@ main(int argc, char **argv) case 'h': usage(); exit(EXIT_SUCCESS); - case 'A': - auth = 1; - break; case '6': ipv6first = 1; break; @@ -1351,9 +1339,6 @@ main(int argc, char **argv) if (plugin_opts == NULL) { plugin_opts = conf->plugin_opts; } - if (auth == 0) { - auth = conf->auth; - } if (fast_open == 0) { fast_open = conf->fast_open; } @@ -1437,10 +1422,6 @@ main(int argc, char **argv) LOGI("resolving hostname to IPv6 address first"); } - if (auth) { - LOGI("onetime authentication enabled"); - } - if (plugin != NULL) { int len = 0; size_t buf_size = 256 * remote_num; @@ -1465,7 +1446,9 @@ main(int argc, char **argv) // Setup keys LOGI("initializing ciphers... %s", method); - int m = enc_init(password, method); + crypto = crypto_init(password, method); + if (crypto == NULL) + FATAL("failed to initialize ciphers"); // Setup proxy context listen_ctx_t listen_ctx; @@ -1491,7 +1474,6 @@ main(int argc, char **argv) } listen_ctx.timeout = atoi(timeout); listen_ctx.iface = iface; - listen_ctx.method = m; listen_ctx.mptcp = mptcp; // Setup signal handler @@ -1538,7 +1520,7 @@ main(int argc, char **argv) } struct sockaddr *addr = (struct sockaddr *)storage; init_udprelay(local_addr, local_port, addr, - get_sockaddr_len(addr), mtu, m, auth, listen_ctx.timeout, iface); + get_sockaddr_len(addr), mtu, crypto, listen_ctx.timeout, iface); } #ifdef HAVE_LAUNCHD @@ -1609,7 +1591,6 @@ start_ss_local_server(profile_t profile) int mtu = 0; int mptcp = 0; - auth = profile.auth; mode = profile.mode; fast_open = profile.fast_open; verbose = profile.verbose; @@ -1646,7 +1627,9 @@ start_ss_local_server(profile_t profile) // Setup keys LOGI("initializing ciphers... %s", method); - int m = enc_init(password, method); + crypto = crypto_init(password, method); + if (crypto == NULL) + FATAL("failed to init ciphers"); struct sockaddr_storage *storage = ss_malloc(sizeof(struct sockaddr_storage)); memset(storage, 0, sizeof(struct sockaddr_storage)); @@ -1663,7 +1646,6 @@ start_ss_local_server(profile_t profile) listen_ctx.remote_addr = remote_addr_tmp; listen_ctx.remote_addr[0] = (struct sockaddr *)storage; listen_ctx.timeout = timeout; - listen_ctx.method = m; listen_ctx.iface = NULL; listen_ctx.mptcp = mptcp; @@ -1692,7 +1674,7 @@ start_ss_local_server(profile_t profile) LOGI("udprelay enabled"); struct sockaddr *addr = (struct sockaddr *)storage; init_udprelay(local_addr, local_port_str, addr, - get_sockaddr_len(addr), mtu, m, auth, timeout, NULL); + get_sockaddr_len(addr), mtu, crypto, timeout, NULL); } if (strcmp(local_addr, ":") > 0) diff --git a/src/local.h b/src/local.h index 98c4c567..4421a7a1 100644 --- a/src/local.h +++ b/src/local.h @@ -1,7 +1,7 @@ /* * local.h - Define the client's buffers and callbacks * - * Copyright (C) 2013 - 2016, Max Lv + * Copyright (C) 2013 - 2017, Max Lv * * This file is part of the shadowsocks-libev. * @@ -26,7 +26,7 @@ #include #include -#include "encrypt.h" +#include "crypto.h" #include "jconf.h" #include "protocol.h" @@ -36,7 +36,6 @@ typedef struct listen_ctx { ev_io io; char *iface; int remote_num; - int method; int timeout; int fd; int mptcp; @@ -53,14 +52,15 @@ typedef struct server { int fd; int stage; - struct enc_ctx *e_ctx; - struct enc_ctx *d_ctx; + cipher_ctx_t *e_ctx; + cipher_ctx_t *d_ctx; struct server_ctx *recv_ctx; struct server_ctx *send_ctx; struct listen_ctx *listener; struct remote *remote; buffer_t *buf; + buffer_t *abuf; struct cork_dllist_item entries; } server_t; @@ -80,6 +80,7 @@ typedef struct remote { uint32_t counter; buffer_t *buf; + struct remote_ctx *recv_ctx; struct remote_ctx *send_ctx; struct server *server; diff --git a/src/manager.c b/src/manager.c index 5672d2bd..6305d9d7 100644 --- a/src/manager.c +++ b/src/manager.c @@ -1,7 +1,7 @@ /* * server.c - Provide shadowsocks service * - * Copyright (C) 2013 - 2016, Max Lv + * Copyright (C) 2013 - 2017, Max Lv * * This file is part of the shadowsocks-libev. * diff --git a/src/manager.h b/src/manager.h index e212f025..964d39cc 100644 --- a/src/manager.h +++ b/src/manager.h @@ -1,7 +1,7 @@ /* * server.h - Define shadowsocks server's buffers and callbacks * - * Copyright (C) 2013 - 2016, Max Lv + * Copyright (C) 2013 - 2017, Max Lv * * This file is part of the shadowsocks-libev. * diff --git a/src/netutils.c b/src/netutils.c index 68687c95..f5eb481a 100644 --- a/src/netutils.c +++ b/src/netutils.c @@ -1,7 +1,7 @@ /* * netutils.c - Network utilities * - * Copyright (C) 2013 - 2016, Max Lv + * Copyright (C) 2013 - 2017, Max Lv * * This file is part of the shadowsocks-libev. * diff --git a/src/netutils.h b/src/netutils.h index 82d1a78d..e2819402 100644 --- a/src/netutils.h +++ b/src/netutils.h @@ -1,7 +1,7 @@ /* * netutils.h - Network utilities * - * Copyright (C) 2013 - 2016, Max Lv + * Copyright (C) 2013 - 2017, Max Lv * * This file is part of the shadowsocks-libev. * diff --git a/src/plugin.c b/src/plugin.c index 8fd892bb..68d22b30 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -1,7 +1,7 @@ /* * plugin.c - Manage plugins * - * Copyright (C) 2013 - 2016, Max Lv + * Copyright (C) 2013 - 2017, Max Lv * * This file is part of the shadowsocks-libev. * diff --git a/src/plugin.h b/src/plugin.h index 71035a4d..b43fa99d 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -1,7 +1,7 @@ /* * acl.h - Define the ACL interface * - * Copyright (C) 2013 - 2016, Max Lv + * Copyright (C) 2013 - 2017, Max Lv * * This file is part of the shadowsocks-libev. * diff --git a/src/redir.c b/src/redir.c index c484efef..8af5dc00 100644 --- a/src/redir.c +++ b/src/redir.c @@ -2,7 +2,7 @@ * redir.c - Provide a transparent TCP proxy through remote shadowsocks * server * - * Copyright (C) 2013 - 2016, Max Lv + * Copyright (C) 2013 - 2017, Max Lv * * This file is part of the shadowsocks-libev. * @@ -79,7 +79,7 @@ static void remote_recv_cb(EV_P_ ev_io *w, int revents); static void remote_send_cb(EV_P_ ev_io *w, int revents); static remote_t *new_remote(int fd, int timeout); -static server_t *new_server(int fd, int method); +static server_t *new_server(int fd); static void free_remote(remote_t *remote); static void close_and_free_remote(EV_P_ remote_t *remote); @@ -89,9 +89,10 @@ static void close_and_free_server(EV_P_ server_t *server); int verbose = 0; int keep_resolving = 1; +static crypto_t *crypto; + static int ipv6first = 0; static int mode = TCP_ONLY; -static int auth = 0; #ifdef HAVE_SETRLIMIT static int nofile = 0; #endif @@ -230,10 +231,6 @@ server_recv_cb(EV_P_ ev_io *w, int revents) LOGI("redir to %s:%d, len=%zu, recv=%zd", ipstr, port, remote->buf->len, r); } - if (auth) { - ss_gen_hash(remote->buf, &remote->counter, server->e_ctx, BUF_SIZE); - } - if (!remote->send_ctx->connected) { // SNI int ret = 0; @@ -259,7 +256,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents) return; } - int err = ss_encrypt(remote->buf, server->e_ctx, BUF_SIZE); + int err = crypto->encrypt(remote->buf, server->e_ctx, BUF_SIZE); if (err) { LOGE("invalid password or cipher"); @@ -378,13 +375,16 @@ remote_recv_cb(EV_P_ ev_io *w, int revents) server->buf->len = r; - int err = ss_decrypt(server->buf, server->d_ctx, BUF_SIZE); - if (err) { + int err = crypto->decrypt(server->buf, server->d_ctx, BUF_SIZE); + if (err == CRYPTO_ERROR) { LOGE("invalid password or cipher"); close_and_free_remote(EV_A_ remote); close_and_free_server(EV_A_ server); return; + } else if (err == CRYPTO_NEED_MORE) { + return; // Wait for more } + int s = send(server->fd, server->buf->data, server->buf->len, 0); if (s == -1) { @@ -477,18 +477,7 @@ remote_send_cb(EV_P_ ev_io *w, int revents) abuf->len += 2; - if (auth) { - abuf->data[0] |= ONETIMEAUTH_FLAG; - ss_onetimeauth(abuf, server->e_ctx->evp.iv, BUF_SIZE); - } - - brealloc(remote->buf, remote->buf->len + abuf->len, BUF_SIZE); - memmove(remote->buf->data + abuf->len, remote->buf->data, remote->buf->len); - memcpy(remote->buf->data, abuf->data, abuf->len); - remote->buf->len += abuf->len; - bfree(abuf); - - int err = ss_encrypt(remote->buf, server->e_ctx, BUF_SIZE); + int err = crypto->encrypt(abuf, server->e_ctx, BUF_SIZE); if (err) { LOGE("invalid password or cipher"); close_and_free_remote(EV_A_ remote); @@ -496,6 +485,17 @@ remote_send_cb(EV_P_ ev_io *w, int revents) return; } + err = crypto->encrypt(remote->buf, server->e_ctx, BUF_SIZE); + if (err) { + LOGE("invalid password or cipher"); + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + return; + } + + bprepend(remote->buf, abuf, BUF_SIZE); + bfree(abuf); + ev_io_start(EV_A_ & remote->recv_ctx->io); } else { ERROR("getpeername"); @@ -595,7 +595,7 @@ close_and_free_remote(EV_P_ remote_t *remote) } static server_t * -new_server(int fd, int method) +new_server(int fd) { server_t *server = ss_malloc(sizeof(server_t)); memset(server, 0, sizeof(server_t)); @@ -615,15 +615,10 @@ new_server(int fd, int method) server->hostname = NULL; server->hostname_len = 0; - if (method) { - server->e_ctx = ss_malloc(sizeof(enc_ctx_t)); - server->d_ctx = ss_malloc(sizeof(enc_ctx_t)); - enc_ctx_init(method, server->e_ctx, 1); - enc_ctx_init(method, server->d_ctx, 0); - } else { - server->e_ctx = NULL; - server->d_ctx = NULL; - } + server->e_ctx = ss_malloc(sizeof(cipher_ctx_t)); + server->d_ctx = ss_malloc(sizeof(cipher_ctx_t)); + crypto->ctx_init(crypto->cipher, server->e_ctx, 1); + crypto->ctx_init(crypto->cipher, server->d_ctx, 0); ev_io_init(&server->recv_ctx->io, server_recv_cb, fd, EV_READ); ev_io_init(&server->send_ctx->io, server_send_cb, fd, EV_WRITE); @@ -641,11 +636,11 @@ free_server(server_t *server) server->remote->server = NULL; } if (server->e_ctx != NULL) { - cipher_context_release(&server->e_ctx->evp); + crypto->ctx_release(server->e_ctx); ss_free(server->e_ctx); } if (server->d_ctx != NULL) { - cipher_context_release(&server->d_ctx->evp); + crypto->ctx_release(server->d_ctx); ss_free(server->d_ctx); } if (server->buf != NULL) { @@ -732,7 +727,7 @@ accept_cb(EV_P_ ev_io *w, int revents) } } - server_t *server = new_server(serverfd, listener->method); + server_t *server = new_server(serverfd); remote_t *remote = new_remote(remotefd, listener->timeout); server->remote = remote; remote->server = server; @@ -817,7 +812,7 @@ main(int argc, char **argv) USE_TTY(); - while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:c:b:a:n:huUvA6", + while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:c:b:a:n:huUv6", long_options, &option_index)) != -1) { switch (c) { case 0: @@ -887,9 +882,6 @@ main(int argc, char **argv) case 'h': usage(); exit(EXIT_SUCCESS); - case 'A': - auth = 1; - break; case '6': ipv6first = 1; break; @@ -946,9 +938,6 @@ main(int argc, char **argv) if (plugin_opts == NULL) { plugin_opts = conf->plugin_opts; } - if (auth == 0) { - auth = conf->auth; - } if (mtu == 0) { mtu = conf->mtu; } @@ -1014,10 +1003,6 @@ main(int argc, char **argv) LOGI("resolving hostname to IPv6 address first"); } - if (auth) { - LOGI("onetime authentication enabled"); - } - if (plugin != NULL) { int len = 0; size_t buf_size = 256 * remote_num; @@ -1048,7 +1033,9 @@ main(int argc, char **argv) // Setup keys LOGI("initializing ciphers... %s", method); - int m = enc_init(password, method); + crypto = crypto_init(password, method); + if (crypto == NULL) + FATAL("failed to initialize ciphers"); // Setup proxy context listen_ctx_t listen_ctx; @@ -1073,7 +1060,6 @@ main(int argc, char **argv) if (plugin != NULL) break; } listen_ctx.timeout = atoi(timeout); - listen_ctx.method = m; listen_ctx.mptcp = mptcp; struct ev_loop *loop = EV_DEFAULT; @@ -1108,7 +1094,7 @@ main(int argc, char **argv) } struct sockaddr *addr = (struct sockaddr *)storage; init_udprelay(local_addr, local_port, addr, - get_sockaddr_len(addr), mtu, m, auth, listen_ctx.timeout, NULL); + get_sockaddr_len(addr), mtu, crypto, listen_ctx.timeout, NULL); } if (mode == UDP_ONLY) { diff --git a/src/redir.h b/src/redir.h index 73b3e85c..22bf5be1 100644 --- a/src/redir.h +++ b/src/redir.h @@ -1,7 +1,7 @@ /* * redir.h - Define the redirector's buffers and callbacks * - * Copyright (C) 2013 - 2016, Max Lv + * Copyright (C) 2013 - 2017, Max Lv * * This file is part of the shadowsocks-libev. * @@ -24,7 +24,7 @@ #define _LOCAL_H #include -#include "encrypt.h" +#include "crypto.h" #include "jconf.h" typedef struct listen_ctx { @@ -32,7 +32,6 @@ typedef struct listen_ctx { int remote_num; int timeout; int fd; - int method; int mptcp; struct sockaddr **remote_addr; } listen_ctx_t; @@ -47,15 +46,17 @@ typedef struct server { int fd; buffer_t *buf; - struct sockaddr_storage destaddr; - struct enc_ctx *e_ctx; - struct enc_ctx *d_ctx; + + cipher_ctx_t *e_ctx; + cipher_ctx_t *d_ctx; struct server_ctx *recv_ctx; struct server_ctx *send_ctx; struct remote *remote; char *hostname; size_t hostname_len; + + struct sockaddr_storage destaddr; } server_t; typedef struct remote_ctx { diff --git a/src/server.c b/src/server.c index 588fb469..7bf2e612 100644 --- a/src/server.c +++ b/src/server.c @@ -1,7 +1,7 @@ /* * server.c - Provide shadowsocks service * - * Copyright (C) 2013 - 2016, Max Lv + * Copyright (C) 2013 - 2017, Max Lv * * This file is part of the shadowsocks-libev. * @@ -99,14 +99,12 @@ static void close_and_free_server(EV_P_ server_t *server); static void server_resolve_cb(struct sockaddr *addr, void *data); static void query_free_cb(void *data); -static size_t parse_header_len(const char atyp, const char *data, size_t offset); -static int is_header_complete(const buffer_t *buf); - int verbose = 0; +static crypto_t *crypto; + static int acl = 0; static int mode = TCP_ONLY; -static int auth = 0; static int ipv6first = 0; static int fast_open = 0; @@ -219,66 +217,6 @@ free_connections(struct ev_loop *loop) } } -static size_t -parse_header_len(const char atyp, const char *data, size_t offset) -{ - size_t len = 0; - if ((atyp & ADDRTYPE_MASK) == 1) { - // IP V4 - len += sizeof(struct in_addr); - } else if ((atyp & ADDRTYPE_MASK) == 3) { - // Domain name - uint8_t name_len = *(uint8_t *)(data + offset); - len += name_len + 1; - } else if ((atyp & ADDRTYPE_MASK) == 4) { - // IP V6 - len += sizeof(struct in6_addr); - } else { - return 0; - } - len += 2; - return len; -} - -static int -is_header_complete(const buffer_t *buf) -{ - size_t header_len = 0; - size_t buf_len = buf->len; - - char atyp = buf->data[header_len]; - - // 1 byte for atyp - header_len++; - - if ((atyp & ADDRTYPE_MASK) == 1) { - // IP V4 - header_len += sizeof(struct in_addr); - } else if ((atyp & ADDRTYPE_MASK) == 3) { - // Domain name - // domain len + len of domain - if (buf_len < header_len + 1) - return 0; - uint8_t name_len = *(uint8_t *)(buf->data + header_len); - header_len += name_len + 1; - } else if ((atyp & ADDRTYPE_MASK) == 4) { - // IP V6 - header_len += sizeof(struct in6_addr); - } else { - return -1; - } - - // len of port - header_len += 2; - - // size of ONETIMEAUTH_BYTES - if (auth || (atyp & ONETIMEAUTH_FLAG)) { - header_len += ONETIMEAUTH_BYTES; - } - - return buf_len >= header_len ? 1 : 0; -} - static char * get_peer_name(int fd) { @@ -607,25 +545,16 @@ server_recv_cb(EV_P_ ev_io *w, int revents) server_t *server = server_recv_ctx->server; remote_t *remote = NULL; - int len = server->buf->len; buffer_t *buf = server->buf; - if (server->stage > STAGE_PARSE) { + if (server->stage == STAGE_STREAM) { remote = server->remote; buf = remote->buf; - len = 0; ev_timer_again(EV_A_ & server->recv_ctx->watcher); } - if (len > BUF_SIZE) { - ERROR("out of recv buffer"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); - return; - } - - ssize_t r = recv(server->fd, buf->data + len, BUF_SIZE - len, 0); + ssize_t r = recv(server->fd, buf->data, BUF_SIZE, 0); if (r == 0) { // connection closed @@ -649,95 +578,21 @@ server_recv_cb(EV_P_ ev_io *w, int revents) } tx += r; + buf->len = r; - if (server->frag >= MAX_FRAG) { - LOGE("fragmentation detected"); - close_and_free_remote(EV_A_ remote); - close_and_free_server(EV_A_ server); - return; - } + int err = crypto->decrypt(buf, server->d_ctx, BUF_SIZE); - if (server->stage == STAGE_ERROR) { - server->buf->len = 0; - server->buf->idx = 0; - return; - } - - // handle incomplete header part 1 - if (server->stage == STAGE_INIT) { - buf->len += r; - - if (buf->len <= enc_get_iv_len() + 1) { - // wait for more - server->frag++; - return; - } - } else { - buf->len = r; - } - - int err = ss_decrypt(buf, server->d_ctx, BUF_SIZE); - - if (err) { + if (err == CRYPTO_ERROR) { report_addr(server->fd, MALICIOUS); close_and_free_remote(EV_A_ remote); close_and_free_server(EV_A_ server); return; - } - - // handle incomplete header part 2 - if (server->stage == STAGE_INIT) { - int ret = is_header_complete(server->buf); - if (ret == 1) { - bfree(server->header_buf); - ss_free(server->header_buf); - server->stage = STAGE_PARSE; - } else if (ret == -1) { - server->stage = STAGE_ERROR; - report_addr(server->fd, MALFORMED); - server->buf->len = 0; - server->buf->idx = 0; - return; - } else { - server->stage = STAGE_HANDSHAKE; - } - } - - if (server->stage == STAGE_HANDSHAKE) { - size_t header_len = server->header_buf->len; - brealloc(server->header_buf, server->buf->len + header_len, BUF_SIZE); - memcpy(server->header_buf->data + header_len, - server->buf->data, server->buf->len); - server->header_buf->len = server->buf->len + header_len; - - int ret = is_header_complete(server->buf); - - if (ret == 1) { - brealloc(server->buf, server->header_buf->len, BUF_SIZE); - memcpy(server->buf->data, server->header_buf->data, server->header_buf->len); - server->buf->len = server->header_buf->len; - bfree(server->header_buf); - ss_free(server->header_buf); - server->stage = STAGE_PARSE; - } else { - if (ret == -1) - server->stage = STAGE_ERROR; - server->frag++; - server->buf->len = 0; - server->buf->idx = 0; - return; - } + } else if (err == CRYPTO_NEED_MORE) { + return; } // handshake and transmit data if (server->stage == STAGE_STREAM) { - if (server->auth && !ss_check_hash(remote->buf, server->chunk, server->d_ctx, BUF_SIZE)) { - LOGE("hash error"); - report_addr(server->fd, BAD); - close_and_free_server(EV_A_ server); - close_and_free_remote(EV_A_ remote); - return; - } int s = send(remote->fd, remote->buf->data, remote->buf->len, 0); if (s == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { @@ -757,7 +612,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents) ev_io_start(EV_A_ & remote->send_ctx->io); } return; - } else if (server->stage == STAGE_PARSE) { + } else if (server->stage == STAGE_INIT) { /* * Shadowsocks TCP Relay Header: * @@ -767,24 +622,6 @@ server_recv_cb(EV_P_ ev_io *w, int revents) * | 1 | Variable | 2 | 10 | * +------+----------+----------+----------------+ * - * If ATYP & ONETIMEAUTH_FLAG(0x10) != 0, Authentication (HMAC-SHA1) is enabled. - * - * The key of HMAC-SHA1 is (IV + KEY) and the input is the whole header. - * The output of HMAC-SHA is truncated to 10 bytes (leftmost bits). - */ - - /* - * Shadowsocks Request's Chunk Authentication for TCP Relay's payload - * (No chunk authentication for response's payload): - * - * +------+-----------+-------------+------+ - * | LEN | HMAC-SHA1 | DATA | ... - * +------+-----------+-------------+------+ - * | 2 | 10 | Variable | ... - * +------+-----------+-------------+------+ - * - * The key of HMAC-SHA1 is (IV + CHUNK ID) - * The output of HMAC-SHA is truncated to 10 bytes (leftmost bits). */ int offset = 0; @@ -797,27 +634,6 @@ server_recv_cb(EV_P_ ev_io *w, int revents) memset(&info, 0, sizeof(struct addrinfo)); memset(&storage, 0, sizeof(struct sockaddr_storage)); - if (auth || (atyp & ONETIMEAUTH_FLAG)) { - size_t header_len = parse_header_len(atyp, server->buf->data, offset); - size_t len = server->buf->len; - - if (header_len == 0 || len < offset + header_len + ONETIMEAUTH_BYTES) { - report_addr(server->fd, MALFORMED); - close_and_free_server(EV_A_ server); - return; - } - - server->buf->len = offset + header_len + ONETIMEAUTH_BYTES; - if (ss_onetimeauth_verify(server->buf, server->d_ctx->evp.iv)) { - report_addr(server->fd, BAD); - close_and_free_server(EV_A_ server); - return; - } - - server->buf->len = len; - server->auth = 1; - } - // get remote addr and port if ((atyp & ADDRTYPE_MASK) == 1) { // IP V4 @@ -924,10 +740,6 @@ server_recv_cb(EV_P_ ev_io *w, int revents) offset += 2; - if (server->auth) { - offset += ONETIMEAUTH_BYTES; - } - if (server->buf->len < offset) { report_addr(server->fd, MALFORMED); close_and_free_server(EV_A_ server); @@ -944,13 +756,6 @@ server_recv_cb(EV_P_ ev_io *w, int revents) LOGI("connect to %s:%d", host, ntohs(port)); } - if (server->auth && !ss_check_hash(server->buf, server->chunk, server->d_ctx, BUF_SIZE)) { - LOGE("hash error"); - report_addr(server->fd, BAD); - close_and_free_server(EV_A_ server); - return; - } - if (!need_query) { remote_t *remote = connect_to_remote(EV_A_ & info, server); @@ -1067,12 +872,7 @@ server_timeout_cb(EV_P_ ev_timer *watcher, int revents) LOGI("TCP connection timeout"); } - if (server->stage < STAGE_PARSE) { - if (verbose) { - size_t len = server->stage ? - server->header_buf->len : server->buf->len; - LOGI("incomplete header: %zu", len); - } + if (server->stage < STAGE_STREAM) { report_addr(server->fd, SUSPICIOUS); } @@ -1184,7 +984,7 @@ remote_recv_cb(EV_P_ ev_io *w, int revents) rx += r; server->buf->len = r; - int err = ss_encrypt(server->buf, server->e_ctx, BUF_SIZE); + int err = crypto->encrypt(server->buf, server->e_ctx, BUF_SIZE); if (err) { LOGE("invalid password or cipher"); @@ -1386,11 +1186,9 @@ new_server(int fd, listen_ctx_t *listener) server->recv_ctx = ss_malloc(sizeof(server_ctx_t)); server->send_ctx = ss_malloc(sizeof(server_ctx_t)); server->buf = ss_malloc(sizeof(buffer_t)); - server->header_buf = ss_malloc(sizeof(buffer_t)); memset(server->recv_ctx, 0, sizeof(server_ctx_t)); memset(server->send_ctx, 0, sizeof(server_ctx_t)); balloc(server->buf, BUF_SIZE); - balloc(server->header_buf, BUF_SIZE); server->fd = fd; server->recv_ctx->server = server; server->recv_ctx->connected = 0; @@ -1402,15 +1200,10 @@ new_server(int fd, listen_ctx_t *listener) server->listen_ctx = listener; server->remote = NULL; - if (listener->method) { - server->e_ctx = ss_malloc(sizeof(enc_ctx_t)); - server->d_ctx = ss_malloc(sizeof(enc_ctx_t)); - enc_ctx_init(listener->method, server->e_ctx, 1); - enc_ctx_init(listener->method, server->d_ctx, 0); - } else { - server->e_ctx = NULL; - server->d_ctx = NULL; - } + server->e_ctx = ss_malloc(sizeof(cipher_ctx_t)); + server->d_ctx = ss_malloc(sizeof(cipher_ctx_t)); + crypto->ctx_init(crypto->cipher, server->e_ctx, 1); + crypto->ctx_init(crypto->cipher, server->d_ctx, 0); int request_timeout = min(MAX_REQUEST_TIMEOUT, listener->timeout) + rand() % MAX_REQUEST_TIMEOUT; @@ -1420,11 +1213,6 @@ new_server(int fd, listen_ctx_t *listener) ev_timer_init(&server->recv_ctx->watcher, server_timeout_cb, request_timeout, listener->timeout); - server->chunk = ss_malloc(sizeof(chunk_t)); - memset(server->chunk, 0, sizeof(chunk_t)); - server->chunk->buf = ss_malloc(sizeof(buffer_t)); - memset(server->chunk->buf, 0, sizeof(buffer_t)); - cork_dllist_add(&connections, &server->entries); return server; @@ -1435,32 +1223,21 @@ free_server(server_t *server) { cork_dllist_remove(&server->entries); - if (server->chunk != NULL) { - if (server->chunk->buf != NULL) { - bfree(server->chunk->buf); - ss_free(server->chunk->buf); - } - ss_free(server->chunk); - } if (server->remote != NULL) { server->remote->server = NULL; } if (server->e_ctx != NULL) { - cipher_context_release(&server->e_ctx->evp); + crypto->ctx_release(server->e_ctx); ss_free(server->e_ctx); } if (server->d_ctx != NULL) { - cipher_context_release(&server->d_ctx->evp); + crypto->ctx_release(server->d_ctx); ss_free(server->d_ctx); } if (server->buf != NULL) { bfree(server->buf); ss_free(server->buf); } - if (server->header_buf != NULL) { - bfree(server->header_buf); - ss_free(server->header_buf); - } ss_free(server->recv_ctx); ss_free(server->send_ctx); @@ -1682,9 +1459,6 @@ main(int argc, char **argv) case 'h': usage(); exit(EXIT_SUCCESS); - case 'A': - auth = 1; - break; case '6': ipv6first = 1; break; @@ -1735,9 +1509,6 @@ main(int argc, char **argv) if (plugin_opts == NULL) { plugin_opts = conf->plugin_opts; } - if (auth == 0) { - auth = conf->auth; - } if (mode == TCP_ONLY) { mode = conf->mode; } @@ -1823,10 +1594,6 @@ main(int argc, char **argv) #endif } - if (auth) { - LOGI("onetime authentication enabled"); - } - if (plugin != NULL) { LOGI("plugin \"%s\" enabled", plugin); } @@ -1852,18 +1619,15 @@ main(int argc, char **argv) // setup keys LOGI("initializing ciphers... %s", method); - int m = enc_init(password, method); + crypto = crypto_init(password, method); + if (crypto == NULL) + FATAL("failed to initialize ciphers"); // initialize ev loop struct ev_loop *loop = EV_DEFAULT; // setup udns - if (nameserver_num == 0) { - nameservers[nameserver_num++] = "8.8.8.8"; - resolv_init(loop, nameservers, nameserver_num, ipv6first); - } else { - resolv_init(loop, nameservers, nameserver_num, ipv6first); - } + resolv_init(loop, nameservers, nameserver_num, ipv6first); for (int i = 0; i < nameserver_num; i++) LOGI("using nameserver: %s", nameservers[i]); @@ -1916,7 +1680,6 @@ main(int argc, char **argv) // Setup proxy context listen_ctx->timeout = atoi(timeout); listen_ctx->fd = listenfd; - listen_ctx->method = m; listen_ctx->iface = iface; listen_ctx->loop = loop; @@ -1940,8 +1703,7 @@ main(int argc, char **argv) port = plugin_port; } // Setup UDP - init_udprelay(host, port, mtu, m, - auth, atoi(timeout), iface); + init_udprelay(host, port, mtu, crypto, atoi(timeout), iface); if (host && strcmp(host, ":") > 0) LOGI("udp server listening at [%s]:%s", host, port); else diff --git a/src/server.h b/src/server.h index 9a3b18b1..8cfff007 100644 --- a/src/server.h +++ b/src/server.h @@ -1,7 +1,7 @@ /* * server.h - Define shadowsocks server's buffers and callbacks * - * Copyright (C) 2013 - 2016, Max Lv + * Copyright (C) 2013 - 2017, Max Lv * * This file is part of the shadowsocks-libev. * @@ -27,7 +27,7 @@ #include #include -#include "encrypt.h" +#include "crypto.h" #include "jconf.h" #include "resolv.h" @@ -37,7 +37,6 @@ typedef struct listen_ctx { ev_io io; int fd; int timeout; - int method; char *iface; struct ev_loop *loop; } listen_ctx_t; @@ -52,16 +51,12 @@ typedef struct server_ctx { typedef struct server { int fd; int stage; - int auth; int frag; buffer_t *buf; - buffer_t *header_buf; - struct chunk *chunk; - - struct enc_ctx *e_ctx; - struct enc_ctx *d_ctx; + cipher_ctx_t *e_ctx; + cipher_ctx_t *d_ctx; struct server_ctx *recv_ctx; struct server_ctx *send_ctx; struct listen_ctx *listen_ctx; diff --git a/src/shadowsocks.h b/src/shadowsocks.h index 5b48ee57..476cbd07 100644 --- a/src/shadowsocks.h +++ b/src/shadowsocks.h @@ -1,7 +1,7 @@ /* * shadowsocks.h - Header files of library interfaces * - * Copyright (C) 2013 - 2016, Max Lv + * Copyright (C) 2013 - 2017, Max Lv * * This file is part of the shadowsocks-libev. * shadowsocks-libev is free software; you can redistribute it and/or modify @@ -37,7 +37,6 @@ typedef struct { char *log; // file path to log int fast_open; // enable tcp fast open int mode; // enable udp relay - int auth; // enable one-time authentication int mtu; // MTU of interface int mptcp; // enable multipath TCP int verbose; // verbose mode @@ -57,7 +56,6 @@ typedef struct { * .log = NULL, * .fast_open = 0, * .mode = 0, - * .auth = 0, * .verbose = 0 * }; */ diff --git a/src/stream.c b/src/stream.c new file mode 100644 index 00000000..09bf4e80 --- /dev/null +++ b/src/stream.c @@ -0,0 +1,651 @@ +/* + * stream.c - Manage stream ciphers + * + * Copyright (C) 2013 - 2017, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#define CIPHER_UNSUPPORTED "unsupported" + +#include + +#include "cache.h" +#include "stream.h" +#include "utils.h" + + +#define SODIUM_BLOCK_SIZE 64 + +#define NONE -1 +#define TABLE 0 +#define RC4 1 +#define RC4_MD5 2 +#define AES_128_CFB 3 +#define AES_192_CFB 4 +#define AES_256_CFB 5 +#define AES_128_CTR 6 +#define AES_192_CTR 7 +#define AES_256_CTR 8 +#define BF_CFB 9 +#define CAMELLIA_128_CFB 10 +#define CAMELLIA_192_CFB 11 +#define CAMELLIA_256_CFB 12 +#define CAST5_CFB 13 +#define DES_CFB 14 +#define IDEA_CFB 15 +#define RC2_CFB 16 +#define SEED_CFB 17 +#define SALSA20 18 +#define CHACHA20 19 +#define CHACHA20IETF 20 + +const char *supported_stream_ciphers[STREAM_CIPHER_NUM] = { + "table", + "rc4", + "rc4-md5", + "aes-128-cfb", + "aes-192-cfb", + "aes-256-cfb", + "aes-128-ctr", + "aes-192-ctr", + "aes-256-ctr", + "bf-cfb", + "camellia-128-cfb", + "camellia-192-cfb", + "camellia-256-cfb", + "cast5-cfb", + "des-cfb", + "idea-cfb", + "rc2-cfb", + "seed-cfb", + "salsa20", + "chacha20", +#if SODIUM_LIBRARY_VERSION_MAJOR >= 8 + "chacha20-ietf" +#endif +}; + +static const char *supported_stream_ciphers_mbedtls[STREAM_CIPHER_NUM] = { + "table", + "ARC4-128", + "ARC4-128", + "AES-128-CFB128", + "AES-192-CFB128", + "AES-256-CFB128", + "AES-128-CTR", + "AES-192-CTR", + "AES-256-CTR", + "BLOWFISH-CFB64", + "CAMELLIA-128-CFB128", + "CAMELLIA-192-CFB128", + "CAMELLIA-256-CFB128", + CIPHER_UNSUPPORTED, + CIPHER_UNSUPPORTED, + CIPHER_UNSUPPORTED, + CIPHER_UNSUPPORTED, + CIPHER_UNSUPPORTED, + "salsa20", + "chacha20", +#if SODIUM_LIBRARY_VERSION_MAJOR >= 8 + "chacha20-ietf" +#endif +}; + +static const int supported_stream_ciphers_nonce_size[STREAM_CIPHER_NUM] = { + 0, 0, 16, 16, 16, 16, 16, 16, 16, 8, 16, 16, 16, 8, 8, 8, 8, 16, 8, 8 +#if SODIUM_LIBRARY_VERSION_MAJOR >= 8 + , 12 +#endif +}; + +static const int supported_stream_ciphers_key_size[STREAM_CIPHER_NUM] = { + 0, 16, 16, 16, 24, 32, 16, 24, 32, 16, 16, 24, 32, 16, 8, 16, 16, 16, 32, 32 +#if SODIUM_LIBRARY_VERSION_MAJOR >= 8 + , 32 +#endif +}; + +static int +crypto_stream_xor_ic(uint8_t *c, const uint8_t *m, uint64_t mlen, + const uint8_t *n, uint64_t ic, const uint8_t *k, + int method) +{ + switch (method) { + case SALSA20: + return crypto_stream_salsa20_xor_ic(c, m, mlen, n, ic, k); + case CHACHA20: + return crypto_stream_chacha20_xor_ic(c, m, mlen, n, ic, k); +#if SODIUM_LIBRARY_VERSION_MAJOR >= 8 + case CHACHA20IETF: + return crypto_stream_chacha20_ietf_xor_ic(c, m, mlen, n, (uint32_t)ic, k); +#endif + } + // always return 0 + return 0; +} + +#ifdef DEBUG +void +dump(char *tag, char *text, int len) +{ + int i; + printf("%s: ", tag); + for (i = 0; i < len; i++) + printf("0x%02x ", (uint8_t)text[i]); + printf("\n"); +} +#endif + +int +cipher_nonce_size(const cipher_t *cipher) +{ + if (cipher == NULL) { + return 0; + } + return cipher->info->iv_size; +} + +int +cipher_key_size(const cipher_t *cipher) +{ + /* + * Semi-API changes (technically public, morally prnonceate) + * Renamed a few headers to include _internal in the name. Those headers are + * not supposed to be included by users. + * Changed md_info_t into an opaque structure (use md_get_xxx() accessors). + * Changed pk_info_t into an opaque structure. + * Changed cipher_base_t into an opaque structure. + */ + if (cipher == NULL) { + return 0; + } + /* From Version 1.2.7 released 2013-04-13 Default Blowfish keysize is now 128-bits */ + return cipher->info->key_bitlen / 8; +} + +const cipher_kt_t * +stream_get_cipher_type(int method) +{ + if (method <= TABLE || method >= STREAM_CIPHER_NUM) { + LOGE("stream_get_cipher_type(): Illegal method"); + return NULL; + } + + if (method == RC4_MD5) { + method = RC4; + } + + if (method >= SALSA20) { + return NULL; + } + + const char *ciphername = supported_stream_ciphers[method]; + const char *mbedtlsname = supported_stream_ciphers_mbedtls[method]; + if (strcmp(mbedtlsname, CIPHER_UNSUPPORTED) == 0) { + LOGE("Cipher %s currently is not supported by mbed TLS library", + ciphername); + return NULL; + } + return mbedtls_cipher_info_from_string(mbedtlsname); +} + +void +stream_cipher_ctx_init(cipher_ctx_t *ctx, int method, int enc) +{ + if (method <= TABLE || method >= STREAM_CIPHER_NUM) { + LOGE("stream_ctx_init(): Illegal method"); + return; + } + + if (method >= SALSA20) { + return; + } + + const char *ciphername = supported_stream_ciphers[method]; + const cipher_kt_t *cipher = stream_get_cipher_type(method); + + ctx->evp = ss_malloc(sizeof(cipher_evp_t)); + memset(ctx->evp, 0, sizeof(cipher_evp_t)); + cipher_evp_t *evp = ctx->evp; + + if (cipher == NULL) { + LOGE("Cipher %s not found in mbed TLS library", ciphername); + FATAL("Cannot initialize mbed TLS cipher"); + } + mbedtls_cipher_init(evp); + if (mbedtls_cipher_setup(evp, cipher) != 0) { + FATAL("Cannot initialize mbed TLS cipher context"); + } +} + +void +stream_cipher_ctx_release(cipher_ctx_t *cipher_ctx) +{ + mbedtls_cipher_free(cipher_ctx->evp); + ss_free(cipher_ctx->evp); +} + +void +cipher_ctx_set_nonce(cipher_ctx_t *cipher_ctx, uint8_t *nonce, size_t nonce_len, + int enc) +{ + const unsigned char *true_key; + + cipher_t *cipher = cipher_ctx->cipher; + + if (nonce == NULL) { + LOGE("cipher_ctx_set_nonce(): NONCE is null"); + return; + } + + if (!enc) { + memcpy(cipher_ctx->nonce, nonce, cipher->nonce_len); + } + + if (cipher->method >= SALSA20) { + return; + } + + if (cipher->method == RC4_MD5) { + unsigned char key_nonce[32]; + memcpy(key_nonce, cipher->key, 16); + memcpy(key_nonce + 16, cipher_ctx->nonce, 16); + true_key = crypto_md5(key_nonce, 32, NULL); + nonce_len = 0; + } else { + true_key = cipher->key; + } + + cipher_evp_t *evp = cipher_ctx->evp; + if (evp == NULL) { + LOGE("cipher_ctx_set_nonce(): Cipher context is null"); + return; + } + if (mbedtls_cipher_setkey(evp, true_key, cipher->key_len * 8, enc) != 0) { + mbedtls_cipher_free(evp); + FATAL("Cannot set mbed TLS cipher key"); + } + if (mbedtls_cipher_set_iv(evp, nonce, nonce_len) != 0) { + mbedtls_cipher_free(evp); + FATAL("Cannot set mbed TLS cipher NONCE"); + } + if (mbedtls_cipher_reset(evp) != 0) { + mbedtls_cipher_free(evp); + FATAL("Cannot finalize mbed TLS cipher context"); + } + +#ifdef DEBUG + dump("NONCE", (char *)nonce, nonce_len); +#endif +} + +static int +cipher_ctx_update(cipher_ctx_t *ctx, uint8_t *output, size_t *olen, + const uint8_t *input, size_t ilen) +{ + cipher_evp_t *evp = ctx->evp; + return mbedtls_cipher_update(evp, (const uint8_t *)input, ilen, + (uint8_t *)output, olen); +} + +int +stream_encrypt_all(buffer_t *plaintext, cipher_t *cipher, size_t capacity) +{ + cipher_ctx_t cipher_ctx; + stream_ctx_init(cipher, &cipher_ctx, 1); + + size_t nonce_len = cipher->nonce_len; + int err = CRYPTO_OK; + + static buffer_t tmp = { 0, 0, 0, NULL }; + brealloc(&tmp, nonce_len + plaintext->len, capacity); + buffer_t *ciphertext = &tmp; + ciphertext->len = plaintext->len; + + uint8_t nonce[MAX_NONCE_LENGTH]; + + rand_bytes(nonce, nonce_len); + cipher_ctx_set_nonce(&cipher_ctx, nonce, nonce_len, 1); + memcpy(ciphertext->data, nonce, nonce_len); + + if (cipher->method >= SALSA20) { + crypto_stream_xor_ic((uint8_t *)(ciphertext->data + nonce_len), + (const uint8_t *)plaintext->data, (uint64_t)(plaintext->len), + (const uint8_t *)nonce, + 0, cipher->key, cipher->method); + } else { + err = cipher_ctx_update(&cipher_ctx, (uint8_t *)(ciphertext->data + nonce_len), + &ciphertext->len, (const uint8_t *)plaintext->data, + plaintext->len); + } + + if (err) { + bfree(plaintext); + stream_ctx_release(&cipher_ctx); + return CRYPTO_ERROR; + } + +#ifdef DEBUG + dump("PLAIN", plaintext->data, plaintext->len); + dump("CIPHER", ciphertext->data + nonce_len, ciphertext->len); +#endif + + stream_ctx_release(&cipher_ctx); + + brealloc(plaintext, nonce_len + ciphertext->len, capacity); + memcpy(plaintext->data, ciphertext->data, nonce_len + ciphertext->len); + plaintext->len = nonce_len + ciphertext->len; + + return CRYPTO_OK; +} + +int +stream_encrypt(buffer_t *plaintext, cipher_ctx_t *cipher_ctx, size_t capacity) +{ + if (cipher_ctx == NULL) + return CRYPTO_ERROR; + + cipher_t *cipher = cipher_ctx->cipher; + + static buffer_t tmp = { 0, 0, 0, NULL }; + + int err = CRYPTO_OK; + size_t nonce_len = 0; + if (!cipher_ctx->init) { + nonce_len = cipher_ctx->cipher->nonce_len; + } + + brealloc(&tmp, nonce_len + plaintext->len, capacity); + buffer_t *ciphertext = &tmp; + ciphertext->len = plaintext->len; + + if (!cipher_ctx->init) { + cipher_ctx_set_nonce(cipher_ctx, cipher_ctx->nonce, nonce_len, 1); + memcpy(ciphertext->data, cipher_ctx->nonce, nonce_len); + cipher_ctx->counter = 0; + cipher_ctx->init = 1; + } + + if (cipher->method >= SALSA20) { + int padding = cipher_ctx->counter % SODIUM_BLOCK_SIZE; + brealloc(ciphertext, nonce_len + (padding + ciphertext->len) * 2, capacity); + if (padding) { + brealloc(plaintext, plaintext->len + padding, capacity); + memmove(plaintext->data + padding, plaintext->data, plaintext->len); + sodium_memzero(plaintext->data, padding); + } + crypto_stream_xor_ic((uint8_t *)(ciphertext->data + nonce_len), + (const uint8_t *)plaintext->data, + (uint64_t)(plaintext->len + padding), + (const uint8_t *)cipher_ctx->nonce, + cipher_ctx->counter / SODIUM_BLOCK_SIZE, cipher->key, + cipher->method); + cipher_ctx->counter += plaintext->len; + if (padding) { + memmove(ciphertext->data + nonce_len, + ciphertext->data + nonce_len + padding, ciphertext->len); + } + } else { + err = cipher_ctx_update(cipher_ctx, + (uint8_t *)(ciphertext->data + nonce_len), + &ciphertext->len, (const uint8_t *)plaintext->data, + plaintext->len); + if (err) { + return CRYPTO_ERROR; + } + } + +#ifdef DEBUG + dump("PLAIN", plaintext->data, plaintext->len); + dump("CIPHER", ciphertext->data + nonce_len, ciphertext->len); +#endif + + brealloc(plaintext, nonce_len + ciphertext->len, capacity); + memcpy(plaintext->data, ciphertext->data, nonce_len + ciphertext->len); + plaintext->len = nonce_len + ciphertext->len; + + return CRYPTO_OK; +} + +int +stream_decrypt_all(buffer_t *ciphertext, cipher_t *cipher, size_t capacity) +{ + size_t nonce_len = cipher->nonce_len; + int err = CRYPTO_OK; + + if (ciphertext->len <= nonce_len) { + return CRYPTO_ERROR; + } + + cipher_ctx_t cipher_ctx; + stream_ctx_init(cipher, &cipher_ctx, 0); + + static buffer_t tmp = { 0, 0, 0, NULL }; + brealloc(&tmp, ciphertext->len, capacity); + buffer_t *plaintext = &tmp; + plaintext->len = ciphertext->len - nonce_len; + + uint8_t nonce[MAX_NONCE_LENGTH]; + memcpy(nonce, ciphertext->data, nonce_len); + cipher_ctx_set_nonce(&cipher_ctx, nonce, nonce_len, 0); + + if (cipher->method >= SALSA20) { + crypto_stream_xor_ic((uint8_t *)plaintext->data, + (const uint8_t *)(ciphertext->data + nonce_len), + (uint64_t)(ciphertext->len - nonce_len), + (const uint8_t *)nonce, 0, cipher->key, cipher->method); + } else { + err = cipher_ctx_update(&cipher_ctx, (uint8_t *)plaintext->data, &plaintext->len, + (const uint8_t *)(ciphertext->data + nonce_len), + ciphertext->len - nonce_len); + } + + if (err) { + bfree(ciphertext); + stream_ctx_release(&cipher_ctx); + return CRYPTO_ERROR; + } + +#ifdef DEBUG + dump("PLAIN", plaintext->data, plaintext->len); + dump("CIPHER", ciphertext->data + nonce_len, ciphertext->len - nonce_len); +#endif + + stream_ctx_release(&cipher_ctx); + + brealloc(ciphertext, plaintext->len, capacity); + memcpy(ciphertext->data, plaintext->data, plaintext->len); + ciphertext->len = plaintext->len; + + return CRYPTO_OK; +} + +int +stream_decrypt(buffer_t *ciphertext, cipher_ctx_t *cipher_ctx, size_t capacity) +{ + if (cipher_ctx == NULL) return -1; + + cipher_t *cipher = cipher_ctx->cipher; + + static buffer_t tmp = { 0, 0, 0, NULL }; + + size_t nonce_len = 0; + int err = CRYPTO_OK; + + brealloc(&tmp, ciphertext->len, capacity); + buffer_t *plaintext = &tmp; + plaintext->len = ciphertext->len; + + if (!cipher_ctx->init) { + if (plaintext->len <= nonce_len) return CRYPTO_ERROR; + + uint8_t nonce[MAX_NONCE_LENGTH]; + nonce_len = cipher->nonce_len; + plaintext->len -= nonce_len; + + memcpy(nonce, ciphertext->data, nonce_len); + cipher_ctx_set_nonce(cipher_ctx, nonce, nonce_len, 0); + cipher_ctx->counter = 0; + cipher_ctx->init = 1; + + if (cipher->method >= RC4_MD5) { + if (cache_key_exist(nonce_cache, (char *)nonce, nonce_len)) { + bfree(ciphertext); + return -1; + } else { + cache_insert(nonce_cache, (char *)nonce, nonce_len, NULL); + } + } + } + + if (cipher->method >= SALSA20) { + int padding = cipher_ctx->counter % SODIUM_BLOCK_SIZE; + brealloc(plaintext, (plaintext->len + padding) * 2, capacity); + + if (padding) { + brealloc(ciphertext, ciphertext->len + padding, capacity); + memmove(ciphertext->data + nonce_len + padding, ciphertext->data + nonce_len, + ciphertext->len - nonce_len); + sodium_memzero(ciphertext->data + nonce_len, padding); + } + crypto_stream_xor_ic((uint8_t *)plaintext->data, + (const uint8_t *)(ciphertext->data + nonce_len), + (uint64_t)(ciphertext->len - nonce_len + padding), + (const uint8_t *)cipher_ctx->nonce, + cipher_ctx->counter / SODIUM_BLOCK_SIZE, cipher->key, + cipher->method); + cipher_ctx->counter += ciphertext->len - nonce_len; + if (padding) { + memmove(plaintext->data, plaintext->data + padding, plaintext->len); + } + } else { + err = cipher_ctx_update(cipher_ctx, (uint8_t *)plaintext->data, &plaintext->len, + (const uint8_t *)(ciphertext->data + nonce_len), + ciphertext->len - nonce_len); + } + + if (err) { + bfree(ciphertext); + return CRYPTO_ERROR; + } + +#ifdef DEBUG + dump("PLAIN", plaintext->data, plaintext->len); + dump("CIPHER", ciphertext->data + nonce_len, ciphertext->len - nonce_len); +#endif + + brealloc(ciphertext, plaintext->len, capacity); + memcpy(ciphertext->data, plaintext->data, plaintext->len); + ciphertext->len = plaintext->len; + + return CRYPTO_OK; +} + +void +stream_ctx_init(cipher_t *cipher, cipher_ctx_t *cipher_ctx, int enc) +{ + sodium_memzero(cipher_ctx, sizeof(cipher_ctx_t)); + stream_cipher_ctx_init(cipher_ctx, cipher->method, enc); + cipher_ctx->cipher = cipher; + + if (enc) { + rand_bytes(cipher_ctx->nonce, cipher->nonce_len); + } +} + +void +stream_ctx_release(cipher_ctx_t *cipher_ctx) +{ + if (cipher_ctx->cipher->method >= SALSA20) { + return; + } + + mbedtls_cipher_free(cipher_ctx->evp); + ss_free(cipher_ctx->evp); +} + +cipher_t* +stream_key_init(int method, const char *pass) +{ + if (method <= TABLE || method >= STREAM_CIPHER_NUM) { + LOGE("cipher->key_init(): Illegal method"); + return NULL; + } + + cipher_t *cipher = (cipher_t *)ss_malloc(sizeof(cipher_t)); + memset(cipher, 0, sizeof(cipher_t)); + + if (method == SALSA20 || method == CHACHA20 || method == CHACHA20IETF) { + cipher_kt_t *cipher_info = (cipher_kt_t *)ss_malloc(sizeof(cipher_kt_t)); + cipher->info = cipher_info; + cipher->info->base = NULL; + cipher->info->key_bitlen = supported_stream_ciphers_key_size[method] * 8; + cipher->info->iv_size = supported_stream_ciphers_nonce_size[method]; + } else { + cipher->info = (cipher_kt_t *)stream_get_cipher_type(method); + } + + if (cipher->info == NULL && cipher->key_len == 0) { + LOGE("Cipher %s not found in crypto library", supported_stream_ciphers[method]); + FATAL("Cannot initialize cipher"); + } + + cipher->key_len = crypto_derive_key(cipher, (const uint8_t *)pass, + cipher->key, cipher_key_size(cipher)); + + if (cipher->key_len == 0) { + FATAL("Cannot generate key and NONCE"); + } + if (method == RC4_MD5) { + cipher->nonce_len = 16; + } else { + cipher->nonce_len = cipher_nonce_size(cipher); + } + cipher->method = method; + + return cipher; +} + +cipher_t * +stream_init(const char *pass, const char *method) +{ + int m = TABLE; + if (method != NULL) { + for (m = TABLE; m < STREAM_CIPHER_NUM; m++) + if (strcmp(method, supported_stream_ciphers[m]) == 0) { + break; + } + if (m >= STREAM_CIPHER_NUM) { + LOGE("Invalid cipher name: %s, use rc4-md5 instead", method); + m = RC4_MD5; + } + } + if (m == TABLE) { + LOGE("Table is deprecated"); + return NULL; + } + return stream_key_init(m, pass); +} + diff --git a/src/stream.h b/src/stream.h new file mode 100644 index 00000000..316d40e5 --- /dev/null +++ b/src/stream.h @@ -0,0 +1,56 @@ +/* + * encrypt.h - Define the enryptor's interface + * + * Copyright (C) 2013 - 2017, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#ifndef _STREAM_H +#define _STREAM_H + +#include +#include +#include +#include +#include +#ifdef HAVE_STDINT_H +#include +#elif HAVE_INTTYPES_H +#include +#endif + +#include +#if SODIUM_LIBRARY_VERSION_MAJOR >= 8 +#define STREAM_CIPHER_NUM 21 +#else +#define STREAM_CIPHER_NUM 20 +#endif + +#include "crypto.h" + +int stream_encrypt_all(buffer_t *, cipher_t *, size_t); +int stream_decrypt_all(buffer_t *, cipher_t *, size_t); +int stream_encrypt(buffer_t *, cipher_ctx_t *, size_t); +int stream_decrypt(buffer_t *, cipher_ctx_t *, size_t); + +void stream_ctx_init(cipher_t *, cipher_ctx_t *, int); +void stream_ctx_release(cipher_ctx_t *); + +cipher_t *stream_init(const char *pass, const char *method); + +#endif // _STREAM_H diff --git a/src/tunnel.c b/src/tunnel.c index ca04c6e0..e171d1cd 100644 --- a/src/tunnel.c +++ b/src/tunnel.c @@ -1,7 +1,7 @@ /* * tunnel.c - Setup a local port forwarding through remote shadowsocks server * - * Copyright (C) 2013 - 2016, Max Lv + * Copyright (C) 2013 - 2017, Max Lv * * This file is part of the shadowsocks-libev. * @@ -73,7 +73,7 @@ static void remote_recv_cb(EV_P_ ev_io *w, int revents); static void remote_send_cb(EV_P_ ev_io *w, int revents); static remote_t *new_remote(int fd, int timeout); -static server_t *new_server(int fd, int method); +static server_t *new_server(int fd); static void free_remote(remote_t *remote); static void close_and_free_remote(EV_P_ remote_t *remote); @@ -87,9 +87,10 @@ int vpn = 0; int verbose = 0; int keep_resolving = 1; +static crypto_t *crypto; + static int ipv6first = 0; static int mode = TCP_ONLY; -static int auth = 0; #ifdef HAVE_SETRLIMIT static int nofile = 0; #endif @@ -196,11 +197,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents) remote->buf->len = r; - if (auth) { - ss_gen_hash(remote->buf, &remote->counter, server->e_ctx, BUF_SIZE); - } - - int err = ss_encrypt(remote->buf, server->e_ctx, BUF_SIZE); + int err = crypto->encrypt(remote->buf, server->e_ctx, BUF_SIZE); if (err) { LOGE("invalid password or cipher"); @@ -324,7 +321,7 @@ remote_recv_cb(EV_P_ ev_io *w, int revents) server->buf->len = r; - int err = ss_decrypt(server->buf, server->d_ctx, BUF_SIZE); + int err = crypto->decrypt(server->buf, server->d_ctx, BUF_SIZE); if (err) { LOGE("invalid password or cipher"); @@ -428,12 +425,7 @@ remote_send_cb(EV_P_ ev_io *w, int revents) memcpy(abuf->data + abuf->len, &port, 2); abuf->len += 2; - if (auth) { - abuf->data[0] |= ONETIMEAUTH_FLAG; - ss_onetimeauth(abuf, server->e_ctx->evp.iv, BUF_SIZE); - } - - int err = ss_encrypt(abuf, server->e_ctx, BUF_SIZE); + int err = crypto->encrypt(abuf, server->e_ctx, BUF_SIZE); if (err) { bfree(abuf); @@ -554,7 +546,7 @@ close_and_free_remote(EV_P_ remote_t *remote) } static server_t * -new_server(int fd, int method) +new_server(int fd) { server_t *server = ss_malloc(sizeof(server_t)); memset(server, 0, sizeof(server_t)); @@ -571,15 +563,10 @@ new_server(int fd, int method) server->send_ctx->server = server; server->send_ctx->connected = 0; - if (method) { - server->e_ctx = ss_malloc(sizeof(struct enc_ctx)); - server->d_ctx = ss_malloc(sizeof(struct enc_ctx)); - enc_ctx_init(method, server->e_ctx, 1); - enc_ctx_init(method, server->d_ctx, 0); - } else { - server->e_ctx = NULL; - server->d_ctx = NULL; - } + server->e_ctx = ss_malloc(sizeof(cipher_ctx_t)); + server->d_ctx = ss_malloc(sizeof(cipher_ctx_t)); + crypto->ctx_init(crypto->cipher, server->e_ctx, 1); + crypto->ctx_init(crypto->cipher, server->d_ctx, 0); ev_io_init(&server->recv_ctx->io, server_recv_cb, fd, EV_READ); ev_io_init(&server->send_ctx->io, server_send_cb, fd, EV_WRITE); @@ -594,11 +581,11 @@ free_server(server_t *server) server->remote->server = NULL; } if (server->e_ctx != NULL) { - cipher_context_release(&server->e_ctx->evp); + crypto->ctx_release(server->e_ctx); ss_free(server->e_ctx); } if (server->d_ctx != NULL) { - cipher_context_release(&server->d_ctx->evp); + crypto->ctx_release(server->d_ctx); ss_free(server->d_ctx); } if (server->buf) { @@ -685,7 +672,7 @@ accept_cb(EV_P_ ev_io *w, int revents) } #endif - server_t *server = new_server(serverfd, listener->method); + server_t *server = new_server(serverfd); remote_t *remote = new_remote(remotefd, listener->timeout); server->destaddr = listener->tunnel_addr; server->remote = remote; @@ -773,10 +760,10 @@ main(int argc, char **argv) USE_TTY(); #ifdef ANDROID - while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:i:c:b:L:a:n:huUvVA6", + while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:i:c:b:L:a:n:huUvV6", long_options, &option_index)) != -1) { #else - while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:i:c:b:L:a:n:huUvA6", + while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:i:c:b:L:a:n:huUv6", long_options, &option_index)) != -1) { #endif switch (c) { @@ -853,9 +840,6 @@ main(int argc, char **argv) case 'h': usage(); exit(EXIT_SUCCESS); - case 'A': - auth = 1; - break; case '6': ipv6first = 1; break; @@ -917,9 +901,6 @@ main(int argc, char **argv) if (plugin_opts == NULL) { plugin_opts = conf->plugin_opts; } - if (auth == 0) { - auth = conf->auth; - } if (tunnel_addr_str == NULL) { tunnel_addr_str = conf->tunnel_address; } @@ -991,10 +972,6 @@ main(int argc, char **argv) LOGI("resolving hostname to IPv6 address first"); } - if (auth) { - LOGI("onetime authentication enabled"); - } - // parse tunnel addr parse_addr(tunnel_addr_str, &tunnel_addr); @@ -1032,7 +1009,9 @@ main(int argc, char **argv) // Setup keys LOGI("initializing ciphers... %s", method); - int m = enc_init(password, method); + crypto = crypto_init(password, method); + if (crypto == NULL) + FATAL("failed to initialize ciphers"); // Setup proxy context struct listen_ctx listen_ctx; @@ -1059,7 +1038,6 @@ main(int argc, char **argv) } listen_ctx.timeout = atoi(timeout); listen_ctx.iface = iface; - listen_ctx.method = m; listen_ctx.mptcp = mptcp; struct ev_loop *loop = EV_DEFAULT; @@ -1094,7 +1072,7 @@ main(int argc, char **argv) } struct sockaddr *addr = (struct sockaddr *)storage; init_udprelay(local_addr, local_port, addr, get_sockaddr_len(addr), - tunnel_addr, mtu, m, auth, listen_ctx.timeout, iface); + tunnel_addr, mtu, crypto, listen_ctx.timeout, iface); } if (mode == UDP_ONLY) { diff --git a/src/tunnel.h b/src/tunnel.h index cd41e565..93739a4b 100644 --- a/src/tunnel.h +++ b/src/tunnel.h @@ -1,7 +1,7 @@ /* * tunnel.h - Define tunnel's buffers and callbacks * - * Copyright (C) 2013 - 2016, Max Lv + * Copyright (C) 2013 - 2017, Max Lv * * This file is part of the shadowsocks-libev. * @@ -24,7 +24,7 @@ #define _TUNNEL_H #include -#include "encrypt.h" +#include "crypto.h" #include "jconf.h" #include "common.h" @@ -34,7 +34,6 @@ typedef struct listen_ctx { ss_addr_t tunnel_addr; char *iface; int remote_num; - int method; int timeout; int fd; int mptcp; @@ -51,8 +50,8 @@ typedef struct server { int fd; buffer_t *buf; - struct enc_ctx *e_ctx; - struct enc_ctx *d_ctx; + cipher_ctx_t *e_ctx; + cipher_ctx_t *d_ctx; struct server_ctx *recv_ctx; struct server_ctx *send_ctx; struct remote *remote; diff --git a/src/udprelay.c b/src/udprelay.c index 4a1a182a..561e5f09 100644 --- a/src/udprelay.c +++ b/src/udprelay.c @@ -1,7 +1,7 @@ /* * udprelay.c - Setup UDP relay for both client and server * - * Copyright (C) 2013 - 2016, Max Lv + * Copyright (C) 2013 - 2017, Max Lv * * This file is part of the shadowsocks-libev. * @@ -681,7 +681,7 @@ remote_recv_cb(EV_P_ ev_io *w, int revents) buf->len = r; #ifdef MODULE_LOCAL - int err = ss_decrypt_all(buf, server_ctx->method, 0, buf_size); + int err = server_ctx->crypto->decrypt_all(buf, server_ctx->crypto->cipher, buf_size); if (err) { // drop the packet silently goto CLEAN_UP; @@ -744,7 +744,7 @@ remote_recv_cb(EV_P_ ev_io *w, int revents) memcpy(buf->data, addr_header, addr_header_len); buf->len += addr_header_len; - int err = ss_encrypt_all(buf, server_ctx->method, 0, buf_size); + int err = server_ctx->crypto->encrypt_all(buf, server_ctx->crypto->cipher, buf_size); if (err) { // drop the packet silently goto CLEAN_UP; @@ -891,7 +891,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents) #ifdef MODULE_REMOTE tx += buf->len; - int err = ss_decrypt_all(buf, server_ctx->method, server_ctx->auth, buf_size); + int err = server_ctx->crypto->decrypt_all(buf, server_ctx->crypto->cipher, buf_size); if (err) { // drop the packet silently goto CLEAN_UP; @@ -1156,11 +1156,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents) memmove(buf->data, buf->data + offset, buf->len); } - if (server_ctx->auth) { - buf->data[0] |= ONETIMEAUTH_FLAG; - } - - int err = ss_encrypt_all(buf, server_ctx->method, server_ctx->auth, buf_size); + int err = server_ctx->crypto->encrypt_all(buf, server_ctx->crypto->cipher, buf_size); if (err) { // drop the packet silently @@ -1310,7 +1306,7 @@ init_udprelay(const char *server_host, const char *server_port, const ss_addr_t tunnel_addr, #endif #endif - int mtu, int method, int auth, int timeout, const char *iface) + int mtu, crypto_t *crypto, int timeout, const char *iface) { // Initialize ev loop struct ev_loop *loop = EV_DEFAULT; @@ -1339,9 +1335,8 @@ init_udprelay(const char *server_host, const char *server_port, #ifdef MODULE_REMOTE server_ctx->loop = loop; #endif - server_ctx->auth = auth; server_ctx->timeout = max(timeout, MIN_UDP_TIMEOUT); - server_ctx->method = method; + server_ctx->crypto = crypto; server_ctx->iface = iface; server_ctx->conn_cache = conn_cache; #ifdef MODULE_LOCAL diff --git a/src/udprelay.h b/src/udprelay.h index 59922256..02050760 100644 --- a/src/udprelay.h +++ b/src/udprelay.h @@ -1,7 +1,7 @@ /* * udprelay.h - Define UDP relay's buffers and callbacks * - * Copyright (C) 2013 - 2016, Max Lv + * Copyright (C) 2013 - 2017, Max Lv * * This file is part of the shadowsocks-libev. * @@ -26,7 +26,7 @@ #include #include -#include "encrypt.h" +#include "crypto.h" #include "jconf.h" #ifdef MODULE_REMOTE @@ -44,8 +44,7 @@ typedef struct server_ctx { ev_io io; int fd; - int method; - int auth; + crypto_t *crypto; int timeout; const char *iface; struct cache *conn_cache; diff --git a/src/utils.c b/src/utils.c index d3e3d676..9670d72d 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1,7 +1,7 @@ /* * utils.c - Misc utilities * - * Copyright (C) 2013 - 2016, Max Lv + * Copyright (C) 2013 - 2017, Max Lv * * This file is part of the shadowsocks-libev. * @@ -243,7 +243,7 @@ void usage() { printf("\n"); - printf("shadowsocks-libev %s with %s\n\n", VERSION, USING_CRYPTO); + printf("shadowsocks-libev %s\n\n", VERSION); printf( " maintained by Max Lv and Linus Yang \n\n"); printf(" usage:\n\n"); diff --git a/src/utils.h b/src/utils.h index 859fcb38..e3b44175 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1,7 +1,7 @@ /* * utils.h - Misc utilities * - * Copyright (C) 2013 - 2016, Max Lv + * Copyright (C) 2013 - 2017, Max Lv * * This file is part of the shadowsocks-libev. * @@ -184,3 +184,4 @@ void *ss_realloc(void *ptr, size_t new_size); } while (0) #endif // _UTILS_H +