1

I am trying to decrypt message (in python) that has been encoded using CryptoJS in JavaScript. I have created an API in python, to post data I am using postman pre-request script.

The Error I am getting:

ValueError: Data must be padded to 16 byte boundary in CBC mode

JavaScript code for encryption

var data = {"feature_0": 0,
            "feature_1": 0, 
            "feature_2": 0, 
            "feature_3": 0, 
            "feature_4": 0, 
            "feature_5": 0
           };
let password = "lazydog";
let salt = "salt";
let iterations = 128;
data = JSON.stringify(data);
let len = 16 - ((data.length) % 16);
data += len.toString().repeat(len);  --> removed (as suggested)
let bytes = CryptoJS.PBKDF2(password, salt, { keySize: 48, iterations: iterations });
let iv = CryptoJS.enc.Hex.parse(bytes.toString().slice(0, 32));
let key = CryptoJS.enc.Hex.parse(bytes.toString().slice(32, 96));

let encrypted = CryptoJS.AES.encrypt(data, key, {iv: iv}); //, mode: CryptoJS.mode.CBC
//encrypted = btoa(encrypted); --> removed (as suggested)
encrypted = encrypted.toString() -->added (as suggested)
postman.setGlobalVariable("data", encrypted);

python code for decryption:

def decode(encrypted):
    data = b64decode(encrypted)  
    byte = PBKDF2("lazydog".encode("utf-8"), "salt".encode("utf-8"), 48, 128)
    iv = byte[0:16]
    key = byte[16:48]
    cipher = AES.new(key, AES.MODE_CBC, iv)
    text = cipher.decrypt(data) ## error is at this line
    text = text[:-text[-1]].decode("utf-8")

    return text

As the error said padding problem I added padding in JS code. Still I am not getting good results. What's wrong I am doing here?

D1V
  • 45
  • 10
  • 1
    CryptoJS returns a `CipherParams` object. `btoa` cannot simply be applied to it. A Base64 encoding is possible e.g. with `encrypted.toString()`, where `encrypted` is the returned `CipherParams` object. With this decryption works. But there are more issues in the CryptoJS code: CryptoJS pads _implicitly_, thus it is padded _twice_ because of the user-defined padding (which is also wrongly implemented!). This custom padding should be removed. Also: The iteration count is generally much too small, and PyCryptodome supports padding/unpadding with a separate module. – Topaco Oct 07 '20 at 06:59
  • I added encrypted.toString() and removed custom padding. I'm getting the same error >ValueError: Data must be padded to 16 byte boundary in CBC mode at cipher.decrypt(data) (I added custom padding as error is showing the padding issue.) – D1V Oct 07 '20 at 07:06
  • 1
    Have you removed the line `encrypted = btoa(encrypted);`? Please post the result of `encrypted.toString()`. – Topaco Oct 07 '20 at 07:08
  • Yeah! I removed btoa(encrypted) too. Result of encrypted.toSting() -> keRg9pJATEsd05nusuvRzp7b EvACiwRRnKWk70USnjKQAHKYLZCFdeb6s0lMwsjeYvhmi4rJUhDWxry85nEeQzYWeQ7UWLbtVn8hzyv7LN4ag6C/t9ubIRTPwlENs3qj2eXLsBsX2Zc7mObCYs9YmPmKS3YdR4uNXXbWyZy0U tC9bhNsRTZs7ElBcjbug7jd3UXxlIzlYygzjb0 8Xuw== – D1V Oct 07 '20 at 07:10
  • when I tried with another data it worked fine (along with edits). Another data: [ { "content": "apple google python cat", "comments": ["helo", "good", "great"] } ] – D1V Oct 07 '20 at 07:26
  • 1
    In the posted Base64 string the `+` characters are missing, see [here](https://repl.it/repls/NegativeIntelligentModel#index.js). Is this a copy/paste issue or are they really missing? If they are really missing, is this the direct output of `encrypted.toString()` on the CryptoJS side or what you get on the Python side? Possibly - but this is just a guess - this is a Postman issue and a different encoding is needed (maybe Base64url). – Topaco Oct 07 '20 at 07:26
  • I replaced spaces with '+' sign in encrypted string. It's working now. Thanks for throwing light :). And yes, the output is from python side. – D1V Oct 07 '20 at 07:35

1 Answers1

0

The encrypted string has posted to API which has written in python. I don't know why but when encrypted passed to python '+' chars are being replaced with ' '(space). By replacing the spaces with '+' char I resolved the problem.

code

var data = {"feature_0": 0,
        "feature_1": 0, 
        "feature_2": 0, 
        "feature_3": 0, 
        "feature_4": 0, 
        "feature_5": 0
       };
let password = "lazydog";
let salt = "salt";
let iterations = 128;
data = JSON.stringify(data);
let bytes = CryptoJS.PBKDF2(password, salt, { keySize: 48, iterations: 
  iterations });
let iv = CryptoJS.enc.Hex.parse(bytes.toString().slice(0, 32));
let key = CryptoJS.enc.Hex.parse(bytes.toString().slice(32, 96));

let encrypted = CryptoJS.AES.encrypt(data, key, {iv: iv});
encrypted = encrypted.toString()
postman.setGlobalVariable("data", encrypted);

python code

def decode(encrypted):
    encrypted = encrypted.replace(' ', '+') --> this line is added
    data = b64decode(encrypted)  
    byte = PBKDF2("lazydog".encode("utf-8"), "salt".encode("utf-8"), 48, 128)
    iv = byte[0:16]
    key = byte[16:48]
    cipher = AES.new(key, AES.MODE_CBC, iv)
    text = cipher.decrypt(data) ## error is at this line
    text = text[:-text[-1]].decode("utf-8")

    return text

As CryptoJs pads data implicitly custom padding has been removed. And removed btoa (which is not required). Then encrypted data is converted to String. suggested by @Topaco in the comments

D1V
  • 45
  • 10