def parse_fragment(fragment): msg_id, frag_byte, rest = cut(fragment, FRAG_ID_SIZE, FRAG_FLAG_SIZE) i_frag_byte = b2i(frag_byte) fragment_id = i_frag_byte & 0b0011_1111 is_last = ( i_frag_byte & FragmentGenerator.LAST_FRAG_FLAG) == FragmentGenerator.LAST_FRAG_FLAG payload_len = len(rest) has_padding = ( i_frag_byte & FragmentGenerator.PADDING_FLAG) == FragmentGenerator.PADDING_FLAG if has_padding: padding_len, padding_bytes = bytes_to_padding_length(rest) payload_len -= (padding_len + padding_bytes) _, payload, _ = cut(rest, padding_bytes, payload_len) else: payload = rest return b2i(msg_id), is_last, fragment_id, payload
def test_get_data_fragment(): udp_payload = get_random_bytes(DATA_FRAG_PAYLOAD_SIZE) f = FragmentGenerator(udp_payload) fragment = f.get_data_fragment() _, frag_byte, payload = cut(fragment, FRAG_ID_SIZE, FRAG_FLAG_SIZE) assert b2i(frag_byte) & FragmentGenerator.LAST_FRAG_FLAG assert b2i(frag_byte) == 0b0100_0000 assert payload[0:DATA_FRAG_PAYLOAD_SIZE] == udp_payload assert len( fragment) == DATA_FRAG_PAYLOAD_SIZE + FRAG_ID_SIZE + FRAG_FLAG_SIZE
def parse_channel_init(self, channel_init): self.last_interaction = time() _, _, payload = cut_init_message(channel_init) ip, port, fragment = cut(payload, IPV4_LEN, PORT_LEN) ip = b2ip(ip) port = b2i(port) self.dest_addr = (ip, port) try: self.out_sock.connect(self.dest_addr) except OSError: # couldn't connect, maybe not a channel init message? print("Couldn't connect to destination. Dropped message.") self.out_sock.close() del ChannelExit.table[self.in_chan_id] return ChannelExit.sock_sel.register(self.out_sock, EVENT_READ, data=self) print(self, "Init", "->", len(channel_init)) self.recv_request(fragment)
def parse_channel_init(self, channel_init, priv_comp): """Takes an already decrypted channel init message and reads the key. """ self.last_interaction = time() msg_ctr, channel_init = cut(channel_init, CTR_PREFIX_LEN) self.request_replay_detector.check_replay_window(b2i(msg_ctr)) sym_key, _, channel_init = process(priv_comp, channel_init) if self.key is not None: assert self.key == sym_key self.initialized = True else: self.key = sym_key print(self, "Init", "->", len(channel_init)) channel_init = msg_ctr + channel_init # we add an empty ctr prefix, because the link encryption expects there # to be one, even though the channel init wasn't sym encrypted # todo look at this one again packet = CHAN_INIT_MSG_FLAG + i2b(self.out_chan_id, CHAN_ID_SIZE) + channel_init ChannelMid.requests.append(packet)
def forward_response(self, response): self.last_interaction = time() # cut the padding off response, _ = cut(response, -CTR_MODE_PADDING) msg_type, response = cut(response, MSG_TYPE_FLAG_LEN) msg_ctr, _ = cut(response, CTR_PREFIX_LEN) # todo find better way if msg_ctr != bytes(CTR_PREFIX_LEN): self.response_replay_detector.check_replay_window(b2i(msg_ctr)) self.response_counter.next() cipher = ctr_cipher(self.key, int(self.response_counter)) forward_msg = cipher.encrypt(response) print(self, "Data", "<-", len(forward_msg)) response = msg_type + i2b(self.in_chan_id, CHAN_ID_SIZE) + bytes(self.response_counter) + forward_msg ChannelMid.responses.append(response)
def decrypt(self, cipher_text): b_link_ctr, header, mac, fragment = cut(cipher_text, CTR_PREFIX_LEN, LINK_HEADER_LEN, GCM_MAC_LEN) link_ctr = b2i(b_link_ctr) cipher = gcm_cipher(self.key, link_ctr) plain_header = cipher.decrypt_and_verify(header, mac) chan_id, msg_ctr, msg_type, _ = cut(plain_header, CHAN_ID_SIZE, CTR_PREFIX_LEN, MSG_TYPE_FLAG_LEN) self.replay_detector.check_replay_window(link_ctr) return b2i(chan_id), msg_ctr, fragment, msg_type
def test_get_data_fragment_with_padding(): udp_payload = get_random_bytes(DATA_FRAG_PAYLOAD_SIZE - 1) f = FragmentGenerator(udp_payload) fragment = f.get_data_fragment() _, frag_byte, _, payload = cut(fragment, FRAG_ID_SIZE, FRAG_FLAG_SIZE, 1) assert b2i(frag_byte) & FragmentGenerator.LAST_FRAG_FLAG assert b2i(frag_byte) & FragmentGenerator.PADDING_FLAG assert b2i(frag_byte) == 0b1100_0000 assert payload[0:DATA_FRAG_PAYLOAD_SIZE - 1] == udp_payload assert len( fragment) == DATA_FRAG_PAYLOAD_SIZE + FRAG_ID_SIZE + FRAG_FLAG_SIZE udp_payload = get_random_bytes(1) f = FragmentGenerator(udp_payload) fragment = f.get_data_fragment() _, frag_byte, _, payload = cut(fragment, FRAG_ID_SIZE, FRAG_FLAG_SIZE, 2) assert b2i(frag_byte) & FragmentGenerator.LAST_FRAG_FLAG assert b2i(frag_byte) & FragmentGenerator.PADDING_FLAG assert b2i(frag_byte) == 0b1100_0000 assert payload[0:1] == udp_payload assert len( fragment) == DATA_FRAG_PAYLOAD_SIZE + FRAG_ID_SIZE + FRAG_FLAG_SIZE
def _decrypt_fragment(self, fragment): ctr = 0 for key in self.sym_keys: ctr, cipher_text = cut(fragment, CTR_PREFIX_LEN) ctr = b2i(ctr) cipher = ctr_cipher(key, ctr) fragment = cipher.decrypt(cipher_text) self.replay_detector.check_replay_window(ctr) return fragment
def handle_client_request(self, request, src_addr): """Takes a message and the source address it came from. The destination header is cut off, parsed and mapped to a channel. Then the payload is separated into mix fragments and sent out with the channel id in front. """ dest_ip, dest_port, payload = cut(request, IPV4_LEN, PORT_LEN) dest_addr = (b2ip(dest_ip), b2i(dest_port)) if (src_addr, dest_addr) not in self.ips2id: channel = self.make_new_channel(src_addr, dest_addr) else: try: channel_id = self.ips2id[src_addr, dest_addr] channel = ChannelEntry.table[channel_id] except KeyError: del self.ips2id[src_addr, dest_addr] channel = self.make_new_channel(src_addr, dest_addr) # add fragments to internal packet list channel.request(payload)
def test_decrypt_fragment(): channel = ChannelEntry(src_addr, dest_addr, public_keys) sym_key = gen_sym_key() channel.sym_keys = [sym_key] * MIX_COUNT fragment = FragmentGenerator(bytes(100)).get_data_fragment() fragment = channel._encrypt_fragment(fragment) packet1 = channel._decrypt_fragment(fragment) for _ in range(MIX_COUNT): msg_ctr, fragment = cut(fragment, CTR_PREFIX_LEN) cipher = ctr_cipher(sym_key, b2i(msg_ctr)) fragment = cipher.decrypt(fragment) packet2 = fragment assert packet1 == packet2
def forward_request(self, request): """Takes a mix fragment, already stripped of the channel id.""" self.last_interaction = time() ctr, cipher_text = cut(request, CTR_PREFIX_LEN) ctr = b2i(ctr) self.request_replay_detector.check_replay_window(ctr) cipher = ctr_cipher(self.key, ctr) forward_msg = cipher.decrypt(cipher_text) + get_random_bytes(CTR_MODE_PADDING) print(self, "Data", "->", len(forward_msg) - CTR_PREFIX_LEN) ChannelMid.requests.append(DATA_MSG_FLAG + i2b(self.out_chan_id, CHAN_ID_SIZE) + forward_msg) timed_out = check_for_timed_out_channels(ChannelMid.table_in) for in_id in timed_out: out_id = ChannelMid.table_in[in_id].out_chan_id del ChannelMid.table_in[in_id] del ChannelMid.table_out[out_id]
"Receives data on the specified ip:port using UDP and prints it on stdout." ) parser.add_argument("ip:port", help="IP and Port pair to listen for datagrams on.") BUFFER_SIZE = 65535 if __name__ == "__main__": args = parser.parse_args() ip, port = getattr(args, "ip:port").split(":") port = int(port) sock = socket(AF_INET, UDP) sock.bind((ip, port)) packets = 0 start_time = None while True: try: data, addr = sock.recvfrom(BUFFER_SIZE) packets += 1 print(str(b2i(data[0:4])).rjust(7), "{:.9f}".format(time())) except KeyboardInterrupt: break
output_file = argv[1] for port_number in argv[2:]: listener_socket = socket(AF_INET, SOCK_DGRAM) listener_socket.bind(("127.0.0.1", int(port_number))) socket_selector.register(listener_socket, EVENT_READ) def set_killed(signal, stackframe): global killed killed = True signal(SIGTERM, set_killed) killed = False with open(output_file, "w") as f: while not killed: events = socket_selector.select(timeout=2) for key, _ in events: sock = key.fileobj data = sock.recv(UDP_MTU) f.write("{} {:.9f}\n".format(str(b2i(data[0:4])).rjust(7), time())) f.flush()