Пример #1
0
def decrypt_rx_psk(ct_tag: bytes, salt: bytes) -> bytes:
    """Get PSK password from user and decrypt Rx-PSK."""
    while True:
        try:
            password = MasterKey.get_password("PSK password")
            phase("Deriving the key decryption key", head=2)
            kdk = argon2_kdf(password, salt, ARGON2_PSK_TIME_COST,
                             ARGON2_PSK_MEMORY_COST, ARGON2_PSK_PARALLELISM)
            psk = auth_and_decrypt(ct_tag, kdk)
            phase(DONE)
            return psk

        except nacl.exceptions.CryptoError:
            print_on_previous_line()
            m_print("Invalid password. Try again.", head=1)
            print_on_previous_line(reps=5, delay=1)
        except (EOFError, KeyboardInterrupt):
            raise SoftError("PSK import aborted.",
                            head=2,
                            delay=1,
                            tail_clear=True)
Пример #2
0
def key_ex_psk_rx(packet: bytes, ts: 'datetime', window_list: 'WindowList',
                  contact_list: 'ContactList', key_list: 'KeyList',
                  settings: 'Settings') -> None:
    """Import Rx-PSK of contact."""
    c_code, onion_pub_key = separate_header(packet, CONFIRM_CODE_LENGTH)
    short_addr = pub_key_to_short_address(onion_pub_key)

    if not contact_list.has_pub_key(onion_pub_key):
        raise FunctionReturn(f"Error: Unknown account '{short_addr}'.",
                             head_clear=True)

    contact = contact_list.get_contact_by_pub_key(onion_pub_key)
    psk_file = ask_path_gui(f"Select PSK for {contact.nick} ({short_addr})",
                            settings,
                            get_file=True)

    try:
        with open(psk_file, 'rb') as f:
            psk_data = f.read()
    except PermissionError:
        raise FunctionReturn("Error: No read permission for the PSK file.")

    if len(psk_data) != PSK_FILE_SIZE:
        raise FunctionReturn("Error: The PSK data in the file was invalid.",
                             head_clear=True)

    salt, ct_tag = separate_header(psk_data, ARGON2_SALT_LENGTH)

    while True:
        try:
            password = MasterKey.get_password("PSK password")
            phase("Deriving the key decryption key", head=2)
            kdk = argon2_kdf(password,
                             salt,
                             time_cost=ARGON2_PSK_TIME_COST,
                             memory_cost=ARGON2_PSK_MEMORY_COST)
            psk = auth_and_decrypt(ct_tag, kdk)
            phase(DONE)
            break

        except nacl.exceptions.CryptoError:
            print_on_previous_line()
            m_print("Invalid password. Try again.", head=1)
            print_on_previous_line(reps=5, delay=1)
        except (EOFError, KeyboardInterrupt):
            raise FunctionReturn("PSK import aborted.",
                                 head=2,
                                 delay=1,
                                 tail_clear=True)

    rx_mk, rx_hk = separate_header(psk, SYMMETRIC_KEY_LENGTH)

    if any(k == bytes(SYMMETRIC_KEY_LENGTH) for k in [rx_mk, rx_hk]):
        raise FunctionReturn("Error: Received invalid keys from contact.",
                             head_clear=True)

    keyset = key_list.get_keyset(onion_pub_key)
    keyset.rx_mk = rx_mk
    keyset.rx_hk = rx_hk
    key_list.store_keys()

    contact.kex_status = KEX_STATUS_HAS_RX_PSK
    contact_list.store_contacts()

    # Pipes protects against shell injection. Source of command's parameter is
    # the program itself, and therefore trusted, but it's still good practice.
    subprocess.Popen(f"shred -n 3 -z -u {pipes.quote(psk_file)}",
                     shell=True).wait()
    if os.path.isfile(psk_file):
        m_print(
            f"Warning! Overwriting of PSK ({psk_file}) failed. Press <Enter> to continue.",
            manual_proceed=True,
            box=True)

    message = f"Added Rx-side PSK for {contact.nick} ({short_addr})."
    local_win = window_list.get_local_window()
    local_win.add_new(ts, message)

    m_print([
        message, '', "Warning!",
        "Physically destroy the keyfile transmission media ",
        "to ensure it does not steal data from this computer!", '',
        f"Confirmation code (to Transmitter): {c_code.hex()}"
    ],
            box=True,
            head=1,
            tail=1)
Пример #3
0
def import_psk_rx_keys(cmd_data: bytes, ts: 'datetime',
                       window_list: 'WindowList', contact_list: 'ContactList',
                       key_list: 'KeyList', settings: 'Settings') -> None:
    """Import Rx-PSK of contact."""
    account = cmd_data.decode()

    if not contact_list.has_contact(account):
        raise FunctionReturn(f"Error: Unknown account '{account}'")

    contact = contact_list.get_contact(account)
    psk_file = ask_path_gui(f"Select PSK for {contact.nick}",
                            settings,
                            get_file=True)

    with open(psk_file, 'rb') as f:
        psk_data = f.read()

    if len(psk_data) != PSK_FILE_SIZE:
        raise FunctionReturn("Error: Invalid PSK data in file.")

    salt = psk_data[:ARGON2_SALT_LEN]
    ct_tag = psk_data[ARGON2_SALT_LEN:]

    while True:
        try:
            password = MasterKey.get_password("PSK password")
            phase("Deriving key decryption key", head=2)
            kdk, _ = argon2_kdf(password, salt, parallelism=1)
            psk_pt = auth_and_decrypt(ct_tag, key=kdk, soft_e=True)
            phase(DONE)
            break

        except nacl.exceptions.CryptoError:
            print_on_previous_line()
            c_print("Invalid password. Try again.", head=1)
            print_on_previous_line(reps=5, delay=1.5)
        except KeyboardInterrupt:
            raise FunctionReturn("PSK import aborted.", head=2)

    rx_key = psk_pt[0:32]
    rx_hek = psk_pt[32:64]

    if any(k == bytes(KEY_LENGTH) for k in [rx_key, rx_hek]):
        raise FunctionReturn("Error: Received invalid keys from contact.")

    keyset = key_list.get_keyset(account)
    keyset.rx_key = rx_key
    keyset.rx_hek = rx_hek
    key_list.store_keys()

    # Pipes protects against shell injection. Source of command's parameter
    # is user's own RxM and therefore trusted, but it's still good practice.
    subprocess.Popen(f"shred -n 3 -z -u {pipes.quote(psk_file)}",
                     shell=True).wait()
    if os.path.isfile(psk_file):
        box_print(
            f"Warning! Overwriting of PSK ({psk_file}) failed. Press <Enter> to continue.",
            manual_proceed=True)

    local_win = window_list.get_local_window()
    message = f"Added Rx-PSK for {contact.nick} ({account})."
    local_win.add_new(ts, message)

    box_print([
        message, '', "Warning!",
        "Physically destroy the keyfile transmission ",
        "media to ensure that no data escapes RxM!"
    ],
              head=1,
              tail=1)
Пример #4
0
def psk_import(cmd_data: bytes, ts: 'datetime', window_list: 'WindowList',
               contact_list: 'ContactList', key_list: 'KeyList',
               settings: 'Settings') -> None:
    """Import rx-PSK of contact."""
    account = cmd_data.decode()

    if not contact_list.has_contact(account):
        raise FunctionReturn(f"Unknown account {account}.")

    contact = contact_list.get_contact(account)
    pskf = ask_path_gui(f"Select PSK for {contact.nick}",
                        settings,
                        get_file=True)

    with open(pskf, 'rb') as f:
        psk_data = f.read()

    if len(
            psk_data
    ) != 136:  # Nonce (24) + Salt (32) + rx-key (32) + rx-hek (32) + tag (16)
        raise FunctionReturn("Invalid PSK data in file.")

    salt = psk_data[:32]
    ct_tag = psk_data[32:]

    while True:
        try:
            password = MasterKey.get_password("PSK password")
            phase("Deriving key decryption key", head=2)
            kdk, _ = argon2_kdf(password,
                                salt,
                                rounds=16,
                                memory=128000,
                                parallelism=1)
            psk_pt = auth_and_decrypt(ct_tag, key=kdk, soft_e=True)
            phase("Done")
            break

        except nacl.exceptions.CryptoError:
            print_on_previous_line()
            c_print("Invalid password. Try again.", head=1)
            print_on_previous_line(reps=5, delay=1.5)
        except KeyboardInterrupt:
            raise FunctionReturn("PSK import aborted.")

    rx_key = psk_pt[0:32]
    rx_hek = psk_pt[32:64]

    if rx_key == bytes(32) or rx_hek == bytes(32):
        raise FunctionReturn("Keys from contact are not valid.")

    keyset = key_list.get_keyset(account)
    keyset.rx_key = rx_key
    keyset.rx_hek = rx_hek
    key_list.store_keys()

    # Pipes protects against shell injection. Source of command
    # is trusted (user's own TxM) but it's still good practice.
    subprocess.Popen("shred -n 3 -z -u {}".format(pipes.quote(pskf)),
                     shell=True).wait()
    if os.path.isfile(pskf):
        box_print(f"Warning! Overwriting of PSK ({pskf}) failed.")
        time.sleep(3)

    local_win = window_list.get_local_window()
    local_win.print_new(ts,
                        f"Added Rx-PSK for {contact.nick} ({account})",
                        print_=False)

    box_print([
        f"Added Rx-PSK for {contact.nick}.", '', "Warning!",
        "Physically destroy the keyfile transmission ",
        "media to ensure that no data escapes RxM!"
    ],
              head=1,
              tail=1)