Example #1
0
def whisper(
    user_input: 'UserInput',
    window: 'TxWindow',
    settings: 'Settings',
    queues: 'QueueDict',
) -> None:
    """\
    Send a message to the contact that overrides their enabled logging
    setting for that message.

    The functionality of this feature is impossible to enforce, but if
    the recipient can be trusted and they do not modify their client,
    this feature can be used to send the message off-the-record.
    """
    try:
        message = user_input.plaintext.strip().split(' ', 1)[1]
    except IndexError:
        raise SoftError("Error: No whisper message specified.",
                        head_clear=True)

    queue_message(user_input=UserInput(message, MESSAGE),
                  window=window,
                  settings=settings,
                  queues=queues,
                  whisper=True,
                  log_as_ph=True)
Example #2
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 #3
0
 def test_user_input(self):
     user_input = UserInput('test_plaintext', FILE)
     self.assertEqual(user_input.plaintext, 'test_plaintext')
     self.assertEqual(user_input.type, FILE)