Use of a non-random IV or nonce in a block cipher mode

ID

dart.cryptography.non_random_iv_with_cbc_mode

Severity

critical

Remediation Complexity

medium

Remediation Risk

medium

Remediation Effort

medium

Resource

Cryptography

Language

Dart

Description

A block-cipher mode that requires a unique IV / nonce per encryption (CBC, CTR, GCM, …) is invoked with a static, all-zero, or attacker- predictable initialization vector. Reusing the IV across messages leaks plaintext and, for GCM specifically, leaks the authentication key.

Rationale

CBC, CTR, GCM, CCM, and OFB all derive their security from the uniqueness of their IV / nonce. The specific failure mode depends on the algorithm:

  • CBC with a fixed IV: identical plaintext prefixes produce identical ciphertext prefixes, leaking equality and enabling block-substitution attacks similar to ECB. With a predictable IV, BEAST-style chosen- plaintext attacks become possible.

  • CTR / GCM with a reused nonce: the keystream repeats. XORing two ciphertexts cancels the keystream and yields the XOR of the plaintexts — recoverable by frequency analysis. For GCM, nonce reuse also leaks the authentication subkey, allowing the attacker to forge arbitrary valid ciphertexts (forbidden-attack on AES-GCM).

In Dart this typically appears as IV.fromLength(16) (all zeros), IV.fromUtf8("static-string"), Uint8List(N) passed to ParametersWithIV, or a nonce: const [0,0,0,…] argument to cipher.encrypt(…​) in package:cryptography.

The following code illustrates a vulnerable pattern detected by this rule:

class StaticIVs {
  void encryptIVs() {
    // VULNERABLE: Use of a non-random IV or nonce in a block cipher mode
    final iv1 = IV.fromLength(16);
    // VULNERABLE: Use of a non-random IV or nonce in a block cipher mode
    final iv2 = IV.allZerosOfLength(16);
    // VULNERABLE: Use of a non-random IV or nonce in a block cipher mode
    final iv3 = IV.fromUtf8("1234567890123456");
    // VULNERABLE: Use of a non-random IV or nonce in a block cipher mode
    final iv4 = IV.fromBase64("AAAAAAAAAAAAAAAAAAAAAA==");
    // VULNERABLE: Use of a non-random IV or nonce in a block cipher mode
    final iv5 = IV(Uint8List(16));
    // VULNERABLE: Use of a non-random IV or nonce in a block cipher mode
    final iv6 = IV(Uint8List.fromList([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]));
    print('$iv1 $iv2 $iv3 $iv4 $iv5 $iv6');
  }
}

Remediation

Generate a fresh random IV per encryption using a cryptographically secure RNG, then store it alongside the ciphertext (an IV is not secret). Higher-level APIs do this for you: IV.fromSecureRandom(16), cipher.newNonce(), or letting `package:cryptography’s AEAD constructors generate the nonce automatically.

// Vulnerable
final iv = IV.fromLength(16);                       // all zeros
final iv2 = IV.fromUtf8('STATIC_IV_FOR_DVFA');
final p = ParametersWithIV(KeyParameter(k), Uint8List(16));

// Fixed: random IV per encryption
final iv = IV.fromSecureRandom(16);
final encrypter = Encrypter(AES(key, mode: AESMode.cbc));
final ciphertext = encrypter.encrypt(plain, iv: iv);
// Persist (iv.bytes, ciphertext) together — iv is not secret.

Configuration

This detector does not need any configuration.

References

© 2026 Xygeni. All rights reserved