Non Random IV with CBC Mode

ID

swift.non_random_iv_with_cbc_mode

Severity

critical

Resource

Predictability

Language

Swift

Tags

CWE:1204, CWE:329, MASWE:0022, NIST.SP.800-53, OWASP:2021:A2, PCI-DSS:6.5.1, crypto

Description

Use of a non-random Initialization Vector (IV) with Cipher Block Chaining (CBC) mode can result in predictable ciphertext, exposing encrypted data to potential attacks. Ensure IVs are generated using a secure random number generator.

Rationale

Cipher Block Chaining (CBC) mode combines using XOR blocks of plaintext with the previous ciphertext block before encryption, thus requiring an initialization vector (IV) for the first block. If IVs are reused, then identical plaintexts would be encrypted to identical ciphertexts. However, even if IVs are not identical but are predictable, then they still break the security of CBC mode against Chosen Plaintext Attacks (CPA). To avoid this, use a fresh random IV for each message.

The same weakness with IVs happens with other modes like Cipher Feedback (CFB). But

Using a non-random or predictable IV with CBC mode can undermine the security of the encryption scheme, leading to potential vulnerabilities like distinguishing attacks or message deduplication attacks.

Poorly implemented IV handling often derives from static values or predictable patterns, compromising the confidentiality of sensitive data.

In Swift, using hardcoded or predictable initialization vectors is a common vulnerability:

Vulnerable: Static IV with CryptoSwift
import CryptoSwift

func encryptWithStaticIV(_ plaintext: String, key: [UInt8]) throws -> [UInt8] {
    // FLAW: Hardcoded static IV
    let iv: [UInt8] = [0x2a, 0x3a, 0x80, 0x05, 0xaf, 0x80, 0x05, 0xaf,
                       0x2a, 0x3a, 0x80, 0x05, 0xaf, 0x80, 0x05, 0xaf]

    let aes = try AES(key: key, blockMode: CBC(iv: iv))  // FLAW
    return try aes.encrypt(Array(plaintext.utf8))
}

Another vulnerable example using string-based IV:

Vulnerable: String constant IV
import CryptoSwift

func encryptWithConstantString(_ plaintext: String, key: [UInt8]) throws -> [UInt8] {
    // FLAW: Constant string used as IV
    let ivString = "this is a constant string"
    let iv = Array(ivString.utf8).prefix(16)

    let aes = try AES(key: key, blockMode: CBC(iv: Array(iv)))  // FLAW
    return try aes.encrypt(Array(plaintext.utf8))
}

These examples use static IVs, making the ciphertext predictable and vulnerable to attacks.

Remediation

Modern cryptography prefers in general Authenticated Encryption with Associated Data (AEAD) schemes, which provide both confidentiality and integrity, over classical confidentiality-only schemes, such as CBC, which are vulnerable to padding oracle attacks. First and foremost, use AEAD schemes such as AES-GCM or XChaCha20-Poly1305 if possible.

Modes such as Galois/Counter Mode (GCM) fail spectacularly when the IV (also called nonce) is reused: A single repeated nonce allows an adversary to recover the authentication subkey and learn the XOR of the two messages with the same nonce. If you use GCM, therefore, make sure to use a fresh nonce for each message. For details on generating fresh nonces for GCM, read NIST SP 800-38D.

To remediate vulnerable code using non-random IVs in CBC mode, always generate the IV using a secure random number generator for each encryption operation. The IV must be unpredictable and unique to mitigate the threats posed by static, reused, or predictable IVs. Always ensure the IV is stored or transmitted alongside the ciphertext, as it is needed for decryption.

For Swift, always use proper random IV generation:

Fixed: Random IV with CryptoSwift
import CryptoSwift
import Foundation

struct EncryptedData {
    let ciphertext: [UInt8]
    let iv: [UInt8]
}

func encryptWithRandomIV(_ plaintext: String, key: [UInt8]) throws -> EncryptedData {
    // FIXED: Generate random IV
    let iv = (0..<16).map { _ in UInt8.random(in: 0...UInt8.max) }

    let aes = try AES(key: key, blockMode: CBC(iv: iv))  // FIXED
    let ciphertext = try aes.encrypt(Array(plaintext.utf8))

    // Store IV alongside ciphertext for decryption
    return EncryptedData(ciphertext: ciphertext, iv: iv)
}

func decrypt(_ encrypted: EncryptedData, key: [UInt8]) throws -> String {
    // Use stored IV for decryption
    let aes = try AES(key: key, blockMode: CBC(iv: encrypted.iv))
    let decrypted = try aes.decrypt(encrypted.ciphertext)

    return String(bytes: decrypted, encoding: .utf8) ?? ""
}

Better yet, use CryptoKit with AES-GCM (AEAD):

Fixed: Using CryptoKit AES-GCM
import CryptoKit
import Foundation

func encryptWithAESGCM(_ plaintext: Data, key: SymmetricKey) throws -> Data {
    // FIXED: AES-GCM automatically generates random nonce
    let sealedBox = try AES.GCM.seal(plaintext, using: key)

    // Combined data includes nonce, ciphertext, and authentication tag
    return sealedBox.combined!
}

func decryptWithAESGCM(_ combined: Data, key: SymmetricKey) throws -> Data {
    // Nonce is automatically extracted from combined data
    let sealedBox = try AES.GCM.SealedBox(combined: combined)
    return try AES.GCM.open(sealedBox, using: key)
}

Using SecRandomCopyBytes for cryptographically secure random IV:

Fixed: Cryptographically secure random IV
import CryptoSwift
import Foundation

func generateSecureIV(length: Int = 16) throws -> [UInt8] {
    // FIXED: Use SecRandomCopyBytes for cryptographically secure random
    var bytes = [UInt8](repeating: 0, count: length)
    let status = SecRandomCopyBytes(kSecRandomDefault, length, &bytes)

    guard status == errSecSuccess else {
        throw NSError(domain: "RandomGenerationError", code: Int(status))
    }

    return bytes
}

func encryptWithSecureIV(_ plaintext: String, key: [UInt8]) throws -> EncryptedData {
    // FIXED: Use cryptographically secure random IV
    let iv = try generateSecureIV(length: 16)

    let aes = try AES(key: key, blockMode: CBC(iv: iv))
    let ciphertext = try aes.encrypt(Array(plaintext.utf8))

    return EncryptedData(ciphertext: ciphertext, iv: iv)
}

Configuration

This detector does not need any configuration.

References