Integrating Frontend JavaScript Code with AES Encryption getting ValueError: Data must be padded to 16 byte boundary in CBC mode

I’m currently working implementing AES encryption in the backend using Python, but I’m encountering some issues in ensuring compatibility between frontend and backedn. I need help in integrating the frontend JavaScript code to work with it.

My backend Python code:

class Crypt():
    def pad(self, data):
        BLOCK_SIZE = 16
        length = BLOCK_SIZE - (len(data) % BLOCK_SIZE)
        return data + (chr(length)*length)

    def unpad(self, data):
        return data[:-(data[-1] if type(data[-1]) == int else ord(data[-1]))]

    def bytes_to_key(self, data, salt, output=48):
        assert len(salt) == 8, len(salt)
        data += salt
        key = sha256(data).digest()
        final_key = key
        while len(final_key) < output:
            key = sha256(key + data).digest()
            final_key += key
        return final_key[:output]

    def bytes_to_key_md5(self, data, salt, output=48):
        assert len(salt) == 8, len(salt)
        data += salt
        key = md5(data).digest()
        final_key = key
        while len(final_key) < output:
            key = md5(key + data).digest()
            final_key += key
        return final_key[:output]

    def encrypt(self, message):
        passphrase = "<secret passpharse value>".encode()
        salt = Random.new().read(8)
        key_iv = self.bytes_to_key_md5(passphrase, salt, 32+16)
        key = key_iv[:32]
        iv = key_iv[32:]
        aes = AES.new(key, AES.MODE_CBC, iv)
        return base64.b64encode(b"Salted__" + salt + aes.encrypt(self.pad(message).encode()))

    def decrypt(self, encrypted):
        passphrase ="<secret passpharse value>".encode()
        encrypted = base64.b64decode(encrypted)
        assert encrypted[0:8] == b"Salted__"
        salt = encrypted[8:16]
        key_iv = self.bytes_to_key_md5(passphrase, salt, 32+16)
        key = key_iv[:32]
        iv = key_iv[32:]
        aes = AES.new(key, AES.MODE_CBC, iv)
        return self.unpad(aes.decrypt(encrypted[16:])).decode().strip('"')
    
    def base64_decoding(self, encoded):
        base64decode = base64.b64decode(encoded)
        return base64decode.decode()
    
crypt = Crypt()

test = "secret message to be send over network"

encrypted_message = crypt.encrypt(test)
print("Encryp msg:", encrypted_message)
decrypted_message = crypt.decrypt(encrypted_message)
print("Decryp:", decrypted_message)

here’s what I’ve tried so far on the frontend with React and CryptoJS:

    import React from "react";
import CryptoJS from 'crypto-js';

const DecryptEncrypt = () => {
    function bytesToKey(passphrase, salt, output = 48) {
        if (salt.length !== 8) {
            throw new Error('Salt must be 8 characters long.');
        }

        let data = CryptoJS.enc.Latin1.parse(passphrase + salt);
        let key = CryptoJS.SHA256(data).toString(CryptoJS.enc.Latin1);
        let finalKey = key;

        while (finalKey.length < output) {
            data = CryptoJS.enc.Latin1.parse(key + passphrase + salt);
            key = CryptoJS.SHA256(data).toString(CryptoJS.enc.Latin1);
            finalKey += key;
        }

        return finalKey.slice(0, output);
    }

    const decryptData = (encryptedData, key) => {
        const decodedEncryptedData = atob(encryptedData);
        const salt = CryptoJS.enc.Hex.parse(decodedEncryptedData.substring(8, 16));
        const ciphertext = CryptoJS.enc.Hex.parse(decodedEncryptedData.substring(16));
        const keyIv = bytesToKey(key, salt.toString(), 32 + 16);
        const keyBytes = CryptoJS.enc.Hex.parse(keyIv.substring(0, 32));
        const iv = CryptoJS.enc.Hex.parse(keyIv.substring(32));

        const decrypted = CryptoJS.AES.decrypt(
            { ciphertext: ciphertext },
            keyBytes,
            { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }
        );

        
        return decrypted.toString(CryptoJS.enc.Utf8);
    };

    const encryptData = (data, key) => {
        const salt = CryptoJS.lib.WordArray.random(8); // Generate random salt
        const keyIv = bytesToKey(key, salt.toString(), 32 + 16);
        const keyBytes = CryptoJS.enc.Hex.parse(keyIv.substring(0, 32));
        const iv = CryptoJS.enc.Hex.parse(keyIv.substring(32));

        const encrypted = CryptoJS.AES.encrypt(data, keyBytes, {
            iv: iv,
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7
        });

        const ciphertext = encrypted.ciphertext.toString(CryptoJS.enc.Hex);
        const saltedCiphertext = "Salted__" + salt.toString(CryptoJS.enc.Hex) + ciphertext;

        return btoa(saltedCiphertext);
    };

    const dataToEncrypt = 'Data to be sent over network';
    const encryptionKey = "<secret passpharse value>";

    const encryptedData = encryptData(dataToEncrypt, encryptionKey);
    console.log("Encrypted data:", encryptedData);

    const decryptedData = decryptData(encryptedData, encryptionKey);
    console.log("Decrypted data:", decryptedData);

    return (<>
        Check
    </>);
}

export default DecryptEncrypt;

I’m encountering some issues in ensuring compatibility between frontend and backedn. Specifically, I’m struggling with properly deriving the key and IV, and encrypting/decrypting the data in a way that matches the backend implementation. Getting error as below when i try to send encrypted text to backend where it throws following error while decrypting,

packagesCryptoCipher_mode_cbc.py", line 246, in decrypt
    raise ValueError("Data must be padded to %d byte boundary in CBC mode" % self.block_size)
ValueError: Data must be padded to 16 byte boundary in CBC mode

I m bit new to implementating AES in a fullstack app, so learning and trying but still stuck with this issue. Could someone who has encountered similar issue or implemented encryption/decryption in JavaScript offer some guidance or suggestions on how to modify my frontend code to achieve compatibility with the backend? Any insights, corrections, or alternative approaches would be very helpful.