Exemple #1
0
def change_master_key(ts: 'datetime', window_list: 'WindowList',
                      contact_list: 'ContactList', group_list: 'GroupList',
                      key_list: 'KeyList', settings: 'Settings',
                      master_key: 'MasterKey') -> None:
    """Prompt user for new master password and derive new master key from that."""
    try:
        old_master_key = master_key.master_key[:]
        master_key.new_master_key()

        phase("Re-encrypting databases")

        ensure_dir(DIR_USER_DATA)
        file_name = f'{DIR_USER_DATA}{settings.software_operation}_logs'
        if os.path.isfile(file_name):
            re_encrypt(old_master_key, master_key.master_key, settings)

        key_list.store_keys()
        settings.store_settings()
        contact_list.store_contacts()
        group_list.store_groups()

        phase(DONE)
        box_print("Master key successfully changed.", head=1)
        clear_screen(delay=1.5)

        local_win = window_list.get_window(LOCAL_ID)
        local_win.add_new(ts, "Changed RxM master key.")

    except KeyboardInterrupt:
        raise FunctionReturn("Password change aborted.",
                             delay=1,
                             head=3,
                             tail_clear=True)
Exemple #2
0
def store_unique(
        file_data: bytes,  # File data to store
        file_dir: str,  # Directory to store file
        file_name: str,  # Name of the file.
) -> None:
    """Store file under a unique filename.

    Add trailing counter .# to ensure buffered packets are read in order.
    """
    ensure_dir(file_dir)

    try:
        file_numbers = [
            f[(len(file_name) + len('.')):] for f in os.listdir(file_dir)
            if f.startswith(file_name)
        ]
        file_numbers = [n for n in file_numbers if n.isdigit()]
        greatest_num = sorted(file_numbers, key=int)[-1]
        ctr = int(greatest_num) + 1
    except IndexError:
        ctr = 0

    with open(f"{file_dir}/{file_name}.{ctr}", 'wb+') as f:
        f.write(file_data)
        f.flush()
        os.fsync(f.fileno())
Exemple #3
0
def change_log_db_key(previous_key: bytes, new_key: bytes,
                      settings: 'Settings') -> None:
    """Re-encrypt log database with a new master key."""
    ensure_dir(DIR_USER_DATA)
    file_name = f'{DIR_USER_DATA}{settings.software_operation}_logs'
    temp_name = f'{file_name}_temp'

    if not os.path.isfile(file_name):
        raise FunctionReturn("Error: Could not find log database.")

    if os.path.isfile(temp_name):
        os.remove(temp_name)

    f_old = open(file_name, 'rb')
    f_new = open(temp_name, 'ab+')

    for ct in iter(lambda: f_old.read(LOG_ENTRY_LENGTH), b''):
        pt = auth_and_decrypt(ct, key=previous_key, database=file_name)
        f_new.write(encrypt_and_sign(pt, key=new_key))

    f_old.close()
    f_new.close()

    os.remove(file_name)
    os.rename(temp_name, file_name)
Exemple #4
0
    def test_invalid_type_is_replaced_with_default(self) -> None:
        # Setup
        ensure_dir(DIR_USER_DATA)
        with open(f"{DIR_USER_DATA}{TX}_serial_settings.json", 'w+') as f:
            f.write("""\
{
    "serial_baudrate": "115200",
    "serial_error_correction": "5",
    "use_serial_usb_adapter": "true",
    "built_in_serial_interface": true
}""")
        # Test
        settings = GatewaySettings(operation=TX,
                                   local_test=True,
                                   dd_sockets=True,
                                   qubes=False)
        self.assertEqual(settings.serial_baudrate, 19200)
        self.assertEqual(settings.serial_error_correction, 5)
        self.assertEqual(settings.use_serial_usb_adapter, True)
        self.assertEqual(settings.built_in_serial_interface, 'ttyS0')

        with open(settings.file_name) as f:
            data = f.read()

        self.assertEqual(data, self.default_serialized)
Exemple #5
0
def get_message(purp_url_token: str, queues: 'QueueDict',
                pub_key_dict: 'PubKeyDict', buf_key: bytes) -> str:
    """Send queued messages to contact."""
    if not validate_url_token(purp_url_token, queues, pub_key_dict):
        return ''

    identified_onion_pub_key = pub_key_dict[purp_url_token]

    # Load outgoing messages for all contacts,
    # return the oldest message for contact

    sub_dir = hashlib.blake2b(identified_onion_pub_key,
                              key=buf_key,
                              digest_size=BLAKE2_DIGEST_LENGTH).hexdigest()
    buf_dir = f"{RELAY_BUFFER_OUTGOING_M_DIR}/{sub_dir}/"
    ensure_dir(buf_dir)

    packets = []
    while len(os.listdir(buf_dir)) > 0:
        packet_ct, db = read_buffer_file(buf_dir,
                                         RELAY_BUFFER_OUTGOING_MESSAGE)
        packet = auth_and_decrypt(packet_ct,
                                  key=buf_key,
                                  database=f"{buf_dir}{db}")
        packets.append(packet.decode())

    if packets:
        all_message_packets = '\n'.join(packets)
        return all_message_packets

    return ''
Exemple #6
0
    def load_master_key(self) -> None:
        """Derive master key from password from stored values (salt, rounds, memory)."""
        ensure_dir(f'{DIR_USER_DATA}/')
        with open(self.file_name, 'rb') as f:
            data = f.read()
        salt = data[0:32]
        k_hash = data[32:64]
        rounds = bytes_to_int(data[64:72])
        memory = bytes_to_int(data[72:80])

        while True:
            password = MasterKey.get_password()
            phase("Deriving master key", head=2, offset=16)
            purp_key, _ = argon2_kdf(password,
                                     salt,
                                     rounds,
                                     memory,
                                     local_testing=self.local_test)
            if hash_chain(purp_key) == k_hash:
                phase("Password correct", done=True)
                self.master_key = purp_key
                clear_screen(delay=0.5)
                break
            else:
                phase("Invalid password", done=True)
                print_on_previous_line(reps=5, delay=1)
Exemple #7
0
def change_master_key(ts: 'datetime', window_list: 'WindowList',
                      contact_list: 'ContactList', group_list: 'GroupList',
                      key_list: 'KeyList', settings: 'Settings',
                      master_key: 'MasterKey') -> None:
    """Derive new master key based on master password delivered by TxM."""
    old_master_key = master_key.master_key[:]
    master_key.new_master_key()
    new_master_key = master_key.master_key

    ensure_dir(f'{DIR_USER_DATA}/')
    file_name = f'{DIR_USER_DATA}/{settings.software_operation}_logs'
    if os.path.isfile(file_name):
        phase("Re-encrypting log-file")
        re_encrypt(old_master_key, new_master_key, settings)
        phase('Done')

    key_list.store_keys()
    settings.store_settings()
    contact_list.store_contacts()
    group_list.store_groups()

    box_print("Master key successfully changed.", head=1)
    clear_screen(delay=1.5)

    local_win = window_list.get_window('local')
    local_win.print_new(ts, "Changed RxM master key.", print_=False)
Exemple #8
0
    def store_groups(self) -> None:
        """Write the list of groups to an encrypted database.

        This function will first generate a header that stores
        information about the group database content and padding at the
        moment of calling. Next, the function will serialize every Group
        object (including dummy groups) to form the constant length
        plaintext that will be encrypted and stored in the database.

        By default, TFC has a maximum number of 50 groups with 50
        members. In addition, the group database stores the header that
        contains four 8-byte values. The database plaintext length with
        50 groups, each with 50 members is
            4*8 + 50*(1024 + 4 + 2*1 + 50*32)
          =  32 + 50*2630
          = 131532 bytes.

        The ciphertext includes a 24-byte nonce and a 16-byte tag, so
        the size of the final database is 131572 bytes.
        """
        pt_bytes = self._generate_group_db_header()
        pt_bytes += b''.join(
            [g.serialize_g() for g in (self.groups + self._dummy_groups())])
        ct_bytes = encrypt_and_sign(pt_bytes, self.master_key.master_key)

        ensure_dir(DIR_USER_DATA)
        with open(self.file_name, 'wb+') as f:
            f.write(ct_bytes)
Exemple #9
0
    def store_settings(self) -> None:
        """Store settings to an encrypted database.

        The plaintext in the encrypted database is a constant
        length bytestring regardless of stored setting values.
        """
        attribute_list = [self.__getattribute__(k) for k in self.key_list]

        bytes_lst = []
        for a in attribute_list:
            if isinstance(a, bool):
                bytes_lst.append(bool_to_bytes(a))
            elif isinstance(a, int):
                bytes_lst.append(int_to_bytes(a))
            elif isinstance(a, float):
                bytes_lst.append(double_to_bytes(a))
            else:
                raise CriticalError("Invalid attribute type in settings.")

        pt_bytes = b''.join(bytes_lst)
        ct_bytes = encrypt_and_sign(pt_bytes, self.master_key.master_key)

        ensure_dir(DIR_USER_DATA)
        with open(self.file_name, 'wb+') as f:
            f.write(ct_bytes)
Exemple #10
0
    def new_master_key(self) -> None:
        """Create a new master key from salt and password."""
        password = MasterKey.new_password()
        salt = csprng()
        rounds = ARGON2_ROUNDS
        memory = ARGON2_MIN_MEMORY

        phase("Deriving master key", head=2)
        while True:
            time_start = time.monotonic()
            master_key, parallellism = argon2_kdf(password,
                                                  salt,
                                                  rounds,
                                                  memory=memory,
                                                  local_test=self.local_test)
            time_final = time.monotonic() - time_start

            if time_final > 3.0:
                self.master_key = master_key
                ensure_dir(f'{DIR_USER_DATA}/')
                with open(self.file_name, 'wb+') as f:
                    f.write(salt + hash_chain(self.master_key) +
                            int_to_bytes(rounds) + int_to_bytes(memory) +
                            int_to_bytes(parallellism))
                phase(DONE)
                break
            else:
                memory *= 2
Exemple #11
0
    def __init__(self,
                 local_testing: bool,
                 dd_sockets: bool,
                 operation=NH) -> None:
        # Fixed settings
        self.relay_to_im_client = True  # False stops forwarding messages to IM client

        # Controllable settings
        self.serial_usb_adapter = True  # False uses system's integrated serial interface
        self.disable_gui_dialog = False  # True replaces Tkinter dialogs with CLI prompts
        self.serial_baudrate = 19200  # The speed of serial interface in bauds per second
        self.serial_error_correction = 5  # Number of byte errors serial datagrams can recover from

        self.software_operation = operation
        self.file_name = '{}{}_settings'.format(DIR_USER_DATA, operation)

        # Settings from launcher / CLI arguments
        self.local_testing_mode = local_testing
        self.data_diode_sockets = dd_sockets

        ensure_dir(DIR_USER_DATA)
        if os.path.isfile(self.file_name):
            self.load_settings()
        else:
            self.setup()
            self.store_settings()

        # Following settings change only when program is restarted
        self.session_serial_error_correction = self.serial_error_correction
        self.session_serial_baudrate = self.serial_baudrate
        self.race_condition_delay = calculate_race_condition_delay(self)

        self.receive_timeout, self.transmit_delay = calculate_serial_delays(
            self.session_serial_baudrate)
Exemple #12
0
def get_file(
    purp_url_token: str,
    queues: 'QueueDict',
    pub_key_dict: 'PubKeyDict',
    buf_key: bytes,
) -> Any:
    """Send queued files to contact."""
    if not validate_url_token(purp_url_token, queues, pub_key_dict):
        return ''

    identified_onion_pub_key = pub_key_dict[purp_url_token]

    sub_dir = hashlib.blake2b(identified_onion_pub_key,
                              key=buf_key,
                              digest_size=BLAKE2_DIGEST_LENGTH).hexdigest()
    buf_dir = f"{RELAY_BUFFER_OUTGOING_F_DIR}/{sub_dir}/"
    ensure_dir(buf_dir)

    if len(os.listdir(buf_dir)) > 0:
        packet_ct, db = read_buffer_file(buf_dir, RELAY_BUFFER_OUTGOING_FILE)
        packet = auth_and_decrypt(packet_ct,
                                  key=buf_key,
                                  database=f"{buf_dir}{db}")
        mem = BytesIO()
        mem.write(packet)
        mem.seek(0)
        return send_file(mem, mimetype="application/octet-stream")

    return ''
Exemple #13
0
def re_encrypt(previous_key: bytes, new_key: bytes, settings: 'Settings') -> None:
    """Re-encrypt database with a new master key."""
    ensure_dir(f'{DIR_USER_DATA}/')
    file_name = f'{DIR_USER_DATA}/{settings.software_operation}_logs'
    temp_name = f'{DIR_USER_DATA}/{settings.software_operation}_logs_temp'

    if not os.path.isfile(file_name):
        raise FunctionReturn(f"Error: Could not find '{file_name}'.")

    if os.path.isfile(temp_name):
        os.remove(temp_name)

    f_old = open(file_name, 'rb')
    f_new = open(temp_name, 'ab+')

    def read_entry():
        """Read log entry."""
        return f_old.read(1325)

    for ct_old in iter(read_entry, b''):
        pt_new = auth_and_decrypt(ct_old, key=previous_key)
        f_new.write(encrypt_and_sign(pt_new, key=new_key))

    f_old.close()
    f_new.close()

    os.remove(file_name)
    os.rename(temp_name, file_name)
Exemple #14
0
    def test_qubes_read_file(self, *_: Any) -> None:
        # Setup
        ensure_dir(f"{QUBES_BUFFER_INCOMING_DIR}/")

        def packet_delayer() -> None:
            """Create packets one at a time."""
            time.sleep(0.1)

            with open(f"{QUBES_BUFFER_INCOMING_DIR}/{QUBES_BUFFER_INCOMING_PACKET}.invalid", 'wb+') as fp:
                fp.write(base64.b85encode(b'data'))

            time.sleep(0.1)

            with open(f"{QUBES_BUFFER_INCOMING_DIR}/{QUBES_BUFFER_INCOMING_PACKET}.0", 'wb+') as fp:
                fp.write(base64.b85encode(b'data'))

        threading.Thread(target=packet_delayer).start()

        gateway = Gateway(operation=RX, local_test=False, dd_sockets=False, qubes=True)

        # Test
        self.assert_se("No packet was available.", gateway.read)

        time.sleep(0.3)

        self.assertIsInstance(gateway, Gateway)
        self.assertEqual(gateway.read(), b'data')

        # Test invalid packet content is handled
        with open(f"{QUBES_BUFFER_INCOMING_DIR}/{QUBES_BUFFER_INCOMING_PACKET}.1", 'wb+') as f:
            f.write(os.urandom(32))
        self.assert_se("Error: Received packet had invalid Base85 encoding.", gateway.read)
Exemple #15
0
    def test_unknown_kv_pair_is_removed(self) -> None:
        # Setup
        ensure_dir(DIR_USER_DATA)
        with open(f"{DIR_USER_DATA}{TX}_serial_settings.json", 'w+') as f:
            f.write("""\
{
    "serial_baudrate": 19200,
    "serial_error_correction": 5,
    "use_serial_usb_adapter": true,
    "built_in_serial_interface": "ttyS0",
    "this_should_not_be_here": 1
}""")
        # Test
        settings = GatewaySettings(operation=TX,
                                   local_test=True,
                                   dd_sockets=True)
        self.assertEqual(settings.serial_baudrate, 19200)
        self.assertEqual(settings.serial_error_correction, 5)
        self.assertEqual(settings.use_serial_usb_adapter, True)
        self.assertEqual(settings.built_in_serial_interface, 'ttyS0')

        with open(settings.file_name) as f:
            data = f.read()

        self.assertEqual(data, self.default_serialized)
Exemple #16
0
    def new_master_key(self) -> None:
        """Create a new master key from salt and password.

        The number of rounds starts at 1 but is increased dynamically based
        on system performance. This allows more security on faster platforms
        without additional cost on key derivation time.
        """
        password = MasterKey.new_password()
        salt = keygen()
        rounds = 1

        phase("Deriving master key", head=2)
        while True:
            time_start = time.monotonic()
            master_key, memory = argon2_kdf(password,
                                            salt,
                                            rounds,
                                            local_testing=self.local_test)
            time_final = time.monotonic() - time_start

            if time_final > 3.0:
                self.master_key = master_key
                master_key_hash = hash_chain(master_key)
                ensure_dir(f'{DIR_USER_DATA}/')
                with open(self.file_name, 'wb+') as f:
                    f.write(salt + master_key_hash + int_to_bytes(rounds) +
                            int_to_bytes(memory))
                phase('Done')
                break
            else:
                rounds *= 2
Exemple #17
0
    def read_qubes_buffer_file(buffer_file_dir: str = '') -> bytes:
        """Read packet from oldest buffer file."""
        buffer_file_dir = buffer_file_dir if buffer_file_dir else BUFFER_FILE_DIR

        ensure_dir(f"{buffer_file_dir}/")

        while not any([f for f in os.listdir(buffer_file_dir) if f.startswith(BUFFER_FILE_NAME)]):
            time.sleep(0.001)

        tfc_buffer_file_numbers   = [f[(len(BUFFER_FILE_NAME)+len('.')):] for f in os.listdir(buffer_file_dir) if f.startswith(BUFFER_FILE_NAME)]
        tfc_buffer_file_numbers   = [n for n in tfc_buffer_file_numbers if n.isdigit()]
        tfc_buffer_files_in_order = [f"{BUFFER_FILE_NAME}.{n}" for n in sorted(tfc_buffer_file_numbers, key=int)]

        try:
            oldest_buffer_file = tfc_buffer_files_in_order[0]
        except IndexError:
            raise SoftError("No packet was available.", output=False)

        with open(f"{buffer_file_dir}/{oldest_buffer_file}", 'rb') as f:
            packet = f.read()

        try:
            packet = base64.b85decode(packet)
        except ValueError:
            raise SoftError("Error: Received packet had invalid Base85 encoding.")

        os.remove(f"{buffer_file_dir}/{oldest_buffer_file}")

        return packet
Exemple #18
0
    def setUp(self):
        self.unittest_dir = cd_unittest()

        self.msg = ("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean condimentum consectetur purus quis"
                    " dapibus. Fusce venenatis lacus ut rhoncus faucibus. Cras sollicitudin commodo sapien, sed bibendu"
                    "m velit maximus in. Aliquam ac metus risus. Sed cursus ornare luctus. Integer aliquet lectus id ma"
                    "ssa blandit imperdiet. Ut sed massa eget quam facilisis rutrum. Mauris eget luctus nisl. Sed ut el"
                    "it iaculis, faucibus lacus eget, sodales magna. Nunc sed commodo arcu. In hac habitasse platea dic"
                    "tumst. Integer luctus aliquam justo, at vestibulum dolor iaculis ac. Etiam laoreet est eget odio r"
                    "utrum, vel malesuada lorem rhoncus. Cras finibus in neque eu euismod. Nulla facilisi. Nunc nec ali"
                    "quam quam, quis ullamcorper leo. Nunc egestas lectus eget est porttitor, in iaculis felis sceleris"
                    "que. In sem elit, fringilla id viverra commodo, sagittis varius purus. Pellentesque rutrum loborti"
                    "s neque a facilisis. Mauris id tortor placerat, aliquam dolor ac, venenatis arcu.")

        self.ts         = datetime.now()
        self.master_key = MasterKey()
        self.settings   = Settings(log_file_masking=True)
        self.file_name  = f'{DIR_USER_DATA}{self.settings.software_operation}_logs'

        self.contact_list = ContactList(nicks=['Alice', 'Bob', 'Charlie', LOCAL_ID])
        self.key_list     = KeyList(    nicks=['Alice', 'Bob', 'Charlie', LOCAL_ID])
        self.group_list   = GroupList( groups=['test_group'])
        self.packet_list  = PacketList(contact_list=self.contact_list, settings=self.settings)
        self.window_list  = WindowList(contact_list=self.contact_list, settings=self.settings, 
                                       group_list=self.group_list, packet_list=self.packet_list)
        self.group_id     = group_name_to_group_id('test_group')
        self.file_keys    = dict()

        self.group_list.get_group('test_group').log_messages = True
        self.args = (self.window_list, self.packet_list, self.contact_list, self.key_list, 
                     self.group_list, self.settings, self.master_key, self.file_keys)

        ensure_dir(DIR_USER_DATA)
Exemple #19
0
    def store_onion_service_private_key(self) -> None:
        """Store Onion Service private key to an encrypted database."""
        ct_bytes = encrypt_and_sign(self.onion_private_key,
                                    self.master_key.master_key)

        ensure_dir(DIR_USER_DATA)
        with open(self.file_name, 'wb+') as f:
            f.write(ct_bytes)
Exemple #20
0
    def test_invalid_data_in_db_raises_critical_error(self, _):
        for delta in [-1, 1]:
            ensure_dir(DIR_USER_DATA)
            with open(self.file_name, 'wb+') as f:
                f.write(os.urandom(MASTERKEY_DB_SIZE + delta))

            with self.assertRaises(SystemExit):
                _ = MasterKey(self.operation, local_test=False)
Exemple #21
0
 def store_settings(self) -> None:
     """Store persistent settings to file."""
     setting_data  = int_to_bytes(self.serial_iface_speed)
     setting_data += int_to_bytes(self.e_correction_ratio)
     setting_data += bool_to_bytes(self.serial_usb_adapter)
     setting_data += bool_to_bytes(self.disable_gui_dialog)
     ensure_dir('{}/'.format(DIR_USER_DATA))
     open(self.file_name, 'wb+').write(setting_data)
Exemple #22
0
def replace_log_db(settings: 'Settings') -> None:
    """Replace the log database with the temp file."""
    ensure_dir(DIR_USER_DATA)
    file_name = f'{DIR_USER_DATA}{settings.software_operation}_logs'
    temp_name = file_name + TEMP_SUFFIX

    if os.path.isfile(temp_name):
        os.replace(temp_name, file_name)
Exemple #23
0
    def store_database(self, pt_bytes: bytes, replace: bool = True) -> None:
        """Encrypt and store data into database."""
        ct_bytes = encrypt_and_sign(pt_bytes, self.database_key)
        ensure_dir(DIR_USER_DATA)
        self.ensure_temp_write(ct_bytes)

        if replace:
            self.replace_database()
Exemple #24
0
 def load_settings(self) -> None:
     """Load persistent settings from file."""
     ensure_dir('{}/'.format(DIR_USER_DATA))
     settings = open(self.file_name, 'rb').read()
     self.serial_iface_speed = bytes_to_int(settings[0:8])
     self.e_correction_ratio = bytes_to_int(settings[8:16])
     self.serial_usb_adapter = bytes_to_bool(settings[16:17])
     self.disable_gui_dialog = bytes_to_bool(settings[17:18])
Exemple #25
0
    def __init__(
        self,
        master_key: 'MasterKey',  # MasterKey object
        operation: str,  # Operation mode of the program (Tx or Rx)
        local_test: bool,  # Local testing setting from command-line argument
        qubes: bool = False  # Qubes setting from command-line argument
    ) -> None:
        """Create a new Settings object.

        The settings below are defaults, and are only to be altered from
        within the program itself. Changes made to the default settings
        are stored in the encrypted settings database, from which they
        are loaded when the program starts.
        """
        # Common settings
        self.disable_gui_dialog = False
        self.max_number_of_group_members = 50
        self.max_number_of_groups = 50
        self.max_number_of_contacts = 50
        self.log_messages_by_default = False
        self.accept_files_by_default = False
        self.show_notifications_by_default = True
        self.log_file_masking = False
        self.ask_password_for_log_access = True

        # Transmitter settings
        self.nc_bypass_messages = False
        self.confirm_sent_files = True
        self.double_space_exits = False
        self.traffic_masking = False
        self.tm_static_delay = 2.0
        self.tm_random_delay = 2.0

        # Relay Settings
        self.allow_contact_requests = True

        # Receiver settings
        self.new_message_notify_preview = False
        self.new_message_notify_duration = 1.0
        self.max_decompress_size = 100_000_000

        self.master_key = master_key
        self.software_operation = operation
        self.local_testing_mode = local_test
        self.qubes = qubes

        self.file_name = f'{DIR_USER_DATA}{operation}_settings'
        self.database = TFCDatabase(self.file_name, master_key)

        self.all_keys = list(vars(self).keys())
        self.key_list = self.all_keys[:self.all_keys.index('master_key')]
        self.defaults = {k: self.__dict__[k] for k in self.key_list}

        ensure_dir(DIR_USER_DATA)
        if os.path.isfile(self.file_name):
            self.load_settings()
        else:
            self.store_settings()
Exemple #26
0
    def store_contacts(self) -> None:
        """Write contacts to encrypted database."""
        contacts = self.contacts + [self.dummy_contact] * (self.settings.max_number_of_contacts - len(self.contacts))
        pt_bytes = b''.join([c.serialize_c() for c in contacts])
        ct_bytes = encrypt_and_sign(pt_bytes, self.master_key.master_key)

        ensure_dir(DIR_USER_DATA)
        with open(self.file_name, 'wb+') as f:
            f.write(ct_bytes)
Exemple #27
0
def change_master_key(user_input: 'UserInput', contact_list: 'ContactList',
                      group_list: 'GroupList', settings: 'Settings',
                      queues: 'QueueDict', master_key: 'MasterKey',
                      onion_service: 'OnionService') -> None:
    """Change the master key on Transmitter/Receiver Program."""
    try:
        if settings.traffic_masking:
            raise FunctionReturn(
                "Error: Command is disabled during traffic masking.",
                head_clear=True)

        try:
            device = user_input.plaintext.split()[1].lower()
        except IndexError:
            raise FunctionReturn(
                f"Error: No target-system ('{TX}' or '{RX}') specified.",
                head_clear=True)

        if device not in [TX, RX]:
            raise FunctionReturn(f"Error: Invalid target system '{device}'.",
                                 head_clear=True)

        if device == RX:
            queue_command(CH_MASTER_KEY, settings, queues)
            return None

        old_master_key = master_key.master_key[:]
        new_master_key = master_key.master_key = master_key.new_master_key()

        phase("Re-encrypting databases")

        queues[KEY_MANAGEMENT_QUEUE].put(
            (KDB_CHANGE_MASTER_KEY_HEADER, master_key))

        ensure_dir(DIR_USER_DATA)
        if os.path.isfile(
                f'{DIR_USER_DATA}{settings.software_operation}_logs'):
            change_log_db_key(old_master_key, new_master_key, settings)

        contact_list.store_contacts()
        group_list.store_groups()
        settings.store_settings()
        onion_service.store_onion_service_private_key()

        phase(DONE)
        m_print("Master key successfully changed.",
                bold=True,
                tail_clear=True,
                delay=1,
                head=1)

    except (EOFError, KeyboardInterrupt):
        raise FunctionReturn("Password change aborted.",
                             tail_clear=True,
                             delay=1,
                             head=2)
Exemple #28
0
    def test_load_master_key_with_invalid_data_raises_critical_error(self, _: Any) -> None:
        # Setup
        ensure_dir(DIR_USER_DATA)
        data = os.urandom(MASTERKEY_DB_SIZE + BLAKE2_DIGEST_LENGTH)
        with open(self.file_name, 'wb+') as f:
            f.write(data)

        # Test
        with self.assertRaises(SystemExit):
            _ = MasterKey(self.operation, local_test=False)
Exemple #29
0
    def store_settings(self) -> None:
        """Store persistent settings to file."""
        setting_data = int_to_bytes(self.serial_baudrate)
        setting_data += int_to_bytes(self.serial_error_correction)
        setting_data += bool_to_bytes(self.serial_usb_adapter)
        setting_data += bool_to_bytes(self.disable_gui_dialog)

        ensure_dir(DIR_USER_DATA)
        with open(self.file_name, 'wb+') as f:
            f.write(setting_data)
Exemple #30
0
    def store_database(self, pt_bytes: bytes, replace: bool = True) -> None:
        """Encrypt and store data into database."""
        ct_bytes = encrypt_and_sign(pt_bytes, self.database_key)
        ensure_dir(DIR_USER_DATA)
        self.ensure_temp_write(ct_bytes)

        # Replace the original file with a temp file. (`os.replace` is atomic as per
        # POSIX requirements): https://docs.python.org/3/library/os.html#os.replace
        if replace:
            self.replace_database()