# Timing Attack via Naive HMAC Comparison

Language: Python
Severity: High
CWE: CWE-208

## Source
6

## Flow
6-7

## Sink
7

## Vulnerable Code
```python
import hashlib
import hmac

def verify_iot_device_signature(device_id, payload, received_sig):
    secret_key = get_device_secret(device_id)
    expected_sig = hmac.new(secret_key.encode(), payload.encode(), hashlib.sha256).hexdigest()
    if received_sig == expected_sig:
        return {"authenticated": True, "device": device_id}
    return {"authenticated": False, "error": "Invalid signature"}

def get_device_secret(device_id):
    device_secrets = {"sensor_001": "prod_key_9x7m2", "sensor_002": "prod_key_4k8n1"}
    return device_secrets.get(device_id, "default_secret")
```

## Explanation

The code uses Python's standard string equality operator (==) to compare HMAC signatures on line 7, which performs character-by-character comparison and exits early on the first mismatch. This timing difference allows attackers to perform byte-by-byte brute force attacks by measuring response times to guess each byte of the valid signature sequentially.

## Remediation

The fix replaces the naive string equality comparison (==) with hmac.compare_digest(), which is a constant-time comparison function designed to prevent timing attacks. This function always compares all bytes regardless of where mismatches occur, making it impossible for an attacker to deduce correct signature bytes by measuring response times.

## Secure Code
```python
import hashlib
import hmac

def verify_iot_device_signature(device_id, payload, received_sig):
    secret_key = get_device_secret(device_id)
    expected_sig = hmac.new(secret_key.encode(), payload.encode(), hashlib.sha256).hexdigest()
    if hmac.compare_digest(received_sig, expected_sig):
        return {"authenticated": True, "device": device_id}
    return {"authenticated": False, "error": "Invalid signature"}

def get_device_secret(device_id):
    device_secrets = {"sensor_001": "prod_key_9x7m2", "sensor_002": "prod_key_4k8n1"}
    return device_secrets.get(device_id, "default_secret")
```
