Пример #1
0
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')
Пример #2
0
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()
Пример #3
0
    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()
Пример #4
0
    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()
Пример #5
0
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)
Пример #6
0
    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')
Пример #7
0
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)
Пример #8
0
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
Пример #9
0
    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