def application_data(plugin):
    while True:
        data = memoryview((yield from iofree.read(5)))
        assert data[
            0] == 0x17, f"{data[0]} != application_data(23) {data.tobytes()}"
        assert (data[1:3] == plugin.tls_version
                ), f"{data[1:3].tobytes()} != version({plugin.tls_version})"
        size = int.from_bytes(data[3:], "big")
        assert size == size & 0x3FFF, f"{size} is over 2^14"
        data = yield from iofree.read(size)
        yield from iofree.write(data)
def tls1_2_request(plugin):
    parser = yield from iofree.get_parser()
    tls_version = plugin.tls_version
    with memoryview((yield from iofree.read(5))) as tls_plaintext_head:
        assert (tls_plaintext_head[:3] == b"\x16\x03\x01"
                ), "invalid tls head: handshake(22) protocol_version(3.1)"
        length = int.from_bytes(tls_plaintext_head[-2:], "big")
    assert length == length & 0x3FFF, f"{length} is over 2^14"
    with memoryview((yield from iofree.read(length))) as fragment:
        assert fragment[
            0] == 1, "expect client_hello(1), but got {fragment[0]}"
        handshake_length = int.from_bytes(fragment[1:4], "big")
        client_hello = fragment[4:handshake_length + 4]
    assert client_hello[:2] == tls_version, "expect: client_version(3.3)"
    verify_id = client_hello[2:34]
    # TODO: replay attact detect
    gmt_unix_time = int.from_bytes(verify_id[:4], "big")
    time_diff = (int(time()) & 0xFFFFFFFF) - gmt_unix_time
    assert abs(
        time_diff) < plugin.time_tolerance, f"expired request: {time_diff}"
    session_length = client_hello[34]
    assert session_length >= 32, "session length should be >= 32"
    session_id = client_hello[35:35 + session_length].tobytes()
    sha1 = hmac.new(plugin.server.cipher.master_key + session_id,
                    verify_id[:22], hashlib.sha1).digest()[:10]
    assert verify_id[22:] == sha1, "hmac verify failed"
    tail = client_hello[35 + session_length:]
    cipher_suites = tail[:2].tobytes()
    compression_methods = tail[2:3]
    (cipher_suites, compression_methods)
    random_bytes = pack_auth_data(plugin.server.cipher.master_key, session_id)
    server_hello = (tls_version + random_bytes +
                    session_length.to_bytes(1, "big") + session_id +
                    binascii.unhexlify(b"c02f000005ff01000100"))
    server_hello = b"\x02\x00" + pack_uint16(server_hello)
    server_hello = b"\x16" + tls_version + pack_uint16(server_hello)
    if random.randint(0, 8) < 1:
        ticket = os.urandom((struct.unpack(">H", os.urandom(2))[0] % 164) * 2 +
                            64)
        ticket = struct.pack(
            ">H",
            len(ticket) + 4) + b"\x04\x00" + pack_uint16(ticket)
        server_hello += b"\x16" + tls_version + ticket
    change_cipher_spec = b"\x14" + tls_version + b"\x00\x01\x01"
    finish_len = random.choice([32, 40])
    change_cipher_spec += (b"\x16" + tls_version +
                           struct.pack(">H", finish_len) +
                           os.urandom(finish_len - 10))
    change_cipher_spec += hmac.new(
        plugin.server.cipher.master_key + session_id, change_cipher_spec,
        hashlib.sha1).digest()[:10]
    parser.write(server_hello + change_cipher_spec)
    yield from ChangeCipherReader(plugin, plugin.server.cipher.master_key,
                                  session_id)
Exemple #3
0
 def read_some(self):
     chunk0 = yield from iofree.read(2 + self.cipher.TAG_SIZE)
     data = memoryview(chunk0)
     length_bytes = self.decrypt(data[:2], data[2:])
     length = int.from_bytes(length_bytes, "big")
     if length != length & 0x3FFF:
         raise Exception("invalid length")
     chunk1 = yield from iofree.read(length + self.cipher.TAG_SIZE)
     data = memoryview(chunk1)
     payload = self.decrypt(data[:length], data[length:])
     return payload
Exemple #4
0
def _read_some():
    parser = yield from iofree.get_parser()
    chunk0 = yield from iofree.read(2 + parser.cipher.TAG_SIZE)
    with memoryview(chunk0) as data:
        length_bytes = parser.decrypt(data[:2], data[2:])
    length = int.from_bytes(length_bytes, "big")
    if length != length & 0x3FFF:  # 16 * 1024 - 1
        raise Exception("length exceed limit")
    chunk1 = yield from iofree.read(length + parser.cipher.TAG_SIZE)
    with memoryview(chunk1) as data:
        payload = parser.decrypt(data[:length], data[length:])
    return payload
Exemple #5
0
def application_data(plugin):
    parser = yield from iofree.get_parser()
    while True:
        with memoryview((yield from iofree.read(5))) as data:
            assert (data[0] == 0x17
                    ), f"{data[0]} != application_data(23) {data.tobytes()}"
            assert (
                data[1:3] == plugin.tls_version
            ), f"{data[1:3].tobytes()} != version({plugin.tls_version})"
            size = int.from_bytes(data[3:], "big")
            assert size == size & 0x3FFF, f"{size} is over 2^14"
            data = yield from iofree.read(size)
            parser.respond(result=data)
Exemple #6
0
def socks5_response(auth):
    data = yield from iofree.read(2)
    assert data[0] == 5, f"bad socks version: {data[0]}"
    method = data[1]
    assert method in (0, 2), f"bad method {data[1]}"
    if auth:
        auth_ver, status = yield from iofree.read_struct("!BB")
        assert auth_ver == 1, f"invalid auth version {auth_ver}"
        assert status == 0, f"invalid status {status}"
    data = yield from iofree.read(3)
    assert data[0] == 5, f"bad socks version: {data[0]}"
    assert data[1] == 0, f"failed REP with code: {data[1]}"
    bind_addr = yield from read_addr()
    return bind_addr
def ChangeCipherReader(plugin, key, session_id):
    with memoryview((yield from iofree.read(11))) as data:
        assert data[
            0] == 0x14, f"{data[0]} != change_cipher_spec(20) {data.tobytes()}"
        assert (data[1:3] == plugin.tls_version
                ), f"{data[1:3].tobytes()} != version({plugin.tls_version})"
        assert data[3:6] == b"\x00\x01\x01", "bad ChangeCipherSpec"
        assert data[6] == 0x16, f"{data[6]} != Finish(22)"
        assert (data[7:9] == plugin.tls_version
                ), f"{data[7:9]} != version({plugin.tls_version})"
        assert data[9] == 0x00, f"{data[9]} != Finish(0)"
        verify_len = int.from_bytes(data[9:11], "big")
        with memoryview((yield from iofree.read(verify_len))) as verify:
            sha1 = hmac.new(key + session_id, b"".join([data, verify[:-10]]),
                            hashlib.sha1).digest()[:10]
            assert sha1 == verify[-10:], "hmac verify failed"
Exemple #8
0
def read_addr():
    atyp = yield from iofree.read(1)
    if atyp == b"\x01":  # IPV4
        data = yield from iofree.read(4)
        host = socket.inet_ntoa(data)
    elif atyp == b"\x04":  # IPV6
        data = yield from iofree.read(16)
        host = socket.inet_ntop(socket.AF_INET6, data)
    elif atyp == b"\x03":  # hostname
        data = yield from iofree.read(1)
        data += yield from iofree.read(data[0])
        host = data[1:].decode("ascii")
    else:
        raise Exception(f"unknown atyp: {atyp}")
    port, = yield from iofree.read_struct("!H")
    return (host, port)
Exemple #9
0
def ss_reader(cipher):
    parser = yield from iofree.get_parser()
    iv = yield from iofree.read(cipher.IV_SIZE)
    decrypt = cipher.make_decrypter(iv)
    while True:
        data = yield from iofree.read_more()
        parser.write(decrypt(data))
Exemple #10
0
def aead_reader(cipher):
    parser = yield from iofree.get_parser()
    parser.cipher = cipher
    salt = yield from iofree.read(cipher.SALT_SIZE)
    parser.decrypt = cipher.make_decrypter(salt)
    while True:
        payload = yield from _read_some()
        parser.write(payload)
Exemple #11
0
def http_response():
    response = yield from HTTPResponse
    assert response.ver == b"HTTP/1.1"
    assert response.code == b"200"
    assert response.status == b"OK"
    yield from iofree.read_until(b"\n", return_tail=True)
    data = yield from iofree.read(4)
    assert data == b"haha"
    (number, ) = yield from iofree.read_struct("!H")
    assert number == 8 * 256 + 8
    number = yield from iofree.read_int(3)
    assert number == int.from_bytes(b"\x11\x11\x11", "big")
    assert (yield from iofree.peek(2)) == b"co"
    assert (yield from iofree.read(7)) == b"content"
    yield from iofree.wait()
    assert len((yield from iofree.read_more(5))) >= 5
    yield from iofree.read()
    yield from iofree.wait_event()
    return b"\r\n".join(response.header_lines)
Exemple #12
0
def tls1_2_response(plugin):
    tls_version = plugin.tls_version
    with memoryview((yield from iofree.read(5))) as tls_plaintext_head:
        assert (tls_plaintext_head[:3] == b"\x16\x03\x03"
                ), "invalid tls head: handshake(22) protocol_version(3.1)"
        length = int.from_bytes(tls_plaintext_head[-2:], "big")
    assert length == length & 0x3FFF, f"{length} is over 2^14"
    with memoryview((yield from iofree.read(length))) as fragment:
        assert fragment[
            0] == 2, f"expect server_hello(2), bug got: {fragment[0]}"
        handshake_length = int.from_bytes(fragment[1:4], "big")
        server_hello = fragment[4:handshake_length + 4]
    assert server_hello[:2] == tls_version, "expect: server_version(3.3)"
    verify_id = server_hello[2:34]
    sha1 = hmac.new(
        plugin.client.ns.cipher.master_key + plugin.session_id,
        verify_id[:-10],
        hashlib.sha1,
    ).digest()[:10]
    assert sha1 == verify_id[-10:], "hmac verify failed"
    assert server_hello[34] == 32, f"expect 32, but got {server_hello[34]}"
    # verify_id = server_hello[35:67]
    # sha1 = hmac.new(
    #     plugin.client.ns.cipher.master_key + plugin.session_id,
    #     fragment[:-10],
    #     hashlib.sha1,
    # ).digest()[:10]
    # assert sha1 == fragment[-10:], "hmac verify failed"
    while True:
        x = yield from iofree.peek(1)
        if x[0] != 22:
            break
        with memoryview((yield from iofree.read(5))) as ticket_head:
            length = int.from_bytes(ticket_head[-2:], "big")
        assert length == length & 0x3FFF, f"{length} is over 2^14"
        yield from iofree.read(length)
    yield from ChangeCipherReader(plugin, plugin.client.ns.cipher.master_key,
                                  plugin.session_id)
    yield from application_data(plugin)
Exemple #13
0
def socks5_request(auth=False):
    parser = yield from iofree.get_parser()
    ver, nmethods = yield from iofree.read_struct("!BB")
    assert ver == 5, f"bad socks version: {ver}"
    assert nmethods != 0, f"nmethods can't be 0"
    methods = yield from iofree.read(nmethods)
    if auth and b"\x02" not in methods:
        parser.write(b"\x05\x02")
        raise Exception("server needs authentication")
    elif b"\x00" not in methods:
        parser.write(b"\x05\x00")
        raise Exception("method not support")
    if auth:
        parser.write(b"\x05\x02")
        auth_ver, username_length = yield from iofree.read_struct("!BB")
        assert auth_ver == 1, f"invalid auth version {auth_ver}"
        username = yield from iofree.read(username_length)
        password_length = (yield from iofree.read(1))[0]
        password = yield from iofree.read(password_length)
        if (username, password) != auth:
            parser.write(b"\x01\x01")
            raise Exception("authenticate failed")
        else:
            parser.write(b"\x01\x00")
    else:
        parser.write(b"\x05\x00")
    ver, cmd, rsv = yield from iofree.read_struct("!BBB")
    if cmd == 1:  # connect
        pass
    elif cmd == 2:  # bind
        raise Exception("doesn't support bind yet")
    elif cmd == 3:  # associate
        raise Exception("doesn't support associate yes")
    else:
        raise Exception(f"unknown cmd: {cmd}")
    target_addr = yield from read_addr()
    return target_addr, cmd
Exemple #14
0
def addr_reader():
    atyp = yield from iofree.read(1)
    if atyp == b"\x01":  # IPV4
        data = yield from iofree.read(4)
        host = socket.inet_ntoa(data)
    elif atyp == b"\x04":  # IPV6
        data = yield from iofree.read(16)
        host = socket.inet_ntop(socket.AF_INET6, data)
    elif atyp == b"\x03":  # hostname
        data = yield from iofree.read(1)
        data += yield from iofree.read(data[0])
        host = data[1:].decode("ascii")
    else:
        raise Exception(f"unknown atyp: {atyp}")
    data_port = yield from iofree.read(2)
    port = int.from_bytes(data_port, "big")
    return (host, port), atyp + data + data_port
Exemple #15
0
    def tls_response(self):
        parser = yield from iofree.get_parser()
        while True:
            head = yield from iofree.read(5)
            assert head[
                1:3] == b"\x03\x03", f"bad legacy_record_version {head[1:3]}"
            length = int.from_bytes(head[3:], "big")
            if (head[0] == ContentType.application_data and length >
                (16384 + 256)) or (head[0] != ContentType.application_data
                                   and length > 16384):
                parser.write(self.pack_fatal(AlertDescription.record_overflow))
                raise Alert(AlertLevel.fatal, AlertDescription.record_overflow)
            content = memoryview((yield from iofree.read(length)))
            if head[0] == ContentType.alert:
                level = AlertLevel.from_value(content[0])
                description = AlertDescription.from_value(content[1])
                raise Alert(level, description)
            elif head[0] == ContentType.handshake:
                self.peer_handshake = self.unpack_handshake(content)
                assert (self.peer_handshake.handshake_type ==
                        HandshakeType.server_hello), "expect server hello"
                peer_pk = self.peer_handshake.extensions[
                    ExtensionType.key_share].key_exchange
                shared_key = crypto_scalarmult(bytes(self.private_key),
                                               peer_pk)
                TLSCipher = self.peer_handshake.cipher_suite
                self.TLSCipher = TLSCipher
                key_index = self.peer_handshake.extensions.get(
                    ExtensionType.pre_shared_key)
                psk = None if key_index is None else self.psk_list[key_index]
                key_scheduler = TLSCipher.tls_hash.scheduler(shared_key, psk)
                self.key_scheduler = key_scheduler
                secret = key_scheduler.server_handshake_traffic_secret(
                    self.handshake_context)
                # server handshake cipher
                self.peer_cipher = TLSCipher(secret)
                client_handshake_traffic_secret = key_scheduler.client_handshake_traffic_secret(
                    self.handshake_context)
            elif head[0] == ContentType.application_data:
                plaintext = self.peer_cipher.decrypt(content,
                                                     head).rstrip(b"\x00")
                content_type = ContentType.from_value(plaintext[-1])
                if content_type == ContentType.handshake:
                    self.unpack_handshake(plaintext[:-1])
                    if self.server_finished:
                        if self.early_data:
                            eoe_data = HandshakeType.end_of_early_data.pack_data(
                                b"")
                            # self.handshake_context.extend(eoe_data)
                            inner_plaintext = ContentType.handshake.tls_inner_plaintext(
                                eoe_data)
                            record = self.cipher.tls_ciphertext(
                                inner_plaintext)
                            parser.write(record)

                        # client handshake cipher
                        cipher = TLSCipher(client_handshake_traffic_secret)
                        client_finished = cipher.verify_data(
                            self.handshake_context)
                        client_finished_data = HandshakeType.finished.pack_data(
                            client_finished)
                        inner_plaintext = ContentType.handshake.tls_inner_plaintext(
                            client_finished_data)
                        record = cipher.tls_ciphertext(inner_plaintext)
                        change_cipher_spec = ContentType.change_cipher_spec.tls_plaintext(
                            b"\x01")
                        parser.write(change_cipher_spec + record)
                        # server application cipher
                        server_secret = key_scheduler.server_application_traffic_secret_0(
                            self.handshake_context)
                        self.peer_cipher = TLSCipher(server_secret)
                        self.server_finished = False

                        # client application cipher
                        client_secret = key_scheduler.client_application_traffic_secret_0(
                            self.handshake_context)
                        self.cipher = TLSCipher(client_secret)
                        self.handshake_context.extend(client_finished_data)
                elif content_type == ContentType.application_data:
                    self.data_callback(plaintext[:-1])
                elif content_type == ContentType.alert:
                    level = AlertLevel.from_value(plaintext[0])
                    description = AlertDescription.from_value(plaintext[1])
                    raise Alert(level, description)
                elif content_type == ContentType.invalid:
                    raise Exception("invalid content type")
                else:
                    raise Exception(f"unexpected content type {content_type}")
            elif head[0] == ContentType.change_cipher_spec:
                assert content == b"\x01", "change_cipher should be 0x01"
            else:
                raise Exception(f"Unknown content type: {head[0]}")
Exemple #16
0
 def reader(self):
     salt = yield from iofree.read(self.cipher.SALT_SIZE)
     self.decrypt = self.cipher.make_decrypter(salt)
     while True:
         payload = yield from self.read_some()
         yield from iofree.write(payload)
Exemple #17
0
    def tls_response(self):
        while True:
            head = yield from iofree.read(5)
            assert head[
                1:3] == b"\x03\x03", f"bad legacy_record_version {head[1:3]}"
            length = int.from_bytes(head[3:], "big")
            content = memoryview((yield from iofree.read(length)))
            if head[0] == ContentType.alert:
                level = AlertLevel.from_value(content[0])
                description = AlertDescription.from_value(content[1])
                raise Alert(level, description)
            elif head[0] == ContentType.handshake:
                self.peer_handshake = self.unpack_handshake(content)
                assert (self.peer_handshake.handshake_type ==
                        HandshakeType.server_hello), "expect server hello"
                peer_pk = self.peer_handshake.extensions[
                    ExtensionType.key_share].key_exchange
                shared_key = crypto_scalarmult(bytes(self.private_key),
                                               peer_pk)
                TLSCipher = self.peer_handshake.cipher_suite
                key_scheduler = TLSCipher.tls_hash.scheduler(shared_key)
                secret = key_scheduler.server_handshake_traffic_secret(
                    self.get_context())
                # server handshake cipher
                self.peer_cipher = TLSCipher(secret)
                client_handshake_traffic_secret = key_scheduler.client_handshake_traffic_secret(
                    self.get_context())
            elif head[0] == ContentType.application_data:
                plaintext = self.peer_cipher.decrypt(content,
                                                     head).rstrip(b"\x00")
                content_type = ContentType.from_value(plaintext[-1])
                if content_type == ContentType.handshake:
                    self.unpack_handshake(plaintext[:-1])
                    if self.server_finished:
                        # client handshake cipher
                        self.cipher = TLSCipher(
                            client_handshake_traffic_secret)
                        context = b"".join(self.handshake_context)
                        client_finished = self.cipher.verify_data(context)
                        inner_plaintext = HandshakeType.finished.tls_inner_plaintext(
                            client_finished)
                        record = self.cipher.tls_ciphertext(inner_plaintext)
                        change_cipher_spec = ContentType.change_cipher_spec.tls_plaintext(
                            b"\x01")
                        yield from iofree.write(change_cipher_spec + record)
                        # server application cipher
                        server_secret = key_scheduler.server_application_traffic_secret_0(
                            self.get_context())
                        self.peer_cipher = TLSCipher(server_secret)
                        self.server_finished = False

                        # client application cipher
                        client_secret = key_scheduler.client_application_traffic_secret_0(
                            self.get_context())
                        self.cipher = TLSCipher(client_secret)

                elif content_type == ContentType.application_data:
                    yield from iofree.write(plaintext[:-1])
                elif content_type == ContentType.alert:
                    level = AlertLevel.from_value(plaintext[0])
                    description = AlertDescription.from_value(plaintext[1])
                    raise Alert(level, description)
                elif content_type == ContentType.invalid:
                    raise Exception("invalid content type")
                else:
                    raise Exception(f"unexpected content type {content_type}")
            elif head[0] == ContentType.change_cipher_spec:
                assert content == b"\x01", "change_cipher should be 0x01"
            else:
                raise Exception(f"Unknown content type: {head[0]}")
Exemple #18
0
def simple():
    parser = yield from iofree.get_parser()
    yield from iofree.read(1)
    assert parser.has_more_data()
    assert not parser.finished()
    raise Exception("special")