# AES-GCM Nonce Reuse Leading to Plaintext Recovery

Language: Python
Severity: Critical
CWE: CWE-323

## Source
7

## Flow
7-11, 7-15

## Sink
11, 15

## Vulnerable Code
```python
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os

class IoTDeviceManager:
    def __init__(self):
        self.cipher = AESGCM(os.urandom(32))
        self.device_nonce = os.urandom(12)
    
    def send_telemetry(self, device_id, sensor_data):
        encrypted_payload = self.cipher.encrypt(self.device_nonce, sensor_data.encode(), device_id.encode())
        return encrypted_payload
    
    def send_command(self, device_id, command):
        encrypted_cmd = self.cipher.encrypt(self.device_nonce, command.encode(), device_id.encode())
        return encrypted_cmd
```

## Explanation

The code reuses the same nonce (self.device_nonce) initialized once in __init__ for multiple AES-GCM encryption operations. In AES-GCM, nonce reuse with the same key catastrophically breaks confidentiality, allowing attackers to XOR ciphertexts to recover plaintext and forge authentication tags.

## Remediation

The fix removes the persistent nonce stored as an instance variable and instead generates a fresh random 12-byte nonce for every encryption operation. The nonce is prepended to the ciphertext so the receiver can extract it for decryption. This ensures that no two encryption operations ever share the same nonce, preventing the nonce reuse attack that breaks AES-GCM confidentiality.

## Secure Code
```python
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os

class IoTDeviceManager:
    def __init__(self):
        self.cipher = AESGCM(os.urandom(32))
    
    def send_telemetry(self, device_id, sensor_data):
        nonce = os.urandom(12)
        encrypted_payload = self.cipher.encrypt(nonce, sensor_data.encode(), device_id.encode())
        return nonce + encrypted_payload
    
    def send_command(self, device_id, command):
        nonce = os.urandom(12)
        encrypted_cmd = self.cipher.encrypt(nonce, command.encode(), device_id.encode())
        return nonce + encrypted_cmd
```
