From e81f8db0b8e2baec15b8db53d9b13ddd5647f681 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A0=B4=E5=A8=83=E9=85=B1?= Date: Wed, 31 Aug 2016 18:04:52 +0800 Subject: [PATCH] add auth_sha1_v3, remove tls1.0_session_auth --- mujson_mgr.py | 32 ++++- shadowsocks/obfsplugin/auth.py | 216 ++++++++++++++++++++++++++++- shadowsocks/obfsplugin/obfs_tls.py | 147 -------------------- 3 files changed, 240 insertions(+), 155 deletions(-) diff --git a/mujson_mgr.py b/mujson_mgr.py index af7befc..e106a9a 100644 --- a/mujson_mgr.py +++ b/mujson_mgr.py @@ -92,7 +92,7 @@ class MuMgr(object): def add(self, user): up = {'enable': True, 'u': 0, 'd': 0, 'method': "aes-128-cfb", - 'protocol': "auth_sha1_v2_compatible", + 'protocol': "auth_sha1_v3_compatible", 'obfs': "tls1.2_ticket_auth_compatible", 'transfer_enable': 1125899906842624} up['passwd'] = self.rand_pass() @@ -206,17 +206,32 @@ def main(): fast_set_obfs = {'0': 'plain', '1': 'http_simple_compatible', '-1': 'http_simple', - '2': 'http_post_compatible', - '-2': 'http_post', - '3': 'tls1.2_ticket_auth_compatible', - '-3': 'tls1.2_ticket_auth'} + '2': 'tls1.2_ticket_auth_compatible', + '-2': 'tls1.2_ticket_auth'} fast_set_protocol = {'0': 'origin', '1': 'verify_sha1_compatible', '-1': 'verify_sha1', '2': 'auth_sha1_compatible', '-2': 'auth_sha1', '3': 'auth_sha1_v2_compatible', - '-3': 'auth_sha1_v2'} + '-3': 'auth_sha1_v2', + '4': 'auth_sha1_v3_compatible', + '-4': 'auth_sha1_v3'} + fast_set_method = {'a0': 'aes-128-cfb', + 'a1': 'aes-192-cfb', + 'a2': 'aes-256-cfb', + 'r': 'rc4-md5', + 'r6': 'rc4-md5-6', + 'c': 'chacha20', + 'ci': 'chacha20-ietf', + 's': 'salsa20', + 'b': 'bf-cfb', + 'm0': 'camellia-128-cfb', + 'm1': 'camellia-192-cfb', + 'm2': 'camellia-256-cfb', + 'a0t': 'aes-128-ctr', + 'a1t': 'aes-192-ctr', + 'a2t': 'aes-256-ctr'} try: optlist, args = getopt.getopt(sys.argv[1:], shortopts, longopts) for key, value in optlist: @@ -251,7 +266,10 @@ def main(): elif key == '-G': user['protocol_param'] = value elif key == '-m': - user['method'] = value + if value in fast_set_method: + user['method'] = fast_set_method[value] + else: + user['method'] = value elif key == '-f': user['forbidden_port'] = value elif key == '-t': diff --git a/shadowsocks/obfsplugin/auth.py b/shadowsocks/obfsplugin/auth.py index 012f658..23b7cca 100644 --- a/shadowsocks/obfsplugin/auth.py +++ b/shadowsocks/obfsplugin/auth.py @@ -42,11 +42,16 @@ def create_auth_sha1(method): def create_auth_sha1_v2(method): return auth_sha1_v2(method) +def create_auth_sha1_v3(method): + return auth_sha1_v3(method) + obfs_map = { 'auth_sha1': (create_auth_sha1,), 'auth_sha1_compatible': (create_auth_sha1,), 'auth_sha1_v2': (create_auth_sha1_v2,), 'auth_sha1_v2_compatible': (create_auth_sha1_v2,), + 'auth_sha1_v3': (create_auth_sha1_v3,), + 'auth_sha1_v3_compatible': (create_auth_sha1_v3,), } def match_begin(str1, str2): @@ -582,7 +587,216 @@ class auth_sha1_v2(verify_base): break if struct.pack('H', self.recv_buf[3:5])[0] + 2 + out_buf += self.recv_buf[pos:length - 4] + self.recv_buf = self.recv_buf[length:] + if pos == length - 4: + sendback = True + + if out_buf: + self.server_info.data.update(self.client_id, self.connection_id) + self.decrypt_packet_num += 1 + return (out_buf, sendback) + +class auth_sha1_v3(verify_base): + def __init__(self, method): + super(auth_sha1_v3, self).__init__(method) + self.recv_buf = b'' + self.unit_len = 8100 + self.decrypt_packet_num = 0 + self.raw_trans = False + self.has_sent_header = False + self.has_recv_header = False + self.client_id = 0 + self.connection_id = 0 + self.max_time_dif = 60 * 60 * 24 # time dif (second) setting + self.salt = b"auth_sha1_v3" + self.no_compatible_method = 'auth_sha1_v3' + + def init_data(self): + return obfs_auth_v2_data() + + def set_server_info(self, server_info): + self.server_info = server_info + try: + max_client = int(server_info.protocol_param) + except: + max_client = 64 + self.server_info.data.set_max_client(max_client) + + def rnd_data(self, buf_size): + if buf_size > 1200: + return b'\x01' + + if buf_size > 400: + rnd_data = os.urandom(common.ord(os.urandom(1)[0]) % 256) + else: + rnd_data = os.urandom(struct.unpack('>H', os.urandom(2))[0] % 512) + + if len(rnd_data) < 128: + return common.chr(len(rnd_data) + 1) + rnd_data + else: + return common.chr(255) + struct.pack('>H', len(rnd_data) + 3) + rnd_data + + def pack_data(self, buf): + data = self.rnd_data(len(buf)) + buf + data = struct.pack('>H', len(data) + 6) + data + adler32 = zlib.adler32(data) & 0xFFFFFFFF + data += struct.pack('H', data_len)) & 0xFFFFFFFF + data = struct.pack('H', data_len) + data + data += hmac.new(self.server_info.iv + self.server_info.key, data, hashlib.sha1).digest()[:10] + return data + + def auth_data(self): + utc_time = int(time.time()) & 0xFFFFFFFF + if self.server_info.data.connection_id > 0xFF000000: + self.server_info.data.local_client_id = b'' + if not self.server_info.data.local_client_id: + self.server_info.data.local_client_id = os.urandom(4) + logging.debug("local_client_id %s" % (binascii.hexlify(self.server_info.data.local_client_id),)) + self.server_info.data.connection_id = struct.unpack(' self.unit_len: + ret += self.pack_data(buf[:self.unit_len]) + buf = buf[self.unit_len:] + ret += self.pack_data(buf) + return ret + + def client_post_decrypt(self, buf): + if self.raw_trans: + return buf + self.recv_buf += buf + out_buf = b'' + while len(self.recv_buf) > 2: + length = struct.unpack('>H', self.recv_buf[:2])[0] + if length >= 8192 or length < 7: + self.raw_trans = True + self.recv_buf = b'' + raise Exception('client_post_decrypt data error') + if length > len(self.recv_buf): + break + + if struct.pack('H', self.recv_buf[3:5])[0] + 2 + out_buf += self.recv_buf[pos:length - 4] + self.recv_buf = self.recv_buf[length:] + + if out_buf: + self.decrypt_packet_num += 1 + return out_buf + + def server_pre_encrypt(self, buf): + if self.raw_trans: + return buf + ret = b'' + while len(buf) > self.unit_len: + ret += self.pack_data(buf[:self.unit_len]) + buf = buf[self.unit_len:] + ret += self.pack_data(buf) + return ret + + def server_post_decrypt(self, buf): + if self.raw_trans: + return (buf, False) + self.recv_buf += buf + out_buf = b'' + if not self.has_recv_header: + if len(self.recv_buf) < 6: + return (b'', False) + crc = struct.pack('H', self.recv_buf[:2])[0] + if length > len(self.recv_buf): + return (b'', False) + sha1data = hmac.new(self.server_info.recv_iv + self.server_info.key, self.recv_buf[:length - 10], hashlib.sha1).digest()[:10] + if sha1data != self.recv_buf[length - 10:length]: + logging.error('auth_sha1_v3 data uncorrect auth HMAC-SHA1') + return self.not_match_return(self.recv_buf) + pos = common.ord(self.recv_buf[6]) + if pos < 255: + pos += 6 + else: + pos = struct.unpack('>H', self.recv_buf[7:9])[0] + 6 + out_buf = self.recv_buf[pos:length - 10] + if len(out_buf) < 12: + logging.info('auth_sha1_v3: too short, data %s' % (binascii.hexlify(self.recv_buf),)) + return self.not_match_return(self.recv_buf) + utc_time = struct.unpack(' self.max_time_dif: + logging.info('auth_sha1_v3: wrong timestamp, time_dif %d, data %s' % (time_dif, binascii.hexlify(out_buf),)) + return self.not_match_return(self.recv_buf) + elif self.server_info.data.insert(client_id, connection_id): + self.has_recv_header = True + out_buf = out_buf[12:] + self.client_id = client_id + self.connection_id = connection_id + else: + logging.info('auth_sha1_v3: auth fail, data %s' % (binascii.hexlify(out_buf),)) + return self.not_match_return(self.recv_buf) + self.recv_buf = self.recv_buf[length:] + self.has_recv_header = True + + sendback = False + while len(self.recv_buf) > 2: + length = struct.unpack('>H', self.recv_buf[:2])[0] + if length >= 8192 or length < 7: + self.raw_trans = True + self.recv_buf = b'' + if self.decrypt_packet_num == 0: + logging.info('auth_sha1_v3: over size') + return (b'E', False) + else: + raise Exception('server_post_decrype data error') + if length > len(self.recv_buf): + break + + if 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.raw_trans_sent: - return buf - self.send_buffer += buf - if not self.has_sent_header: - self.has_sent_header = True - 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"0016c02bc02fc00ac009c013c01400330039002f0035000a0100006fff01000100000a00080006001700180019000b0002010000230000337400000010002900270568322d31360568322d31350568322d313402683208737064792f332e3108687474702f312e31000500050100000000000d001600140401050106010201040305030603020304020202") - data = b"\x01\x00" + struct.pack('>H', len(data)) + data - data = b"\x16" + self.tls_version + struct.pack('>H', len(data)) + data - return data - if self.has_recv_header: - data = b"\x14" + self.tls_version + b"\x00\x01\x01" #ChangeCipherSpec - data += b"\x16" + self.tls_version + b"\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.raw_trans_sent = True - return ret - return b'' - - def client_decode(self, buf): - if self.has_recv_header: - return (buf, 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') - self.has_recv_header = True - return (b'', True) - - def server_encode(self, buf): - if self.raw_trans_sent or self.has_sent_header: - return buf - self.has_sent_header = True - data = self.tls_version + self.pack_auth_data(self.client_id) + b"\x20" + self.client_id + binascii.unhexlify(b"0016c02bc02fc00ac009c013c01400330039002f0035000a0100006fff01000100000a00080006001700180019000b0002010000230000337400000010002900270568322d31360568322d31350568322d313402683208737064792f332e3108687474702f312e31000500050100000000000d001600140401050106010201040305030603020304020202") - data = b"\x02\x00" + struct.pack('>H', len(data)) + data #server hello - data = b"\x16" + self.tls_version + struct.pack('>H', len(data)) + data - data += b"\x14" + self.tls_version + b"\x00\x01\x01" #ChangeCipherSpec - data += b"\x16" + self.tls_version + b"\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.raw_trans_sent = True - self.raw_trans_recv = True - if self.method == 'tls1.0_session_auth': - return (b'E'*64, False, False) - return (buf, True, False) - - def server_decode(self, buf): - if self.raw_trans_recv: - return (buf, True, False) - - if self.has_recv_header: - 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') - buf = buf[37:] - self.raw_trans_recv = True - return (buf, True, False) - - self.has_recv_header = True - ogn_buf = buf - if not match_begin(buf, b'\x16' + self.tls_version): - 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.info("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.info("tls_auth wrong time") - return self.decode_error_return(ogn_buf) - if sha1 != verifyid[22:]: - logging.info("tls_auth wrong sha1") - return self.decode_error_return(ogn_buf) - if self.server_info.data.client_data.get(verifyid[:22]): - logging.info("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) - class tls_ticket_auth(plain.plain): def __init__(self, method): self.method = method