Exemple #1
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 a BLAKE2b-based checksum before the database is
        replaced.

        The function then reads the up-to-date database content.
        """
        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 failed integrity check, 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()

        database_data, digest = separate_trailer(database_data,
                                                 BLAKE2_DIGEST_LENGTH)

        if blake2b(database_data) != digest:
            raise CriticalError(
                f"Invalid data in login database {self.database_name}")

        return database_data
Exemple #2
0
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 SoftError("Error: Received file had an invalid structure.")

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

    if not file_name.isprintable() or not file_name or '/' in file_name:
        raise SoftError("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 SoftError("Error: Received file had an invalid key.")

    decrypt_and_store_file(ts, file_ct, file_key, file_name, onion_pub_key,
                           nick, window_list, settings)
Exemple #3
0
    def detect_errors(self, packet: bytes) -> bytes:
        """Handle received packet error detection and/or correction."""
        if self.settings.qubes:
            try:
                packet = base64.b85decode(packet)
            except ValueError:
                raise SoftError(
                    "Error: Received packet had invalid Base85 encoding.")

        if self.settings.session_serial_error_correction and not self.settings.qubes:
            try:
                packet, _ = self.rs.decode(packet)
                return bytes(packet)
            except ReedSolomonError:
                raise SoftError(
                    "Error: Reed-Solomon failed to correct errors in the received packet.",
                    bold=True)
        else:
            packet, checksum = separate_trailer(packet, PACKET_CHECKSUM_LENGTH)
            if hashlib.blake2b(
                    packet,
                    digest_size=PACKET_CHECKSUM_LENGTH).digest() != checksum:
                raise SoftError(
                    "Warning! Received packet had an invalid checksum.",
                    bold=True)
            return packet
Exemple #4
0
    def verify_file(database_name: str) -> bool:
        """Verify integrity of file content."""
        with open(database_name, 'rb') as f:
            file_data = f.read()

        purp_data, digest = separate_trailer(file_data, BLAKE2_DIGEST_LENGTH)

        return blake2b(purp_data) == digest
Exemple #5
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.")
Exemple #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.")
Exemple #7
0
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)
Exemple #8
0
 def test_separate_header(self):
     self.assertEqual(
         separate_trailer(b"cypherpunk", trailer_length=len(b"punk")),
         (b"cypher", b"punk"))