Ejemplo n.º 1
0
    def test_successful_packet_decryption_with_offset(self):
        # Setup
        self.create_encrypted_packet(tx_harac=2, rx_harac=1, key=(hash_chain(KEY_LENGTH*b'\x01')))

        # Test
        assembly_pt, account, origin = decrypt_assembly_packet(self.packet, self.window_list, self.contact_list, self.key_list)
        self.assertEqual(rm_padding_bytes(assembly_pt), PRIVATE_MESSAGE_HEADER + b'test')
        self.assertEqual(account, '*****@*****.**')
        self.assertEqual(origin, ORIGIN_CONTACT_HEADER)
Ejemplo n.º 2
0
    def assemble_and_store_file(self,
                                ts:            'datetime',
                                onion_pub_key: bytes,
                                window_list:   'WindowList'
                                ) -> None:
        """Assemble file packet and store it."""
        padded  = b''.join([p[ASSEMBLY_PACKET_HEADER_LENGTH:] for p in self.assembly_pt_list])
        payload = rm_padding_bytes(padded)

        process_assembled_file(ts, payload, onion_pub_key, self.contact.nick, self.settings, window_list)
Ejemplo n.º 3
0
    def assemble_and_store_file(self, ts: 'datetime', onion_pub_key: bytes,
                                window_list: 'WindowList') -> None:
        """Assemble file packet and store it."""
        padded = b''.join(
            [p[ASSEMBLY_PACKET_HEADER_LENGTH:] for p in self.assembly_pt_list])
        payload = rm_padding_bytes(padded)

        no_fields = 3 if len(self.assembly_pt_list) > 1 else 2
        *_, payload = separate_headers(payload,
                                       no_fields * [ENCODED_INTEGER_LENGTH])

        process_assembled_file(ts, payload, onion_pub_key, self.contact.nick,
                               self.settings, window_list)
Ejemplo n.º 4
0
    def assemble_command_packet(self) -> bytes:
        """Assemble command 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:
            payload, cmd_hash = separate_trailer(payload, BLAKE2_DIGEST_LENGTH)
            if blake2b(payload) != cmd_hash:
                raise FunctionReturn("Error: Received an invalid command.")

        try:
            return decompress(payload, self.settings.max_decompress_size)
        except zlib.error:
            raise FunctionReturn("Error: Decompression of command failed.")
Ejemplo n.º 5
0
    def assemble_command_packet(self) -> bytes:
        """Assemble command 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:
            cmd_hash = payload[-KEY_LENGTH:]
            payload = payload[:-KEY_LENGTH]
            if hash_chain(payload) != cmd_hash:
                raise FunctionReturn("Error: Received an invalid command.")

        try:
            return zlib.decompress(payload)
        except zlib.error:
            raise FunctionReturn("Error: Decompression of command failed.")
Ejemplo n.º 6
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.")
Ejemplo n.º 7
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.")
Ejemplo n.º 8
0
 def test_padding_removal(self):
     for i in range(0, 1000):
         string = os.urandom(i)
         length = PADDING_LEN - (len(string) % PADDING_LEN)
         padded = string + length * bytes([length])
         self.assertEqual(rm_padding_bytes(padded), string)
Ejemplo n.º 9
0
 def test_removal_of_padding_does_not_alter_the_original_message(
         self) -> None:
     for message_length in range(4 * PADDING_LENGTH):
         message = os.urandom(message_length)
         padded = byte_padding(message)
         self.assertEqual(rm_padding_bytes(padded), message)
Ejemplo n.º 10
0
def access_history(window:       Union['Window', 'Window_'],
                   contact_list: 'ContactList',
                   settings:     'Settings',
                   master_key:   'MasterKey',
                   msg_to_load:  int = 0,
                   export:       bool = False) -> None:
    """Decrypt 'msg_to_load' last messages from log database and display/export it.

    :param window:       Window object
    :param contact_list: ContactList object
    :param settings:     Settings object
    :param master_key:   Master key object
    :param msg_to_load:  Number of messages to load
    :param export:       When True, write logged messages into
                         plaintext file instead of printing them.
    :return:             None
    """

    def read_entry():
        """Read encrypted log entry.

        Length  |  Data type
        --------|--------------------------------
             24 |  XSalsa20 nonce
              4 |  Timestamp
              4 |  UTF-32 BOM
          4*255 |  Padded account (UTF-32)
              1 |  Origin header
              1 |  Assembly packet header
            255 |  Padded assembly packet (UTF-8)
             16 |  Poly1305 tag
        """
        return log_file.read(1325)

    ensure_dir(f'{DIR_USER_DATA}/')
    file_name = f'{DIR_USER_DATA}/{settings.software_operation}_logs'
    if not os.path.isfile(file_name):
        raise FunctionReturn(f"Error: Could not find '{file_name}'.")

    log_file          = open(file_name, 'rb')
    ts_message_list   = []  # type: List[Tuple[str, str, bytes, str]]
    assembly_p_buffer = dict()
    group_timestamp   = b''

    for ct in iter(read_entry, b''):
        pt      = auth_and_decrypt(ct, key=master_key.master_key)
        account = bytes_to_str(pt[5:1029])

        if window.type == 'contact' and window.uid != account:
            continue

        t_stamp         = parse_ts_bytes(pt[0:4], settings)
        origin_byte     = pt[4:5]
        origin          = origin_byte.decode()
        assembly_header = pt[1029:1030]
        assembly_pt     = pt[1030:]

        if assembly_header == M_S_HEADER:
            depadded     = rm_padding_bytes(assembly_pt)
            decompressed = zlib.decompress(depadded)
            if decompressed[:1] == PRIVATE_MESSAGE_HEADER:
                if window.type == 'group':
                    continue
                decoded = decompressed[1:].decode()

            elif decompressed[:1] == GROUP_MESSAGE_HEADER:

                group_name, decoded = [f.decode() for f in decompressed[9:].split(US_BYTE)]
                if group_name != window.name:
                    continue
                if group_timestamp == decompressed[1:9]:
                    continue
                else:
                    group_timestamp = decompressed[1:9]

            ts_message_list.append((t_stamp, account, origin_byte, decoded))

        elif assembly_header == M_L_HEADER:
            assembly_p_buffer[origin + account] = assembly_pt

        elif assembly_header == M_A_HEADER:
            if (origin + account) in assembly_p_buffer:
                assembly_p_buffer[origin + account] += assembly_pt

        elif assembly_header == M_E_HEADER:
            if (origin + account) in assembly_p_buffer:
                assembly_p_buffer[origin + account] += assembly_pt

                pt_buf       = assembly_p_buffer.pop(origin + account)
                inner_l      = rm_padding_bytes(pt_buf)
                msg_key      = inner_l[-32:]
                enc_msg      = inner_l[:-32]
                decrypted    = auth_and_decrypt(enc_msg, key=msg_key)
                decompressed = zlib.decompress(decrypted)

                if decompressed[:1] == PRIVATE_MESSAGE_HEADER:
                    if window.type == 'group':
                        continue
                    decoded = decompressed[1:].decode()

                elif decompressed[:1] == GROUP_MESSAGE_HEADER:
                    group_name, decoded = [f.decode() for f in decompressed[9:].split(US_BYTE)]
                    if group_name != window.name:
                        continue
                    if group_timestamp == decompressed[1:9]:  # Skip duplicates of outgoing messages
                        continue
                    else:
                        group_timestamp = decompressed[1:9]

                ts_message_list.append((t_stamp, account, origin_byte, decoded))

        elif assembly_header == M_C_HEADER:
            assembly_p_buffer.pop(origin + account, None)

    log_file.close()

    if not export:
        clear_screen()
        print('')

    tty_w  = get_tty_w()

    system = dict(tx="TxM",     rx="RxM",     ut="Unittest")[settings.software_operation]
    m_dir  = dict(tx="sent to", rx="to/from", ut="to/from")[settings.software_operation]

    f_name = open(f"{system} - Plaintext log ({window.name})", 'w+') if export else sys.stdout
    subset = '' if msg_to_load == 0 else f"{msg_to_load} most recent "
    title  = textwrap.fill(f"Log file of {subset}message(s) {m_dir} {window.name}", tty_w)

    print(title,       file=f_name)
    print(tty_w * '═', file=f_name)

    for timestamp, account, origin_, message in ts_message_list[-msg_to_load:]:

        nick = "Me" if origin_ == ORIGIN_USER_HEADER else contact_list.get_contact(account).nick

        print(textwrap.fill(f"{timestamp} {nick}:", tty_w), file=f_name)
        print('',                                           file=f_name)
        print(textwrap.fill(message, tty_w),                file=f_name)
        print('',                                           file=f_name)
        print(tty_w * '─',                                  file=f_name)

    if export:
        f_name.close()
    else:
        print('')
Ejemplo n.º 11
0
    def assemble_and_store_file(self) -> None:
        """Assemble file packet and store it."""
        padded = b''.join([p[1:] for p in self.assembly_pt_list])
        payload = rm_padding_bytes(padded)

        process_received_file(payload, self.contact.nick)
Ejemplo n.º 12
0
 def test_removal_of_padding_does_not_alter_original_string(self):
     for length in range(1000):
         string = os.urandom(length)
         padded = byte_padding(string)
         self.assertEqual(rm_padding_bytes(padded), string)
Ejemplo n.º 13
0
 def test_function(self):
     for i in range(0, 1000):
         string = i * b'm'
         length = 255 - (len(string) % 255)
         padded = string + length * bytes([length])
         self.assertEqual(rm_padding_bytes(padded), string)