javascriptflutterencryptionencryption-asymmetric

How to get same results from dart:encrypt package and crypto-js?


I am using dart > encrypt package in flutter. I want to do some encryption-decryption logic in web-worker in flutter-web project. I have tried to use crypto-js package and its vanilla JS https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js.

I want to verify the results if same encryption and decryption values appear with same key.

I have gone through some solutions in stackoverflow as well but couldn't find a suitable working which can provide me same generated values.

Following is dart code and its result:

    import 'package:encrypt/encrypt.dart';
    .
    .
    final key = Key.fromUtf8("VishnuTestKeyIsHereLongValid32Ch");
    final encrypter = Encrypter(AES(key));
    final iv = IV.fromLength(16);
    var encrypted = encrypter.encrypt("Vishnu", iv: iv);
    print(encrypted.base64); // prints 8beisXeeEE055QEPq+kumw==

I am expecting same value to be decrypted from Crypto-js. https://jsfiddle.net/vcgupta/grkexuwz/4/

//html:
 <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js"></script>

//js:

var base64Decrypted = '8beisXeeEE055QEPq+kumw=='

var myKey = "VishnuTestKeyIsHereLongValid32Ch"
var result = CryptoJS.AES.decrypt(base64Decrypted, myKey).toString(CryptoJS.enc.Utf8)

console.log(result);

I want to same operations alternatively. Encrypt in javascript and decrypt in dart.

Can anybody help me what I am missing in above solution?


Solution

  • Dart/encrypt uses CTR mode (aka SIC) and PKCS7 padding by default (i.e. the padding is not implicitly disabled for stream cipher modes). Since you do not specify a mode in the CryptoJS code, the default mode is applied, i.e. CBC (s. here). Thus both codes are incompatible.

    Also, in the CryptoJS code, the key must be passed as WordArray (if it is passed as string, a built-in key derivation is used). Additionally, the IV must be passed explicitly (as WordArray). In the Dart code a zero IV (16 0x00 values) is applied.

    Furthermore, you should use a newer version of CryptoJS (you are running 3.1.2, the current version is 4.1.1).

    Overall, decryption with CryptoJS is:

    var base64Decrypted = '8beisXeeEE055QEPq+kumw=='
    var myKey = "VishnuTestKeyIsHereLongValid32Ch"
    var result = CryptoJS.AES.decrypt(
                     base64Decrypted, 
                     CryptoJS.enc.Utf8.parse(myKey), 
                     {
                         iv: CryptoJS.enc.Utf8.parse("\0".repeat(16)), // pass IV
                         mode: CryptoJS.mode.CTR,                      // apply CTR (aka SIC)
                         padding: CryptoJS.pad.Pkcs7                   // apply PKCS#7 (CryptoJS default)
                     }
                 );
    
    console.log(result.toString());                   // 566973686e75   // padding removed
    console.log(result.toString(CryptoJS.enc.Utf8));  // Vishnu         // Utf-8 decoded
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>

    Keep in mind that a static IV (like a zero IV) is a vulnerability. Instead, a random IV should be used for each encryption, which is passed along with the ciphertext to the decrypting side, usually concatenated (the IV is not secret).


    Edit: Regarding the bug mentioned in the comment which occurs for plaintexts larger than 1 block (16 bytes): Fixed. Additionally added the following test for plaintexts longer than 1 block:

    var base64Decrypted = '87a0+WiecyRYz2l3zpRKsWHZyfUbnZ5Ar3nzYokfuD3zN7iV0M2TefRJe4pr4hh+' // from Dart
    var myKey = 'VishnuTestKeyIsHereLongValid32Ch'
    var result = CryptoJS.AES.decrypt(
                     base64Decrypted, 
                     CryptoJS.enc.Utf8.parse(myKey), 
                     {
                         iv: CryptoJS.enc.Utf8.parse('\0'.repeat(16)), // pass IV
                         mode: CryptoJS.mode.CTR,                      // apply CTR (aka SIC)
                         padding: CryptoJS.pad.Pkcs7                   // apply PKCS#7 (CryptoJS default)
                     }
                 );
    
    console.log(result.toString());                   // 54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f67   // padding removed
    console.log(result.toString(CryptoJS.enc.Utf8));  // The quick brown fox jumps over the lazy dog                                              // Utf-8 decoded
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>