diff --git a/shadowsocks/encrypt.py b/shadowsocks/encrypt.py index 2494395..c3fc069 100644 --- a/shadowsocks/encrypt.py +++ b/shadowsocks/encrypt.py @@ -159,6 +159,40 @@ def encrypt_all(password, method, op, data): result.append(cipher.update(data)) return b''.join(result) +def encrypt_key(password, method): + method = method.lower() + (key_len, iv_len, m) = method_supported[method] + if key_len > 0: + key, _ = EVP_BytesToKey(password, key_len, iv_len) + else: + key = password + return key + +def encrypt_iv_len(method): + method = method.lower() + (key_len, iv_len, m) = method_supported[method] + return iv_len + +def encrypt_new_iv(method): + method = method.lower() + (key_len, iv_len, m) = method_supported[method] + return random_string(iv_len) + +def encrypt_all_iv(key, method, op, data, ref_iv): + result = [] + method = method.lower() + (key_len, iv_len, m) = method_supported[method] + if op: + iv = ref_iv[0] + result.append(iv) + else: + iv = data[:iv_len] + data = data[iv_len:] + ref_iv[0] = iv + cipher = m(method, key, iv, op) + result.append(cipher.update(data)) + return b''.join(result) + CIPHERS_TO_TEST = [ 'aes-128-cfb', diff --git a/shadowsocks/obfs.py b/shadowsocks/obfs.py index 94a14f2..6adb855 100644 --- a/shadowsocks/obfs.py +++ b/shadowsocks/obfs.py @@ -85,6 +85,18 @@ class obfs(object): def server_post_decrypt(self, buf): return self.obfs.server_post_decrypt(buf) + def client_udp_pre_encrypt(self, buf): + return self.obfs.client_udp_pre_encrypt(buf) + + def client_udp_post_decrypt(self, buf): + return self.obfs.client_udp_post_decrypt(buf) + + def server_udp_pre_encrypt(self, buf): + return self.obfs.server_udp_pre_encrypt(buf) + + def server_udp_post_decrypt(self, buf): + return self.obfs.server_udp_post_decrypt(buf) + def dispose(self): self.obfs.dispose() del self.obfs diff --git a/shadowsocks/obfsplugin/plain.py b/shadowsocks/obfsplugin/plain.py index 5bba57c..8bb0000 100644 --- a/shadowsocks/obfsplugin/plain.py +++ b/shadowsocks/obfsplugin/plain.py @@ -69,6 +69,18 @@ class plain(object): def server_post_decrypt(self, buf): return buf + def client_udp_pre_encrypt(self, buf): + return buf + + def client_udp_post_decrypt(self, buf): + return buf + + def server_udp_pre_encrypt(self, buf): + return buf + + def server_udp_post_decrypt(self, buf): + return buf + def dispose(self): pass diff --git a/shadowsocks/obfsplugin/verify.py b/shadowsocks/obfsplugin/verify.py index fada8d9..639ba5c 100644 --- a/shadowsocks/obfsplugin/verify.py +++ b/shadowsocks/obfsplugin/verify.py @@ -342,3 +342,19 @@ class verify_sha1(verify_base): return out_buf + def client_udp_pre_encrypt(self, buf): + ret = self.pack_auth_data(buf) + return chr(ord(buf[0]) | 0x10) + buf[1:] + + def server_udp_post_decrypt(self, buf): + if buf and ((ord(buf[0]) & 0x10) == 0x10): + if len(buf) <= 11: + return b'E' + sha1data = hmac.new(self.server_info.recv_iv + self.server_info.key, buf[:-10], hashlib.sha1).digest()[:10] + if sha1data != buf[-10:]: + logging.error('server_udp_post_decrypt data uncorrect auth HMAC-SHA1') + return b'E' + return to_bytes(chr(ord(buf[0]) & 0xEF)) + buf[1:-10] + else: + return buf + diff --git a/shadowsocks/udprelay.py b/shadowsocks/udprelay.py index 80a30eb..d5cab1b 100644 --- a/shadowsocks/udprelay.py +++ b/shadowsocks/udprelay.py @@ -71,7 +71,7 @@ import random import binascii import traceback -from shadowsocks import encrypt, eventloop, lru_cache, common, shell +from shadowsocks import encrypt, obfs, eventloop, lru_cache, common, shell from shadowsocks.common import pre_parse_header, parse_header, pack_addr # we clear at most TIMEOUTS_CLEAN_SIZE timeouts each time @@ -890,6 +890,20 @@ class UDPRelay(object): self.server_transfer_ul = 0 self.server_transfer_dl = 0 + self.protocol_data = obfs.obfs(config['protocol']).init_data() + self._protocol = obfs.obfs(config['protocol']) + server_info = obfs.server_info(self.protocol_data) + server_info.host = self._listen_addr + server_info.port = self._listen_port + server_info.protocol_param = config['protocol_param'] + server_info.obfs_param = '' + server_info.iv = b'' + server_info.recv_iv = b'' + server_info.key = encrypt.encrypt_key(self._password, self._method) + server_info.head_len = 30 + server_info.tcp_mss = 1440 + self._protocol.set_server_info(server_info) + self._sockets = set() self._fd_to_handlers = {} self._reqid_to_hd = {} @@ -991,11 +1005,14 @@ class UDPRelay(object): else: data = data[3:] else: - data = encrypt.encrypt_all(self._password, self._method, 0, data) + ref_iv = [0] + data = encrypt.encrypt_all_iv(self._protocol.obfs.server_info.key, self._method, 0, data, ref_iv) # decrypt data if not data: logging.debug('UDP handle_server: data is empty after decrypt') return + self._protocol.obfs.server_info.recv_iv = ref_iv[0] + data = self._protocol.server_udp_post_decrypt(data) #logging.info("UDP data %s" % (binascii.hexlify(data),)) if not self._is_local: @@ -1121,7 +1138,11 @@ class UDPRelay(object): r_addr[0], r_addr[1])) if self._is_local: - data = encrypt.encrypt_all(self._password, self._method, 1, data) + ref_iv = [encrypt.encrypt_new_iv(self._method)] + self._protocol.obfs.server_info.iv = ref_iv[0] + data = self._protocol.client_udp_pre_encrypt(data) + logging.info("%s" % (binascii.hexlify(data),)) + data = encrypt.encrypt_all_iv(self._protocol.obfs.server_info.key, self._method, 1, data, ref_iv) if not data: return else: @@ -1151,15 +1172,21 @@ class UDPRelay(object): # drop return data = pack_addr(r_addr[0]) + struct.pack('>H', r_addr[1]) + data - response = encrypt.encrypt_all(self._password, self._method, 1, - data) + ref_iv = [encrypt.encrypt_new_iv(self._method)] + self._protocol.obfs.server_info.iv = ref_iv[0] + data = self._protocol.server_udp_pre_encrypt(data) + response = encrypt.encrypt_all_iv(self._protocol.obfs.server_info.key, self._method, 1, + data, ref_iv) if not response: return else: - data = encrypt.encrypt_all(self._password, self._method, 0, - data) + ref_iv = [0] + data = encrypt.encrypt_all_iv(self._protocol.obfs.server_info.key, self._method, 0, + data, ref_iv) if not data: return + self._protocol.obfs.server_info.recv_iv = ref_iv[0] + data = self._protocol.client_udp_post_decrypt(data) header_result = parse_header(data) if header_result is None: return