# Salt

## **Understanding AES, Salt, and IV**

* **AES (Advanced Encryption Standard):**
  * A powerful and widely used symmetric encryption standard.
  * Uses the same key for both encryption and decryption.
  * Secure and efficient for mobile applications.
* **What is Salt, and why is it needed?**
  * **Salt** is a random string added before encryption.
  * Ensures that encrypting the same data with the same passphrase produces different results.
  * Prevents dictionary attacks and lookup table attacks.
* **IV (Initialization Vector):**
  * **IV** is a randomly generated initialization value used in encryption modes like CBC.
  * Ensures that the same plaintext encrypts into different ciphertexts.

## **Applying Security to Password Encryption in the App**

### **The `encryptAESCryptoJS` Function**

<figure><img src="https://2074953091-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F63bX4jdEctx3wiLiur0x%2Fuploads%2Fgit-blob-5361426ade03d47590bf95864a771ab5c438e9bf%2Fimage2%20(8).png?alt=media" alt=""><figcaption><p>encryptAESCryptoJS</p></figcaption></figure>

* **Functionality:**
  * Encrypts a plaintext string (`plainText`) using a passphrase.

```dart
  String encryptAESCryptoJS(String plainText, String passphrase) {
    try {
      final salt = _genRandomWithNonZero(8);
      final keyndIV = _deriveKeyAndIV(passphrase, salt);
      final key = e.Key(keyndIV[0]);
      final iv = e.IV(keyndIV[1]);

      final encrypter = e.Encrypter(e.AES(key, mode: e.AESMode.cbc));
      final encrypted = encrypter.encrypt(plainText, iv: iv);
      final Uint8List encryptedBytesWithSalt = Uint8List.fromList(_createUint8ListFromString("Salted__") + salt + encrypted.bytes);
      return base64.encode(encryptedBytesWithSalt);
    } catch (error) {
      rethrow;
    }
  }
```

* **How It Works:**
  1. **Generate a Random Salt:**

     * Uses `_genRandomWithNonZero(8)` to create an 8-byte random `salt`.
     * The salt ensures randomness and security in the encryption process.

     ```dart
       Uint8List _genRandomWithNonZero(int seedLength) {
           final random = Random.secure();
           const int randomMax = 245;
           final Uint8List uint8list = Uint8List(seedLength);
           for (int i = 0; i < seedLength; i++) {
             uint8list[i] = random.nextInt(randomMax) + 1;
           }
           return uint8list;
       }
     ```
  2. **Derive Key and IV:**
     * `_deriveKeyAndIV(passphrase, salt)` generates a `key` and `IV` from the passphrase and salt.
     * Uses multiple MD5 hash iterations to generate enough data for both the key and IV.

       ```dart
          List<Uint8List> _deriveKeyAndIV(String passphrase, Uint8List salt) {
             final password = _createUint8ListFromString(passphrase);
             Uint8List concatenatedHashes = Uint8List(0);
             Uint8List currentHash = Uint8List(0);
             bool enoughBytesForKey = false;
             Uint8List preHash = Uint8List(0);
          
             while (!enoughBytesForKey) {
             final int preHashLength = currentHash.length + password.length + salt.length;
             if (currentHash.isNotEmpty) {
              preHash = Uint8List.fromList(currentHash + password + salt);
             } else {
              preHash = Uint8List.fromList(password + salt);
             }
          
                 currentHash = preHash.myMd5;
                 concatenatedHashes = Uint8List.fromList(concatenatedHashes + currentHash);
                 if (concatenatedHashes.length >= 48) enoughBytesForKey = true;
             }
          
             final keyBytes = concatenatedHashes.sublist(0, 32);
             final ivBytes = concatenatedHashes.sublist(32, 48);
             return [keyBytes, ivBytes];
         }
       ```
  3. **Encrypt the Data:**
     * Uses the `key` and `IV` to encrypt `plainText` with the AES algorithm in CBC mode.
     * Produces an encrypted byte sequence.
  4. **Prepare the Final Encrypted Data:**

     * Concatenates `"Salted__"` + `salt` + `encrypted bytes`.
     * Encodes the entire sequence in base64 for easy storage or transmission.

     ```dart
       Uint8List _createUint8ListFromString(String s) {
         final ret = Uint8List(s.length);
         for (var i = 0; i < s.length; i++) {
           ret[i] = s.codeUnitAt(i);
         }
         return ret;
      }
     ```
* **Why Does the Encrypted Output Differ Each Time?**
  * Since a new random `salt` is generated every time, even if the same `plainText` and `passphrase` are used, the encrypted result will always be different.
  * This enhances security and prevents attacks based on comparing encrypted outputs.

### **`decryptAESCryptoJS` Function**

<figure><img src="https://2074953091-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F63bX4jdEctx3wiLiur0x%2Fuploads%2Fgit-blob-5e645bfee5df77fca8df470f4bd3942c4a4b937a%2Fimage3%20(3).png?alt=media" alt=""><figcaption><p>decryptAESCryptoJS</p></figcaption></figure>

* **Functionality:**
  * Decrypts a string that was encrypted using `encryptAESCryptoJS`.

```dart
 String decryptAESCryptoJS(String encrypted, String passphrase) {
  try {
    final Uint8List encryptedBytesWithSalt = base64.decode(encrypted);

    final Uint8List encryptedBytes = encryptedBytesWithSalt.sublist(16, encryptedBytesWithSalt.length);
    final salt = encryptedBytesWithSalt.sublist(8, 16);
    final keyndIV = _deriveKeyAndIV(passphrase, salt);
    final key = e.Key(keyndIV[0]);
    final iv = e.IV(keyndIV[1]);

    final encrypter = e.Encrypter(e.AES(key, mode: e.AESMode.cbc));
    final decrypted = encrypter.decrypt64(base64.encode(encryptedBytes), iv: iv);
    return decrypted;
  } catch (error) {
    rethrow;
  }
}
```

* **How It Works:**
  1. **Decode Base64:**
     * Converts the encrypted string from base64 format back to its original byte form.
  2. **Extract Salt and Encrypted Data:**
     * Discards the first 8 bytes (`"Salted__"`).
     * Extracts the next 8 bytes as the `salt`.
     * Retrieves the remaining bytes as the encrypted data.
  3. **Derive Key and IV Again:**
     * Uses the `_deriveKeyAndIV` function with the extracted `salt` and `passphrase` to regenerate the encryption key and IV.
  4. **Decrypt the Data:**
     * Uses the `key` and `IV` to decrypt the encrypted data using AES in CBC mode.
     * The result is the original `plainText`.

### **`verify` and `verifyEncrypted` Functions**

* **Purpose:**
  * Ensures the integrity and correctness of the encryption and decryption process.
* **How It Works:**
  * **`verify`**: Compares the original `plainText` with the decrypted result of an encrypted string.

    ```dart
          bool verify({
            required String text,
            required String encrypted,
            required String passphrase,
          }) {
            try {
             return text == decryptAESCryptoJS(encrypted, passphrase);
            } catch (e) {
             return false;
            }
         }
    ```
  * **`verifyEncrypted`**: Decrypts two encrypted strings and compares their results.

    ```dart
            bool verifyEncrypted({
            required String encrypted1,
            required String encrypted2,
            required String passphrase,
            }) {
                if (encrypted1 == encrypted2) return false;
                try {
                  return decryptAESCryptoJS(encrypted1, passphrase) == decryptAESCryptoJS(encrypted2, passphrase);
                } catch (e) {
                  return false;
                }
            }
    ```

## **How to Use the Code in a Flutter Project**

### **Install Dependencies**

* Add the following to `pubspec.yaml`:

  ```yaml
  dependencies:
    encrypt: ^5.0.1
  ```
* Run the command:

  ```
  flutter pub get
  ```

### **Using Encryption and Decryption Functions**

* **Combine the previous code snippets into a `MyEncrypt` class.**

```dart
import 'dart:convert';
import 'dart:math';
import 'dart:typed_data';

import 'package:dart_core/dart_core.dart';
import 'package:encrypt/encrypt.dart' as e;
// import 'package:flutter/services.dart';

class MyEncrypt {
    const MyEncrypt();
    
    /*
    Learn more | https://pub.dev/packages/encrypt
    
    Generate password
    Run |
    flutter pub global activate encrypt
    secure-random
    
    */
    
    // #TESTED
    String encryptAESCryptoJS(String plainText, String passphrase) {
    try {
        final salt = _genRandomWithNonZero(8);
        final keyndIV = _deriveKeyAndIV(passphrase, salt);
        final key = e.Key(keyndIV[0]);
        final iv = e.IV(keyndIV[1]);
    
          final encrypter = e.Encrypter(e.AES(key, mode: e.AESMode.cbc));
          final encrypted = encrypter.encrypt(plainText, iv: iv);
          final Uint8List encryptedBytesWithSalt = Uint8List.fromList(_createUint8ListFromString("Salted__") + salt + encrypted.bytes);
          return base64.encode(encryptedBytesWithSalt);
        } catch (error) {
          rethrow;
        }
    }
    
    // #TESTED
    String decryptAESCryptoJS(String encrypted, String passphrase) {
    try {
    final Uint8List encryptedBytesWithSalt = base64.decode(encrypted);
    
          final Uint8List encryptedBytes = encryptedBytesWithSalt.sublist(16, encryptedBytesWithSalt.length);
          final salt = encryptedBytesWithSalt.sublist(8, 16);
          final keyndIV = _deriveKeyAndIV(passphrase, salt);
          final key = e.Key(keyndIV[0]);
          final iv = e.IV(keyndIV[1]);
    
          final encrypter = e.Encrypter(e.AES(key, mode: e.AESMode.cbc));
          final decrypted = encrypter.decrypt64(base64.encode(encryptedBytes), iv: iv);
          return decrypted;
        } catch (error) {
          rethrow;
        }
    }
    
    bool verify({
    required String text,
    required String encrypted,
    required String passphrase,
    }) {
        try {
          return text == decryptAESCryptoJS(encrypted, passphrase);
        } catch (e) {
          return false;
        }
    }
    
    bool verifyEncrypted({
    required String encrypted1,
    required String encrypted2,
    required String passphrase,
    }) {
        if (encrypted1 == encrypted2) return false;
        try {
          return decryptAESCryptoJS(encrypted1, passphrase) == decryptAESCryptoJS(encrypted2, passphrase);
        } catch (e) {
          return false;
        }
    }
    
    List<Uint8List> _deriveKeyAndIV(String passphrase, Uint8List salt) {
    final password = _createUint8ListFromString(passphrase);
    Uint8List concatenatedHashes = Uint8List(0);
    Uint8List currentHash = Uint8List(0);
    bool enoughBytesForKey = false;
    Uint8List preHash = Uint8List(0);
    
        while (!enoughBytesForKey) {
          final int preHashLength = currentHash.length + password.length + salt.length;
          if (currentHash.isNotEmpty) {
            preHash = Uint8List.fromList(currentHash + password + salt);
          } else {
            preHash = Uint8List.fromList(password + salt);
          }
    
          currentHash = preHash.myMd5;
          concatenatedHashes = Uint8List.fromList(concatenatedHashes + currentHash);
          if (concatenatedHashes.length >= 48) enoughBytesForKey = true;
        }
    
        final keyBytes = concatenatedHashes.sublist(0, 32);
        final ivBytes = concatenatedHashes.sublist(32, 48);
        return [keyBytes, ivBytes];
    }
    
    Uint8List _createUint8ListFromString(String s) {
        final ret = Uint8List(s.length);
        for (var i = 0; i < s.length; i++) {
          ret[i] = s.codeUnitAt(i);
        }
        return ret;
    }
    
    Uint8List _genRandomWithNonZero(int seedLength) {
        final random = Random.secure();
        const int randomMax = 245;
        final Uint8List uint8list = Uint8List(seedLength);
        for (int i = 0; i < seedLength; i++) {
          uint8list[i] = random.nextInt(randomMax) + 1;
        }
        return uint8list;
    }
}
```

* **Encrypt Data:**

  ```dart
  final encryptHelper = MyEncrypt();
  final plainText = "Data to be encrypted";
  final passphrase = "Secret password";

  final encryptedText = encryptHelper.encryptAESCryptoJS(plainText, passphrase);
  ```
* **Decrypt Data:**

  ```dart
  final decryptedText = encryptHelper.decryptAESCryptoJS(encryptedText, passphrase);
  ```
* **Verify the Result:**

  ```dart
  final isVerified = encryptHelper.verify(
    text: plainText,
    encrypted: encryptedText,
    passphrase: passphrase,
  );
  ```

### **Notes on Usage**

* **Protecting the `passphrase`:**
  * Do not store the passphrase as plain text in the source code or database.
  * Use secure methods such as environment variables or secret management services.
* **Managing Keys and Sensitive Data:**
  * Restrict access to encryption-related code.
  * Follow data security regulations and best practices.

## **Why Are Salt and IV Important?**

* **Salt:**
  * Prevents dictionary and rainbow table attacks.
  * Ensures unique encryption results for the same plaintext.
  * Without Salt, attackers can compare ciphertexts to detect patterns.
  * This is particularly dangerous if multiple users share the same passphrase or sensitive data.
  * Using Salt is like adding a unique spice to a dish, making each encryption process different.
  * This prevents attackers from guessing your "recipe."
* **IV (Initialization Vector):**
  * Ensures security in CBC mode encryption.
  * Prevents repeating patterns in encrypted data.

## **Conclusion**

* **Security is not optional; it's a mandatory requirement in app development.**
* **Using AES encryption with Salt and IV effectively protects user data.**
* **With this guide, you and your team can implement a secure encryption solution for your Flutter app.**
* If you want to skip the complex parts and use it right away, check out my package: <https://pub.dev/packages/my_salt>.

**References:**

* [Documentation for the `encrypt` package on pub.dev](https://pub.dev/packages/encrypt)
* [Understanding AES Encryption](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard)
* [Security best practices in Flutter](https://flutter.dev/docs/development/data-and-backend/encryption)

[Buy Me a Coffee](https://buymeacoffee.com/ducmng12g) | [Support Me on Ko-fi](https://ko-fi.com/I2I81AEJG8)
