Exemplo n.º 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:
    """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)
Exemplo n.º 2
0
def nh_bypass_msg(key: str, settings: 'Settings') -> None:
    """Print messages about bypassing NH."""
    m = dict(start="Bypass NH if needed. Press <Enter> to send local key.",
             finish="Remove bypass of NH. Press <Enter> to continue.")

    if settings.nh_bypass_messages:
        box_print(m[key], manual_proceed=True)
Exemplo n.º 3
0
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
Exemplo n.º 4
0
def add_psk_tx_keys(cmd_data: bytes, ts: 'datetime', window_list: 'WindowList',
                    contact_list: 'ContactList', key_list: 'KeyList',
                    settings: 'Settings', pubkey_buf: Dict[str,
                                                           bytes]) -> None:
    """Add contact and Tx-PSKs."""
    tx_key = cmd_data[0:32]
    tx_hek = cmd_data[32:64]

    account, nick = [f.decode() for f in cmd_data[64:].split(US_BYTE)]

    contact_list.add_contact(account, DUMMY_USER, nick, bytes(FINGERPRINT_LEN),
                             bytes(FINGERPRINT_LEN),
                             settings.log_messages_by_default,
                             settings.accept_files_by_default,
                             settings.show_notifications_by_default)

    # The Rx-side keys are set as null-byte strings to indicate they have not
    # been added yet. This does not allow existential forgeries as
    # decrypt_assembly_packet does not allow use of zero-keys for decryption.
    key_list.add_keyset(account,
                        tx_key=tx_key,
                        rx_key=bytes(KEY_LENGTH),
                        tx_hek=tx_hek,
                        rx_hek=bytes(KEY_LENGTH))

    pubkey_buf.pop(account, None)

    message = f"Added Tx-PSK for {nick} ({account})."
    local_win = window_list.get_window(LOCAL_ID)
    local_win.add_new(ts, message)

    box_print(message)
    clear_screen(delay=1)
Exemplo n.º 5
0
    def process_long_header(self, packet: bytes) -> None:
        """Process first packet of long transmission."""
        if self.long_active:
            self.add_masking_packet_to_logfile(
                increase=len(self.assembly_pt_list))

        if self.type == FILE:
            self.new_file_packet()
            try:
                self.packets = bytes_to_int(packet[1:9])
                self.time = str(
                    datetime.timedelta(seconds=bytes_to_int(packet[9:17])))
                self.size = readable_size(bytes_to_int(packet[17:25]))
                self.name = packet[25:].split(US_BYTE)[0].decode()
                packet = self.lh + packet[25:]

                box_print([
                    f'Receiving file from {self.contact.nick}:',
                    f'{self.name} ({self.size})',
                    f'ETA {self.time} ({self.packets} packets)'
                ])

            except (struct.error, UnicodeError, ValueError):
                self.add_masking_packet_to_logfile()
                raise FunctionReturn(
                    "Error: Received file packet had an invalid header.")

        self.assembly_pt_list = [packet]
        self.long_active = True
        self.is_complete = False
Exemplo n.º 6
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)
Exemplo n.º 7
0
def process_public_key(ts: 'datetime', packet: bytes,
                       window_list: 'WindowList', settings: 'Settings',
                       pubkey_buf: Dict[str, str]) -> None:
    """Display public from contact."""
    pub_key = packet[1:33]
    origin = packet[33:34]
    account = packet[34:].decode()

    if origin == ORIGIN_CONTACT_HEADER:
        pub_key_enc = b58encode(pub_key)
        ssl = {48: 8, 49: 7, 50: 5}.get(len(pub_key_enc), 5)
        pub_key_enc = pub_key_enc if settings.local_testing_mode else ' '.join(
            split_string(pub_key_enc, item_len=ssl))

        pubkey_buf[account] = pub_key_enc

        box_print(
            [f"Received public key from {account}", '', pubkey_buf[account]],
            head=1,
            tail=1)

        local_win = window_list.get_local_window()
        local_win.print_new(
            ts,
            f"Received public key from {account}: {pub_key_enc}",
            print_=False)

    if origin == ORIGIN_USER_HEADER and account in pubkey_buf:
        clear_screen()
        box_print([f"Public key for {account}", '', pubkey_buf[account]],
                  head=1,
                  tail=1)
Exemplo n.º 8
0
def add_x25519_keys(packet: bytes, ts: 'datetime', window_list: 'WindowList',
                    contact_list: 'ContactList', key_list: 'KeyList',
                    settings: 'Settings', pubkey_buf: Dict[str,
                                                           bytes]) -> None:
    """Add contact and their X25519 keys."""
    tx_key = packet[0:32]
    tx_hek = packet[32:64]
    rx_key = packet[64:96]
    rx_hek = packet[96:128]

    account, nick = [f.decode() for f in packet[128:].split(US_BYTE)]

    contact_list.add_contact(account, DUMMY_USER, nick, bytes(FINGERPRINT_LEN),
                             bytes(FINGERPRINT_LEN),
                             settings.log_messages_by_default,
                             settings.accept_files_by_default,
                             settings.show_notifications_by_default)

    key_list.add_keyset(account, tx_key, rx_key, tx_hek, rx_hek)

    pubkey_buf.pop(account, None)

    message = f"Added X25519 keys for {nick} ({account})."
    local_win = window_list.get_window(LOCAL_ID)
    local_win.add_new(ts, message)

    box_print(message)
    clear_screen(delay=1)
Exemplo n.º 9
0
def ensure_im_connection() -> None:
    """\
    Check that nh.py has connection to Pidgin
    before launching other processes.
    """
    phase("Waiting for enabled account in Pidgin", offset=1)

    while True:
        try:
            bus = dbus.SessionBus(private=True)
            obj = bus.get_object("im.pidgin.purple.PurpleService",
                                 "/im/pidgin/purple/PurpleObject")
            purple = dbus.Interface(obj, "im.pidgin.purple.PurpleInterface")

            while not purple.PurpleAccountsGetAllActive():
                time.sleep(0.01)
            phase('OK', done=True)

            accounts = []
            for a in purple.PurpleAccountsGetAllActive():
                accounts.append(purple.PurpleAccountGetUsername(a)[:-1])

            just_len = len(max(accounts, key=len))
            justified = ["Active accounts in Pidgin:"] + [
                "* {}".format(a.ljust(just_len)) for a in accounts
            ]
            box_print(justified, head=1, tail=1)
            return None

        except (IndexError, dbus.exceptions.DBusException):
            continue
        except (EOFError, KeyboardInterrupt):
            clear_screen()
            exit()
Exemplo n.º 10
0
def change_nick(user_input: 'UserInput', window: 'Window',
                contact_list: 'ContactList', group_list: 'GroupList',
                settings: 'Settings', c_queue: 'Queue') -> None:
    """Change nick of contact."""
    if window.type == 'group':
        raise FunctionReturn("Error: Group is selected.")

    try:
        nick = user_input.plaintext.split()[1]
    except IndexError:
        raise FunctionReturn("Error: No nick specified.")

    rx_acco = window.contact.rx_account
    success, error_msg = validate_nick(nick,
                                       (contact_list, group_list, rx_acco))
    if not success:
        raise FunctionReturn(error_msg)
    window.contact.nick = nick
    window.name = nick
    contact_list.store_contacts()

    packet = CHANGE_NICK_HEADER + rx_acco.encode() + US_BYTE + nick.encode()
    queue_command(packet, settings, c_queue)

    box_print(f"Changed {rx_acco} nick to {nick}.")
Exemplo n.º 11
0
def process_local_key(packet: bytes, contact_list: 'ContactList',
                      key_list: 'KeyList') -> None:
    """Decrypt local key packet, add local contact/keyset."""
    try:
        clear_screen()
        box_print(["Received encrypted local key"], tail=1)

        kdk = get_b58_key('localkey')

        try:
            pt = auth_and_decrypt(packet[1:], key=kdk, soft_e=True)
        except nacl.exceptions.CryptoError:
            raise FunctionReturn("Invalid key decryption key.", delay=1.5)

        key = pt[0:32]
        hek = pt[32:64]
        conf_code = pt[64:65]

        # Add local contact to contact list database
        contact_list.add_contact('local', 'local', 'local', bytes(32),
                                 bytes(32), False, False, True)

        # Add local contact to keyset database
        key_list.add_keyset('local', key, bytes(32), hek, bytes(32))
        box_print([f"Confirmation code for TxM: {conf_code.hex()}"], head=1)

    except KeyboardInterrupt:
        raise FunctionReturn("Local key setup aborted.", delay=1)
Exemplo n.º 12
0
def psk_command(cmd_data: bytes, ts: 'datetime', window_list: 'WindowList',
                contact_list: 'ContactList', key_list: 'KeyList',
                settings: 'Settings', pubkey_buf: Dict[str, str]) -> None:
    """Add contact and tx-PSKs."""
    tx_key = cmd_data[0:32]
    tx_hek = cmd_data[32:64]

    account, nick = [f.decode() for f in cmd_data[64:].split(US_BYTE)]

    contact_list.add_contact(account, 'user_placeholder', nick, bytes(32),
                             bytes(32), settings.log_msg_by_default,
                             settings.store_file_default,
                             settings.n_m_notify_privacy)

    # The Rx-side keys are set as null-byte strings to indicate they have not been added yet.
    key_list.add_keyset(account, tx_key, bytes(32), tx_hek, bytes(32))

    pubkey_buf.pop(account, None)

    local_win = window_list.get_window('local')
    local_win.print_new(ts,
                        f"Added Tx-PSK for {nick} ({account}).",
                        print_=False)

    box_print([f"Successfully added {nick}."])
    clear_screen(delay=1)
Exemplo n.º 13
0
def ecdhe_command(cmd_data: bytes, ts: 'datetime', window_list: 'WindowList',
                  contact_list: 'ContactList', key_list: 'KeyList',
                  settings: 'Settings', pubkey_buf: Dict[str, str]) -> None:
    """Add contact and it's X25519 keys."""
    tx_key = cmd_data[0:32]
    tx_hek = cmd_data[32:64]
    rx_key = cmd_data[64:96]
    rx_hek = cmd_data[96:128]

    account, nick = [f.decode() for f in cmd_data[128:].split(US_BYTE)]

    contact_list.add_contact(account, 'user_placeholder', nick, bytes(32),
                             bytes(32), settings.log_msg_by_default,
                             settings.store_file_default,
                             settings.n_m_notify_privacy)

    key_list.add_keyset(account, tx_key, rx_key, tx_hek, rx_hek)

    pubkey_buf.pop(account, None)

    local_win = window_list.get_window('local')
    local_win.print_new(ts,
                        f"Added X25519 keys for {nick} ({account}).",
                        print_=False)

    box_print([f"Successfully added {nick}."])
    clear_screen(delay=1)
Exemplo n.º 14
0
def new_psk(account: str, user: str, nick: str, contact_list: 'ContactList',
            settings: 'Settings', queues: Dict[bytes, 'Queue']) -> None:
    """Generate new pre-shared key for manual key delivery.

    :param account:      The contact's account name (e.g. [email protected])
    :param user:         The user's account name (e.g. [email protected])
    :param nick:         Nick of contact
    :param contact_list: Contact list object
    :param settings:     Settings object
    :param queues:       Dictionary of multiprocessing queues
    :return:             None
    """
    try:
        tx_key = keygen()
        tx_hek = keygen()
        salt = keygen()
        password = MasterKey.new_password("password for PSK")

        phase("Deriving key encryption key", head=2)
        kek, _ = argon2_kdf(password,
                            salt,
                            rounds=16,
                            memory=128000,
                            parallelism=1)
        phase('Done')

        ct_tag = encrypt_and_sign(tx_key + tx_hek, key=kek)
        store_d = ask_path_gui(f"Select removable media for {nick}", settings)
        f_name = f"{store_d}/{user}.psk - Give to {account}"

        try:
            with open(f_name, 'wb+') as f:
                f.write(salt + ct_tag)
        except PermissionError:
            raise FunctionReturn(
                "Error: Did not have permission to write to directory.")

        packet = KEY_EX_PSK_TX_HEADER \
                 + tx_key \
                 + tx_hek \
                 + account.encode() + US_BYTE +  nick.encode()

        queue_command(packet, settings, queues[COMMAND_PACKET_QUEUE])

        contact_list.add_contact(account, user, nick, bytes(32), bytes(32),
                                 settings.log_msg_by_default,
                                 settings.store_file_default,
                                 settings.n_m_notify_privacy)

        queues[KEY_MANAGEMENT_QUEUE].put(
            ('ADD', account, tx_key, bytes(32), tx_hek, bytes(32)))

        box_print([f"Successfully added {nick}."], head=1)
        clear_screen(delay=1)

    except KeyboardInterrupt:
        raise FunctionReturn("PSK generation aborted.")
Exemplo n.º 15
0
    def test_box_print(self):
        self.assertIsNone(box_print("Test message", head=1, tail=1))
        self.assertIsNone(
            box_print(["Test message", '', "Another message"], head=1, tail=1))

        o_input = builtins.input
        builtins.input = lambda x: ''
        self.assertIsNone(box_print("Test message", manual_proceed=True))
        builtins.input = o_input
Exemplo n.º 16
0
def show_win_activity(window_list: 'WindowList') -> None:
    """Show number of unread messages in each window."""
    unread_wins = [
        w for w in window_list if (w.uid != 'local' and w.unread_messages > 0)
    ]
    print_list = ["Window activity"] if unread_wins else ["No window activity"]
    for w in unread_wins:
        print_list.append(f"{w.name}: {w.unread_messages}")
    box_print(print_list)
    print_on_previous_line(reps=(len(print_list) + 2), delay=1.5)
Exemplo n.º 17
0
def rxm_show_cmd_win(window:   'Window',
                     settings: 'Settings',
                     c_queue:  'Queue') -> None:
    """Show command window on RxM until user presses Enter."""
    packet = WINDOW_CHANGE_HEADER + LOCAL_WIN_ID_BYTES
    queue_command(packet, settings, c_queue)

    box_print(f"<Enter> returns RxM to {window.name}'s window", manual_proceed=True)
    print_on_previous_line(reps=4, flush=True)

    packet = WINDOW_CHANGE_HEADER + window.uid.encode()
    queue_command(packet, settings, c_queue)
Exemplo n.º 18
0
def local_key_installed(ts: 'datetime', window_list: 'WindowList',
                        contact_list: 'ContactList') -> None:
    """Clear local key bootstrap process from screen."""
    local_win = window_list.get_window('local')
    local_win.print_new(ts, "Created a new local key.", print_=False)

    box_print(["Successfully added a new local key."])
    clear_screen(delay=1)

    if not contact_list.has_contacts():
        clear_screen()
        c_print("Waiting for new contacts", head=1, tail=1)
Exemplo n.º 19
0
def rxm_display_f_win(window:   'Window',
                      settings: 'Settings',
                      c_queue:  'Queue'):
    """Show file reception window on RxM until user presses Enter."""
    packet = WINDOW_CHANGE_HEADER + FILE_R_WIN_ID_BYTES
    queue_command(packet, settings, c_queue)

    box_print(f"<Enter> returns RxM to {window.name}'s window", manual_proceed=True)
    print_on_previous_line(reps=4, flush=True)

    packet = WINDOW_CHANGE_HEADER + window.uid.encode()
    queue_command(packet, settings, c_queue)
Exemplo n.º 20
0
def local_key_installed(ts: 'datetime', window_list: 'WindowList',
                        contact_list: 'ContactList') -> None:
    """Clear local key bootstrap process from screen."""
    message = "Successfully completed local key exchange."
    local_win = window_list.get_window(LOCAL_ID)
    local_win.add_new(ts, message)

    box_print(message)
    clear_screen(delay=1)

    if not contact_list.has_contacts():
        c_print("Waiting for new contacts", head=1, tail=1)
Exemplo n.º 21
0
def remove_group(cmd_data: bytes, ts: 'datetime', window_list: 'WindowList',
                 group_list: 'GroupList') -> None:
    """Remove group."""
    group_name = cmd_data.decode()

    if group_name not in group_list.get_list_of_group_names():
        raise FunctionReturn(f"RxM has no group {group_name} to remove.")

    group_list.remove_group(group_name)

    box_print(f"Removed group {group_name}", head=1, tail=1)
    local_win = window_list.get_window('local')
    local_win.print_new(ts, f"Removed group {group_name}.", print_=False)
Exemplo n.º 22
0
def process_local_key(ts: 'datetime', packet: bytes, window_list: 'WindowList',
                      contact_list: 'ContactList', key_list: 'KeyList',
                      settings: 'Settings') -> None:
    """Decrypt local key packet and add local contact/keyset."""
    bootstrap = not key_list.has_local_key()

    try:
        while True:
            clear_screen()
            box_print("Received encrypted local key", tail=1)
            kdk = get_b58_key(B58_LOCAL_KEY, settings)

            try:
                pt = auth_and_decrypt(packet[1:], key=kdk, soft_e=True)
                break
            except nacl.exceptions.CryptoError:
                if bootstrap:
                    raise FunctionReturn(
                        "Error: Incorrect key decryption key.", delay=1.5)
                c_print("Incorrect key decryption key.", head=1)
                clear_screen(delay=1.5)

        key = pt[0:32]
        hek = pt[32:64]
        conf_code = pt[64:65]

        # Add local contact to contact list database
        contact_list.add_contact(LOCAL_ID, LOCAL_ID, LOCAL_ID,
                                 bytes(FINGERPRINT_LEN),
                                 bytes(FINGERPRINT_LEN), False, False, True)

        # Add local keyset to keyset database
        key_list.add_keyset(rx_account=LOCAL_ID,
                            tx_key=key,
                            rx_key=csprng(),
                            tx_hek=hek,
                            rx_hek=csprng())

        box_print(f"Confirmation code for TxM: {conf_code.hex()}", head=1)

        local_win = window_list.get_local_window()
        local_win.add_new(ts, "Added new local key.")

        if bootstrap:
            window_list.active_win = local_win

    except KeyboardInterrupt:
        raise FunctionReturn("Local key setup aborted.",
                             delay=1,
                             head=3,
                             tail_clear=True)
Exemplo n.º 23
0
def change_master_key(user_input: 'UserInput', contact_list: 'ContactList',
                      group_list: 'GroupList', settings: 'Settings',
                      queues: Dict[bytes,
                                   'Queue'], master_key: 'MasterKey') -> None:
    """Change master key on TxM/RxM."""
    try:
        if settings.session_traffic_masking:
            raise FunctionReturn(
                "Error: Command is disabled during traffic masking.")

        try:
            device = user_input.plaintext.split()[1].lower()
        except IndexError:
            raise FunctionReturn("Error: No target system specified.")

        if device not in [TX, RX]:
            raise FunctionReturn("Error: Invalid target system.")

        if device == RX:
            queue_command(CHANGE_MASTER_K_HEADER, settings,
                          queues[COMMAND_PACKET_QUEUE])
            return None

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

        phase("Re-encrypting databases")

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

        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, new_master_key, settings)

        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)

    except KeyboardInterrupt:
        raise FunctionReturn("Password change aborted.",
                             delay=1,
                             head=3,
                             tail_clear=True)
Exemplo n.º 24
0
def rxm_show_sys_win(user_input: 'UserInput', window: 'TxWindow',
                     settings: 'Settings', c_queue: 'Queue') -> None:
    """Display system window on RxM until user presses Enter."""
    cmd = user_input.plaintext.split()[0]
    win_name = dict(cmd=LOCAL_ID, fw=WIN_TYPE_FILE)[cmd]

    command = WINDOW_SELECT_HEADER + win_name.encode()
    queue_command(command, settings, c_queue)

    box_print(f"<Enter> returns RxM to {window.name}'s window",
              manual_proceed=True)
    print_on_previous_line(reps=4, flush=True)

    command = WINDOW_SELECT_HEADER + window.uid.encode()
    queue_command(command, settings, c_queue)
Exemplo n.º 25
0
def group_create(group_name:   str,
                 purp_members: List[str],
                 group_list:   'GroupList',
                 contact_list: 'ContactList',
                 settings:     'Settings',
                 queues:       Dict[bytes, 'Queue'],
                 _:            'MasterKey') -> None:
    """Create a new group.
    
    Validate group name and determine what members that can be added.
    """
    validate_group_name(group_name, contact_list, group_list)

    accounts      = set(contact_list.get_list_of_accounts())
    purp_accounts = set(purp_members)
    accepted      = list(accounts      & purp_accounts)
    rejected      = list(purp_accounts - accounts)

    if len(accepted) > settings.max_number_of_group_members:
        raise FunctionReturn(f"Error: TFC settings only allow {settings.max_number_of_group_members} members per group.")

    if len(group_list) == settings.max_number_of_groups:
        raise FunctionReturn(f"Error: TFC settings only allow {settings.max_number_of_groups} groups.")

    group_list.add_group(group_name,
                         settings.log_messages_by_default,
                         settings.show_notifications_by_default,
                         members=[contact_list.get_contact(c) for c in accepted])

    fields  = [f.encode() for f in ([group_name] + accepted)]
    command = GROUP_CREATE_HEADER + US_BYTE.join(fields)
    queue_command(command, settings, queues[COMMAND_PACKET_QUEUE])

    group_management_print(NEW_GROUP,        accepted, contact_list, group_name)
    group_management_print(UNKNOWN_ACCOUNTS, rejected, contact_list, group_name)

    if accepted:
        if yes("Publish list of group members to participants?"):
            for member in accepted:
                m_list = [m for m in accepted if m != member]
                queue_message(user_input=UserInput(US_STR.join([group_name] + m_list), MESSAGE),
                              window    =MockWindow(member, [contact_list.get_contact(member)]),
                              settings  =settings,
                              m_queue   =queues[MESSAGE_PACKET_QUEUE],
                              header    =GROUP_MSG_INVITEJOIN_HEADER,
                              log_as_ph =True)
    else:
        box_print(f"Created an empty group '{group_name}'", head=1)
Exemplo n.º 26
0
def process_imported_file(ts: 'datetime', packet: bytes,
                          window_list: 'WindowList', settings: 'Settings'):
    """Decrypt and store imported file."""
    while True:
        try:
            print('')
            key = get_b58_key(B58_FILE_KEY, settings)
        except KeyboardInterrupt:
            raise FunctionReturn("File import aborted.", head=2)

        try:
            phase("Decrypting file", head=1)
            file_pt = auth_and_decrypt(packet[1:], key, soft_e=True)
            phase(DONE)
            break
        except (nacl.exceptions.CryptoError, nacl.exceptions.ValueError):
            phase('ERROR', done=True)
            c_print("Invalid decryption key. Try again.")
            print_on_previous_line(reps=7, delay=1.5)
        except KeyboardInterrupt:
            phase('ABORT', done=True)
            raise FunctionReturn("File import aborted.")

    try:
        phase("Decompressing file")
        file_dc = zlib.decompress(file_pt)
        phase(DONE)
    except zlib.error:
        phase('ERROR', done=True)
        raise FunctionReturn("Error: Decompression of file data failed.")

    try:
        f_name = bytes_to_str(file_dc[:PADDED_UTF32_STR_LEN])
    except UnicodeError:
        raise FunctionReturn("Error: Received file name had invalid encoding.")

    if not f_name.isprintable() or not f_name:
        raise FunctionReturn("Error: Received file had an invalid name.")

    f_data = file_dc[PADDED_UTF32_STR_LEN:]
    final_name = store_unique(f_data, DIR_IMPORTED, f_name)

    message = f"Stored imported file as '{final_name}'"
    box_print(message, head=1)

    local_win = window_list.get_local_window()
    local_win.add_new(ts, message)
Exemplo n.º 27
0
def nh_bypass_msg(key: str, settings: 'Settings') -> None:
    """Print messages about bypassing NH.

    During ciphertext delivery of local key exchange, NH bypass messages
    tell user when to bypass and remove bypass of NH. This makes initial
    key bootstrap more secure in case key decryption key input is not safe.
    """
    m = {
        NH_BYPASS_START:
        "Bypass NH if needed. Press <Enter> to send local key.",
        NH_BYPASS_STOP: "Remove bypass of NH. Press <Enter> to continue."
    }

    if settings.nh_bypass_messages:
        box_print(m[key],
                  manual_proceed=True,
                  head=(1 if key == NH_BYPASS_STOP else 0))
Exemplo n.º 28
0
def remove_group(cmd_data: bytes, ts: 'datetime', window_list: 'WindowList',
                 group_list: 'GroupList') -> None:
    """Remove group."""
    group_name = cmd_data.decode()

    window_list.remove_window(group_name)

    if group_name not in group_list.get_list_of_group_names():
        raise FunctionReturn(f"RxM has no group '{group_name}' to remove.")

    group_list.remove_group(group_name)

    message = f"Removed group {group_name}."
    box_print(message, head=1, tail=1)

    local_win = window_list.get_window(LOCAL_ID)
    local_win.add_new(ts, message)
Exemplo n.º 29
0
def change_master_key(user_input:   'UserInput',
                      contact_list: 'ContactList',
                      group_list:   'GroupList',
                      settings:     'Settings',
                      queues:       Dict[bytes, 'Queue'],
                      master_key:   'MasterKey') -> None:
    """Change master key on TxM/RxM."""
    try:
        if settings.session_trickle:
            raise FunctionReturn("Command disabled during trickle connection.")

        try:
            device = user_input.plaintext.split()[1]
        except IndexError:
            raise FunctionReturn("No target system specified.")

        if device.lower() not in ['tx', 'txm', 'rx', 'rxm']:
            raise FunctionReturn("Invalid target system.")

        if device.lower() in ['rx', 'rxm']:
            queue_command(CHANGE_MASTER_K_HEADER, settings, queues[COMMAND_PACKET_QUEUE])
            print('')
            return None

        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")

        queues[KEY_MANAGEMENT_QUEUE].put(('KEY', master_key))

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

        box_print("Master key successfully changed.", head=1)
        clear_screen(delay=1.5)
    except KeyboardInterrupt:
        raise FunctionReturn("Password change aborted.")
Exemplo n.º 30
0
def print_kdk(kdk_bytes: bytes, settings: 'Settings') -> None:
    """Print symmetric key decryption key.

    If local testing is not enabled, this function will add spacing between
    key decryption key to help user keep track of key typing progress. The
    length of the Base58 encoded key varies between 48..50 characters, thus
    spacing is adjusted to get even length for each substring.

    :param kdk_bytes: Key decryption key
    :param settings:  Settings object
    :return:          None
    """
    kdk_enc = b58encode(kdk_bytes)
    ssl = {48: 8, 49: 7, 50: 5}.get(len(kdk_enc), 5)
    kdk = kdk_enc if settings.local_testing_mode else ' '.join(
        split_string(kdk_enc, item_len=ssl))

    box_print(["Local key decryption key (to RxM)", kdk])