# Cryptographic Weakness via ECB Mode Block Cipher with Predictable IV in AES Encryption

Language: Python
Severity: High
CWE: CWE-327

## Source
4

## Flow
4-5

## Sink
5

## Vulnerable Code
```python
from Crypto.Cipher import AES
import hashlib
def encrypt_hsm_token(device_serial, secret_payload):
    raw_key = hashlib.md5(device_serial.encode()).digest()
    cipher_ctx = AES.new(raw_key, AES.MODE_ECB)
    pad_len = 16 - (len(secret_payload) % 16)
    padded_data = secret_payload.encode() + bytes([pad_len] * pad_len)
    encrypted_blob = cipher_ctx.encrypt(padded_data)
    return encrypted_blob.hex()
```

## Explanation

Two compounding cryptographic weaknesses exist: (1) The encryption key is derived from MD5 of a predictable device serial number (line 4), making key recovery trivial via brute-force or dictionary attack. (2) AES is used in ECB mode (line 5), which is deterministic and stateless — identical 16-byte plaintext blocks always produce identical ciphertext blocks, leaking structural patterns of the plaintext. Together, an attacker can perform block pattern analysis, ciphertext comparison attacks, and key recovery to fully compromise the encrypted HSM token.

## Remediation

The fix addresses both vulnerabilities: (1) MD5-based key derivation from a predictable serial is replaced with PBKDF2-HMAC-SHA256 using a random 16-byte salt and 200,000 iterations, making key recovery computationally infeasible even if the serial is known. (2) AES-ECB mode is replaced with AES-GCM, which uses a random 12-byte nonce to ensure identical plaintexts produce different ciphertexts and provides built-in authentication via a 16-byte tag to detect tampering. The salt, nonce, and authentication tag are prepended to the ciphertext for use during decryption.

## Secure Code
```python
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
import hashlib

def encrypt_hsm_token(device_serial, secret_payload):
    # Derive a strong 256-bit key using PBKDF2-HMAC-SHA256 with a random salt
    salt = get_random_bytes(16)
    raw_key = hashlib.pbkdf2_hmac('sha256', device_serial.encode(), salt, iterations=200000, dklen=32)

    # Use AES-GCM for authenticated encryption with a random nonce
    nonce = get_random_bytes(12)
    cipher_ctx = AES.new(raw_key, AES.MODE_GCM, nonce=nonce)
    ciphertext, auth_tag = cipher_ctx.encrypt_and_digest(secret_payload.encode())

    # Prepend salt + nonce + tag to ciphertext so decryption side can recover them
    encrypted_blob = salt + nonce + auth_tag + ciphertext
    return encrypted_blob.hex()
```
