Example #1
0
    def generate_dummy_group(self) -> bytes:
        """Generate a byte string that represents a dummy group."""
        name = str_to_bytes('dummy_group')
        log_messages = bool_to_bytes(False)
        notifications = bool_to_bytes(False)
        members = self.settings.m_members_in_group * ['dummy_member']
        member_bytes = b''.join([str_to_bytes(m) for m in members])

        return name + log_messages + notifications + member_bytes
Example #2
0
 def dump_c(self) -> bytes:
     """Return contact data as constant length byte string."""
     return   str_to_bytes(self.rx_account) \
            + str_to_bytes(self.tx_account) \
            + str_to_bytes(self.nick) \
            + self.tx_fingerprint \
            + self.rx_fingerprint \
            + bool_to_bytes(self.log_messages) \
            + bool_to_bytes(self.file_reception) \
            + bool_to_bytes(self.notifications)
Example #3
0
 def serialize_c(self) -> bytes:
     """Return contact data as constant length byte string."""
     return (str_to_bytes(self.rx_account)
             + str_to_bytes(self.tx_account)
             + str_to_bytes(self.nick)
             + self.tx_fingerprint
             + self.rx_fingerprint
             + bool_to_bytes(self.log_messages)
             + bool_to_bytes(self.file_reception)
             + bool_to_bytes(self.notifications))
Example #4
0
    def dump_g(self) -> bytes:
        """Return group data as constant length byte string."""
        name = str_to_bytes(self.name)
        log_messages = bool_to_bytes(self.log_messages)
        notifications = bool_to_bytes(self.notifications)
        members = self.get_list_of_member_accounts()

        num_of_dummies = self.settings.m_members_in_group - len(self.members)
        members += num_of_dummies * ['dummy_member']
        member_bytes = b''.join([str_to_bytes(m) for m in members])

        return name + log_messages + notifications + member_bytes
Example #5
0
    def serialize_g(self) -> bytes:
        """Return group data as constant length byte string."""
        name = str_to_bytes(self.name)
        log_messages = bool_to_bytes(self.log_messages)
        notifications = bool_to_bytes(self.notifications)
        members = self.get_list_of_member_accounts()
        num_of_dummies = self.settings.max_number_of_group_members - len(
            self.members)
        members += num_of_dummies * [DUMMY_MEMBER]
        member_bytes = b''.join([str_to_bytes(m) for m in members])

        return name + log_messages + notifications + member_bytes
Example #6
0
    def generate_dummy_contact() -> bytes:
        """Generate byte string for dummy contact."""
        rx_account     = str_to_bytes('dummy_contact')
        tx_account     = str_to_bytes('dummy_user')
        nick           = str_to_bytes('dummy_nick')
        tx_fingerprint = bytes(32)
        rx_fingerprint = bytes(32)
        logging_bytes  = bool_to_bytes(False)
        file_r_bytes   = bool_to_bytes(False)
        notify_bytes   = bool_to_bytes(False)

        return rx_account + tx_account + nick \
               + tx_fingerprint + rx_fingerprint \
               + logging_bytes + file_r_bytes + notify_bytes
Example #7
0
def deliver_contact_data(
        header: bytes,  # Key type (x448, PSK)
        nick: str,  # Contact's nickname
        onion_pub_key: bytes,  # Public key of contact's v3 Onion Service
        tx_mk: bytes,  # Message key for outgoing messages
        rx_mk: bytes,  # Message key for incoming messages
        tx_hk: bytes,  # Header key for outgoing messages
        rx_hk: bytes,  # Header key for incoming messages
        queues: 'QueueDict',  # Dictionary of multiprocessing queues
        settings: 'Settings',  # Settings object
) -> None:
    """Deliver contact data to Destination Computer."""
    c_code = blake2b(onion_pub_key, digest_size=CONFIRM_CODE_LENGTH)
    command = (header + onion_pub_key + tx_mk + rx_mk + tx_hk + rx_hk +
               str_to_bytes(nick))

    queue_command(command, settings, queues)

    while True:
        purp_code = ask_confirmation_code("Receiver")
        if purp_code == c_code.hex():
            break

        elif purp_code == "":
            phase("Resending contact data", head=2)
            queue_command(command, settings, queues)
            phase(DONE)
            print_on_previous_line(reps=5)

        else:
            m_print("Incorrect confirmation code.", head=1)
            print_on_previous_line(reps=4, delay=2)
Example #8
0
    def test_valid_import(self):
        file_name   = str_to_bytes('testfile.txt')
        data        = file_name + os.urandom(1000)
        compressed  = zlib.compress(data, level=9)
        key         = os.urandom(32)
        key_b58     = b58encode(key)
        packet      = IMPORTED_FILE_CT_HEADER + encrypt_and_sign(compressed, key)
        ts          = datetime.datetime.now()
        window_list = WindowList(nicks=['local'])
        o_input     = builtins.input
        input_list  = ['2QJL5gVSPEjMTaxWPfYkzG9UJxzZDNSx6PPeVWdzS5CFN7knZy', key_b58]
        gen         = iter(input_list)

        def mock_input(_):
            return str(next(gen))

        builtins.input = mock_input

        # Setup
        self.assertIsNone(process_imported_file(ts, packet, window_list))
        self.assertTrue(os.path.isfile(f"{DIR_IMPORTED}/testfile.txt"))

        # Teardown
        builtins.input = o_input
        shutil.rmtree(f'{DIR_IMPORTED}/')
Example #9
0
    def test_successful_storage_of_file(self, _):
        compressed = zlib.compress(str_to_bytes("test_file.txt") +
                                   b'file_data',
                                   level=COMPRESSION_LEVEL)
        file_data = encrypt_and_sign(compressed, self.file_key)

        self.assertIsNone(
            process_file(self.ts, self.account, file_data, *self.args))
Example #10
0
    def test_non_printable_name_raises_fr(self, _):
        compressed = zlib.compress(str_to_bytes("file\x01") + b'file_data',
                                   level=COMPRESSION_LEVEL)
        file_data = encrypt_and_sign(compressed, self.file_key)

        self.assert_fr("Error: Name of file from Alice was invalid.",
                       process_file, self.ts, self.account, file_data,
                       *self.args)
Example #11
0
 def dump_k(self) -> bytes:
     """Return keyset data as constant length byte string."""
     return str_to_bytes(self.rx_account) \
            + self.tx_key \
            + self.rx_key \
            + self.tx_hek \
            + self.rx_hek \
            + int_to_bytes(self.tx_harac) \
            + int_to_bytes(self.rx_harac)
Example #12
0
    def test_slash_in_name_raises_se(self, _: Any) -> None:
        compressed = zlib.compress(str_to_bytes("Alice/file.txt") +
                                   b'file_data',
                                   level=COMPRESSION_LEVEL)
        file_data = encrypt_and_sign(compressed, self.file_key)

        self.assert_se("Error: Name of file from Alice was invalid.",
                       process_file, self.ts, self.account, file_data,
                       *self.args)
Example #13
0
    def test_valid_import(self):
        # Setup
        file_name  = str_to_bytes('testfile.txt')
        data       = file_name + os.urandom(1000)
        compressed = zlib.compress(data, level=COMPRESSION_LEVEL)
        packet     = IMPORTED_FILE_HEADER + encrypt_and_sign(compressed, self.key)

        # Test
        self.assertIsNone(process_imported_file(self.ts, packet, self.window_list, self.settings))
        self.assertTrue(os.path.isfile(f"{DIR_IMPORTED}testfile.txt"))
Example #14
0
    def test_invalid_name_raises_fr(self):
        # Setup
        file_name  = str_to_bytes('\x01testfile.txt')
        data       = file_name + os.urandom(1000)
        compressed = zlib.compress(data, level=COMPRESSION_LEVEL)
        packet     = IMPORTED_FILE_HEADER + encrypt_and_sign(compressed, self.key)

        # Test
        self.assertFR("Error: Received file had an invalid name.",
                      process_imported_file, self.ts, packet, self.window_list, self.settings)
Example #15
0
        def queue_delayer():
            time.sleep(0.1)

            # Queue local key packet
            local_key_packet = LOCAL_KEY_PACKET_HEADER + encrypt_and_sign(
                local_key + local_hek + conf_code, key=kek)
            queues[LOCAL_KEY_PACKET_HEADER].put(
                (datetime.datetime.now(), local_key_packet))
            time.sleep(0.1)

            # Queue screen clearing command
            queue_packet(tx_key, tx_hek, INITIAL_HARAC, CLEAR_SCREEN_HEADER)

            # Queue message that goes to buffer
            queue_packet(tx_key, tx_hek, INITIAL_HARAC,
                         PRIVATE_MESSAGE_HEADER + b'Hi Bob', b'*****@*****.**')

            # Queue public key for Bob
            public_key_packet = PUBLIC_KEY_PACKET_HEADER + KEY_LENGTH * b'a' + ORIGIN_CONTACT_HEADER + b'*****@*****.**'
            queues[PUBLIC_KEY_PACKET_HEADER].put(
                (datetime.datetime.now(), public_key_packet))
            time.sleep(0.1)

            # Queue X25519 keyset for Bob
            command = KEY_EX_X25519_HEADER + 4 * (
                KEY_LENGTH * b'a') + b'*****@*****.**' + US_BYTE + b'Bob'
            queue_packet(hash_chain(tx_key), tx_hek, INITIAL_HARAC + 1,
                         command)

            # Queue window selection packet
            command = WINDOW_SELECT_HEADER + b'*****@*****.**'
            queue_packet(hash_chain(hash_chain(tx_key)), tx_hek,
                         INITIAL_HARAC + 2, command)

            # Queue message that is displayed directly
            packet = b'Hi again, Bob'
            queue_packet(tx_key, tx_hek, INITIAL_HARAC, packet,
                         b'*****@*****.**')

            # Queue file window selection command
            command = WINDOW_SELECT_HEADER + WIN_TYPE_FILE.encode()
            queue_packet(hash_chain(hash_chain(hash_chain(tx_key))), tx_hek,
                         INITIAL_HARAC + 3, command)

            # Queue imported file packet
            file_data = str_to_bytes('testfile') + 500 * b'a'
            compressed = zlib.compress(file_data, level=COMPRESSION_LEVEL)
            packet = IMPORTED_FILE_HEADER + encrypt_and_sign(compressed,
                                                             key=fdk)
            queues[IMPORTED_FILE_HEADER].put((datetime.datetime.now(), packet))
            time.sleep(0.1)

            # Queue exit message to break loop
            queues[UNITTEST_QUEUE].put(EXIT)
            time.sleep(0.1)
Example #16
0
    def generate_dummy_keyset() -> bytes:
        """Generate bytestring for dummy keyset."""
        tx_account = str_to_bytes('dummy_contact')
        tx_key = bytes(32)
        rx_key = bytes(32)
        tx_hek = bytes(32)
        rx_hek = bytes(32)
        tx_harac = int_to_bytes(0)
        rx_harac = int_to_bytes(0)

        return tx_account + tx_key + rx_key + tx_hek + rx_hek + tx_harac + rx_harac
Example #17
0
 def setUp(self):
     self.ts = datetime.fromtimestamp(1502750000)
     self.window_list = WindowList(nicks=[LOCAL_ID])
     self.contact_list = ContactList()
     self.key_list = KeyList()
     self.settings = Settings()
     self.packet = (nick_to_pub_key("Alice") +
                    SYMMETRIC_KEY_LENGTH * b'\x01' +
                    bytes(SYMMETRIC_KEY_LENGTH) +
                    SYMMETRIC_KEY_LENGTH * b'\x02' +
                    bytes(SYMMETRIC_KEY_LENGTH) + str_to_bytes('Alice'))
     self.args = self.packet, self.ts, self.window_list, self.contact_list, self.key_list, self.settings
Example #18
0
 def setUp(self) -> None:
     """Pre-test actions."""
     self.unit_test_dir = cd_unit_test()
     self.ts            = datetime.now()
     self.packet        = b''
     self.file_keys     = dict()
     self.file_buf      = dict()
     self.contact_list  = ContactList(nicks=['Alice'])
     self.window_list   = WindowList()
     self.file_key      = SYMMETRIC_KEY_LENGTH*b'a'
     self.settings      = Settings()
     self.compressed    = zlib.compress(str_to_bytes("test_file.txt") + b'file_data', level=COMPRESSION_LEVEL)
     self.args          = self.file_keys, self.file_buf, self.contact_list, self.window_list, self.settings
Example #19
0
    def test_successful_storage_during_traffic_masking(self, _: Any) -> None:
        # Setup
        self.settings.traffic_masking = True
        self.window_list.active_win   = self.window_list.get_window(nick_to_pub_key('Bob'))

        compressed = zlib.compress(str_to_bytes("testfile.txt") + b'file_data', level=COMPRESSION_LEVEL)
        file_data  = encrypt_and_sign(compressed, self.file_key)

        self.assertIsNone(process_file(self.ts, self.account, file_data, *self.args))

        self.assertEqual(self.window_list.get_window(nick_to_pub_key('Bob')).message_log[0][1],
                         "Stored file from Alice as 'testfile.txt'.")

        self.assertTrue(os.path.isfile(f'{DIR_RECV_FILES}Alice/testfile.txt'))
Example #20
0
    def serialize_c(self) -> bytes:
        """Return contact data as a constant length byte string.

        This function serializes the contact's data into a byte string
        that has the exact length of 3*32 + 4*1 + 1024 = 1124 bytes. The
        length is guaranteed regardless of the content or length of the
        attributes' values, including the contact's nickname. The
        purpose of the constant length serialization is to hide any
        metadata about the contact the ciphertext length of the contact
        database would reveal.
        """
        return (self.onion_pub_key + self.tx_fingerprint +
                self.rx_fingerprint + self.kex_status +
                bool_to_bytes(self.log_messages) +
                bool_to_bytes(self.file_reception) +
                bool_to_bytes(self.notifications) + str_to_bytes(self.nick))
Example #21
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)
Example #22
0
    def store_settings(self) -> None:
        """Store settings to encrypted database."""
        attribute_list = [self.__getattribute__(k) for k in self.key_list]

        # Convert attributes into constant length byte string
        pt_bytes = b''
        for a in attribute_list:
            if isinstance(a, bool): pt_bytes += bool_to_bytes(a)
            elif isinstance(a, int): pt_bytes += int_to_bytes(a)
            elif isinstance(a, float): pt_bytes += double_to_bytes(a)
            elif isinstance(a, str): pt_bytes += str_to_bytes(a)
            else: raise CriticalError("Invalid attribute type in settings.")

        ct_bytes = encrypt_and_sign(pt_bytes, self.master_key.master_key)

        ensure_dir(f'{DIR_USER_DATA}/')
        with open(self.file_name, 'wb+') as f:
            f.write(ct_bytes)
Example #23
0
def export_file(settings: 'Settings', gateway: 'Gateway'):
    """Encrypt and export file to NH.

    This is a faster method of sending large files. It is used together with '/fi' import_file
    command that loads ciphertext to RxM for later decryption. Key is generated automatically
    so that bad passwords by users do not affect security of ciphertexts.

    As use of this command reveals use of TFC, it is disabled during trickle connection.
    """
    if settings.session_trickle:
        raise FunctionReturn("Command disabled during trickle connection.")

    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. No file was sent.")

    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=9))
    phase("Done")

    phase("Encrypting data")
    file_key = keygen()
    file_ct  = encrypt_and_sign(comp, key=file_key)
    phase("Done")

    phase("Exporting data")
    transmit(EXPORTED_FILE_CT_HEADER + file_ct, settings, gateway)
    phase("Done")

    box_print([f"Decryption key for file {name}:", '', b58encode(file_key)], head=1, tail=1)
Example #24
0
    def serialize_g(self) -> bytes:
        """Return group data as a constant length bytestring.

        This function serializes the group's data into a bytestring
        that always has a constant length. The exact length depends on
        the attribute `max_number_of_group_members` of TFC's Settings
        object. With the default setting of 50 members per group, the
        length of the serialized data is
            1024 + 4 + 2*1 + 50*32 = 2630 bytes
        The purpose of the constant length serialization is to hide any
        metadata the ciphertext length of the group database could
        reveal.
        """
        members = self.get_list_of_member_pub_keys()
        number_of_dummies = self.settings.max_number_of_group_members - len(
            self.members)
        members += number_of_dummies * [onion_address_to_pub_key(DUMMY_MEMBER)]
        member_bytes = b''.join(members)

        return (str_to_bytes(self.name) + self.group_id +
                bool_to_bytes(self.log_messages) +
                bool_to_bytes(self.notifications) + member_bytes)
Example #25
0
    def test_invalid_name_raises_fr(self):
        # Setup
        file_name   = str_to_bytes('\x01testfile.txt')
        data        = file_name + os.urandom(1000)
        compressed  = zlib.compress(data, level=9)
        key         = os.urandom(32)
        key_b58     = b58encode(key)
        packet      = IMPORTED_FILE_CT_HEADER + encrypt_and_sign(compressed, key)
        ts          = datetime.datetime.now()
        window_list = WindowList(nicks=['local'])
        o_input     = builtins.input
        input_list  = ['2QJL5gVSPEjMTaxWPfYkzG9UJxzZDNSx6PPeVWdzS5CFN7knZy', key_b58]
        gen         = iter(input_list)

        def mock_input(_):
            return str(next(gen))

        builtins.input = mock_input

        # Test
        self.assertFR("Received file had an invalid name.", process_imported_file, ts, packet, window_list)

        # Teardown
        builtins.input = o_input
Example #26
0
def write_log_entry(assembly_packet: bytes,
                    account:         str,
                    settings:        'Settings',
                    master_key:      'MasterKey',
                    origin:          bytes = ORIGIN_USER_HEADER) -> None:
    """Add assembly packet to encrypted logfile.

    This method of logging allows reconstruction of conversation while protecting
    the metadata about the length of messages other log file formats would reveal.

    TxM can only log sent messages. This is not useful for recalling conversations
    but serves an important role in audit of RxM-side logs, where malware could
    have substituted logged data on RxM.

    To protect possibly sensitive files that must not be logged, only placeholder
    data is logged about them. This helps hiding the amount of communication
    comparison with log file size and output packet count would otherwise reveal.

    :param assembly_packet: Assembly packet to log
    :param account:         Recipient's account (UID)
    :param origin:          Direction of logged packet
    :param settings:        Settings object
    :param master_key:      Master key object
    :return:                None
    """
    unix_timestamp  = int(time.time())
    timestamp_bytes = struct.pack('<L', unix_timestamp)
    encoded_account = str_to_bytes(account)

    pt_bytes = timestamp_bytes + origin + encoded_account + assembly_packet
    ct_bytes = encrypt_and_sign(pt_bytes, key=master_key.master_key)

    ensure_dir(f'{DIR_USER_DATA}/')
    file_name = f'{DIR_USER_DATA}/{settings.software_operation}_logs'
    with open(file_name, 'ab+') as f:
        f.write(ct_bytes)
Example #27
0
 def test_bytes_to_str(self):
     encoded = str_to_bytes('test')
     self.assertEqual(bytes_to_str(encoded), 'test')
Example #28
0
 def test_str_to_bytes(self):
     encoded = str_to_bytes('test')
     self.assertIsInstance(encoded, bytes)
     self.assertEqual(len(encoded), PADDED_UTF32_STR_LENGTH)
Example #29
0
def send_file(path:     str,
              settings: 'Settings',
              queues:   'QueueDict',
              window:   'TxWindow'
              ) -> None:
    """Send file to window members in a single transmission.

    This is the default mode for file transmission, used when traffic
    masking is not enabled. The file is loaded and compressed before it
    is encrypted. The encrypted file is then exported to Networked
    Computer along with a list of Onion Service public keys (members in
    window) of all recipients to whom the Relay Program will multi-cast
    the file to.

    Once the file ciphertext has been exported, this function will
    multi-cast the file decryption key to each recipient inside an
    automated key delivery message that uses a special FILE_KEY_HEADER
    in place of standard PRIVATE_MESSAGE_HEADER. To know for which file
    ciphertext the key is for, an identifier must be added to the key
    delivery message. The identifier in this case is the BLAKE2b digest
    of the ciphertext itself. The reason of using the digest as the
    identifier is, it authenticates both the ciphertext and its origin.
    To understand this, consider the following attack scenario:

    Let the file ciphertext identifier be just a random 32-byte value "ID".

    1) Alice sends Bob and Chuck (a malicious common peer) a file
       ciphertext and identifier CT|ID (where | denotes concatenation).

    2) Chuck who has compromised Bob's Networked Computer interdicts the
       CT|ID from Alice.

    3) Chuck decrypts CT in his end, makes edits to the plaintext PT to
       create PT'.

    4) Chuck re-encrypts PT' with the same symmetric key to produce CT'.

    5) Chuck re-uses the ID and produces CT'|ID.

    6) Chuck uploads the CT'|ID to Bob's Networked Computer and replaces
       the interdicted CT|ID with it.

    7) When Bob' Receiver Program receives the automated key delivery
       message from Alice, his Receiver program uses the bundled ID to
       identify the key is for CT'.

    8) Bob's Receiver decrypts CT' using the newly received key and
       obtains Chuck's PT', that appears to come from Alice.

    Now, consider a situation where the ID is instead calculated
    ID = BLAKE2b(CT), if Chuck edits the PT, the CT' will by definition
    be different from CT, and the BLAKE2b digest will also be different.
    In order to make Bob decrypt CT', Chuck needs to also change the
    hash in Alice's key delivery message, which means Chuck needs to
    create an existential forgery of the TFC message. Since the Poly1305
    tag prevents this, the calculated ID is enough to authenticate the
    ciphertext.

    If Chuck attempts to send their own key delivery message, Chuck's
    own Onion Service public key used to identify the TFC message key
    (decryption key for the key delivery message) will be permanently
    associated with the file hash, so if they inject a file CT, and Bob
    has decided to enable file reception for Chuck, the file CT will
    appear to come from Chuck, and not from Alice. From the perspective
    of Bob, it's as if Chuck had dropped Alice's file and sent him
    another file instead.
    """
    from src.transmitter.windows import MockWindow  # Avoid circular import

    if settings.traffic_masking:
        raise FunctionReturn("Error: Command is disabled during traffic masking.", head_clear=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.", head_clear=True)

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

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

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

    phase("Encrypting data")
    file_key = csprng()
    file_ct  = encrypt_and_sign(comp, file_key)
    ct_hash  = blake2b(file_ct)
    phase(DONE)
    print_on_previous_line(flush=True)

    phase("Exporting data")
    no_contacts  = int_to_bytes(len(window))
    ser_contacts = b''.join([c.onion_pub_key for c in window])
    file_packet  = FILE_DATAGRAM_HEADER + no_contacts + ser_contacts + file_ct
    queue_to_nc(file_packet, queues[RELAY_PACKET_QUEUE])

    key_delivery_msg = base64.b85encode(ct_hash + file_key).decode()
    for contact in window:
        queue_message(user_input=UserInput(key_delivery_msg, MESSAGE),
                      window    =MockWindow(contact.onion_pub_key, [contact]),
                      settings  =settings,
                      queues    =queues,
                      header    =FILE_KEY_HEADER,
                      log_as_ph =True)
    phase(DONE)
    print_on_previous_line(flush=True)
    m_print(f"Sent file '{name}' to {window.type_print} {window.name}.")
Example #30
0
 def test_str_to_bytes(self):
     encoded = str_to_bytes('test')
     self.assertIsInstance(encoded, bytes)
     self.assertEqual(len(encoded), 1024)