diff --git a/config.json b/config.json index cbf648b..eaaf50c 100644 --- a/config.json +++ b/config.json @@ -9,7 +9,7 @@ "method": "aes-256-cfb", "protocol": "auth_sha1_compatible", "protocol_param": "", - "obfs": "tls1.0_session_auth_compatible", + "obfs": "http_simple_compatible", "obfs_param": "", "redirect": "", "dns_ipv6": false, diff --git a/shadowsocks/obfsplugin/http_simple.py b/shadowsocks/obfsplugin/http_simple.py index 7b7fe88..4fd5f45 100644 --- a/shadowsocks/obfsplugin/http_simple.py +++ b/shadowsocks/obfsplugin/http_simple.py @@ -119,7 +119,7 @@ class http_simple(plain.plain): header = b'HTTP/1.1 200 OK\r\nConnection: keep-alive\r\nContent-Encoding: gzip\r\nContent-Type: text/html\r\nDate: ' header += to_bytes(datetime.datetime.now().strftime('%a, %d %b %Y %H:%M:%S GMT')) - header += b'\r\nServer: nginx\r\nTransfer-Encoding: chunked\r\nVary: Accept-Encoding\r\n\r\n' + header += b'\r\nServer: nginx\r\nVary: Accept-Encoding\r\n\r\n' self.has_sent_header = True return header + buf diff --git a/shadowsocks/obfsplugin/obfs_tls.py b/shadowsocks/obfsplugin/obfs_tls.py index 00be536..5462639 100644 --- a/shadowsocks/obfsplugin/obfs_tls.py +++ b/shadowsocks/obfsplugin/obfs_tls.py @@ -28,6 +28,7 @@ import time import random import hmac import hashlib +import string from shadowsocks import common from shadowsocks.obfsplugin import plain @@ -40,11 +41,16 @@ def create_tls_obfs(method): def create_tls_auth_obfs(method): return tls_auth(method) +def create_tls_ticket_auth_obfs(method): + return tls_ticket_auth(method) + obfs_map = { 'tls_simple': (create_tls_obfs,), 'tls_simple_compatible': (create_tls_obfs,), 'tls1.0_session_auth': (create_tls_auth_obfs,), 'tls1.0_session_auth_compatible': (create_tls_auth_obfs,), + 'tls1.2_ticket_auth': (create_tls_ticket_auth_obfs,), + 'tls1.2_ticket_auth_compatible': (create_tls_ticket_auth_obfs,), } def match_begin(str1, str2): @@ -268,3 +274,196 @@ class tls_auth(plain.plain): # (buffer_to_recv, is_need_decrypt, is_need_to_encode_and_send_back) return (b'', False, True) +class tls_ticket_auth(plain.plain): + def __init__(self, method): + self.method = method + self.handshake_status = 0 + self.send_buffer = b'' + self.recv_buffer = b'' + self.client_id = b'' + self.max_time_dif = 60 * 60 # time dif (second) setting + self.tls_version = b'\x03\x03' + + def init_data(self): + return obfs_auth_data() + + def sni(self, url): + url = common.to_bytes(url) + data = b"\x00" + struct.pack('>H', len(url)) + url + data = b"\x00\x00" + struct.pack('>H', len(data) + 2) + struct.pack('>H', len(data)) + data + return data + + def pack_auth_data(self, client_id): + utc_time = int(time.time()) & 0xFFFFFFFF + data = struct.pack('>I', utc_time) + os.urandom(18) + data += hmac.new(self.server_info.key + client_id, data, hashlib.sha1).digest()[:10] + return data + + def client_encode(self, buf): + if self.handshake_status == -1: + return buf + if self.handshake_status == 8: + return b"\x17" + self.tls_version + struct.pack('>H', len(buf)) + buf + self.send_buffer += b"\x17" + self.tls_version + struct.pack('>H', len(buf)) + buf + if self.handshake_status == 0: + self.handshake_status = 1 + data = self.tls_version + self.pack_auth_data(self.server_info.data.client_id) + b"\x20" + self.server_info.data.client_id + binascii.unhexlify(b"001cc02bc02fcca9cca8cc14cc13c00ac014c009c013009c0035002f000a" + b"0100") + ext = binascii.unhexlify(b"ff01000100") + host = self.server_info.obfs_param or self.server_info.host + if host and host[-1] in string.digits: + host = '' + ext += self.sni(host) + ext += b"\x00\x17\x00\x00" + ext += b"\x00\x23\x00\xd0" + os.urandom(208) # ticket + ext += binascii.unhexlify(b"000d001600140601060305010503040104030301030302010203") + ext += binascii.unhexlify(b"000500050100000000") + ext += binascii.unhexlify(b"00120000") + ext += binascii.unhexlify(b"75500000") + ext += binascii.unhexlify(b"000b00020100") + ext += binascii.unhexlify(b"000a0006000400170018") + data += struct.pack('>H', len(ext)) + ext + data = b"\x01\x00" + struct.pack('>H', len(data)) + data + data = b"\x16\x03\x01" + struct.pack('>H', len(data)) + data + return data + elif self.handshake_status == 1 and len(buf) == 0: + data = b"\x14" + self.tls_version + "\x00\x01\x01" #ChangeCipherSpec + data += b"\x16" + self.tls_version + "\x00\x20" + os.urandom(22) #Finished + data += hmac.new(self.server_info.key + self.server_info.data.client_id, data, hashlib.sha1).digest()[:10] + ret = data + self.send_buffer + self.send_buffer = b'' + self.handshake_status = 8 + return ret + return b'' + + def client_decode(self, buf): + if self.handshake_status == -1: + return (buf, False) + + if self.handshake_status == 8: + ret = b'' + self.recv_buffer += buf + while len(self.recv_buffer) > 5: + if ord(self.recv_buffer[0]) != 0x17: + logging.error("data = %s" % (binascii.hexlify(self.recv_buffer))) + raise Exception('server_decode appdata error') + size = struct.unpack('>H', self.recv_buffer[3:5])[0] + if len(self.recv_buffer) < size + 5: + break + buf = self.recv_buffer[5:size+5] + ret += buf + self.recv_buffer = self.recv_buffer[size+5:] + return (ret, False) + + if len(buf) < 11 + 32 + 1 + 32: + raise Exception('client_decode data error') + verify = buf[11:33] + if hmac.new(self.server_info.key + self.server_info.data.client_id, verify, hashlib.sha1).digest()[:10] != buf[33:43]: + raise Exception('client_decode data error') + return (b'', True) + + def server_encode(self, buf): + if self.handshake_status == -1: + return buf + if self.handshake_status == 8: + ret = b'' + while len(buf) > 8192: + ret += b"\x17" + self.tls_version + struct.pack('>H', 8192) + buf[:8192] + buf = buf[8192:] + if len(buf) > 0: + ret += b"\x17" + self.tls_version + struct.pack('>H', len(buf)) + buf + return ret + self.handshake_status = 3 + data = self.tls_version + self.pack_auth_data(self.client_id) + b"\x20" + self.client_id + binascii.unhexlify(b"c02f000005ff01000100") + data = b"\x02\x00" + struct.pack('>H', len(data)) + data #server hello + data = b"\x16\x03\x03" + struct.pack('>H', len(data)) + data + data += b"\x14" + self.tls_version + "\x00\x01\x01" #ChangeCipherSpec + data += b"\x16" + self.tls_version + "\x00\x20" + os.urandom(22) #Finished + data += hmac.new(self.server_info.key + self.client_id, data, hashlib.sha1).digest()[:10] + return data + + def decode_error_return(self, buf): + self.handshake_status = -1 + if self.method == 'tls1.2_session_auth': + return (b'E', False, False) + return (buf, True, False) + + def server_decode(self, buf): + if self.handshake_status == -1: + return (buf, True, False) + + if self.handshake_status == 8: + ret = b'' + self.recv_buffer += buf + while len(self.recv_buffer) > 5: + if ord(self.recv_buffer[0]) != 0x17: + logging.error("data = %s" % (binascii.hexlify(self.recv_buffer))) + raise Exception('server_decode appdata error') + size = struct.unpack('>H', self.recv_buffer[3:5])[0] + if len(self.recv_buffer) < size + 5: + break + ret += self.recv_buffer[5:size+5] + self.recv_buffer = self.recv_buffer[size+5:] + return (ret, True, False) + + if self.handshake_status == 3: + verify = buf + verify_len = 43 - 10 + if len(buf) < 43: + raise Exception('server_decode data error') + if not match_begin(buf, b"\x14" + self.tls_version + "\x00\x01\x01"): #ChangeCipherSpec + raise Exception('server_decode data error') + buf = buf[6:] + if not match_begin(buf, b"\x16" + self.tls_version + "\x00\x20"): #Finished + raise Exception('server_decode data error') + if hmac.new(self.server_info.key + self.client_id, verify[:verify_len], hashlib.sha1).digest()[:10] != verify[verify_len:verify_len+10]: + raise Exception('server_decode data error') + if len(buf) < 37: + raise Exception('server_decode data error') + self.recv_buffer = buf[37:] + self.handshake_status = 8 + return self.server_decode(b'') + + self.handshake_status = 2 + ogn_buf = buf + if not match_begin(buf, b'\x16\x03\x01'): + return self.decode_error_return(ogn_buf) + buf = buf[3:] + if struct.unpack('>H', buf[:2])[0] != len(buf) - 2: + return self.decode_error_return(ogn_buf) + buf = buf[2:] + if not match_begin(buf, b'\x01\x00'): #client hello + return self.decode_error_return(ogn_buf) + buf = buf[2:] + if struct.unpack('>H', buf[:2])[0] != len(buf) - 2: + return self.decode_error_return(ogn_buf) + buf = buf[2:] + if not match_begin(buf, self.tls_version): + return self.decode_error_return(ogn_buf) + buf = buf[2:] + verifyid = buf[:32] + buf = buf[32:] + sessionid_len = ord(buf[0]) + if sessionid_len < 32: + logging.error("tls_auth wrong sessionid_len") + return self.decode_error_return(ogn_buf) + sessionid = buf[1:sessionid_len + 1] + buf = buf[sessionid_len+1:] + self.client_id = sessionid + sha1 = hmac.new(self.server_info.key + sessionid, verifyid[:22], hashlib.sha1).digest()[:10] + utc_time = struct.unpack('>I', verifyid[:4])[0] + time_dif = common.int32((int(time.time()) & 0xffffffff) - utc_time) + if time_dif < -self.max_time_dif or time_dif > self.max_time_dif \ + or common.int32(utc_time - self.server_info.data.startup_time) < -self.max_time_dif / 2: + logging.debug("tls_auth wrong time") + return self.decode_error_return(ogn_buf) + if sha1 != verifyid[22:]: + logging.debug("tls_auth wrong sha1") + return self.decode_error_return(ogn_buf) + if self.server_info.data.client_data.get(verifyid[:22]): + logging.error("replay attack detect, id = %s" % (binascii.hexlify(verifyid))) + return self.decode_error_return(ogn_buf) + self.server_info.data.client_data.sweep() + self.server_info.data.client_data[verifyid[:22]] = sessionid + # (buffer_to_recv, is_need_decrypt, is_need_to_encode_and_send_back) + return (b'', False, True) + diff --git a/shadowsocks/tcprelay.py b/shadowsocks/tcprelay.py index c07b28c..c77faf5 100644 --- a/shadowsocks/tcprelay.py +++ b/shadowsocks/tcprelay.py @@ -282,9 +282,6 @@ class TCPRelayHandler(object): if sock == self._remote_sock: self._server.server_transfer_ul += len(data) self._update_activity(len(data)) - elif not self._is_local and self._obfs is not None: - obfs_encode = self._obfs.server_encode(data) - data = obfs_encode if data: l = len(data) s = sock.send(data) @@ -612,7 +609,8 @@ class TCPRelayHandler(object): self.destroy() return if obfs_decode[2]: - self._write_to_sock(b'', self._local_sock) + data = self._obfs.server_encode(b'') + self._write_to_sock(data, self._local_sock) if obfs_decode[1]: if not self._protocol.obfs.server_info.recv_iv: iv_len = len(self._protocol.obfs.server_info.iv) @@ -704,6 +702,7 @@ class TCPRelayHandler(object): if self._encrypt_correct: data = self._protocol.server_pre_encrypt(data) data = self._encryptor.encrypt(data) + data = self._obfs.server_encode(data) self._update_activity(len(data)) self._server.server_transfer_dl += len(data) else: