Search code examples
javascriptaescryptojs

CryptoJS decryption with counter


I have following python code for AES decryption using pycryptodome, and which I verified and working.

key = "password"
key = hashlib.sha256(key.encode("utf-8")).digest()
iv = b'This is an IV456' #Random.new().read(AES.block_size)
iv_int = int(binascii.hexlify(iv), 16)  # Convert the IV to a Python integer. 
ctr = Counter.new(AES.block_size * 8, initial_value=iv_int) # Create a new Counter object with IV = iv_int.
aes = AES.new(key, AES.MODE_CTR, counter=ctr)
decryptData = aes.decrypt(encryptedData) # Decrypt and return the plaintext.

Now I need to convert this code in python using CryptoJS, I have done some coding but no idea how counter ctr generated.

var i8a = new Uint8Array(data);
var password = "password";
var key = CryptoJS.SHA256(password);  
var input = CryptoJS.lib.WordArray.create(i8a);
var output =CryptoJS.AES.decrypt(input, key, { mode: CryptoJS.mode.CTR});                                                                                                                                                                                                                                                                                    
var str = output.toString(CryptoJS.enc.Base64);  

But the str is always none.

Edit:

Based on below comment I have added iv and noPadding option with config but still the decrypt return empty.

  var iv = "112197289293498629157884805399637669174";
  //iv = CryptoJS.enc.Base64.parse(iv); //enable this doesn't make any changes. 
  var output =CryptoJS.AES.decrypt(input, key,{ mode: CryptoJS.mode.CTR,iv:iv,padding: CryptoJS.pad.NoPadding});

Where I got the iv value from python side iv_int.

And cannot remove the counter from python script where I am getting error like, 'counter' keyword parameter is required with CTR mode

Note: I am sending the encrypted data from python to js through webRTC data channel.

Edit2:

The encrypt code in python which I actually used in python,

  def encrypt(row):
        key = "password"
        key = hashlib.sha256(key.encode("utf-8")).digest()
        iv = b'This is an IV456' #Random.new().read(AES.block_size)
        iv_int = int(binascii.hexlify(iv), 16) 

        ctr = Counter.new(AES.block_size * 8, initial_value=iv_int)
        cipher = AES.new(self.key, AES.MODE_CTR,counter=ctr)
        return base64.b64encode(cipher.encrypt(raw))

Input data for encryption

fp = open(filepathFull, "rb")
while (True) and not done_reading:
    data = fp.read(16384)
    encrypted = encrypt(data) 
    sendWebrtcDataChannel(encrypted)

Solution

  • The following changes are necessary in the CryptoJS code:

    • The initial value of the counter must be used as IV. Since the IV consists of ASCII characters, it can be parsed with the Latin1 Encoder or alternatively as hex string with the Hex Encoder. The IV must be passed in decrypt.
    • decrypt expects the ciphertext as CipherParams object.
    • CryptoJS uses CBC and PKCS7 as default. Other values must be specified explicitly in decrypt. Since the CTR mode does not use padding, mode and padding must be specified.

    The following JavaScript code is identical to the Python code:

    // Testdata: Ciphertext as Array
    var data = [0xa6, 0x8a, 0x3c, 0x0d, 0xeb, 0x9a, 0xfc, 0x9d, 0xb1, 0x83, 0xb0, 0x47, 0x9c, 0x63, 0x65, 0x1d, 0x16, 0x6e, 0x53, 0xac, 0xf4, 0xda, 0x0f, 0xac, 0xe3, 0x56, 0xf4, 0xfe, 0x2e, 0xf5, 0x1a, 0x19, 0xd5, 0x50, 0x5f, 0x1a, 0x85, 0x34, 0x6c, 0xac, 0x47, 0xd6, 0x2c];
    
    var i8a = new Uint8Array(data);
    var password = "password";                                              
    var key = CryptoJS.SHA256(password);  
    var iv = CryptoJS.enc.Latin1.parse("This is an IV456");                 // Parse the IV with Latin1 encoder
    //var iv = CryptoJS.enc.Hex.parse('5468697320697320616e204956343536');  // or with Hex encoder
    
    var input = CryptoJS.lib.WordArray.create(i8a);
    var output = CryptoJS.AES.decrypt(
      {
        ciphertext: input                                                   // Pass the ciphertext as CipherParams object
      }, 
      key, 
      {
        iv: iv,                                                             // Pass IV 
        mode: CryptoJS.mode.CTR,                                            // Specify mode
        padding: CryptoJS.pad.NoPadding                                     // Specify padding
      });
    
    var str = output.toString(CryptoJS.enc.Base64);
    console.log(str);
    var str = output.toString(CryptoJS.enc.Utf8);
    console.log(str); 
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>

    If in the Python code

    encryptedData = binascii.unhexlify("a68a3c0deb9afc9db183b0479c63651d166e53acf4da0face356f4fe2ef51a19d5505f1a85346cac47d62c")
    

    is used for the ciphertext, the same plaintext results.