def client_establish_socket(self) -> None: """Initialize the transmitter (IPC client).""" try: target = RECEIVER if self.settings.software_operation == NC else RELAY phase(f"Connecting to {target}") while True: try: if self.settings.software_operation == TX: socket_number = SRC_DD_LISTEN_SOCKET if self.settings.data_diode_sockets else RP_LISTEN_SOCKET else: socket_number = DST_DD_LISTEN_SOCKET if self.settings.data_diode_sockets else DST_LISTEN_SOCKET try: self.tx_socket = multiprocessing.connection.Client((LOCALHOST, socket_number)) except ConnectionRefusedError: time.sleep(0.1) continue phase(DONE) break except socket.error: time.sleep(0.1) except KeyboardInterrupt: graceful_exit()
def read(self) -> bytes: """Read data via socket/serial interface.""" if self.settings.local_testing_mode: while True: try: return self.txm_interface.recv() except KeyboardInterrupt: pass except EOFError: graceful_exit("IPC client disconnected.") else: while True: try: start_time = 0.0 read_buffer = bytearray() while True: read = self.txm_interface.read(1000) if read: start_time = time.monotonic() read_buffer.extend(read) else: if read_buffer: delta = time.monotonic() - start_time if delta > self.settings.receive_timeout: return bytes(read_buffer) else: time.sleep(0.001) except KeyboardInterrupt: pass except SerialException: self.txm_interface = self.establish_serial() self.read()
def establish_serial(self) -> Any: """Create a new Serial object.""" try: serial_nh = self.search_serial_interface() return serial.Serial(serial_nh, self.settings.session_serial_baudrate, timeout=0) except SerialException: graceful_exit( "SerialException. Ensure $USER is in the dialout group.")
def __init__(self, operation: str, local_test: bool) -> None: """Create a new MasterKey object.""" self.file_name = f'{DIR_USER_DATA}{operation}_login_data' self.local_test = local_test ensure_dir(DIR_USER_DATA) try: if os.path.isfile(self.file_name): self.master_key = self.load_master_key() else: self.master_key = self.new_master_key() except (EOFError, KeyboardInterrupt): graceful_exit()
def client_establish_socket(self) -> None: """Establish IPC client.""" try: phase("Waiting for connection to NH", offset=11) while True: try: socket_number = TXM_DD_LISTEN_SOCKET if self.settings.data_diode_sockets else NH_LISTEN_SOCKET self.interface = multiprocessing.connection.Client( ('localhost', socket_number)) phase("Established", done=True) break except socket.error: time.sleep(0.1) except KeyboardInterrupt: graceful_exit()
def server_establish_socket(self) -> None: """Initialize the receiver (IPC server). The multiprocessing connection during local test does not utilize authentication keys* because a MITM-attack against the connection requires endpoint compromise, and in such situation, MITM attack is not nearly as effective as key/screen logging or RAM dump. * https://docs.python.org/3/library/multiprocessing.html#authentication-keys Similar to the case of standard mode of operation, all sensitive data that passes through the socket/serial interface and Relay Program is encrypted. A MITM attack between the sockets could of course be used to e.g. inject public keys, but like with all key exchanges, that would only work if the user neglects fingerprint verification. Another reason why the authentication key is useless, is the key needs to be pre-shared. This means there's two ways to share it: 1) Hard-code the key to source file from where malware could read it. 2) Force the user to manually copy the PSK from one program to another. This would change the workflow that the local test configuration tries to simulate. To conclude, the local test configuration should never be used under a threat model where endpoint security is of importance. """ try: socket_number = RP_LISTEN_SOCKET if self.settings.software_operation == NC else DST_LISTEN_SOCKET listener = multiprocessing.connection.Listener( (LOCALHOST, socket_number)) self.rx_socket = listener.accept() except KeyboardInterrupt: graceful_exit()
def test_graceful_exit(self): with self.assertRaises(SystemExit): graceful_exit('test message') graceful_exit('test message', clear=False) graceful_exit('test message', exit_code=1) graceful_exit('test message', exit_code=2)