# ChaCha20-Poly1305 Nonce Reuse Leading to Plaintext Recovery

Language: Python
Severity: High
CWE: CWE-323

## Source
7

## Flow
7-12

## Sink
12

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

class IoTSensorEncryptor:
    def __init__(self):
        self.cipher = ChaCha20Poly1305(os.urandom(32))
        self.device_nonce = os.urandom(12)
    
    def transmit_telemetry(self, sensor_data, device_id):
        associated = f"device:{device_id}".encode()
        encrypted = self.cipher.encrypt(self.device_nonce, sensor_data.encode(), associated)
        return encrypted
```

## Explanation

The nonce is generated once in __init__ (line 7) and stored as an instance variable, then reused for every encryption operation in transmit_telemetry (line 12). ChaCha20-Poly1305 requires a unique nonce for each encryption with the same key; reusing nonces allows attackers to XOR ciphertexts together to recover plaintext data and forge authentication tags.

## Remediation

The fix generates a fresh random 12-byte nonce for every call to transmit_telemetry instead of reusing a single nonce stored as an instance variable. The nonce is prepended to the ciphertext so the receiver can extract it for decryption. This ensures that each encryption operation uses a unique nonce, preventing the XOR-based plaintext recovery attack.

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

class IoTSensorEncryptor:
    def __init__(self):
        self.cipher = ChaCha20Poly1305(os.urandom(32))
    
    def transmit_telemetry(self, sensor_data, device_id):
        nonce = os.urandom(12)
        associated = f"device:{device_id}".encode()
        encrypted = self.cipher.encrypt(nonce, sensor_data.encode(), associated)
        return nonce + encrypted
```
