# AES-GCM Nonce Reuse Leading to Keystream Recovery

Language: Python
Severity: Critical
CWE: CWE-323

## Source
7

## Flow
7-10-13

## Sink
10

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

class IoTSensorEncryptor:
    def __init__(self, master_key):
        self.cipher = AESGCM(master_key)
        self.device_nonce = os.urandom(12)
    
    def encrypt_telemetry(self, sensor_data, device_id):
        encrypted = self.cipher.encrypt(self.device_nonce, sensor_data.encode(), device_id.encode())
        return encrypted.hex()
    
    def batch_encrypt_readings(self, readings_list, device_id):
        return [self.encrypt_telemetry(reading, device_id) for reading in readings_list]
```

## Explanation

The nonce (self.device_nonce) is generated once in __init__ and reused across multiple encrypt_telemetry calls. In AES-GCM, nonce reuse with the same key allows attackers to XOR two ciphertexts encrypted with the same nonce to recover keystream material, compromising confidentiality and authenticity of all encrypted messages.

## Remediation

The fix generates a fresh random 12-byte nonce for every encryption operation instead of reusing a single nonce stored as an instance variable. The nonce is prepended to the ciphertext so the decryptor can extract it for decryption. This ensures that no two encryptions ever share the same nonce, preventing keystream recovery attacks.

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

class IoTSensorEncryptor:
    def __init__(self, master_key):
        self.cipher = AESGCM(master_key)
    
    def encrypt_telemetry(self, sensor_data, device_id):
        nonce = os.urandom(12)
        encrypted = self.cipher.encrypt(nonce, sensor_data.encode(), device_id.encode())
        return (nonce + encrypted).hex()
    
    def batch_encrypt_readings(self, readings_list, device_id):
        return [self.encrypt_telemetry(reading, device_id) for reading in readings_list]
```
