BreakWa11
9 years ago
7 changed files with 342 additions and 130 deletions
@ -0,0 +1,276 @@ |
|||
#!/usr/bin/env python |
|||
# |
|||
# Copyright 2015-2015 breakwa11 |
|||
# |
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may |
|||
# not use this file except in compliance with the License. You may obtain |
|||
# a copy of the License at |
|||
# |
|||
# http://www.apache.org/licenses/LICENSE-2.0 |
|||
# |
|||
# Unless required by applicable law or agreed to in writing, software |
|||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
|||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
|||
# License for the specific language governing permissions and limitations |
|||
# under the License. |
|||
|
|||
from __future__ import absolute_import, division, print_function, \ |
|||
with_statement |
|||
|
|||
import os |
|||
import sys |
|||
import hashlib |
|||
import logging |
|||
import binascii |
|||
import struct |
|||
import base64 |
|||
import time |
|||
import random |
|||
import hmac |
|||
import hashlib |
|||
|
|||
from shadowsocks import common |
|||
from shadowsocks.obfsplugin import plain |
|||
from shadowsocks.common import to_bytes, to_str, ord |
|||
from shadowsocks import lru_cache |
|||
|
|||
def create_tls_obfs(method): |
|||
return tls_simple(method) |
|||
|
|||
def create_tls_auth_obfs(method): |
|||
return tls_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,), |
|||
} |
|||
|
|||
def match_begin(str1, str2): |
|||
if len(str1) >= len(str2): |
|||
if str1[:len(str2)] == str2: |
|||
return True |
|||
return False |
|||
|
|||
class tls_simple(plain.plain): |
|||
def __init__(self, method): |
|||
self.method = method |
|||
self.has_sent_header = False |
|||
self.has_recv_header = False |
|||
self.raw_trans_sent = False |
|||
self.send_buffer = b'' |
|||
self.tls_version = b'\x03\x01' |
|||
|
|||
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 + os.urandom(32) + binascii.unhexlify(b"000016c02bc02fc00ac009c013c01400330039002f0035000a0100006fff01000100000a00080006001700180019000b0002010000230000337400000010002900270568322d31360568322d31350568322d313402683208737064792f332e3108687474702f312e31000500050100000000000d001600140401050106010201040305030603020304020202") |
|||
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: |
|||
ret = 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) |
|||
self.has_recv_header = True |
|||
return (b'', True) |
|||
|
|||
def server_encode(self, buf): |
|||
if self.has_sent_header: |
|||
return buf |
|||
self.has_sent_header = True |
|||
# TODO |
|||
data = self.tls_version + os.urandom(32) |
|||
data = b"\x02\x00" + struct.pack('>H', len(data)) + data |
|||
data = b"\x16" + self.tls_version + struct.pack('>H', len(data)) + data |
|||
return data |
|||
|
|||
def decode_error_return(self, buf): |
|||
self.has_sent_header = True |
|||
if self.method == 'tls_simple': |
|||
return (b'E', False, False) |
|||
return (buf, True, False) |
|||
|
|||
def server_decode(self, buf): |
|||
if self.has_recv_header: |
|||
return (buf, True, False) |
|||
|
|||
self.has_recv_header = True |
|||
if not match_begin(buf, b'\x16' + self.tls_version): |
|||
return self.decode_error_return(buf) |
|||
buf = buf[3:] |
|||
if struct.unpack('>H', buf[:2])[0] != len(buf) - 2: |
|||
return self.decode_error_return(buf) |
|||
buf = buf[2:] |
|||
if not match_begin(buf, b'\x01\x00'): #client hello |
|||
return self.decode_error_return(buf) |
|||
buf = buf[2:] |
|||
if struct.unpack('>H', buf[:2])[0] != len(buf) - 2: |
|||
return self.decode_error_return(buf) |
|||
buf = buf[2:] |
|||
if not match_begin(buf, self.tls_version): |
|||
return self.decode_error_return(buf) |
|||
buf = buf[2:] |
|||
verifyid = buf[:32] |
|||
buf = buf[32:] |
|||
sessionid_len = ord(buf[1]) |
|||
sessionid = buf[1:sessionid_len + 1] |
|||
buf = buf[sessionid_len+1:] |
|||
# (buffer_to_recv, is_need_decrypt, is_need_to_encode_and_send_back) |
|||
return (b'', False, True) |
|||
|
|||
class obfs_client_data(object): |
|||
def __init__(self, cid): |
|||
self.client_id = cid |
|||
self.auth_code = {} |
|||
|
|||
class obfs_auth_data(object): |
|||
def __init__(self): |
|||
self.client_data = lru_cache.LRUCache(60 * 5) |
|||
self.client_id = os.urandom(32) |
|||
self.startup_time = int(time.time() - 60 * 30) & 0xFFFFFFFF |
|||
|
|||
class tls_auth(plain.plain): |
|||
def __init__(self, method): |
|||
self.method = method |
|||
self.has_sent_header = False |
|||
self.has_recv_header = False |
|||
self.raw_trans_sent = False |
|||
self.raw_trans_recv = False |
|||
self.send_buffer = b'' |
|||
self.client_id = b'' |
|||
self.max_time_dif = 60 * 60 # time dif (second) setting |
|||
self.tls_version = b'\x03\x01' |
|||
|
|||
def init_data(self): |
|||
return obfs_auth_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.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 + "\x00\x01\x01" #ChangeCipherSpec |
|||
data += b"\x16" + self.tls_version + "\x00\x01\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) |
|||
self.has_recv_header = True |
|||
return (b'', True) |
|||
|
|||
def server_encode(self, buf): |
|||
if 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 + "\x00\x01\x01" #ChangeCipherSpec |
|||
data += b"\x16" + self.tls_version + "\x00\x01\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_recv = True |
|||
if self.method == 'tls_simple': |
|||
return (b'E', 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 = 44 - 10 |
|||
if len(buf) < 44: |
|||
logging.error('server_decode data error') |
|||
return decode_error_return(b'') |
|||
if not match_begin(buf, b"\x14" + self.tls_version + "\x00\x01\x01"): #ChangeCipherSpec |
|||
logging.error('server_decode data error') |
|||
return decode_error_return(b'') |
|||
buf = buf[6:] |
|||
if not match_begin(buf, b"\x16" + self.tls_version + "\x00\x01\x20"): #Finished |
|||
logging.error('server_decode data error') |
|||
return decode_error_return(b'') |
|||
if hmac.new(self.server_info.key + self.client_id, verify[:verify_len], hashlib.sha1).digest()[:10] != verify[verify_len:verify_len+10]: |
|||
logging.error('server_decode data error') |
|||
return decode_error_return(b'') |
|||
if len(buf) < 38: |
|||
logging.error('server_decode data error') |
|||
return decode_error_return(b'') |
|||
buf = buf[38:] |
|||
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.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 verifyid[4:22] in self.server_info.data.client_data: |
|||
logging.error("replay attack detect, id = %s" % (binascii.hexlify(verifyid))) |
|||
return self.decode_error_return(ogn_buf) |
|||
# (buffer_to_recv, is_need_decrypt, is_need_to_encode_and_send_back) |
|||
return (b'', False, True) |
|||
|
Loading…
Reference in new issue