# 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)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://wong-coupon.gitbook.io/flutter/security/salt.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
