CryptoJS unpad issue

Question:

I am decrypting the encoded data fetched from API using CryptoJS data is coming from node js crypto-js in a string format.

Here is what I have tried

import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad,unpad

make = make.json()['data']
key = "SecretPassphrase"
encrypted_make = self.decrypt(make,key)

def decrypt(self,encrypted, passphrase):
    encrypted = base64.b64decode(encrypted)
    IV = encrypted[:16]
    aes = AES.new(passphrase.encode('utf-8'), AES.MODE_CFB, IV, segment_size=128)
    return unpad(aes.decrypt(encrypted[16:]),aes.block_size)

I am getting the issue {ValueError}Padding is incorrect. Can I know what I am trying wrong?

Answers:

As per the discussion from the chat, we found out that the issue was not related to padding. Actually it is related to the decryption key.

The CryptoJS API considers the provided key as a pass phrase, not as a key. Thus, it processes it to create a key from it. When using that passphrase as the decryption key on the Python side, it results in inconsistent decoded data because the decryption key does not match the encryption key generated from the given pass phrase on the CryptoJS side.

According to the CryptoJS documentation, we have three points:

  • the default AES operating mode is CBC, not CFB.
  • the pass phrase is hashed using the MD5 algorithm to produce a key.
  • the generated encrypted message starts with b"Salted__" before the 8bytes IV.

In order to convert the CryptoJS pass phrase to a key, the following piece of code helps (taken from Github):

def bytes_to_key(data, salt="12345678"):
    # Simplified version of M2Crypto.m2.bytes_to_key(). Based on:
    # https://github.com/ajmirsky/M2Crypto/blob/master/M2Crypto/EVP.py#L105
    # http://stackoverflow.com/questions/8008253/c-sharp-version-of-openssl-evp-bytestokey-method
    assert len(salt) == 8, len(salt)
    data += salt
    key = md5(data).digest()
    key += md5(key + data).digest()
    return key

Then the decryption logic mainly remains the same and goes like:

def bytes_to_key(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 decrypt(encrypted, passphrase):
    assert encrypted[0:8] == b"Salted__"
    salt = encrypted[8:16]
    key_iv = bytes_to_key(passphrase, salt, 32 + 16)
    iv = key_iv[32:]
    key = key_iv[:32]
    aes = AES.new(key, AES.MODE_CBC, iv)
    return unpad(aes.decrypt(encrypted[16:]), 16)
Answered By: Jib
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.