def __init__(self, operation: str, local_test: bool, dd_sockets: bool, qubes: bool, ) -> None: """Create a new Gateway object.""" self.settings = GatewaySettings(operation, local_test, dd_sockets, qubes) self.tx_serial = None # type: Optional[serial.Serial] self.rx_serial = None # type: Optional[serial.Serial] self.rx_socket = None # type: Optional[multiprocessing.connection.Connection] self.tx_socket = None # type: Optional[multiprocessing.connection.Connection] # Initialize Reed-Solomon erasure code handler self.rs = RSCodec(2 * self.settings.session_serial_error_correction) # Set True when the serial interface is initially found so that # further interface searches know to announce disconnection. self.init_found = False if self.settings.local_testing_mode: if self.settings.software_operation in [TX, NC]: self.client_establish_socket() if self.settings.software_operation in [NC, RX]: self.server_establish_socket() elif not self.settings.qubes: self.establish_serial()
def receiver_loop(settings: 'Settings', queues: Dict[bytes, 'Queue']) -> None: """Decode and queue received packets.""" rs = RSCodec(2 * settings.session_ec_ratio) gw_queue = queues[GATEWAY_QUEUE] while True: try: if gw_queue.empty(): time.sleep(0.001) packet = gw_queue.get() ts = datetime.datetime.now() try: packet = bytes(rs.decode(bytearray(packet))) except ReedSolomonError: box_print(["Warning! Failed to correct errors in received packet."], head=1, tail=1) continue p_header = packet[:1] if p_header in [PUBLIC_KEY_PACKET_HEADER, MESSAGE_PACKET_HEADER, LOCAL_KEY_PACKET_HEADER, COMMAND_PACKET_HEADER, IMPORTED_FILE_CT_HEADER]: queues[p_header].put((ts, packet)) except (KeyboardInterrupt, EOFError): pass
def test_prim_fcr_long(self) -> None: nn = 48 kk = 34 tt = nn - kk rs = RSCodec(tt, fcr=120, prim=0x187) hex_enc_msg = ('08faa123555555c000000354064432c0280e1b4d090cfc04' '887400000003500000000e1985ff9c6b33066ca9f43d12e8') strf = str enc_msg = bytearray.fromhex(strf(hex_enc_msg)) dec_msg = enc_msg[:kk] tem = rs.encode(dec_msg) self.assertEqual(enc_msg, tem, msg="encoded does not match expected") tdm, rtem = rs.decode(tem) self.assertEqual(tdm, dec_msg, msg="decoded does not match original") self.assertEqual(rtem, tem, msg="decoded mesecc does not match original") tem1 = bytearray(tem) num_errs = tt >> 1 for i in sample(range(nn), num_errs): tem1[i] ^= 0xff tdm, rtem = rs.decode(tem1) self.assertEqual(tdm, dec_msg, msg="decoded with errors does not match original") self.assertEqual(rtem, tem, msg="decoded mesecc with errors does not match original") tem1 = bytearray(tem) num_errs += 1 for i in sample(range(nn), num_errs): tem1[i] ^= 0xff self.assertRaises(ReedSolomonError, rs.decode, tem1)
def test_prim_fcr_basic(self) -> None: nn = 30 kk = 18 tt = nn - kk rs = RSCodec(tt, fcr=120, prim=0x187) hex_enc_msg = ('00faa123555555c000000354064432' 'c02800fe97c434e1ff5365cf8fafe4') strf = str enc_msg = bytearray.fromhex(strf(hex_enc_msg)) dec_msg = enc_msg[:kk] tem = rs.encode(dec_msg) self.assertEqual(enc_msg, tem, msg="encoded does not match expected") tdm, rtem = rs.decode(tem) self.assertEqual(tdm, dec_msg, msg="decoded does not match original") self.assertEqual(rtem, tem, msg="decoded mesecc does not match original") tem1 = bytearray(tem) # Clone a copy # Encoding and decoding intact message seem OK, so test errors num_errs = tt >> 1 # Inject tt/2 errors (expected to recover fully) for i in sample(range(nn), num_errs): # inject errors in random places tem1[i] ^= 0xff # flip all 8 bits tdm, _ = rs.decode(tem1) self.assertEqual(tdm, dec_msg, msg="decoded with errors does not match original") tem1 = bytearray(tem) # Clone another copy num_errs += 1 # Inject tt/2 + 1 errors (expected to fail and detect it) for i in sample(range(nn), num_errs): # Inject errors in random places tem1[i] ^= 0xff # Flip all 8 bits # If this fails, it means excessive errors not detected self.assertRaises(ReedSolomonError, rs.decode, tem1)
def test_simple(self) -> None: rs = RSCodec() msg = bytearray("hello world " * 10, "latin1") enc = rs.encode(msg) dec, dec_enc = rs.decode(enc) self.assertEqual(dec, msg) self.assertEqual(dec_enc, enc)
def rxm_outgoing(queues: Dict[bytes, 'Queue'], settings: 'Settings', gateway: 'Gateway', unittest: bool = False) -> None: """Loop that outputs packets from queues to RxM. Commands (and local keys) from TxM to RxM have higher priority than messages and public keys from contacts. This prevents contact from doing DoS on RxM by filling queue with packets. """ rs = RSCodec(2 * settings.session_serial_error_correction) c_queue = queues[TXM_TO_RXM_QUEUE] m_queue = queues[RXM_OUTGOING_QUEUE] while True: try: time.sleep(0.01) while c_queue.qsize() != 0: packet = rs.encode(bytearray(c_queue.get())) gateway.write(packet) if m_queue.qsize() != 0: packet = rs.encode(bytearray(m_queue.get())) gateway.write(packet) if unittest: break except (EOFError, KeyboardInterrupt): pass
def setUp(self): self.settings = Settings() self.unittest_dir = cd_unittest() self.gateway = Gateway() self.rs = RSCodec(2 * self.gateway.settings.serial_error_correction) self.ts = datetime.now() self.queues = gen_queue_dict() self.args = self.queues, self.gateway
def setUp(self) -> None: """Pre-test actions.""" self.settings = Settings() self.unit_test_dir = cd_unit_test() self.gateway = Gateway() self.rs = RSCodec(2 * self.gateway.settings.serial_error_correction) self.ts = datetime.now() self.queues = gen_queue_dict() self.args = self.queues, self.gateway self.queues[TX_BUF_KEY_QUEUE].put(SYMMETRIC_KEY_LENGTH * b'a')
def transmit(packet: bytes, settings: 'Settings', gateway: 'Gateway') -> None: """Add Reed-Solomon erasure code and output packet via gateway.""" rs = RSCodec(2 * settings.session_ec_ratio) packet = rs.encode(packet) gateway.write(packet) if not settings.session_trickle: if settings.long_packet_rand_d: random_delay = random.SystemRandom().uniform(0, settings.max_val_for_rand_d) time.sleep(random_delay)
def txm_incoming(settings: 'Settings', q_to_tip: 'Queue', q_to_rxm: 'Queue', q_to_im: 'Queue', q_to_nh: 'Queue') -> None: """Load messages from TxM and forward them to appropriate process via queue.""" rs = RSCodec(2 * settings.session_ec_ratio) while True: try: if q_to_tip.empty(): time.sleep(0.001) packet = q_to_tip.get() try: packet = bytes(rs.decode(packet)) except ReedSolomonError: box_print( ["Warning! Failed to correct errors in received packet."], head=1, tail=1) continue ts = datetime.datetime.now().strftime(settings.t_fmt) header = packet[:1] if header == UNENCRYPTED_PACKET_HEADER: q_to_nh.put(packet[1:]) elif header in [LOCAL_KEY_PACKET_HEADER, COMMAND_PACKET_HEADER]: p_type = 'local key' if header == LOCAL_KEY_PACKET_HEADER else 'command' print("{} - {} TxM > RxM".format(ts, p_type)) q_to_rxm.put(packet) elif header in [MESSAGE_PACKET_HEADER, PUBLIC_KEY_PACKET_HEADER]: payload_len, p_type = ( 32, 'pub key') if header == PUBLIC_KEY_PACKET_HEADER else ( 344, 'message') payload = packet[1:1 + payload_len] trailer = packet[1 + payload_len:] user, contact = trailer.split(US_BYTE) print("{} - {} TxM > {} > {}".format(ts, p_type, user.decode(), contact.decode())) q_to_im.put((header, payload, user, contact)) q_to_rxm.put(header + payload + ORIGIN_USER_HEADER + contact) elif header == EXPORTED_FILE_CT_HEADER: payload = packet[1:] file_name = os.urandom(16).hex() with open(file_name, 'wb+') as f: f.write(payload) print("{} - Exported file from TxM as {}".format( ts, file_name)) except (EOFError, KeyboardInterrupt): pass
def setUp(self): self.settings = Settings() self.rs = RSCodec(2 * self.settings.serial_error_correction) self.o_urandom = os.urandom self.queues = {TXM_INCOMING_QUEUE: Queue(), RXM_OUTGOING_QUEUE: Queue(), TXM_TO_IM_QUEUE: Queue(), TXM_TO_NH_QUEUE: Queue(), TXM_TO_RXM_QUEUE: Queue(), NH_TO_IM_QUEUE: Queue(), EXIT_QUEUE: Queue()}
def test_correction(self) -> None: rs = RSCodec() msg = bytearray("hello world " * 10, "latin1") enc = rs.encode(msg) rmsg, renc = rs.decode(enc) self.assertEqual(rmsg, msg) self.assertEqual(renc, enc) for i in [27, -3, -9, 7, 0]: enc[i] = 99 rmsg, renc = rs.decode(enc) self.assertEqual(rmsg, msg) enc[82] = 99 self.assertRaises(ReedSolomonError, rs.decode, enc)
def test_long(self) -> None: rs = RSCodec() msg = bytearray("a" * 10000, "latin1") enc = rs.encode(msg) dec, dec_enc = rs.decode(enc) self.assertEqual(dec, msg) self.assertEqual(dec_enc, enc) enc2 = list(enc) enc2[177] = 99 enc2[2212] = 88 dec2, dec_enc2 = rs.decode(bytes(enc2)) self.assertEqual(dec2, msg) self.assertEqual(dec_enc2, enc)
def rxm_outgoing(settings: 'Settings', q_to_rxm: 'Queue', gateway: 'Gateway') -> None: """Output packets from RxM-queue to RxM.""" rs = RSCodec(2 * settings.session_ec_ratio) while True: try: if q_to_rxm.empty(): time.sleep(0.001) continue from_q = q_to_rxm.get() packet = rs.encode(bytearray(from_q)) gateway.write(packet) except (EOFError, KeyboardInterrupt): pass
def test_check(self) -> None: rs = RSCodec() msg = bytearray("hello world " * 10, "latin1") enc = rs.encode(msg) rmsg, renc = rs.decode(enc) self.assertEqual(rs.check(enc), [True]) self.assertEqual(rs.check(renc), [True]) for i in [27, -3, -9, 7, 0]: enc[i] = 99 rmsg, renc = rs.decode(enc) self.assertEqual(rs.check(enc), [False]) self.assertEqual(rs.check(renc), [True])
def test_detect_errors(self, *_: Any) -> None: gateway = Gateway(operation=RX, local_test=False, dd_sockets=False) packet = b'packet' # Test BLAKE2b based checksum gateway.settings.session_serial_error_correction = 0 self.assertEqual( gateway.detect_errors(gateway.add_error_correction(packet)), packet) # Test unrecoverable error raises FR self.assert_se("Warning! Received packet had an invalid checksum.", gateway.detect_errors, 300 * b'a') # Test Reed-Solomon erasure code gateway.settings.session_serial_error_correction = 5 gateway.rs = RSCodec(gateway.settings.session_serial_error_correction) self.assertEqual( gateway.detect_errors(gateway.add_error_correction(packet)), packet) # Test unrecoverable error raises FR self.assert_se( "Error: Reed-Solomon failed to correct errors in the received packet.", gateway.detect_errors, 300 * b'a')
class TestRxMOutGoing(unittest.TestCase): def setUp(self): self.settings = Settings() self.gateway = Gateway() self.rs = RSCodec(2 * self.settings.serial_error_correction) self.queues = {TXM_INCOMING_QUEUE: Queue(), RXM_OUTGOING_QUEUE: Queue(), TXM_TO_IM_QUEUE: Queue(), TXM_TO_NH_QUEUE: Queue(), TXM_TO_RXM_QUEUE: Queue(), NH_TO_IM_QUEUE: Queue(), EXIT_QUEUE: Queue()} def tearDown(self): for k in self.queues: while not self.queues[k].empty(): self.queues[k].get() time.sleep(0.1) self.queues[k].close() def test_loop(self): # Setup packet = b'testpacket' self.queues[TXM_TO_RXM_QUEUE].put(packet) self.queues[RXM_OUTGOING_QUEUE].put(packet) time.sleep(0.1) # Test self.assertIsNone(rxm_outgoing(self.queues, self.settings, self.gateway, unittest=True)) self.assertEqual(packet, self.rs.decode(self.gateway.packets[0]))
def calculate_race_condition_delay(settings: Union['Settings', 'NHSettings'], txm: bool = False) -> float: """Calculate NH race condition delay. This value is the max time it takes for NH to deliver command received from TxM all the way to RxM. :param settings: Settings object :param txm: When True, allocate time for command delivery from TxM to NH :return: Time to wait to prevent race condition """ rs = RSCodec(2 * settings.session_serial_error_correction) max_account_length = 254 max_message_length = PACKET_LENGTH + 2 * max_account_length command_length = 365*2 if txm else 365 max_bytes = (len(rs.encode(os.urandom(max_message_length))) + len(rs.encode(os.urandom(command_length)))) return (max_bytes * BAUDS_PER_BYTE) / settings.serial_baudrate
def update_delivery_time(self) -> None: """Calculate transmission time. Transmission time is based on average delays and settings. """ no_packets = self.count_number_of_packets() if self.settings.session_traffic_masking: avg_delay = self.settings.traffic_masking_static_delay + ( self.settings.traffic_masking_random_delay / 2) if self.settings.multi_packet_random_delay: avg_delay += (self.settings.max_duration_of_random_delay / 2) total_time = len(self.window) * no_packets * avg_delay total_time *= 2 # Accommodate command packets between file packets total_time += no_packets * TRAFFIC_MASKING_QUEUE_CHECK_DELAY else: # Determine total data to be transmitted over serial rs = RSCodec(2 * self.settings.session_serial_error_correction) total_data = 0 for c in self.window: data = os.urandom(PACKET_LENGTH) + c.rx_account.encode( ) + c.tx_account.encode() enc_data = rs.encode(data) total_data += no_packets * len(enc_data) # Determine time required to send all data total_time = 0.0 if self.settings.local_testing_mode: total_time += no_packets * LOCAL_TESTING_PACKET_DELAY else: total_bauds = total_data * BAUDS_PER_BYTE total_time += total_bauds / self.settings.session_serial_baudrate total_time += no_packets * self.settings.txm_inter_packet_delay if self.settings.multi_packet_random_delay: total_time += no_packets * ( self.settings.max_duration_of_random_delay / 2) # Update delivery time self.time_bytes = int_to_bytes(int(total_time)) self.time_print = str(datetime.timedelta(seconds=int(total_time)))
def test_receiver_loop(self) -> None: # Setup gateway = Gateway(local_test=False) rs = RSCodec(2 * gateway.settings.serial_error_correction) queues = { MESSAGE_DATAGRAM_HEADER: Queue(), FILE_DATAGRAM_HEADER: Queue(), COMMAND_DATAGRAM_HEADER: Queue(), LOCAL_KEY_DATAGRAM_HEADER: Queue() } all_q = dict(queues) all_q.update({GATEWAY_QUEUE: Queue()}) ts = datetime.now() ts_bytes = int_to_bytes(int(ts.strftime('%Y%m%d%H%M%S%f')[:-4])) for key in queues: packet = key + ts_bytes + bytes(ONION_SERVICE_PUBLIC_KEY_LENGTH) encoded = rs.encode(packet) broken_p = key + bytes.fromhex('df9005313af4136d') + bytes( ONION_SERVICE_PUBLIC_KEY_LENGTH) broken_p += rs.encode(b'a') def queue_delayer() -> None: """Place datagrams into queue after delay.""" time.sleep(0.01) all_q[GATEWAY_QUEUE].put( (datetime.now(), rs.encode(8 * b'1' + b'undecodable'))) all_q[GATEWAY_QUEUE].put((datetime.now(), broken_p)) all_q[GATEWAY_QUEUE].put((datetime.now(), encoded)) threading.Thread(target=queue_delayer).start() # Test self.assertIsNone(receiver_loop(all_q, gateway, unit_test=True)) time.sleep(0.01) self.assertEqual(queues[key].qsize(), 1) # Teardown tear_queue(queues[key])
def calculate_race_condition_delay(serial_error_correction: int, serial_baudrate: int) -> float: """\ Calculate the delay required to prevent Relay Program race condition. When Transmitter Program outputs a command to exit or wipe data, Relay program will also receive a copy of the command. If the Relay Program acts on the command too early, the Receiver Program will not receive the exit/wipe command at all. This function calculates the delay Transmitter Program should wait before outputting command to the Relay Program, to ensure the Receiver Program has received its encrypted command. """ rs = RSCodec(2 * serial_error_correction) message_length = PACKET_LENGTH + ONION_ADDRESS_LENGTH enc_msg_length = len(rs.encode(os.urandom(message_length))) enc_cmd_length = len(rs.encode(os.urandom(COMMAND_LENGTH))) max_bytes = enc_msg_length + (2 * enc_cmd_length) return (max_bytes * BAUDS_PER_BYTE) / serial_baudrate
def transmit(packet: bytes, settings: 'Settings', gateway: 'Gateway', delay: bool = True) -> None: """Add Reed-Solomon erasure code and output packet via gateway. Note that random.SystemRandom() uses Kernel CSPRNG (/dev/urandom), not Python's weak RNG based on Mersenne Twister: https://docs.python.org/2/library/random.html#random.SystemRandom """ rs = RSCodec(2 * settings.session_serial_error_correction) packet = rs.encode(packet) gateway.write(packet) if settings.local_testing_mode: time.sleep(LOCAL_TESTING_PACKET_DELAY) if not settings.session_traffic_masking: if settings.multi_packet_random_delay and delay: random_delay = random.SystemRandom().uniform(0, settings.max_duration_of_random_delay) time.sleep(random_delay)
def test_add_error_correction(self, *_) -> None: gateway = Gateway(operation=RX, local_test=False, dd_sockets=False, qubes=False) packet = b"packet" # Test BLAKE2b based checksum gateway.settings.session_serial_error_correction = 0 self.assertEqual(gateway.add_error_correction(packet), packet + blake2b(packet, digest_size=PACKET_CHECKSUM_LENGTH)) # Test Reed-Solomon erasure code gateway.settings.session_serial_error_correction = 5 gateway.rs = RSCodec(gateway.settings.session_serial_error_correction) self.assertEqual(gateway.add_error_correction(packet), gateway.rs.encode(packet))
def receiver_loop(queues: Dict[bytes, 'Queue'], settings: 'Settings', unittest: bool = False) -> None: """Decode received packets and forward them to packet queues. This function also determines the timestamp for received message. """ rs = RSCodec(2 * settings.session_serial_error_correction) gw_queue = queues[GATEWAY_QUEUE] while True: with ignored(EOFError, KeyboardInterrupt): if gw_queue.qsize() == 0: time.sleep(0.01) packet = gw_queue.get() timestamp = datetime.now() try: packet = bytes(rs.decode(packet)) except ReedSolomonError: box_print( "Error: Failed to correct errors in received packet.", head=1, tail=1) continue p_header = packet[:1] if p_header in [ PUBLIC_KEY_PACKET_HEADER, MESSAGE_PACKET_HEADER, LOCAL_KEY_PACKET_HEADER, COMMAND_PACKET_HEADER, IMPORTED_FILE_HEADER ]: queues[p_header].put((timestamp, packet)) if unittest: break
def test_receiver_loop(self): # Setup settings = Settings() rs = RSCodec(2 * settings.serial_error_correction) queues = { LOCAL_KEY_PACKET_HEADER: Queue(), PUBLIC_KEY_PACKET_HEADER: Queue(), MESSAGE_PACKET_HEADER: Queue(), COMMAND_PACKET_HEADER: Queue(), IMPORTED_FILE_HEADER: Queue() } all_q = dict(queues) all_q.update({GATEWAY_QUEUE: Queue()}) for key in queues: packet = key + bytes(KEY_LENGTH) encoded = rs.encode(packet) def queue_delayer(): time.sleep(0.1) all_q[GATEWAY_QUEUE].put(b'undecodable') all_q[GATEWAY_QUEUE].put(encoded) threading.Thread(target=queue_delayer).start() # Test self.assertIsNone(receiver_loop(all_q, settings, unittest=True)) time.sleep(0.1) self.assertEqual(queues[key].qsize(), 1) # Teardown while not queues[key].empty(): queues[key].get() time.sleep(0.1) queues[key].close()
def test_c_exp_12(self) -> None: rsc = RSCodec(12, c_exp=12) rsc2 = RSCodec(12, nsize=4095) self.assertEqual(rsc.c_exp, rsc2.c_exp) self.assertEqual(rsc.nsize, rsc2.nsize) mes = 'a'*(4095-12) mesecc = rsc.encode(mes) mesecc[2] = 1 mesecc[-1] = 1 rmes, rmesecc = rsc.decode(mesecc) self.assertEqual(rsc.check(mesecc), [False]) self.assertEqual(rsc.check(rmesecc), [True]) self.assertEqual([x for x in rmes], [ord(x) for x in mes])
def test_c_exp_9(self): rsc = RSCodec(12, c_exp=9) rsc2 = RSCodec(12, nsize=511) self.assertEqual(rsc.c_exp, rsc2.c_exp) self.assertEqual(rsc.nsize, rsc2.nsize) mes = 'a' * ((511 - 12) * 2) mesecc = rsc.encode(mes) mesecc[2] = 1 mesecc[-1] = 1 rmes, rmesecc = rsc.decode(mesecc) self.assertEqual(rsc.check(mesecc), [False, False]) self.assertEqual(rsc.check(rmesecc), [True, True]) self.assertEqual([x for x in rmes], [ord(x) for x in mes])
def test_detect_errors(self, *_: Any) -> None: gateway = Gateway(operation=RX, local_test=False, dd_sockets=False, qubes=False) packet = b'packet' # Test BLAKE2b based checksum gateway.settings.session_serial_error_correction = 0 self.assertEqual( gateway.detect_errors(gateway.add_error_correction(packet)), packet) # Test unrecoverable error raises SoftError self.assert_se("Warning! Received packet had an invalid checksum.", gateway.detect_errors, 300 * b'a') # Test Reed-Solomon erasure code gateway.settings.session_serial_error_correction = 5 gateway.rs = RSCodec(gateway.settings.session_serial_error_correction) self.assertEqual( gateway.detect_errors(gateway.add_error_correction(packet)), packet) # Test unrecoverable error raises SoftError self.assert_se( "Error: Reed-Solomon failed to correct errors in the received packet.", gateway.detect_errors, 300 * b'a') # Qubes # Test with B58 encoding gateway.settings.qubes = True packet_with_error_correction = base64.b85encode( gateway.add_error_correction(packet)) self.assertEqual(gateway.detect_errors(packet_with_error_correction), packet) # Test invalid B85 encoding raises SoftError packet_with_error_correction = base64.b85encode( gateway.add_error_correction(packet)) packet_with_error_correction += b'\x00' self.assert_se("Error: Received packet had invalid Base85 encoding.", gateway.detect_errors, packet_with_error_correction) gateway.settings.qubes = False
def test_multiple_rs_codec(self) -> None: """Test multiple RSCodec instances with different parameters.""" mes = 'A' * 30 rs_256 = RSCodec(102) rs_1024 = RSCodec(900, c_exp=10) bytearray(rs_1024.decode(rs_1024.encode(mes))[0]) rs_256.encode(mes) rs_1024.encode(mes) bytearray(rs_256.decode(rs_256.encode(mes))[0])
def txm_incoming(queues: Dict[bytes, 'Queue'], settings: 'Settings', unittest: bool = False) -> None: """Loop that places messages received from TxM to appropriate queues.""" rs = RSCodec(2 * settings.session_serial_error_correction) q_to_tip = queues[TXM_INCOMING_QUEUE] m_to_rxm = queues[RXM_OUTGOING_QUEUE] c_to_rxm = queues[TXM_TO_RXM_QUEUE] q_to_im = queues[TXM_TO_IM_QUEUE] q_to_nh = queues[TXM_TO_NH_QUEUE] while True: with ignored(EOFError, KeyboardInterrupt): while q_to_tip.qsize() == 0: time.sleep(0.01) packet = q_to_tip.get() try: packet = bytes(rs.decode(packet)) except ReedSolomonError: box_print( "Warning! Failed to correct errors in received packet.", head=1, tail=1) continue ts = datetime.now().strftime("%m-%d / %H:%M:%S") header = packet[:1] if header == UNENCRYPTED_PACKET_HEADER: q_to_nh.put(packet[1:]) elif header in [LOCAL_KEY_PACKET_HEADER, COMMAND_PACKET_HEADER]: p_type = 'local key' if header == LOCAL_KEY_PACKET_HEADER else 'command' print("{} - {} TxM > RxM".format(ts, p_type)) c_to_rxm.put(packet) elif header in [MESSAGE_PACKET_HEADER, PUBLIC_KEY_PACKET_HEADER]: payload_len, p_type = { PUBLIC_KEY_PACKET_HEADER: (KEY_LENGTH, 'pub key'), MESSAGE_PACKET_HEADER: (MESSAGE_LENGTH, 'message') }[header] payload = packet[1:1 + payload_len] trailer = packet[1 + payload_len:] user, contact = trailer.split(US_BYTE) print("{} - {} TxM > {} > {}".format(ts, p_type, user.decode(), contact.decode())) q_to_im.put((header, payload, user, contact)) m_to_rxm.put(header + payload + ORIGIN_USER_HEADER + contact) elif header == EXPORTED_FILE_HEADER: payload = packet[1:] file_name = os.urandom(8).hex() while os.path.isfile(file_name): file_name = os.urandom(8).hex() with open(file_name, 'wb+') as f: f.write(payload) print("{} - Exported file from TxM as {}".format( ts, file_name)) if unittest: break