{"title":"AES-GCM Nonce Reuse Leading to Plaintext Recovery","language":"Python","severity":"Critical","cwe":"CWE-323","source_lines":[7],"flow_lines":[7,11,7,15],"sink_lines":[11,15],"vulnerable_code":"from cryptography.hazmat.primitives.ciphers.aead import AESGCM\nimport os\n\nclass IoTDeviceManager:\n    def __init__(self):\n        self.cipher = AESGCM(os.urandom(32))\n        self.device_nonce = os.urandom(12)\n    \n    def send_telemetry(self, device_id, sensor_data):\n        encrypted_payload = self.cipher.encrypt(self.device_nonce, sensor_data.encode(), device_id.encode())\n        return encrypted_payload\n    \n    def send_command(self, device_id, command):\n        encrypted_cmd = self.cipher.encrypt(self.device_nonce, command.encode(), device_id.encode())\n        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":"from cryptography.hazmat.primitives.ciphers.aead import AESGCM\nimport os\n\nclass IoTDeviceManager:\n    def __init__(self):\n        self.cipher = AESGCM(os.urandom(32))\n    \n    def send_telemetry(self, device_id, sensor_data):\n        nonce = os.urandom(12)\n        encrypted_payload = self.cipher.encrypt(nonce, sensor_data.encode(), device_id.encode())\n        return nonce + encrypted_payload\n    \n    def send_command(self, device_id, command):\n        nonce = os.urandom(12)\n        encrypted_cmd = self.cipher.encrypt(nonce, command.encode(), device_id.encode())\n        return nonce + encrypted_cmd"}