> ## Documentation Index
> Fetch the complete documentation index at: https://voltaire.tevm.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# AES-GCM Security

> Security properties, threats, and best practices for AES-GCM

<Card title="Try it Live" icon="play" href="https://playground.tevm.sh?example=crypto/aesgcm.ts">
  Run AES-GCM examples in the interactive playground
</Card>

## Overview

AES-GCM is a **NIST-approved** authenticated encryption mode providing both confidentiality and integrity. When used correctly, it offers strong security guarantees. However, **nonce reuse is catastrophic** and several other pitfalls exist.

## Security Properties

### Confidentiality

**Semantic Security (IND-CPA):**

* Ciphertext reveals no information about plaintext
* Identical plaintexts produce different ciphertexts (with different nonces)
* Requires unique nonces for each encryption

**Key Strength:**

* AES-128: \~2¹²⁸ operations to break (\~340 undecillion)
* AES-256: \~2²⁵⁶ operations to break
* Post-quantum: AES-128 reduced to \~2⁶⁴, AES-256 to \~2¹²⁸ (Grover's algorithm)

**Recommendation:** Use **AES-256** for long-term security and post-quantum resistance.

### Integrity and Authentication

**Unforgeable (INT-CTXT):**

* Cannot create valid ciphertext without the key
* Authentication tag is 128 bits (2¹²⁸ possible tags)
* Brute-force forgery: \~2¹²⁸ attempts

**Tag Properties:**

* Computed over ciphertext AND additional authenticated data
* Verified in constant time (timing-attack resistant)
* Any modification (ciphertext, tag, or AAD) causes decryption failure

### Authenticated Encryption with Associated Data (AEAD)

AES-GCM provides **all three security properties** simultaneously:

1. **Confidentiality:** Plaintext secrecy
2. **Integrity:** Tampering detection
3. **Authenticity:** Proof of origin (with correct key)

## Critical Security Requirements

### 1. NEVER Reuse Nonces

**CATASTROPHIC SECURITY FAILURE**

Nonce reuse with the same key completely breaks security:

```typescript theme={null}
// DANGEROUS - Nonce reuse
const key = await AesGcm.generateKey(256);
const nonce = AesGcm.generateNonce();

const ct1 = await AesGcm.encrypt(msg1, key, nonce); // OK
const ct2 = await AesGcm.encrypt(msg2, key, nonce); // BREAKS SECURITY!
```

**What an attacker can do with reused nonces:**

1. **Recover XOR of plaintexts:**
   ```
   ct1 XOR ct2 = (msg1 XOR keystream) XOR (msg2 XOR keystream)
                = msg1 XOR msg2
   ```

2. **Recover authentication key (H):**
   * With two ciphertexts using same nonce
   * Can forge arbitrary ciphertexts
   * Complete authentication bypass

3. **Recover plaintext:**
   * If one plaintext is known or guessable
   * XOR attack reveals other plaintext

**Example Attack:**

```typescript theme={null}
// Attacker intercepts two ciphertexts with same nonce
const ct1 = await AesGcm.encrypt(
  new TextEncoder().encode('Transfer $100 to Alice'),
  key,
  nonce
);

const ct2 = await AesGcm.encrypt(
  new TextEncoder().encode('Transfer $999 to Alice'),
  key,
  nonce // Same nonce!
);

// Attacker can XOR ciphertexts to learn plaintext differences
// And forge new valid ciphertexts!
```

**Prevention:**

```typescript theme={null}
// CORRECT: New nonce for each encryption
const key = await AesGcm.generateKey(256);

const nonce1 = AesGcm.generateNonce();
const ct1 = await AesGcm.encrypt(msg1, key, nonce1);

const nonce2 = AesGcm.generateNonce(); // Different nonce!
const ct2 = await AesGcm.encrypt(msg2, key, nonce2);
```

### 2. Use Cryptographically Secure Random

**REQUIRED:** Use `crypto.getRandomValues()` for nonces and keys

```typescript theme={null}
// CORRECT
const nonce = AesGcm.generateNonce(); // Uses crypto.getRandomValues()

// WRONG - Never do this
const badNonce = new Uint8Array(12);
for (let i = 0; i < 12; i++) {
  badNonce[i] = Math.floor(Math.random() * 256); // NOT SECURE!
}
```

**Why `Math.random()` is insecure:**

* Predictable pseudorandom (not cryptographic)
* Seeded from system time (guessable)
* Attacker can predict future nonces
* Leads to nonce collisions

### 3. Protect Keys at Rest

**Never store keys in plaintext:**

```typescript theme={null}
// WRONG - Store raw key
localStorage.setItem('key', JSON.stringify(keyBytes));

// RIGHT - Encrypt key with password
const salt = crypto.getRandomValues(Bytes16());
const passwordKey = await AesGcm.deriveKey(password, salt, 600000, 256);
const encryptedKey = await AesGcm.encrypt(keyBytes, passwordKey, nonce);

localStorage.setItem('encryptedKey', JSON.stringify({
  salt: Array(salt),
  nonce: Array(nonce),
  ciphertext: Array(encryptedKey)
}));
```

**Key storage best practices:**

* Server: Use HSM, key management service (KMS), or environment variables
* Browser: Encrypt with user password before storing
* Mobile: Use secure enclave (iOS) or keystore (Android)
* Never commit keys to version control

### 4. Rotate Keys Periodically

**Limit encryptions per key:**

```typescript theme={null}
class KeyRotation {
  constructor() {
    this.key = null;
    this.encryptionCount = 0;
    this.keyCreatedTime = 0;
    this.MAX_ENCRYPTIONS = 2 ** 32; // ~4 billion
    this.MAX_KEY_AGE = 30 * 86400000; // 30 days
  }

  async rotateIfNeeded() {
    const needsRotation =
      this.key === null ||
      this.encryptionCount >= this.MAX_ENCRYPTIONS ||
      Date.now() - this.keyCreatedTime >= this.MAX_KEY_AGE;

    if (needsRotation) {
      this.key = await AesGcm.generateKey(256);
      this.encryptionCount = 0;
      this.keyCreatedTime = Date.now();
      console.log('Key rotated');
    }
  }

  async encrypt(plaintext) {
    await this.rotateIfNeeded();
    this.encryptionCount++;

    const nonce = AesGcm.generateNonce();
    return await AesGcm.encrypt(plaintext, this.key, nonce);
  }
}
```

### 5. Use Strong Passwords for Key Derivation

**Weak password = Weak encryption**

```typescript theme={null}
// WEAK - Easily brute-forced
const weakKey = await AesGcm.deriveKey('12345', salt, 100000, 256);

// STRONG - High entropy
const strongKey = await AesGcm.deriveKey(
  'correct-horse-battery-staple-2024!',
  salt,
  600000, // High iteration count
  256
);
```

**Password recommendations:**

* **Minimum:** 12 characters
* **Recommended:** 16+ characters or passphrase
* **Include:** Uppercase, lowercase, numbers, symbols
* **Avoid:** Dictionary words, personal information, common patterns

**PBKDF2 iterations:**

* **Minimum:** 100,000 (legacy)
* **Recommended:** 600,000+ (OWASP 2023)
* **High security:** 1,000,000+

Trade-off: Higher iterations = slower but more resistant to brute-force.

## Attack Scenarios and Mitigations

### Nonce Collision (Birthday Paradox)

**Problem:** Random nonces eventually collide

**Collision probability:**

* After 2³² encryptions: \~0.005% (acceptable)
* After 2⁴⁸ encryptions: 50% (dangerous)

**Mitigation:**

```typescript theme={null}
// Option 1: Limit encryptions per key
if (encryptionCount > 2 ** 32) {
  key = await AesGcm.generateKey(256);
  encryptionCount = 0;
}

// Option 2: Use counter-based nonces
class NonceCounter {
  constructor() {
    this.counter = 0n;
  }

  next() {
    const nonce = new Uint8Array(12);
    const view = new DataView(nonce.buffer);
    view.setBigUint64(0, this.counter, false);
    this.counter++;
    return nonce;
  }
}
```

### Key Exhaustion

**Problem:** Too many encryptions with same key

**NIST Recommendation:** Max 2³² encryptions per key for random nonces

**Mitigation:** Implement automatic key rotation

### Weak Password Attacks

**Problem:** PBKDF2-derived keys vulnerable to dictionary attacks

**Attack:** Offline brute-force of common passwords

**Mitigation:**

1. Enforce strong password policy
2. Use high iteration count (600,000+)
3. Consider additional key derivation (scrypt, Argon2)
4. Use hardware-based key storage when possible

### Side-Channel Attacks

**Timing Attacks:**

* **Risk:** Tag comparison reveals information
* **Mitigation:** Constant-time verification (built into WebCrypto)

**Cache-Timing Attacks:**

* **Risk:** AES table lookups leak key information
* **Mitigation:** Use AES-NI (hardware acceleration)

**Power Analysis:**

* **Risk:** Power consumption reveals operations
* **Mitigation:** Use hardware security modules (HSM)

### Chosen-Ciphertext Attacks

**Problem:** Attacker modifies ciphertext to learn about plaintext

**Protection:** Authentication tag prevents this

* Any modification causes decryption failure
* No partial plaintext revealed
* All-or-nothing decryption

### Key Compromise

**Problem:** Attacker obtains encryption key

**Impact:**

* All past ciphertexts can be decrypted
* Future encryptions can be forged

**Mitigation:**

* Use forward secrecy (ephemeral keys)
* Rotate keys regularly
* Limit key access with least privilege
* Use HSM/KMS for key protection

## Common Vulnerabilities

### 1. Storing Nonce with Ciphertext (Acceptable)

**Acceptable - Nonce is not secret:**

```typescript theme={null}
// Store nonce with ciphertext (common pattern)
const stored = new Uint8Array(nonce.length + ciphertext.length);
stored.set(nonce, 0);
stored.set(ciphertext, nonce.length);

// This is SAFE - nonce doesn't need to be secret
// Only requirement: unique per encryption
```

### 2. Reusing AAD (Safe)

**Safe - AAD can be reused:**

```typescript theme={null}
const aad = new TextEncoder().encode('version:1.0');

// OK to use same AAD for multiple encryptions
const ct1 = await AesGcm.encrypt(msg1, key, nonce1, aad);
const ct2 = await AesGcm.encrypt(msg2, key, nonce2, aad);
```

### 3. Short Authentication Tags (Avoid)

**Not applicable - AES-GCM uses 128-bit tags**

Voltaire always uses full 128-bit tags (maximum security). Some implementations allow truncated tags (96, 104, 112 bits) - this weakens authentication.

## Best Practices Summary

### DO

✓ Generate new nonce for each encryption
✓ Use `AesGcm.generateNonce()` (cryptographically secure)
✓ Use AES-256 for sensitive data
✓ Store nonce with ciphertext (it's not secret)
✓ Rotate keys periodically
✓ Use strong passwords (≥16 chars, high entropy)
✓ Use high PBKDF2 iterations (≥600,000)
✓ Handle decryption errors gracefully
✓ Clear sensitive data from memory when done
✓ Use hardware security modules (HSM) for keys

### DON'T

✗ Never reuse nonces with the same key
✗ Never use `Math.random()` for nonces
✗ Never store keys in plaintext
✗ Never ignore decryption errors
✗ Never exceed 2³² encryptions per key (random nonces)
✗ Never use weak passwords for key derivation
✗ Never commit keys to version control
✗ Never assume partial decryption on error
✗ Never use predictable nonces (e.g., timestamps alone)

## Security Checklist

Before deploying AES-GCM encryption:

* [ ] Nonces are unique for each encryption
* [ ] Using cryptographically secure random (`crypto.getRandomValues()`)
* [ ] Using AES-256 (not AES-128) for sensitive data
* [ ] Keys stored encrypted or in secure storage (HSM/KMS)
* [ ] Key rotation implemented (\< 2³² encryptions per key)
* [ ] Strong password policy enforced (≥16 chars)
* [ ] PBKDF2 iterations ≥ 600,000
* [ ] Decryption errors handled properly
* [ ] No keys in version control or logs
* [ ] Authentication failures logged for monitoring
* [ ] Key access follows least privilege principle
* [ ] Backup/recovery procedures for encrypted data
* [ ] Compliance with regulations (GDPR, HIPAA, etc.)

## Compliance and Standards

### NIST Approved

AES-GCM is approved by NIST for:

* FIPS 140-2/140-3 compliance
* Government use (classified data with AES-256)
* Commercial applications

**Standards:**

* NIST SP 800-38D (GCM specification)
* FIPS 197 (AES algorithm)
* RFC 5116 (AEAD algorithms)

### Industry Compliance

**PCI DSS:** AES-256 required for cardholder data
**HIPAA:** AES-256 recommended for PHI
**GDPR:** Strong encryption required for personal data

## Cryptographic Limits

### NIST SP 800-38D Limits

**Maximum plaintext length:** 2³⁹ - 256 bits (\~68 GB)

**Maximum invocations:** 2³² per key (random nonces)

**Tag length:** 128 bits (full security), minimum 96 bits (reduced)

**Nonce length:** 96 bits (recommended), 1 to 2⁶⁴ bits (supported)

### Practical Limits

```typescript theme={null}
// Maximum per key with random nonces
const MAX_ENCRYPTIONS = 2 ** 32; // ~4.3 billion

// Maximum plaintext size
const MAX_PLAINTEXT_SIZE = (2 ** 39 - 256) / 8; // ~68 GB

// Safe operation
if (encryptionCount >= MAX_ENCRYPTIONS) {
  throw new Error('Key exhausted - rotate key');
}

if (plaintext.length > MAX_PLAINTEXT_SIZE) {
  throw new Error('Plaintext too large for single encryption');
}
```

## References

* [NIST SP 800-38D - GCM Specification](https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf)
* [NIST Cryptographic Standards](https://csrc.nist.gov/projects/cryptographic-standards-and-guidelines)
* [RFC 5116 - AEAD Algorithms](https://www.rfc-editor.org/rfc/rfc5116.html)
* [OWASP Cryptographic Storage Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html)
* [OWASP Password Storage Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html)
* [Ferguson & Schneier: Practical Cryptography](https://www.schneier.com/books/practical-cryptography/)
