Search code examples
pythonpython-3.xencryptionaespycryptodome

TypeError: Object type <class 'str'> cannot be passed to C code using pycryptodome


I have this code, it used to work in Python 3.6 or 3.7 (I don't exactly remember which version)

Now I'm trying the same code in Python 3.9.1 and it gives me error. I'm using pycryptodome

from base64 import b64encode, b64decode
from Crypto.Cipher import AES

SOME_SECRET_KEY = 'HPo7OLqB4Fkk4E2yGOtwqw8H5fHR9kNx67OR4g4UdlA='
SOME_IV = 'p5ldmBPdd/9pjC0bDC/nSg=='
secret_key_in_bytes = bytes(b64decode(SOME_SECRET_KEY))
iv_in_bytes = bytes(b64decode(SOME_IV))

def decrypt_with_padding(data):
    cypto_obj = AES.new(secret_key_in_bytes, AES.MODE_CBC, iv_in_bytes)
    decrypted = cypto_obj.decrypt(b64decode(data))
    bytes_to_string = decrypted.decode("utf-8")
    decrypted_data = pkcs5_unpad(bytes_to_string)
    return decrypted_data


def encrypt_with_padding(data):
    cypto_obj = AES.new(secret_key_in_bytes, AES.MODE_CBC, iv_in_bytes)
    encrypted_data = cypto_obj.encrypt(pkcs5_pad(str(data)))
    return b64encode(encrypted_data).decode()


def pkcs5_pad(s, BLOCK_SIZE=16):
    return s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * chr(
        BLOCK_SIZE - len(s) % BLOCK_SIZE
    )


def pkcs5_unpad(s):
    return s[0: -ord(s[-1])]


if __name__ == "__main__":
    data = '{"idServicio":79, "idProducto":209 , "referencia": "40425475190118187271", "montoPago": 9999, "telefono":"1111111111", "horaLocal":"20200401222821"}'
    encrypted = encrypt_with_padding(data)
    print(encrypted)
    decrypted = decrypt_with_padding(encrypted)
    print(decrypted)

This is the error

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-3-d94343c778d2> in <module>
     33 if __name__ == "__main__":
     34     data = '{"idServicio":79, "idProducto":209 , "referencia": "40425475190118187271", "montoPago": 9999, "telefono":"1111111111", "horaLocal":"20200401222821"}'
---> 35     encrypted = encrypt_with_padding(data)
     36     print(encrypted)
     37     decrypted = decrypt_with_padding(encrypted)

<ipython-input-3-d94343c778d2> in encrypt_with_padding(data)
     17 def encrypt_with_padding(data):
     18     cypto_obj = AES.new(secret_key_in_bytes, AES.MODE_CBC, iv_in_bytes)
---> 19     encrypted_data = cypto_obj.encrypt(pkcs5_pad(str(data)))
     20     return b64encode(encrypted_data).decode()
     21 

/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/Crypto/Cipher/_mode_cbc.py in encrypt(self, plaintext, output)
    176 
    177         result = raw_cbc_lib.CBC_encrypt(self._state.get(),
--> 178                                          c_uint8_ptr(plaintext),
    179                                          c_uint8_ptr(ciphertext),
    180                                          c_size_t(len(plaintext)))

/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/Crypto/Util/_raw_api.py in c_uint8_ptr(data)
    136             return data
    137         else:
--> 138             raise TypeError("Object type %s cannot be passed to C code" % type(data))
    139 
    140     class VoidPointer_cffi(_VoidPointer):

TypeError: Object type <class 'str'> cannot be passed to C code

I have tried converting string to bytes using the prefix b'string' to the data variable but it neither works.

Thanks for your time


Solution

  • The way to convert from str to byte is using .encode('utf-8'). cypto_obj.encrypt receives a byte object, so you need to encode the output of pkcs5_pad:

    def pkcs5_pad(s, BLOCK_SIZE=16):                                                                                                                                                              
        return (s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * chr(                                                                                                                                     
            BLOCK_SIZE - len(s) % BLOCK_SIZE                                                                                                                                                      
        )).encode('utf-8')