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): tls_version = plugin.tls_version tls_plaintext_head = memoryview((yield from iofree.read(5))) 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" fragment = memoryview((yield from iofree.read(length))) 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] yield from iofree.write(server_hello + change_cipher_spec) yield from ChangeCipherReader(plugin, plugin.server.cipher.master_key, session_id)
def socks5_request(auth=False): 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: yield from iofree.write(b"\x05\x02") raise Exception("server needs authentication") elif b"\x00" not in methods: yield from iofree.write(b"\x05\x00") raise Exception("method not support") if auth: yield from iofree.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: yield from iofree.write(b"\x01\x01") raise Exception("authenticate failed") else: yield from iofree.write(b"\x01\x00") else: yield from iofree.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
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]}")
def ss_reader(cipher): iv = yield from iofree.read(cipher.IV_SIZE) decrypt = cipher.make_decrypter(iv) while True: data = yield from iofree.read_more() yield from iofree.write(decrypt(data))
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)