Search code examples
pythonsshaesparamikopycryptodome

Only byte strings can be passed to C code. python paramiko


I have a simple client-server program that uses python Paramiko.

The client sends an encrypted string to the server and the server needs to decrypt it to process it.

This is the client:

def collect_stats():
    try:
        cpu = psutil.cpu_percent(interval=1)
        memory = psutil.virtual_memory().percent
        disk = psutil.disk_usage('/').percent
        str_to_send_back = "{} {} {}".format(cpu, memory, disk).strip()
        str_to_send_back = str_to_send_back.encode()
        str_to_send_back = encrypt(str_to_send_back)
        print(str_to_send_back)

The server picks up the string and tries to decode it (at the bottom of this code snipper):

class server:
    command='cd %temp% && cd' # Necessary to pull client temp directory path
    run_client_script = 'cd %temp% && python client.py' # To run client script

    def __init__(self, client_ip, cliet_port, username, password):
        self.client_ip = client_ip
        self.cliet_port = cliet_port
        self.username = username
        self.password = password
        self.ssh = ssh(self.client_ip, self.username, self.password)

    def upload_script_and_get_stats(self):
        try:
            # Built temp directory path for client machine
            client_temp_dir_location = str(self.ssh.send_command(self.command)).strip()

            # Client script file name
            file_name = "\\client.py"

            # Build absolute path of source file
            source_file_location = os.path.dirname(os.path.abspath(__file__)) + file_name

            # Build absolute path of destination file
            destination_file_location = client_temp_dir_location + file_name

            # Upload client script
            ftp_client = self.ssh.open_sftp(source_file_location, destination_file_location)

            # Run client script and get result
            result = self.ssh.send_command(self.run_client_script)

            # SERVER DECODES STRING HERE
            result = self.decrypt(result)
            return str(result)


        except Exception as e:
            print("Oops this error occured in transfer_files_to_client(): " + str(e))
        finally:
            self.ssh.close()

    def decrypt(self, ciphertext):
        try:
            print(ciphertext)

            obj2 = AES.new(b'This is a key123', AES.MODE_CFB, b'This is an IV456')
            return obj2.decrypt(ciphertext)
        except Exception as e:
            print("FDSDSFDSF: "+str(e))

However, the decode method in server throws this error:

FDSDSFDSF: Only byte strings can be passed to C code

However what gets passed to the method is this:

b'\xb5\xf7\xbc\xd5\xfc\xff;\x83\xd3\xab\xb1mc\xc3'

which is already encoded.


Solution

  • Your ciphertext is a string that contains the string representation of a bytes object. I assume your server and client are running on Python 3 and so

    print(str_to_send_back)
    

    in the client just prints the representation of the bytes object to standard output. Try using some string friendly encoding for your bytes, such as base64:

    from base64 import b64encode
    
    def collect_stats():
        try:
            ...
            # str_to_send_back is actually bytes
            # Decode the bytes that contain ASCII text for printing
            print(b64encode(str_to_send_back).decode('ascii'))
    

    and decode in the receiving end:

    from base64 import b64decode
    
    def decrypt(self, ciphertext):
        # Removed the over eager "exception handling". The traceback is a
        # lot more informative and useful. Add *specific* exception handling
        # as necessary
        aes = AES.new(b'This is a key123', AES.MODE_CFB, b'This is an IV456')
        return aes.decrypt(b64decode(ciphertext))