{"title":"AES-GCM Nonce Reuse in Authenticated Encryption","language":"Python","severity":"Critical","cwe":"CWE-323","source_lines":[7],"flow_lines":[7,12,7,17],"sink_lines":[12,17],"vulnerable_code":"from cryptography.hazmat.primitives.ciphers.aead import AESGCM\nimport os\n\nclass IoTDeviceSecureComm:\n    def __init__(self):\n        self.master_key = AESGCM.generate_key(bit_length=256)\n        self.cipher = AESGCM(self.master_key)\n        self.device_nonce = os.urandom(12)\n    \n    def encrypt_sensor_telemetry(self, device_id, temperature, humidity, pressure):\n        telemetry_payload = f\"{device_id}|{temperature}|{humidity}|{pressure}\".encode()\n        encrypted_data = self.cipher.encrypt(self.device_nonce, telemetry_payload, None)\n        return encrypted_data\n    \n    def encrypt_command(self, device_id, command, params):\n        cmd_payload = f\"CMD:{device_id}:{command}:{params}\".encode()\n        encrypted_cmd = self.cipher.encrypt(self.device_nonce, cmd_payload, None)\n        return encrypted_cmd\n\niot_comm = IoTDeviceSecureComm()\ntelemetry1 = iot_comm.encrypt_sensor_telemetry(\"SENSOR_001\", 22.5, 65.3, 1013.2)\ntelemetry2 = iot_comm.encrypt_sensor_telemetry(\"SENSOR_001\", 23.1, 64.8, 1012.9)\ncommand = iot_comm.encrypt_command(\"SENSOR_001\", \"REBOOT\", \"force=true\")","explanation":"The code reuses the same nonce (self.device_nonce) for multiple AES-GCM encryption operations. AES-GCM is a nonce-based authenticated encryption scheme that requires a unique nonce for every encryption with the same key. Reusing a nonce catastrophically breaks the security guarantees, allowing attackers to recover the authentication key and potentially decrypt messages or forge authenticated ciphertexts.","remediation":"The fix removes the cached instance-level nonce and instead generates a fresh random 12-byte nonce for every encryption operation via a dedicated `_generate_nonce()` method. The nonce is prepended to the ciphertext so the receiver can extract it for decryption, which is the standard practice for AES-GCM. This ensures that no two encryption operations ever reuse the same nonce with the same key.","secure_code":"from cryptography.hazmat.primitives.ciphers.aead import AESGCM\nimport os\n\nclass IoTDeviceSecureComm:\n    def __init__(self):\n        self.master_key = AESGCM.generate_key(bit_length=256)\n        self.cipher = AESGCM(self.master_key)\n    \n    def _generate_nonce(self):\n        return os.urandom(12)\n    \n    def encrypt_sensor_telemetry(self, device_id, temperature, humidity, pressure):\n        telemetry_payload = f\"{device_id}|{temperature}|{humidity}|{pressure}\".encode()\n        nonce = self._generate_nonce()\n        encrypted_data = self.cipher.encrypt(nonce, telemetry_payload, None)\n        return nonce + encrypted_data\n    \n    def encrypt_command(self, device_id, command, params):\n        cmd_payload = f\"CMD:{device_id}:{command}:{params}\".encode()\n        nonce = self._generate_nonce()\n        encrypted_cmd = self.cipher.encrypt(nonce, cmd_payload, None)\n        return nonce + encrypted_cmd\n    \n    def decrypt_message(self, encrypted_message):\n        nonce = encrypted_message[:12]\n        ciphertext = encrypted_message[12:]\n        return self.cipher.decrypt(nonce, ciphertext, None)\n\niot_comm = IoTDeviceSecureComm()\ntelemetry1 = iot_comm.encrypt_sensor_telemetry(\"SENSOR_001\", 22.5, 65.3, 1013.2)\ntelemetry2 = iot_comm.encrypt_sensor_telemetry(\"SENSOR_001\", 23.1, 64.8, 1012.9)\ncommand = iot_comm.encrypt_command(\"SENSOR_001\", \"REBOOT\", \"force=true\")"}