Exemple #1
0
def process_received_packet(ts: 'datetime', ts_bytes: bytes, header: bytes,
                            payload_bytes: bytes, onion_pub_key: bytes,
                            short_addr: str, queues: 'QueueDict',
                            gateway: 'Gateway') -> None:
    """Process received packet."""
    if header == PUBLIC_KEY_DATAGRAM_HEADER:
        if len(payload_bytes) == TFC_PUBLIC_KEY_LENGTH:
            msg = f"Received public key from {short_addr} at {ts.strftime('%b %d - %H:%M:%S.%f')[:-4]}:"
            print_key(msg, payload_bytes, gateway.settings, public_key=True)
            queues[PUB_KEY_SEND_QUEUE].put((onion_pub_key, payload_bytes))

    elif header == MESSAGE_DATAGRAM_HEADER:
        queues[DST_MESSAGE_QUEUE].put(header + ts_bytes + onion_pub_key +
                                      ORIGIN_CONTACT_HEADER + payload_bytes)
        rp_print(f"Message   from contact {short_addr}", ts)

    elif header in [
            GROUP_MSG_INVITE_HEADER, GROUP_MSG_JOIN_HEADER,
            GROUP_MSG_MEMBER_ADD_HEADER, GROUP_MSG_MEMBER_REM_HEADER,
            GROUP_MSG_EXIT_GROUP_HEADER
    ]:
        queues[GROUP_MSG_QUEUE].put((header, payload_bytes, short_addr))

    else:
        rp_print(f"Received invalid packet from {short_addr}", ts, bold=True)
Exemple #2
0
def deliver_local_key(local_key_packet: bytes, kek: bytes, c_code: bytes,
                      settings: 'Settings', queues: 'QueueDict') -> None:
    """Deliver encrypted local key to Destination Computer."""
    nc_bypass_msg(NC_BYPASS_START, settings)
    queue_to_nc(local_key_packet, queues[RELAY_PACKET_QUEUE])

    while True:
        print_key("Local key decryption key (to Receiver)", kek, settings)
        purp_code = ask_confirmation_code("Receiver")
        if purp_code == c_code.hex():
            nc_bypass_msg(NC_BYPASS_STOP, settings)
            break
        elif purp_code == "":
            phase("Resending local key", head=2)
            queue_to_nc(local_key_packet, queues[RELAY_PACKET_QUEUE])
            phase(DONE)
            print_on_previous_line(
                reps=(9 if settings.local_testing_mode else 10))
        else:
            m_print([
                "Incorrect confirmation code. If Receiver did not receive",
                "the encrypted local key, resend it by pressing <Enter>."
            ],
                    head=1)
            print_on_previous_line(
                reps=(9 if settings.local_testing_mode else 10), delay=2)
Exemple #3
0
def process_public_key(ts: 'datetime', packet: bytes,
                       window_list: 'WindowList', settings: 'Settings',
                       pubkey_buf: Dict[str, bytes]) -> None:
    """Display contact's public key and add it to buffer."""
    pub_key = packet[1:33]
    origin = packet[33:34]

    try:
        account = packet[34:].decode()
    except UnicodeError:
        raise FunctionReturn(
            "Error! Account for received public key had invalid encoding.")

    if origin not in [ORIGIN_CONTACT_HEADER, ORIGIN_USER_HEADER]:
        raise FunctionReturn(
            "Error! Received public key had an invalid origin header.")

    if origin == ORIGIN_CONTACT_HEADER:
        pubkey_buf[account] = pub_key
        print_key(f"Received public key from {account}:", pub_key, settings)

        local_win = window_list.get_local_window()
        pub_key_b58 = ' '.join(
            split_string(b58encode(pub_key),
                         item_len=(51 if settings.local_testing_mode else 3)))
        local_win.add_new(
            ts, f"Received public key from {account}: {pub_key_b58}")

    elif origin == ORIGIN_USER_HEADER and account in pubkey_buf:
        clear_screen()
        print_key(f"Public key for {account}:", pubkey_buf[account], settings)
Exemple #4
0
def export_file(settings: 'Settings', nh_queue: 'Queue') -> None:
    """Encrypt and export file to NH.

    This is a faster method to send large files. It is used together
    with file import (/fi) command that uploads ciphertext to RxM for
    RxM-side decryption. Key is generated automatically so that bad
    passwords selected by users do not affect security of ciphertexts.
    """
    if settings.session_traffic_masking:
        raise FunctionReturn(
            "Error: Command is disabled during traffic masking.")

    path = ask_path_gui("Select file to export...", settings, get_file=True)
    name = path.split('/')[-1]
    data = bytearray()
    data.extend(str_to_bytes(name))

    if not os.path.isfile(path):
        raise FunctionReturn("Error: File not found.")

    if os.path.getsize(path) == 0:
        raise FunctionReturn("Error: Target file is empty.")

    phase("Reading data")
    with open(path, 'rb') as f:
        data.extend(f.read())
    phase(DONE)

    phase("Compressing data")
    comp = bytes(zlib.compress(bytes(data), level=COMPRESSION_LEVEL))
    phase(DONE)

    phase("Encrypting data")
    file_key = csprng()
    file_ct = encrypt_and_sign(comp, key=file_key)
    phase(DONE)

    phase("Exporting data")
    queue_to_nh(EXPORTED_FILE_HEADER + file_ct, settings, nh_queue)
    phase(DONE)

    print_key(f"Decryption key for file '{name}':",
              file_key,
              settings,
              no_split=True,
              file_key=True)
Exemple #5
0
def get_data_loop(onion_addr: str, url_token: str, short_addr: str,
                  onion_pub_key: bytes, queues: 'QueueDict',
                  session: 'Session', gateway: 'Gateway') -> None:
    """Load TFC data from contact's Onion Service using valid URL token."""
    while True:
        try:
            # See if a file is available
            try:
                file_data = session.get(
                    f'http://{onion_addr}.onion/{url_token}/files',
                    stream=True).content
                if file_data:
                    ts = datetime.now()
                    ts_bytes = int_to_bytes(
                        int(ts.strftime('%Y%m%d%H%M%S%f')[:-4]))
                    packet = FILE_DATAGRAM_HEADER + ts_bytes + onion_pub_key + ORIGIN_CONTACT_HEADER + file_data
                    queues[DST_MESSAGE_QUEUE].put(packet)
                    rp_print(f"File      from contact {short_addr}", ts)

            except requests.exceptions.RequestException:
                pass

            # See if messages are available
            try:
                r = session.get(
                    f'http://{onion_addr}.onion/{url_token}/messages',
                    stream=True)
            except requests.exceptions.RequestException:
                return None

            for line in r.iter_lines(
            ):  # Iterates over newline-separated datagrams

                if not line:
                    continue

                try:
                    header, payload = separate_header(
                        line, DATAGRAM_HEADER_LENGTH)  # type: bytes, bytes
                    payload_bytes = base64.b85decode(payload)
                except (UnicodeError, ValueError):
                    continue

                ts = datetime.now()
                ts_bytes = int_to_bytes(int(
                    ts.strftime('%Y%m%d%H%M%S%f')[:-4]))

                if header == PUBLIC_KEY_DATAGRAM_HEADER:
                    if len(payload_bytes) == TFC_PUBLIC_KEY_LENGTH:
                        msg = f"Received public key from {short_addr} at {ts.strftime('%b %d - %H:%M:%S.%f')[:-4]}:"
                        print_key(msg,
                                  payload_bytes,
                                  gateway.settings,
                                  public_key=True)

                elif header == MESSAGE_DATAGRAM_HEADER:
                    queues[DST_MESSAGE_QUEUE].put(header + ts_bytes +
                                                  onion_pub_key +
                                                  ORIGIN_CONTACT_HEADER +
                                                  payload_bytes)
                    rp_print(f"Message   from contact {short_addr}", ts)

                elif header in [
                        GROUP_MSG_INVITE_HEADER, GROUP_MSG_JOIN_HEADER,
                        GROUP_MSG_MEMBER_ADD_HEADER,
                        GROUP_MSG_MEMBER_REM_HEADER,
                        GROUP_MSG_EXIT_GROUP_HEADER
                ]:
                    queues[GROUP_MSG_QUEUE].put(
                        (header, payload_bytes, short_addr))

                else:
                    rp_print(f"Received invalid packet from {short_addr}",
                             ts,
                             bold=True)

        except requests.exceptions.RequestException:
            break
Exemple #6
0
def new_local_key(contact_list: 'ContactList', settings: 'Settings',
                  queues: 'QueueDict') -> None:
    """Run local key exchange protocol.

    Local key encrypts commands and data sent from Source Computer to
    user's Destination Computer. The key is delivered to Destination
    Computer in packet encrypted with an ephemeral, symmetric, key
    encryption key.

    The check-summed Base58 format key decryption key is typed to
    Receiver Program manually. This prevents local key leak in following
    scenarios:

        1. CT is intercepted by an adversary on compromised Networked
           Computer, but no visual eavesdropping takes place.

        2. CT is not intercepted by an adversary on Networked Computer,
           but visual eavesdropping records key decryption key.

        3. CT is delivered from Source Computer to Destination Computer
           directly (bypassing compromised Networked Computer), and
           visual eavesdropping records key decryption key.

    Once the correct key decryption key is entered to Receiver Program,
    it will display the 2-hexadecimal confirmation code generated by
    the Transmitter Program. The code will be entered back to
    Transmitter Program to confirm the user has successfully delivered
    the key decryption key.

    The protocol is completed with Transmitter Program sending
    LOCAL_KEY_RDY signal to the Receiver Program, that then moves to
    wait for public keys from contact.
    """
    try:
        if settings.traffic_masking and contact_list.has_local_contact():
            raise FunctionReturn(
                "Error: Command is disabled during traffic masking.",
                head_clear=True)

        m_print("Local key setup", bold=True, head_clear=True, head=1, tail=1)

        if not contact_list.has_local_contact():
            time.sleep(0.5)

        key = csprng()
        hek = csprng()
        kek = csprng()
        c_code = os.urandom(CONFIRM_CODE_LENGTH)

        local_key_packet = LOCAL_KEY_DATAGRAM_HEADER + encrypt_and_sign(
            plaintext=key + hek + c_code, key=kek)

        # Deliver local key to Destination computer
        nc_bypass_msg(NC_BYPASS_START, settings)
        queue_to_nc(local_key_packet, queues[RELAY_PACKET_QUEUE])
        while True:
            print_key("Local key decryption key (to Receiver)", kek, settings)
            purp_code = ask_confirmation_code('Receiver')
            if purp_code == c_code.hex():
                nc_bypass_msg(NC_BYPASS_STOP, settings)
                break
            elif purp_code == '':
                phase("Resending local key", head=2)
                queue_to_nc(local_key_packet, queues[RELAY_PACKET_QUEUE])
                phase(DONE)
                print_on_previous_line(
                    reps=(9 if settings.local_testing_mode else 10))
            else:
                m_print([
                    "Incorrect confirmation code. If Receiver did not receive",
                    "the encrypted local key, resend it by pressing <Enter>."
                ],
                        head=1)
                print_on_previous_line(
                    reps=(9 if settings.local_testing_mode else 10), delay=2)

        # Add local contact to contact list database
        contact_list.add_contact(LOCAL_PUBKEY, LOCAL_NICK,
                                 bytes(FINGERPRINT_LENGTH),
                                 bytes(FINGERPRINT_LENGTH),
                                 KEX_STATUS_LOCAL_KEY, False, False, False)

        # Add local contact to keyset database
        queues[KEY_MANAGEMENT_QUEUE].put(
            (KDB_ADD_ENTRY_HEADER, LOCAL_PUBKEY, key, csprng(), hek, csprng()))

        # Notify Receiver that confirmation code was successfully entered
        queue_command(LOCAL_KEY_RDY, settings, queues)

        m_print("Successfully completed the local key exchange.",
                bold=True,
                tail_clear=True,
                delay=1,
                head=1)
        os.system(RESET)

    except (EOFError, KeyboardInterrupt):
        raise FunctionReturn("Local key setup aborted.",
                             tail_clear=True,
                             delay=1,
                             head=2)
Exemple #7
0
def new_local_key(contact_list: 'ContactList',
                  settings:     'Settings',
                  queues:       Dict[bytes, 'Queue']) -> None:
    """Run Tx-side local key exchange protocol.

    Local key encrypts commands and data sent from TxM to RxM. The key is
    delivered to RxM in packet encrypted with an ephemeral symmetric key.

    The checksummed Base58 format key decryption key is typed on RxM manually.
    This prevents local key leak in following scenarios:

        1. CT is intercepted by adversary on compromised NH but no visual
           eavesdropping takes place.
        2. CT is not intercepted by adversary on NH but visual eavesdropping
           records decryption key.
        3. CT is delivered from TxM to RxM (compromised NH is bypassed) and
           visual eavesdropping records decryption key.

    Once correct key decryption key is entered on RxM, Receiver program will
    display the 1-byte confirmation code generated by Transmitter program.
    The code will be entered on TxM to confirm user has successfully delivered
    the key decryption key.

    The protocol is completed with Transmitter program sending an ACK message
    to Receiver program, that then moves to wait for public keys from contact.
    """
    try:
        if settings.session_traffic_masking and contact_list.has_local_contact:
            raise FunctionReturn("Error: Command is disabled during traffic masking.")

        clear_screen()
        c_print("Local key setup", head=1, tail=1)

        c_code = os.urandom(1)
        key    = csprng()
        hek    = csprng()
        kek    = csprng()
        packet = LOCAL_KEY_PACKET_HEADER + encrypt_and_sign(key + hek + c_code, key=kek)

        nh_bypass_msg(NH_BYPASS_START, settings)
        queue_to_nh(packet, settings, queues[NH_PACKET_QUEUE])

        while True:
            print_key("Local key decryption key (to RxM)", kek, settings)
            purp_code = ask_confirmation_code()
            if purp_code == c_code.hex():
                break
            elif purp_code == RESEND:
                phase("Resending local key", head=2)
                queue_to_nh(packet, settings, queues[NH_PACKET_QUEUE])
                phase(DONE)
                print_on_previous_line(reps=(9 if settings.local_testing_mode else 10))
            else:
                box_print(["Incorrect confirmation code. If RxM did not receive",
                           "encrypted local key, resend it by typing 'resend'."], head=1)
                print_on_previous_line(reps=(11 if settings.local_testing_mode else 12), delay=2)

        nh_bypass_msg(NH_BYPASS_STOP, settings)

        # 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, False)

        # Add local contact to keyset database
        queues[KEY_MANAGEMENT_QUEUE].put((KDB_ADD_ENTRY_HEADER, LOCAL_ID,
                                          key, csprng(),
                                          hek, csprng()))

        # Notify RxM that confirmation code was successfully entered
        queue_command(LOCAL_KEY_INSTALLED_HEADER, settings, queues[COMMAND_PACKET_QUEUE])

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

    except KeyboardInterrupt:
        raise FunctionReturn("Local key setup aborted.", delay=1, head=3, tail_clear=True)