# AES-GCM Authentication Tag Omission

Language: Python
Severity: High
CWE: CWE-345

## Source
8

## Flow
5-6-7-8-9

## Sink
9

## Vulnerable Code
```python
from Crypto.Cipher import AES
import os

def encrypt_iot_telemetry(device_id, sensor_data):
    master_key = os.environ.get('IOT_MASTER_KEY', 'default_key_1234').encode().ljust(32)[:32]
    nonce = os.urandom(12)
    cipher = AES.new(master_key, AES.MODE_GCM, nonce=nonce)
    ciphertext = cipher.encrypt(sensor_data.encode())
    encrypted_payload = nonce + ciphertext
    return encrypted_payload.hex()

def decrypt_iot_telemetry(device_id, encrypted_hex):
    master_key = os.environ.get('IOT_MASTER_KEY', 'default_key_1234').encode().ljust(32)[:32]
    encrypted_payload = bytes.fromhex(encrypted_hex)
    nonce = encrypted_payload[:12]
    ciphertext = encrypted_payload[12:]
    cipher = AES.new(master_key, AES.MODE_GCM, nonce=nonce)
    plaintext = cipher.decrypt(ciphertext)
    return plaintext.decode()
```

## Explanation

The code uses AES-GCM mode but fails to generate and verify the authentication tag. Without calling cipher.digest() after encryption and cipher.verify() before decryption, the encrypted data has no integrity protection, allowing attackers to tamper with ciphertext without detection.

## Remediation

The fix uses encrypt_and_digest() to generate both ciphertext and the 16-byte authentication tag during encryption, and decrypt_and_verify() during decryption to validate the tag before returning plaintext. The device_id is also included as authenticated associated data (AAD) via cipher.update() to bind the ciphertext to the specific device, preventing cross-device replay attacks. The payload format is now nonce (12 bytes) + tag (16 bytes) + ciphertext.

## Secure Code
```python
from Crypto.Cipher import AES
import os

def encrypt_iot_telemetry(device_id, sensor_data):
    master_key = os.environ.get('IOT_MASTER_KEY', 'default_key_1234').encode().ljust(32)[:32]
    nonce = os.urandom(12)
    cipher = AES.new(master_key, AES.MODE_GCM, nonce=nonce)
    cipher.update(device_id.encode())
    ciphertext, tag = cipher.encrypt_and_digest(sensor_data.encode())
    encrypted_payload = nonce + tag + ciphertext
    return encrypted_payload.hex()

def decrypt_iot_telemetry(device_id, encrypted_hex):
    master_key = os.environ.get('IOT_MASTER_KEY', 'default_key_1234').encode().ljust(32)[:32]
    encrypted_payload = bytes.fromhex(encrypted_hex)
    nonce = encrypted_payload[:12]
    tag = encrypted_payload[12:28]
    ciphertext = encrypted_payload[28:]
    cipher = AES.new(master_key, AES.MODE_GCM, nonce=nonce)
    cipher.update(device_id.encode())
    plaintext = cipher.decrypt_and_verify(ciphertext, tag)
    return plaintext.decode()
```
