Search code examples
javascriptencryptioncryptographycryptojs

Wrong amount of significant bytes when decrypting AES CFB with CryptoJS


I am trying to decrypt AES-256-encrypted Base64-encoded data. This part of my JS code:

var data = "Ic9OcXxn2MnpgFwH4SHkxSY3laYB+kkevevwOPeQjLEeUsAVcHzLdBJZ1liWK5d94I/uNwyzbk+/l6QH/WsU0mzxuXcqBYl4iRIA7UIfchYJTsoaWAnSIjsioFUBAfc8YCODID0HW4AY7nK6Bb0mTP55HxlWstE92w1uJVMmBmJRscrAxySNlAFzVVGxuiiCc3sJimfbMNajXOUeFgvSzw==";

var base64data = CryptoJS.enc.Base64.parse(data);

var encrypted = new CryptoJS.lib.WordArray.init(base64data.words.slice(4));
var iv = new CryptoJS.lib.WordArray.init(base64data.words.slice(0, 4));
var key = CryptoJS.enc.Utf8.parse("secure%password!secure%password!");

var cipher = CryptoJS.lib.CipherParams.create({
  ciphertext: encrypted
});

var decrypted = CryptoJS.AES.decrypt(cipher, key, {
  iv: iv,
  mode: CryptoJS.mode.CFB
});

var result = decrypted.toString(CryptoJS.enc.Utf8);
console.log(decrypted.toString(CryptoJS.enc.Utf8)); 
// Wrong Output: {"first_name": "Han

console.log(decrypted.sigBytes);

decrypted.sigBytes = 144

console.log(decrypted.toString(CryptoJS.enc.Utf8)); // Correct
// Correct Output: {"first_name": "Hans-J\u00fcrgen", "last_name": "M\u00fcller", "city": "Hamburg", "number": "20a", "zip": "20456", "street": "Ladenstra\u00dfe"}
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/mode-cfb.js"></script>

The first output of the WordBuffer results in only part of the decrypted data, because the significant bytes are set to 19 instead of 144. After correcting this, the output is wrong.

Why do I have to correct the sigBytes manual? Any ideas? Thank you!


Solution

  • I found the answer myself. The problem is actually not in the javascript code. It was in the Python code, which encrypted the data.

    When setting the segment size to 128 in pycrpyto using AES CFB you have to pad your date to crypt it to be a multiple of 16 bytes.

    Here is my full python encryption code in which data refers to some byte string and key 32-byte long encryption key.

    length = 16 - (len(data) % 16)
    data += bytes([length]) * length
    
    iv = Random.new().read(AES.block_size)
    key = options.encrypt_key.encode()
    cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=128)
    
    crypted = cipher.encrypt(data)
    entry = iv + crypted
    entry = base64.b64encode(entry)
    

    Entry is send to the client which decrypts the data again with the following code in which data is the base64 encoded crypted data from the python code and key again is the same 32-byte long key:

    var base64data = CryptoJS.enc.Base64.parse(data);
    
    var encrypted = new CryptoJS.lib.WordArray.init(base64data.words.slice(4));
    var iv = new CryptoJS.lib.WordArray.init(base64data.words.slice(0, 4));
    
    var cipher = CryptoJS.lib.CipherParams.create({ ciphertext: encrypted });
    
    var decrypted = CryptoJS.AES.decrypt(cipher, key, {iv: iv, mode: CryptoJS.mode.CFB});
    

    This works good and in every case.