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:
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:
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 a more technical explanation, read NIST SP 800-38A : Recommendation for Block Cipher Modes of Operation.
For Swift, always use proper random IV generation:
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):
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:
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)
}
References
-
CWE-329 : Generation of Predictable IV with CBC Mode.
-
Block cipher modes of operation, in Wikipedia.
-
NIST SP 800-38A : Recommendation for Block Cipher Modes of Operation.
-
NIST SP 800-38D : Recommendation for Block Cipher Modes of Operation: Galois/Counter Mode (GCM) and GMAC
-
MASWE-0022: Predictable Initialization Vectors (IVs).
-
CWE-1204: Generation of Weak Initialization Vector (IV)
-
SecRandomCopyBytes: Apple’s cryptographically secure random number generator