Exemplo n.º 1
0
 def test_auth_and_decrypt_with_the_official_test_vectors(self) -> None:
     self.assertEqual(
         auth_and_decrypt(self.nonce_ct_tag_ietf, self.key, ad=self.ad),
         self.plaintext)
     self.assertEqual(
         auth_and_decrypt(self.nonce_ct_tag_libsodium, self.key,
                          ad=self.ad), self.plaintext)
Exemplo n.º 2
0
    def test_auth_and_decrypt_with_official_test_vectors(self):
        self.assertEqual(
            auth_and_decrypt(self.nonce_ct_tag, self.key, ad=self.ad),
            self.plaintext)

        self.assertEqual(
            auth_and_decrypt(self.ietf_nonce_ct_tag, self.key, ad=self.ad),
            self.plaintext)
Exemplo n.º 3
0
def decrypt_assembly_packet(packet:        bytes,          # Assembly packet ciphertext
                            onion_pub_key: bytes,          # Onion Service pubkey of associated contact
                            origin:        bytes,          # Direction of packet
                            window_list:   'WindowList',   # WindowList object
                            contact_list:  'ContactList',  # ContactList object
                            key_list:      'KeyList'       # Keylist object
                            ) -> bytes:                    # Decrypted assembly packet
    """Decrypt assembly packet from contact/local Transmitter."""
    ct_harac, ct_assemby_packet = separate_header(packet, header_length=HARAC_CT_LENGTH)
    local_window                = window_list.get_local_window()
    command                     = onion_pub_key == LOCAL_PUBKEY

    p_type    = "command" if command                                      else "packet"
    direction = "from"    if command or (origin == ORIGIN_CONTACT_HEADER) else "sent to"
    nick      = contact_list.get_contact_by_pub_key(onion_pub_key).nick

    # Load keys
    keyset  = key_list.get_keyset(onion_pub_key)
    key_dir = TX if origin == ORIGIN_USER_HEADER else RX

    header_key  = getattr(keyset, f'{key_dir}_hk')  # type: bytes
    message_key = getattr(keyset, f'{key_dir}_mk')  # type: bytes

    if any(k == bytes(SYMMETRIC_KEY_LENGTH) for k in [header_key, message_key]):
        raise FunctionReturn("Warning! Loaded zero-key for packet decryption.")

    # Decrypt hash ratchet counter
    try:
        harac_bytes = auth_and_decrypt(ct_harac, header_key)
    except nacl.exceptions.CryptoError:
        raise FunctionReturn(
            f"Warning! Received {p_type} {direction} {nick} had an invalid hash ratchet MAC.", window=local_window)

    # Catch up with hash ratchet offset
    purp_harac   = bytes_to_int(harac_bytes)
    stored_harac = getattr(keyset, f'{key_dir}_harac')
    offset       = purp_harac - stored_harac
    if offset < 0:
        raise FunctionReturn(
            f"Warning! Received {p_type} {direction} {nick} had an expired hash ratchet counter.", window=local_window)

    process_offset(offset, origin, direction, nick, local_window)
    for harac in range(stored_harac, stored_harac + offset):
        message_key = blake2b(message_key + int_to_bytes(harac), digest_size=SYMMETRIC_KEY_LENGTH)

    # Decrypt packet
    try:
        assembly_packet = auth_and_decrypt(ct_assemby_packet, message_key)
    except nacl.exceptions.CryptoError:
        raise FunctionReturn(f"Warning! Received {p_type} {direction} {nick} had an invalid MAC.", window=local_window)

    # Update message key and harac
    keyset.update_mk(key_dir,
                     blake2b(message_key + int_to_bytes(stored_harac + offset), digest_size=SYMMETRIC_KEY_LENGTH),
                     offset + 1)

    return assembly_packet
Exemplo n.º 4
0
 def test_invalid_size_key_raises_critical_error(self) -> None:
     invalid_keys = [
         key_length * b'a' for key_length in
         [1, SYMMETRIC_KEY_LENGTH - 1, SYMMETRIC_KEY_LENGTH + 1, 1000]
     ]
     for invalid_key in invalid_keys:
         with self.assertRaises(SystemExit):
             encrypt_and_sign(self.libsodium_plaintext, invalid_key)
         with self.assertRaises(SystemExit):
             auth_and_decrypt(self.nonce_ct_tag_ietf, invalid_key)
Exemplo n.º 5
0
def decrypt_assembly_packet(packet: bytes, window_list: 'WindowList',
                            contact_list: 'ContactList',
                            key_list: 'KeyList') -> Tuple[bytes, str, bytes]:
    """Decrypt assembly packet from contact/local TxM."""
    enc_harac = packet[1:49]
    enc_msg = packet[49:345]
    window = window_list.get_local_window()

    origin, direction, key_dir, p_type, account, nick = get_packet_values(
        packet, window, contact_list)

    # Load keys
    keyset = key_list.get_keyset(account)
    header_key = getattr(keyset, f'{key_dir}_hek')
    message_key = getattr(keyset, f'{key_dir}_key')

    if any(k == bytes(KEY_LENGTH) for k in [header_key, message_key]):
        raise FunctionReturn("Warning! Loaded zero-key for packet decryption.")

    # Decrypt hash ratchet counter
    try:
        harac_bytes = auth_and_decrypt(enc_harac, header_key, soft_e=True)
    except nacl.exceptions.CryptoError:
        raise FunctionReturn(
            f"Warning! Received {p_type} {direction} {nick} had an invalid hash ratchet MAC.",
            window=window)

    # Catch up with hash ratchet offset
    purp_harac = bytes_to_int(harac_bytes)
    stored_harac = getattr(keyset, f'{key_dir}_harac')
    offset = purp_harac - stored_harac
    if offset < 0:
        raise FunctionReturn(
            f"Warning! Received {p_type} {direction} {nick} had an expired hash ratchet counter.",
            window=window)

    process_offset(offset, origin, direction, nick, window)
    for _ in range(offset):
        message_key = hash_chain(message_key)

    # Decrypt packet
    try:
        assembly_packet = auth_and_decrypt(enc_msg, message_key, soft_e=True)
    except nacl.exceptions.CryptoError:
        raise FunctionReturn(
            f"Warning! Received {p_type} {direction} {nick} had an invalid MAC.",
            window=window)

    # Update keys in database
    keyset.update_key(key_dir, hash_chain(message_key), offset + 1)

    return assembly_packet, account, origin
Exemplo n.º 6
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)
Exemplo n.º 7
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.º 8
0
    def _load_keys(self) -> None:
        """Load KeySets from the encrypted database.

        This function first reads and decrypts the database content. It
        then splits the plaintext into a list of 176-byte blocks. Each
        block contains the serialized data of one KeySet. Next, the
        function will remove from the list all dummy KeySets (that start
        with the `dummy_id` byte string). The function will then
        populate the `self.keysets` list with KeySet objects, the data
        of which is sliced and decoded from the dummy-free blocks.
        """
        with open(self.file_name, 'rb') as f:
            ct_bytes = f.read()

        pt_bytes  = auth_and_decrypt(ct_bytes, self.master_key.master_key, database=self.file_name)
        blocks    = split_byte_string(pt_bytes, item_len=KEYSET_LENGTH)
        df_blocks = [b for b in blocks if not b.startswith(self.dummy_id)]

        for block in df_blocks:
            if len(block) != KEYSET_LENGTH:
                raise CriticalError("Invalid data in key database.")

            onion_pub_key, tx_mk, rx_mk, tx_hk, rx_hk, tx_harac_bytes, rx_harac_bytes \
                = separate_headers(block, [ONION_SERVICE_PUBLIC_KEY_LENGTH] + 4*[SYMMETRIC_KEY_LENGTH] + [HARAC_LENGTH])

            self.keysets.append(KeySet(onion_pub_key=onion_pub_key,
                                       tx_mk=tx_mk,
                                       rx_mk=rx_mk,
                                       tx_hk=tx_hk,
                                       rx_hk=rx_hk,
                                       tx_harac=bytes_to_int(tx_harac_bytes),
                                       rx_harac=bytes_to_int(rx_harac_bytes),
                                       store_keys=self.store_keys))
Exemplo n.º 9
0
    def load_database(self) -> bytes:
        """Load data from database.

        This function first checks if a temporary file exists from
        previous session. The integrity of the temporary file is
        verified with the Poly1305 MAC before the database is
        replaced.

        The function then reads the up-to-date database content
        and decrypts it.
        """
        if os.path.isfile(self.database_temp):
            if self.verify_file(self.database_temp):
                os.replace(self.database_temp, self.database_name)
            else:
                # If temp file is not authentic, the file is most likely corrupt, so
                # we delete it and continue using the old file to ensure atomicity.
                os.remove(self.database_temp)

        with open(self.database_name, 'rb') as f:
            database_data = f.read()

        return auth_and_decrypt(database_data,
                                self.database_key,
                                database=self.database_name)
Exemplo n.º 10
0
 def __iter__(self) -> Iterator[bytes]:
     """Iterate over encrypted log entries."""
     for log_entry in self.c.execute("SELECT log_entry FROM log_entries"):
         plaintext = auth_and_decrypt(log_entry[0],
                                      self.database_key,
                                      database=self.database_name)
         yield plaintext
Exemplo n.º 11
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)
Exemplo n.º 12
0
Arquivo: files.py Projeto: savg110/tfc
def decrypt_and_store_file(
        ts: 'datetime',  # Timestamp of received packet
        file_ct: bytes,  # File ciphertext
        file_key: bytes,  # File decryption key
        file_name: str,  # Name of the file
        onion_pub_key: bytes,  # Onion Service pubkey of sender
        nick: str,  # Nickname of sender
        window_list: 'WindowList',  # WindowList object
        settings: 'Settings'  # Settings object
) -> None:
    """Decrypt and store file."""
    try:
        file_pt = auth_and_decrypt(file_ct, file_key)
    except nacl.exceptions.CryptoError:
        raise SoftError("Error: Decryption of file data failed.")

    try:
        file_dc = decompress(file_pt, settings.max_decompress_size)
    except zlib.error:
        raise SoftError("Error: Decompression of file data failed.")

    file_dir = f'{DIR_RECV_FILES}{nick}/'
    final_name = store_unique(file_dc, file_dir, file_name)

    message = f"Stored file from {nick} as '{final_name}'."
    if settings.traffic_masking and window_list.active_win is not None:
        window = window_list.active_win
    else:
        window = window_list.get_window(onion_pub_key)
    window.add_new(ts, message, onion_pub_key, output=True, event_msg=True)
Exemplo n.º 13
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 ''
Exemplo n.º 14
0
def decrypt_local_key(ts: 'datetime', packet: bytes, kdk_hashes: List[bytes],
                      packet_hashes: List[bytes], settings: 'Settings',
                      l_queue: 'local_key_queue') -> Tuple['datetime', bytes]:
    """Decrypt local key packet."""
    while True:
        kdk = get_b58_key(B58_LOCAL_KEY, settings)
        kdk_hash = blake2b(kdk)

        # Check if the key was an old one.
        if kdk_hash in kdk_hashes:
            m_print("Error: Entered an old local key decryption key.", delay=1)
            continue

        try:
            plaintext = auth_and_decrypt(packet, kdk)
        except nacl.exceptions.CryptoError:
            ts, plaintext = process_local_key_buffer(kdk, l_queue)

        protect_kdk(kdk)

        # Cache hashes needed to recognize reissued local key packets and key decryption keys.
        kdk_hashes.append(kdk_hash)
        packet_hashes.append(blake2b(packet))

        return ts, plaintext
Exemplo n.º 15
0
    def load_settings(self) -> None:
        """Load settings from the encrypted database."""
        with open(self.file_name, 'rb') as f:
            ct_bytes = f.read()

        pt_bytes = auth_and_decrypt(ct_bytes,
                                    self.master_key.master_key,
                                    database=self.file_name)

        # Update settings based on plaintext byte string content
        for key in self.key_list:

            attribute = self.__getattribute__(key)

            if isinstance(attribute, bool):
                value = bytes_to_bool(
                    pt_bytes[0])  # type: Union[bool, int, float]
                pt_bytes = pt_bytes[ENCODED_BOOLEAN_LENGTH:]

            elif isinstance(attribute, int):
                value = bytes_to_int(pt_bytes[:ENCODED_INTEGER_LENGTH])
                pt_bytes = pt_bytes[ENCODED_INTEGER_LENGTH:]

            elif isinstance(attribute, float):
                value = bytes_to_double(pt_bytes[:ENCODED_FLOAT_LENGTH])
                pt_bytes = pt_bytes[ENCODED_FLOAT_LENGTH:]

            else:
                raise CriticalError(
                    "Invalid data type in settings default values.")

            setattr(self, key, value)
Exemplo n.º 16
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 ''
Exemplo n.º 17
0
def process_local_key_buffer(
        kdk: bytes, l_queue: 'local_key_queue') -> Tuple[datetime, bytes]:
    """Check if the KDK was for a packet further ahead in the queue."""
    buffer = []  # type: List[Tuple[datetime, bytes]]
    while l_queue.qsize() > 0:
        tup = l_queue.get()  # type: Tuple[datetime, bytes]
        if tup not in buffer:
            buffer.append(tup)

    for i, tup in enumerate(buffer):
        try:
            plaintext = auth_and_decrypt(tup[1], kdk)

            # If we reach this point, decryption was successful.
            for unexamined in buffer[i + 1:]:
                l_queue.put(unexamined)
            buffer = []
            ts = tup[0]

            return ts, plaintext

        except nacl.exceptions.CryptoError:
            continue

    # Finished the buffer without finding local key CT
    # for the kdk. Maybe the kdk is from another session.
    raise SoftError("Error: Incorrect key decryption key.", delay=1)
Exemplo n.º 18
0
    def verify_file(self, database_name: str) -> bool:
        """Verify integrity of database file content."""
        conn = sqlite3.connect(database_name)
        c = conn.cursor()

        try:
            log_entries = c.execute("SELECT log_entry FROM log_entries")
        except sqlite3.DatabaseError:
            return False

        for ct_log_entry in log_entries:
            try:
                auth_and_decrypt(ct_log_entry[0], self.database_key)
            except nacl.exceptions.CryptoError:
                return False

        return True
Exemplo n.º 19
0
    def verify_file(self, database_name: str) -> bool:
        """Verify integrity of file content."""
        with open(database_name, 'rb') as f:
            purp_data = f.read()

        try:
            _ = auth_and_decrypt(purp_data, self.database_key)
            return True
        except nacl.exceptions.CryptoError:
            return False
Exemplo n.º 20
0
    def load_onion_service_private_key(self) -> bytes:
        """Load the Onion Service private key from the encrypted database."""
        with open(self.file_name, 'rb') as f:
            ct_bytes = f.read()

        onion_private_key = auth_and_decrypt(ct_bytes,
                                             self.master_key.master_key,
                                             database=self.file_name)

        if len(onion_private_key) != ONION_SERVICE_PRIVATE_KEY_LENGTH:
            raise CriticalError("Invalid Onion Service private key length.")

        return onion_private_key
Exemplo n.º 21
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.º 22
0
    def assemble_message_packet(self) -> bytes:
        """Assemble message packet."""
        padded  = b''.join([p[ASSEMBLY_PACKET_HEADER_LENGTH:] for p in self.assembly_pt_list])
        payload = rm_padding_bytes(padded)

        if len(self.assembly_pt_list) > 1:
            msg_ct, msg_key = separate_trailer(payload, SYMMETRIC_KEY_LENGTH)
            try:
                payload = auth_and_decrypt(msg_ct, msg_key)
            except nacl.exceptions.CryptoError:
                raise FunctionReturn("Error: Decryption of message failed.")

        try:
            return decompress(payload, MAX_MESSAGE_SIZE)
        except zlib.error:
            raise FunctionReturn("Error: Decompression of message failed.")
Exemplo n.º 23
0
Arquivo: files.py Projeto: todun/tfc
def process_assembled_file(
        ts: 'datetime',  # Timestamp last received packet
        payload: bytes,  # File name and content
        onion_pub_key: bytes,  # Onion Service pubkey of sender
        nick: str,  # Nickname of sender
        settings: 'Settings',  # Settings object
        window_list: 'WindowList',  # WindowList object
) -> None:
    """Process received file assembly packets."""
    try:
        file_name_b, file_data = payload.split(US_BYTE, 1)
    except ValueError:
        raise FunctionReturn("Error: Received file had an invalid structure.")

    try:
        file_name = file_name_b.decode()
    except UnicodeError:
        raise FunctionReturn(
            "Error: Received file name had an invalid encoding.")

    if not file_name.isprintable() or not file_name or '/' in file_name:
        raise FunctionReturn("Error: Received file had an invalid name.")

    file_ct, file_key = separate_trailer(file_data, SYMMETRIC_KEY_LENGTH)

    if len(file_key) != SYMMETRIC_KEY_LENGTH:
        raise FunctionReturn("Error: Received file had an invalid key.")

    try:
        file_pt = auth_and_decrypt(file_ct, file_key)
    except nacl.exceptions.CryptoError:
        raise FunctionReturn("Error: Decryption of file data failed.")

    try:
        file_dc = decompress(file_pt, settings.max_decompress_size)
    except zlib.error:
        raise FunctionReturn("Error: Decompression of file data failed.")

    file_dir = f'{DIR_RECV_FILES}{nick}/'
    final_name = store_unique(file_dc, file_dir, file_name)

    message = f"Stored file from {nick} as '{final_name}'."
    if settings.traffic_masking and window_list.active_win is not None:
        window = window_list.active_win
    else:
        window = window_list.get_window(onion_pub_key)
    window.add_new(ts, message, onion_pub_key, output=True, event_msg=True)
Exemplo n.º 24
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.º 25
0
Arquivo: files.py Projeto: gtog/tfc
def process_file(
        ts: 'datetime',  # Timestamp of received_packet
        onion_pub_key: bytes,  # Onion Service pubkey of sender
        file_ct: bytes,  # File ciphertext
        file_key: bytes,  # File decryption key
        contact_list: 'ContactList',  # ContactList object
        window_list: 'WindowList',  # WindowList object
        settings: 'Settings'  # Settings object
) -> None:
    """Store file received from a contact."""
    nick = contact_list.get_contact_by_pub_key(onion_pub_key).nick

    phase("Processing received file", head=1)
    try:
        file_pt = auth_and_decrypt(file_ct, file_key)
    except nacl.exceptions.CryptoError:
        raise FunctionReturn(
            f"Error: Decryption key for file from {nick} was invalid.")

    try:
        file_dc = decompress(file_pt, settings.max_decompress_size)
    except zlib.error:
        raise FunctionReturn(f"Error: Failed to decompress file from {nick}.")
    phase(DONE)
    print_on_previous_line(reps=2)

    try:
        file_name = bytes_to_str(file_dc[:PADDED_UTF32_STR_LENGTH])
    except UnicodeError:
        raise FunctionReturn(
            f"Error: Name of file from {nick} had invalid encoding.")

    if not file_name.isprintable() or not file_name or '/' in file_name:
        raise FunctionReturn(f"Error: Name of file from {nick} was invalid.")

    f_data = file_dc[PADDED_UTF32_STR_LENGTH:]

    file_dir = f'{DIR_RECV_FILES}{nick}/'
    final_name = store_unique(f_data, file_dir, file_name)
    message = f"Stored file from {nick} as '{final_name}'."

    if settings.traffic_masking and window_list.active_win is not None:
        window = window_list.active_win
    else:
        window = window_list.get_window(onion_pub_key)

    window.add_new(ts, message, onion_pub_key, output=True, event_msg=True)
Exemplo n.º 26
0
    def assemble_message_packet(self) -> bytes:
        """Assemble message packet."""
        padded = b''.join([p[1:] for p in self.assembly_pt_list])
        payload = rm_padding_bytes(padded)

        if len(self.assembly_pt_list) > 1:
            msg_ct = payload[:-KEY_LENGTH]
            msg_key = payload[-KEY_LENGTH:]

            try:
                payload = auth_and_decrypt(msg_ct, msg_key, soft_e=True)
            except (nacl.exceptions.CryptoError, nacl.exceptions.ValueError):
                raise FunctionReturn("Error: Decryption of message failed.")

        try:
            return zlib.decompress(payload)
        except zlib.error:
            raise FunctionReturn("Error: Decompression of message failed.")
Exemplo n.º 27
0
    def _load_contacts(self) -> None:
        """Load contacts from the encrypted database.

        This function first reads and decrypts the database content. It
        then splits the plaintext into a list of 1124-byte blocks: each
        block contains the serialized data of one contact. Next, the
        function will remove from the list all dummy contacts (that
        start with dummy contact's public key). The function will then
        populate the `self.contacts` list with Contact objects, the data
        of which is sliced and decoded from the dummy-free blocks.
        """
        with open(self.file_name, 'rb') as f:
            ct_bytes = f.read()

        pt_bytes = auth_and_decrypt(ct_bytes,
                                    self.master_key.master_key,
                                    database=self.file_name)
        blocks = split_byte_string(pt_bytes, item_len=CONTACT_LENGTH)
        df_blocks = [
            b for b in blocks
            if not b.startswith(self.dummy_contact.onion_pub_key)
        ]

        for block in df_blocks:
            if len(block) != CONTACT_LENGTH:
                raise CriticalError("Invalid data in contact database.")

            (onion_pub_key, tx_fingerprint, rx_fingerprint, kex_status_byte,
             log_messages_byte, file_reception_byte,
             notifications_byte, nick_bytes) = separate_headers(
                 block,
                 [ONION_SERVICE_PUBLIC_KEY_LENGTH] + 2 * [FINGERPRINT_LENGTH] +
                 [KEX_STATUS_LENGTH] + 3 * [ENCODED_BOOLEAN_LENGTH])

            self.contacts.append(
                Contact(onion_pub_key=onion_pub_key,
                        tx_fingerprint=tx_fingerprint,
                        rx_fingerprint=rx_fingerprint,
                        kex_status=kex_status_byte,
                        log_messages=bytes_to_bool(log_messages_byte),
                        file_reception=bytes_to_bool(file_reception_byte),
                        notifications=bytes_to_bool(notifications_byte),
                        nick=bytes_to_str(nick_bytes)))
Exemplo n.º 28
0
    def load_contacts(self) -> None:
        """Load contacts from encrypted database."""
        with open(self.file_name, 'rb') as f:
            ct_bytes = f.read()

        pt_bytes = auth_and_decrypt(ct_bytes, self.master_key.master_key)
        entries  = split_byte_string(pt_bytes, item_len=CONTACT_LENGTH)
        contacts = [e for e in entries if not e.startswith(self.dummy_id)]

        for c in contacts:
            assert len(c) == CONTACT_LENGTH

            self.contacts.append(Contact(rx_account    =bytes_to_str( c[   0:1024]),
                                         tx_account    =bytes_to_str( c[1024:2048]),
                                         nick          =bytes_to_str( c[2048:3072]),
                                         tx_fingerprint=              c[3072:3104],
                                         rx_fingerprint=              c[3104:3136],
                                         log_messages  =bytes_to_bool(c[3136:3137]),
                                         file_reception=bytes_to_bool(c[3137:3138]),
                                         notifications =bytes_to_bool(c[3138:3139])))
Exemplo n.º 29
0
def process_imported_file(ts:          'datetime',
                          packet:      bytes,
                          window_list: 'WindowList'):
    """Decrypt and store imported file."""
    while True:
        try:
            print('')
            key = get_b58_key('imported_file')
            phase("Decrypting file", head=1)
            file_pt = auth_and_decrypt(packet[1:], key, soft_e=True)
            phase("Done")
            break
        except nacl.exceptions.CryptoError:
            c_print("Invalid decryption key. Try again.", head=2)
            print_on_previous_line(reps=6, delay=1.5)
        except KeyboardInterrupt:
            raise FunctionReturn("File import aborted.")

    try:
        phase("Decompressing file")
        file_dc = zlib.decompress(file_pt)
        phase("Done")
    except zlib.error:
        raise FunctionReturn("Decompression of file data failed.")

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

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

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

    message = "Stored imported file to {}/{}".format(DIR_IMPORTED, final_name)
    box_print(message, head=1)

    local_win = window_list.get_local_window()
    local_win.print_new(ts, message, print_=False)
Exemplo n.º 30
0
def process_received_file(payload: bytes, nick: str) -> None:
    """Process received file assembly packets"""
    try:
        f_name, _, _, f_data = payload.split(US_BYTE)
    except ValueError:
        raise FunctionReturn("Received file had invalid structure.")

    try:
        f_name_d = f_name.decode()
    except UnicodeError:
        raise FunctionReturn("Received file had an invalid name.")

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

    try:
        f_data = base64.b85decode(f_data)
    except (binascii.Error, ValueError):
        raise FunctionReturn("Received file had invalid encoding.")

    file_ct  = f_data[:-32]
    file_key = f_data[-32:]
    if len(file_key) != 32:
        raise FunctionReturn("Received file had an invalid key.")

    try:
        file_pt = auth_and_decrypt(file_ct, file_key, soft_e=True)
    except nacl.exceptions.CryptoError:
        raise FunctionReturn("Decryption of file data failed.")

    try:
        file_dc = zlib.decompress(file_pt)
    except zlib.error:
        raise FunctionReturn("Decompression of file data failed.")

    if len(file_dc) == 0:
        raise FunctionReturn("Received file did not contain data.")

    f_dir      = f'{DIR_RX_FILES}/{nick}'
    final_name = store_unique(file_dc, f_dir, f_name_d)
    box_print(["Stored file from {} as {}.".format(nick, final_name)])