{"title":"ChaCha20-Poly1305 Nonce Reuse Leading to Plaintext Recovery","language":"Python","severity":"High","cwe":"CWE-323","source_lines":[7],"flow_lines":[7,12],"sink_lines":[12],"vulnerable_code":"from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305\nimport os\n\nclass IoTSensorEncryptor:\n    def __init__(self):\n        self.cipher = ChaCha20Poly1305(os.urandom(32))\n        self.device_nonce = os.urandom(12)\n    \n    def transmit_telemetry(self, sensor_data, device_id):\n        associated = f\"device:{device_id}\".encode()\n        encrypted = self.cipher.encrypt(self.device_nonce, sensor_data.encode(), associated)\n        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":"from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305\nimport os\n\nclass IoTSensorEncryptor:\n    def __init__(self):\n        self.cipher = ChaCha20Poly1305(os.urandom(32))\n    \n    def transmit_telemetry(self, sensor_data, device_id):\n        nonce = os.urandom(12)\n        associated = f\"device:{device_id}\".encode()\n        encrypted = self.cipher.encrypt(nonce, sensor_data.encode(), associated)\n        return nonce + encrypted"}