def decrypt(key: bytes, cipher_text: str) -> str: if not cipher_text: return None method = DEFAULT_METHOD cipher_text = base64.urlsafe_b64decode(cipher_text.encode()) crypto = AEncryptor(key, method, CTX) plain_text = crypto.decrypt(cipher_text) if plain_text[0] == 1: plain_text = zlib.decompress(plain_text[1:]) else: plain_text = plain_text[1:] return plain_text.decode('utf-8')
def encrypt(key: bytes, plain_text: str, method=DEFAULT_METHOD) -> str: if not plain_text: return None zip_flag = 0 plain_text = plain_text.encode('utf-8') plain_text_zip = zlib.compress(plain_text) if len(plain_text_zip) < len(plain_text): plain_text = plain_text_zip zip_flag = 1 plain_text = chr(zip_flag).encode('latin1') + plain_text crypto = AEncryptor(key, method, CTX) cipher_text = crypto.encrypt(plain_text) return base64.urlsafe_b64encode(cipher_text).decode()
def __init__(self, reader, writer, user, skey, method, proxy, user_mgr, s_port, logger): self.__cipher = AEncryptor(skey, method, CTX) self._client_reader = reader self._client_writer = writer self._client_address = writer.get_extra_info('peername')[0] self._client_writer.transport.set_write_buffer_limits(0, 0) self._proxy = proxy self._s_port = s_port self._logger = logger self.user = user self.user_mgr = user_mgr self._init_time = time.time() self._last_active = self._init_time self._gone = False self._next_stream_id = 1 self._stream_writer = {} self._stream_task = {} self._stream_context = {} self._client_writer_lock = asyncio.Lock()
def __init__(self, reader, writer, key, method, proxy, logger): self.__cipher = AEncryptor(key, method, CTX) self._client_reader = reader self._client_writer = writer self._proxy = proxy self._logger = logger self._init_time = time.time() self._last_active = self._init_time self._gone = False self._next_stream_id = 1 self._stream_writer = {} self._stream_status = {} self._remote_status = {} self._stream_task = {} self._client_writer_lock = asyncio.Lock()
class hxs2_connection(): bufsize = 8192 def __init__(self, reader, writer, key, method, proxy, logger): self.__cipher = AEncryptor(key, method, CTX) self._client_reader = reader self._client_writer = writer self._proxy = proxy self._logger = logger self._init_time = time.time() self._last_active = self._init_time self._gone = False self._next_stream_id = 1 self._stream_writer = {} self._stream_status = {} self._remote_status = {} self._stream_task = {} self._client_writer_lock = asyncio.Lock() async def wait_close(self): self._logger.debug('start recieving frames...') timeout_count = 0 while True: if self._gone and len(self._stream_writer) == 0: break time_ = time.time() if time_ - self._last_active > 300: break # read frame_len try: fut = self._client_reader.readexactly(2) frame_len = await asyncio.wait_for(fut, timeout=10) frame_len, = struct.unpack('>H', frame_len) timeout_count = 0 except (asyncio.IncompleteReadError, ValueError, InvalidTag) as e: self._logger.debug('read frame_len error: %r' % e) break except asyncio.TimeoutError: timeout_count += 1 # client should sent keep_alive chunk if timeout_count > 10: # destroy connection self._logger.debug('read frame_len timed out.') break continue except OSError as e: self._logger.debug('read frame_len error: %r' % e) break # read chunk_data try: fut = self._client_reader.readexactly(frame_len) # chunk size shoule be lower than 16kB frame_data = await asyncio.wait_for(fut, timeout=5) frame_data = self.__cipher.decrypt(frame_data) except (OSError, InvalidTag, asyncio.TimeoutError) as e: # something went wrong... self._logger.debug('read frame error: %r' % e) break # parse chunk_data # +------+-------------------+----------+ # | type | flags | stream_id | payload | # +------+-------------------+----------+ # | 1 | 1 | 2 | Variable | # +------+-------------------+----------+ header, payload = frame_data[:4], frame_data[4:] frame_type, frame_flags, stream_id = struct.unpack('>BBH', header) payload = io.BytesIO(payload) if frame_type != 6: self._last_active = time.time() self._logger.debug('recv frame_type: %d, stream_id: %d' % (frame_type, stream_id)) if frame_type == 0: # DATA # first 2 bytes of payload indicates data_len data_len, = struct.unpack('>H', payload.read(2)) data = payload.read(data_len) if len(data) != data_len: # something went wrong, destory connection self._logger.debug('data_len mismatch') break # check if remote socket writable if self._remote_status[stream_id] & EOF_SENT: continue # sent data to stream try: self._stream_writer[stream_id].write(data) await self._stream_writer[stream_id].drain() except OSError: # remote closed, reset stream pass elif frame_type == 1: # HEADER if self._next_stream_id == stream_id: # open new stream self._next_stream_id += 1 host_len = payload.read(1)[0] host = payload.read(host_len).decode('ascii') port, = struct.unpack('>H', payload.read(2)) # rest of the payload is discarded asyncio.ensure_future(self.create_connection(stream_id, host, port, self._proxy)) elif stream_id < self._next_stream_id: if frame_flags & END_STREAM_FLAG: if self._stream_status[stream_id] == OPEN: self._stream_status[stream_id] = EOF_RECV self._stream_writer[stream_id].write_eof() self._remote_status[stream_id] = EOF_SENT elif self._stream_status[stream_id] == EOF_SENT: self._stream_status[stream_id] = CLOSED self._stream_writer[stream_id].close() self._remote_status[stream_id] = CLOSED del self._stream_writer[stream_id] else: self._logger.error('recv END_STREAM_FLAG, stream already closed.') elif frame_type == 3: # RST_STREAM self._stream_status[stream_id] = CLOSED if stream_id in self._stream_writer: self._stream_writer[stream_id].close() del self._stream_writer[stream_id] self._remote_status[stream_id] = CLOSED elif frame_type == 6: # PING if frame_flags == 0: await self.send_frame(6, 1, 0, b'\x00' * random.randint(64, 256)) elif frame_type == 7: # GOAWAY # no more new stream # make no sense when client sending this... self._gone = True elif frame_type == 8: # WINDOW_UPDATE pass else: self._logger.debug('else') break # exit loop, close all streams... self._logger.info('recv from hxs2 connect ended') for stream_id, writer in self._stream_writer.items(): try: writer.close() except Exception: pass async def create_connection(self, stream_id, host, port, proxy): self._logger.info('connecting %s:%s via %s' % (host, port, proxy)) t = time.time() try: reader, writer = await open_connection(host, port, self._proxy) except Exception as e: # tell client request failed. self._logger.info('connect %s:%s failed: %r' % (host, port, e)) data = b'\x01' * random.randint(64, 256) await self.send_frame(3, 0, stream_id, data) else: # tell client request success, header frame, first byte is \x00 t = time.time() - t self._logger.info('connect %s:%s connected, %.3fs' % (host, port, t)) # client may reset the connection # TODO: maybe keep this connection for later? if stream_id in self._stream_status and self._stream_status[stream_id] == CLOSED: writer.close() return data = b'\x00' * random.randint(64, 256) await self.send_frame(1, 0, stream_id, data) # registor stream self._stream_writer[stream_id] = writer self._stream_status[stream_id] = OPEN self._remote_status[stream_id] = OPEN # start forward from remote_reader to client_writer task = asyncio.ensure_future(self.read_from_stream(stream_id, reader)) self._stream_task[stream_id] = task async def send_frame(self, type_, flags, stream_id, payload): self._logger.debug('send frame_type: %d, stream_id: %d' % (type_, stream_id)) if type_ != 6: self._last_active = time.time() await self._client_writer_lock.acquire() try: header = struct.pack('>BBH', type_, flags, stream_id) data = header + payload ct = self.__cipher.encrypt(data) self._client_writer.write(struct.pack('>H', len(ct)) + ct) await self._client_writer.drain() except OSError as e: # destroy connection self._logger.error('send_frame error %r' % e) raise e finally: self._client_writer_lock.release() async def read_from_stream(self, stream_id, reader): self._logger.debug('start read from stream') timeout_count = 0 while not self._remote_status[stream_id] & EOF_RECV: fut = reader.read(self.bufsize) try: data = await asyncio.wait_for(fut, timeout=6) timeout_count = 0 except asyncio.TimeoutError: timeout_count += 1 if timeout_count <= 10: continue self._remote_status[stream_id] = CLOSED # TODO: reset stream data = b'' except OSError: self._remote_status[stream_id] = CLOSED # TODO: reset stream data = b'' if not data: self._remote_status[stream_id] |= EOF_RECV await self.send_frame(1, END_STREAM_FLAG, stream_id, b'\x00' * random.randint(8, 2048)) self._stream_status[stream_id] |= EOF_SENT if self._stream_status[stream_id] & EOF_RECV: if stream_id in self._stream_writer: self._stream_writer[stream_id].close() del self._stream_writer[stream_id] self._remote_status[stream_id] = CLOSED return if not self._stream_status[stream_id] & EOF_SENT: payload = struct.pack('>H', len(data)) + data + b'\x00' * random.randint(8, 255) await self.send_frame(0, 0, stream_id, payload)
async def get_key(self): self.logger.debug('hxsocks2 getKey') usn, psw = (self.proxy.username, self.proxy.password) self.logger.info('%s connect to server', self.name) from .connection import open_connection self.remote_reader, self.remote_writer, _ = await open_connection( self.proxy.hostname, self.proxy.port, proxy=self.proxy.get_via(), timeout=self.timeout, tunnel=True) # prep key exchange request self.__pskcipher = Encryptor(self._psk, self.method) ecc = ECC(self.__pskcipher._key_len) pubk = ecc.get_pub_key() ts = int(time.time()) // 30 ts = struct.pack('>I', ts) padding_len = random.randint(64, 255) data = b''.join([chr(len(pubk)).encode('latin1'), pubk, hmac.new(psw.encode(), ts + pubk + usn.encode(), hashlib.sha256).digest(), b'\x00' * padding_len]) data = chr(20).encode() + struct.pack('>H', len(data)) + data ct = self.__pskcipher.encrypt(data) # send key exchange request self.remote_writer.write(ct) await self.remote_writer.drain() # read iv iv = await self._rfile_read(self.__pskcipher._iv_len, self.timeout) self.__pskcipher.decrypt(iv) # read server response if is_aead(self.method): ct_len = await self._rfile_read(18, self.timeout) ct_len = self.__pskcipher.decrypt(ct_len) ct_len, = struct.unpack('!H', ct_len) ct = await self._rfile_read(ct_len + 16) ct = self.__pskcipher.decrypt(ct) data = ct[2:] else: resp_len = await self._rfile_read(2, self.timeout) resp_len = self.__pskcipher.decrypt(resp_len) resp_len, = struct.unpack('>H', resp_len) data = await self._rfile_read(resp_len) data = self.__pskcipher.decrypt(data) data = io.BytesIO(data) resp_code = byte2int(data.read(1)) if resp_code == 0: self.logger.debug('hxsocks read key exchange respond') pklen = byte2int(data.read(1)) scertlen = byte2int(data.read(1)) siglen = byte2int(data.read(1)) server_key = data.read(pklen) auth = data.read(32) server_cert = data.read(scertlen) signature = data.read(siglen) # TODO: ask user if a certificate should be accepted or not. host, port = self.proxy._host_port server_id = '%s_%d' % (host, port) if server_id not in KNOWN_HOSTS: self.logger.info('hxs: server %s new cert %s saved.', server_id, hashlib.sha256(server_cert).hexdigest()[:8]) with open('./.hxs_known_hosts/' + server_id + '.cert', 'wb') as f: f.write(server_cert) KNOWN_HOSTS[server_id] = server_cert elif KNOWN_HOSTS[server_id] != server_cert: self.logger.error('hxs: server %s certificate mismatch! PLEASE CHECK!', server_id) raise ConnectionResetError(0, 'hxs: bad certificate') if auth == hmac.new(psw.encode(), pubk + server_key + usn.encode(), hashlib.sha256).digest(): try: ECC.verify_with_pub_key(server_cert, auth, signature, self.hash_algo) shared_secret = ecc.get_dh_key(server_key) self.logger.debug('hxs key exchange success') self.__cipher = AEncryptor(shared_secret, self.method, CTX) # start reading from connection asyncio.ensure_future(self.read_from_connection()) asyncio.ensure_future(self.stat()) self.connected = True return except InvalidSignature: self.logger.error('hxs getKey Error: server auth failed, bad signature') else: self.logger.error('hxs getKey Error: server auth failed, bad username or password') else: self.logger.error('hxs getKey Error. bad password or timestamp.') raise ConnectionResetError(0, 'hxs getKey Error')
class Hxs2Connection(): bufsize = 32768 def __init__(self, reader, writer, user, skey, method, proxy, user_mgr, s_port, logger): self.__cipher = AEncryptor(skey, method, CTX) self._client_reader = reader self._client_writer = writer self._client_address = writer.get_extra_info('peername')[0] self._client_writer.transport.set_write_buffer_limits(0, 0) self._proxy = proxy self._s_port = s_port self._logger = logger self.user = user self.user_mgr = user_mgr self._init_time = time.time() self._last_active = self._init_time self._gone = False self._next_stream_id = 1 self._stream_writer = {} self._stream_task = {} self._stream_context = {} self._client_writer_lock = asyncio.Lock() async def wait_close(self): self._logger.debug('start recieving frames...') timeout_count = 0 while True: try: if self._gone and not self._stream_writer: break time_ = time.time() if time_ - self._last_active > 300: break # read frame_len try: fut = self._client_reader.readexactly(2) frame_len = await asyncio.wait_for(fut, timeout=10) frame_len, = struct.unpack('>H', frame_len) timeout_count = 0 except (asyncio.IncompleteReadError, ValueError, InvalidTag, ConnectionResetError) as err: self._logger.debug('read frame_len error: %r', err) break except asyncio.TimeoutError: timeout_count += 1 # client should sent keep_alive chunk if timeout_count > 10: # destroy connection self._logger.debug('read frame_len timed out.') break continue except OSError as err: self._logger.debug('read frame_len error: %r', err) break # read chunk_data try: fut = self._client_reader.readexactly(frame_len) # chunk size shoule be lower than 16kB frame_data = await asyncio.wait_for(fut, timeout=8) frame_data = self.__cipher.decrypt(frame_data) except (OSError, InvalidTag, asyncio.TimeoutError, asyncio.streams.IncompleteReadError) as err: # something went wrong... self._logger.debug('read frame error: %r', err) break # parse chunk_data # +------+-------------------+----------+ # | type | flags | stream_id | payload | # +------+-------------------+----------+ # | 1 | 1 | 2 | Variable | # +------+-------------------+----------+ header, payload = frame_data[:4], frame_data[4:] frame_type, frame_flags, stream_id = struct.unpack( '>BBH', header) payload = io.BytesIO(payload) if frame_type != 6: self._last_active = time.time() self._logger.debug('recv frame_type: %d, stream_id: %d', frame_type, stream_id) if frame_type == 0: # DATA # first 2 bytes of payload indicates data_len data_len, = struct.unpack('>H', payload.read(2)) data = payload.read(data_len) if len(data) != data_len: # something went wrong, destory connection self._logger.debug('data_len mismatch') break # check if remote socket writable if self._stream_context[stream_id].remote_status & EOF_SENT: continue # sent data to stream try: self._stream_writer[stream_id].write(data) self._stream_context[ stream_id].traffic_from_client += len(data) self._stream_context[ stream_id].last_active = time.time() await self._stream_writer[stream_id].drain() except OSError: # remote closed, reset stream self._stream_context[stream_id].stream_status = CLOSED if stream_id in self._stream_writer: self._stream_writer[stream_id].close() del self._stream_writer[stream_id] self._stream_context[stream_id].remote_status = CLOSED elif frame_type == 1: # HEADER if self._next_stream_id == stream_id: # open new stream self._next_stream_id += 1 host_len = payload.read(1)[0] host = payload.read(host_len).decode('ascii') port, = struct.unpack('>H', payload.read(2)) # rest of the payload is discarded asyncio.ensure_future( self.create_connection(stream_id, host, port)) elif stream_id < self._next_stream_id: self._logger.debug( 'sid %s END_STREAM. status %s', stream_id, self._stream_context[stream_id].stream_status) if frame_flags & END_STREAM_FLAG: if self._stream_context[ stream_id].stream_status == OPEN: self._stream_context[ stream_id].stream_status = EOF_RECV self._stream_writer[stream_id].write_eof() self._stream_context[ stream_id].remote_status = EOF_SENT elif self._stream_context[ stream_id].stream_status == EOF_SENT: self._stream_context[ stream_id].stream_status = CLOSED self._stream_writer[stream_id].close() self._stream_context[ stream_id].remote_status = CLOSED del self._stream_writer[stream_id] self.log_access(stream_id) else: self._logger.error( 'recv END_STREAM_FLAG, stream already closed.' ) elif frame_type == 3: # RST_STREAM self._stream_context[stream_id].stream_status = CLOSED if stream_id in self._stream_writer: self._stream_writer[stream_id].close() del self._stream_writer[stream_id] self._stream_context[stream_id].remote_status = CLOSED elif frame_type == 6: # PING if frame_flags == 0: await self.send_frame( 6, 1, 0, b'\x00' * random.randint(64, 256)) elif frame_type == 7: # GOAWAY # no more new stream # make no sense when client sending this... self._gone = True elif frame_type == 8: # WINDOW_UPDATE pass else: self._logger.debug('else') break except Exception as err: self._logger.error('read from connection error: %r', err) self._logger.error(traceback.format_exc()) # exit loop, close all streams... self._logger.info('recv from hxs2 connect ended') for stream_id, writer in self._stream_writer.items(): try: writer.close() except Exception: pass async def create_connection(self, stream_id, host, port): self._logger.info('connecting %s:%s %s %s', host, port, self.user, self._client_address) timelog = time.time() try: self.user_mgr.user_access_ctrl(self._s_port, host, self._client_address, self.user) reader, writer = await open_connection(host, port, self._proxy) writer.transport.set_write_buffer_limits(0, 0) except Exception as err: # tell client request failed. self._logger.error('connect %s:%s failed: %r', host, port, err) data = b'\x01' * random.randint(64, 256) await self.send_frame(3, 0, stream_id, data) else: # tell client request success, header frame, first byte is \x00 timelog = time.time() - timelog if timelog > 1: self._logger.info('connect %s:%s connected, %.3fs', host, port, timelog) # client may reset the connection # TODO: maybe keep this connection for later? if stream_id in self._stream_context and self._stream_context[ stream_id].stream_status == CLOSED: writer.close() return data = b'\x00' * random.randint(64, 256) await self.send_frame(1, 0, stream_id, data) # registor stream self._stream_writer[stream_id] = writer self._stream_context[stream_id] = ForwardContext(host) # start forward from remote_reader to client_writer task = asyncio.ensure_future( self.read_from_remote(stream_id, reader)) self._stream_task[stream_id] = task async def send_frame(self, type_, flags, stream_id, payload): self._logger.debug('send frame_type: %d, stream_id: %d', type_, stream_id) if type_ != 6: self._last_active = time.time() await self._client_writer_lock.acquire() try: header = struct.pack('>BBH', type_, flags, stream_id) data = header + payload ct = self.__cipher.encrypt(data) self._client_writer.write(struct.pack('>H', len(ct)) + ct) await self._client_writer.drain() except OSError as err: # destroy connection self._logger.error('send_frame error %r', err) raise err finally: self._client_writer_lock.release() async def read_from_remote(self, stream_id, remote_reader): self._logger.debug('start read from stream') timeout_count = 0 while not self._stream_context[stream_id].remote_status & EOF_RECV: fut = remote_reader.read(self.bufsize) try: data = await asyncio.wait_for(fut, timeout=6) self._stream_context[stream_id].last_active = time.time() except asyncio.TimeoutError: timeout_count += 1 if self._stream_context[stream_id].stream_status != OPEN: data = b'' elif time.time( ) - self._stream_context[stream_id].last_active < 120: continue self._stream_context[stream_id].remote_status = CLOSED # TODO: reset stream data = b'' except OSError: self._stream_context[stream_id].remote_status = CLOSED # TODO: reset stream data = b'' if not data: self._stream_context[stream_id].remote_status |= EOF_RECV await self.send_frame(1, END_STREAM_FLAG, stream_id, b'\x00' * random.randint(8, 2048)) self._stream_context[stream_id].stream_status |= EOF_SENT if self._stream_context[stream_id].stream_status & EOF_RECV: if stream_id in self._stream_writer: self._stream_writer[stream_id].close() del self._stream_writer[stream_id] self._stream_context[stream_id].remote_status = CLOSED self._logger.debug('sid %s stream closed.(rfr)', stream_id) self.log_access(stream_id) break if not self._stream_context[stream_id].stream_status & EOF_SENT: self._stream_context[stream_id].traffic_from_remote += len( data) payload = struct.pack( '>H', len(data)) + data + b'\x00' * random.randint(8, 255) await self.send_frame(0, 0, stream_id, payload) self._logger.debug('sid %s read_from_remote end. status %s', stream_id, self._stream_context[stream_id].stream_status) def log_access(self, stream_id): traffic = (self._stream_context[stream_id].traffic_from_client, self._stream_context[stream_id].traffic_from_remote) self.user_mgr.user_access_log(self._s_port, self._stream_context[stream_id].host, traffic, self._client_address, self.user)
def test_one(method): data = os.urandom(10240) if is_aead(method): cipher = AEncryptor(b'123456', method, b"ctx", check_iv=False) cipher1 = AEncryptor(b'123456', method, b"ctx", check_iv=False) else: cipher = Encryptor(b'123456', method, check_iv=False) cipher1 = Encryptor(b'123456', method, check_iv=False) ct1 = cipher.encrypt(data) cipher1.decrypt(ct1) time_log = time.time() for _ in range(1024): ct1 = cipher.encrypt(data) ct2 = cipher.encrypt(data) cipher1.decrypt(ct1) cipher1.decrypt(ct2) return time.time() - time_log
async def _handle(self, client_reader, client_writer): self.client_address = client_writer.get_extra_info('peername') self.client_reader = client_reader self.logger.debug('incoming connection {}'.format(self.client_address)) KM = self.server.kmgr try: fut = self.client_reader.readexactly(self.encryptor._iv_len) iv = await asyncio.wait_for(fut, timeout=10) self.encryptor.decrypt(iv) except IVError: self.logger.error('iv reused, {}'.format(self.client_address)) await self.play_dead() return except (asyncio.TimeoutError, asyncio.IncompleteReadError, ConnectionResetError): self.logger.warning('iv read failed, {}'.format(self.client_address)) return req_count = 0 while True: if req_count: # Not shadowsocks request self.logger.debug('excepting next request: {}'.format(self.client_address)) fut = client_reader.readexactly(2) try: await asyncio.wait_for(fut, timeout=120) except (OSError, ConnectionResetError, asyncio.IncompleteReadError, asyncio.TimeoutError) as e: self.logger.debug('closed: {} {}'.format(e, self.client_address)) return try: fut = self.read(1) cmd = await asyncio.wait_for(fut, timeout=10) except asyncio.TimeoutError: self.logger.debug('read cmd timed out. {}'.format(self.client_address)) return except (ConnectionResetError, asyncio.IncompleteReadError): self.logger.debug('read cmd reset. {}'.format(self.client_address)) return except InvalidTag: self.logger.error('InvalidTag while read cmd. {}'.format(self.client_address)) await self.play_dead() return cmd = cmd[0] self.logger.debug('cmd: {} {}'.format(cmd, self.client_address)) if cmd in (1, 3, 4): # A shadowsocks request result = await self.handle_ss(client_reader, client_writer, addr_type=cmd) if result: await self.play_dead() return elif cmd in (10, 20): # hxsocks / hxsocks2 client key exchange req_count += 1 rint = random.randint(64, 2048) req_len = await self.read(2) req_len, = struct.unpack('>H', req_len) data = await self.read(req_len) data = io.BytesIO(data) ts = int(time.time()) // 30 pklen = data.read(1)[0] client_pkey = data.read(pklen) client_auth = data.read(32) def _send(data): if self.encryptor._encryptor: data = struct.pack('>H', len(data)) + data ct = self.encryptor.encrypt(data) client_writer.write(struct.pack('>H', len(ct)) + ct) return data = struct.pack('>H', len(data)) + data client_writer.write(self.encryptor.encrypt(data)) def auth(): for _ts in [ts, ts - 1, ts + 1]: for user, passwd in KM.iter_user(): h = hmac.new(passwd.encode(), struct.pack('>I', _ts) + client_pkey + user.encode(), hashlib.sha256).digest() if compare_digest(h, client_auth): return user client = auth() if not client: self.logger.error('user not found. {}'.format(self.client_address)) await self.play_dead() return pkey, passwd = KM.key_xchange(client, client_pkey, self.encryptor._key_len) if pkey: self.logger.info('new key exchange. client: {} {}'.format(client, self.client_address)) h = hmac.new(passwd.encode(), client_pkey + pkey + client.encode(), hashlib.sha256).digest() scert = KM.SERVER_CERT.get_pub_key() signature = KM.SERVER_CERT.sign(h, DEFAULT_HASH) data = bytes((0, len(pkey), len(scert), len(signature))) + pkey + h + scert + signature + os.urandom(rint) _send(data) if cmd == 20: client_pkey = hashlib.md5(client_pkey).digest() conn = hxs2_connection(client_reader, client_writer, KM.get_skey_by_pubkey(client_pkey), self.server.method, self.server.proxy, self.logger) await conn.wait_close() return continue else: self.logger.error('Private_key already registered. client: {} {}'.format(client, self.client_address)) await self.play_dead() return elif cmd == 11: # a connect request req_count += 1 client_pkey = await self.read(16) rint = random.randint(64, 2048) def _send(code, cipher): if code == 1: client_writer.write(struct.pack('>H', rint) + os.urandom(rint)) else: ct = cipher.encrypt(bytes((code, )) + os.urandom(rint)) client_writer.write(struct.pack('>H', len(ct)) + ct) if KM.check_key(client_pkey): self.logger.error('client key not exist or expired. {}'.format(self.client_address)) ctlen = await self.read(2) ctlen, = struct.unpack('>H', ctlen) await self.read(ctlen) _send(1, None) continue user = KM.get_user_by_pubkey(client_pkey) cipher = AEncryptor(KM.get_skey_by_pubkey(client_pkey), self.server.method, CTX) ctlen = await self.read(2) ctlen, = struct.unpack('>H', ctlen) ct = await self.read(ctlen) try: data = cipher.decrypt(ct) except InvalidTag: self.logger.error('hxs connect req InvalidTag. {} {}'.format(user, self.client_address)) # await self.play_dead() return buf = io.BytesIO(data) ts = buf.read(4) if abs(struct.unpack('>I', ts)[0] - time.time()) > 600: self.logger.error('bad timestamp, possible replay attrack. {} {}'.format(user, self.client_address)) # KM.del_key(client_pkey) # _send(1, None) await self.play_dead() return host_len = buf.read(1)[0] addr = buf.read(host_len).decode('ascii') port, = struct.unpack('>H', buf.read(2)) self.logger.info('connecting to {}:{} via {}, {} {} {}'.format(addr, port, self.server.proxy, user, req_count, self.client_address)) try: remote_reader, remote_writer = await open_connection(addr, port, self.server.proxy) _send(0, cipher) except Exception: self.logger.error('connect to {}:{} failed!'.format(addr, port)) _send(2, cipher) continue context = ForwardContext() tasks = [self.hxs_forward_from_remote(remote_reader, client_writer, cipher, context), self.hxs_forward_from_client(client_reader, client_writer, remote_writer, cipher, context), ] await asyncio.wait(tasks) remote_writer.close() if context.readable or context.writeable: return continue elif cmd == 12: # get public key req_len = await self.read(2) req_len, = struct.unpack('>H', req_len) data = await self.read(req_len) # drop data # return public key with padding rint = random.randint(64, 2048) scert = KM.SERVER_CERT.get_pub_key() data = struct.pack('>H', len(scert)) + scert + os.urandom(rint) data = struct.pack('>H', len(data)) + data # the first response, just encrypt and sent client_writer.write(self.encryptor.encrypt(data)) continue else: # TODO: security self.logger.error('bad cmd: %s, %s' % (cmd, self.client_address)) await self.play_dead() return