> ## 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 Encryption

> Authenticated encryption with AES-GCM

<Info>
  Source: [aes\_gcm.zig](https://github.com/evmts/voltaire/blob/main/src/crypto/aes_gcm.zig)

  Tests: [aes\_gcm.test.ts](https://github.com/evmts/voltaire/blob/main/src/crypto/aes_gcm.test.ts)
</Info>

<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 an **authenticated encryption algorithm** combining AES (Advanced Encryption Standard) in Galois/Counter Mode, providing both confidentiality and authenticity with a single key.

**Ethereum context**: **Not on Ethereum** - Used for encrypted wallet storage (e.g., UTC/JSON keystore format) and secure messaging. Not part of Ethereum protocol.

Key features:

* **Authenticated encryption**: Confidentiality + integrity in one operation
* **Performance**: Hardware-accelerated on modern CPUs
* **Parallelizable**: Can encrypt/decrypt blocks in parallel
* **Additional data**: Authenticate without encrypting (AAD)
* **Standards-compliant**: NIST approved, widely used
* **Key sizes**: 128, 192, or 256 bits
* **Implementations**: Native Zig (16KB), NO WASM (not in browser crypto standard libs)

## Quick Start

```typescript theme={null}
import * as AesGcm from '@tevm/voltaire/AesGcm';

// 1. Generate key (256-bit recommended)
const key = await AesGcm.generateKey(256);

// 2. Generate nonce (12 bytes)
const nonce = AesGcm.generateNonce();

// 3. Encrypt data
const plaintext = new TextEncoder().encode('Secret message');
const ciphertext = await AesGcm.encrypt(plaintext, key, nonce);

// 4. Decrypt data
const decrypted = await AesGcm.decrypt(ciphertext, key, nonce);
const message = new TextDecoder().decode(decrypted);
console.log(message); // "Secret message"

// 5. With additional authenticated data (AAD)
const aad = new TextEncoder().encode('metadata');
const ciphertextWithAAD = await AesGcm.encrypt(plaintext, key, nonce, aad);
const decryptedWithAAD = await AesGcm.decrypt(ciphertextWithAAD, key, nonce, aad);
```

## API Reference

### Key Management

#### `generateKey(bits: 128 | 256): Promise<CryptoKey>`

Generates a cryptographically secure AES key.

**Parameters:**

* `bits` - Key size (128 or 256 bits)
  * 128-bit: Faster, still very secure
  * 256-bit: Maximum security, recommended for sensitive data

```typescript theme={null}
// AES-256 (recommended)
const key256 = await AesGcm.generateKey(256);

// AES-128 (faster)
const key128 = await AesGcm.generateKey(128);
```

#### `deriveKey(password: string | Uint8Array, salt: Uint8Array, iterations: number, bits: 128 | 256): Promise<CryptoKey>`

Derives key from password using PBKDF2-HMAC-SHA256.

**Parameters:**

* `password` - User password (string or bytes)
* `salt` - Salt for key derivation (≥16 bytes recommended)
* `iterations` - PBKDF2 iterations (≥100,000 recommended)
* `bits` - Key size (128 or 256)

```typescript theme={null}
// Generate salt (store with encrypted data)
import * as Hex from '@tevm/voltaire/Hex';
const salt = Hex('0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d');

// Derive key from password
const key = await AesGcm.deriveKey(
  'user-password',
  salt,
  100000,    // Iterations (adjust for security/performance)
  256        // 256-bit key
);

// Use derived key for encryption
const nonce = AesGcm.generateNonce();
const ciphertext = await AesGcm.encrypt(plaintext, key, nonce);
```

#### `importKey(keyData: Uint8Array): Promise<CryptoKey>`

Imports raw key bytes as CryptoKey.

```typescript theme={null}
import * as Hex from '@tevm/voltaire/Hex';

// Import 32-byte key (256-bit)
const rawKey = Hex('0xf8e9a72d4c3b1a6e7d5f2c8b9e1a4d7f3c6b9e2a5d8f1c4b7e0a3d6f9c2b5e8a');

const key = await AesGcm.importKey(rawKey);
```

#### `exportKey(key: CryptoKey): Promise<Uint8Array>`

Exports CryptoKey to raw bytes.

```typescript theme={null}
const key = await AesGcm.generateKey(256);
const rawBytes = await AesGcm.exportKey(key);

console.log(rawBytes); // Uint8Array(32)
// Store securely (encrypted, not plaintext!)
```

### Encryption/Decryption

#### `encrypt(plaintext: Uint8Array, key: CryptoKey, nonce: Uint8Array, additionalData?: Uint8Array): Promise<Uint8Array>`

Encrypts data with AES-GCM, returns ciphertext with authentication tag appended.

**Parameters:**

* `plaintext` - Data to encrypt
* `key` - AES key (from `generateKey` or `deriveKey`)
* `nonce` - 12-byte nonce/IV (must be unique per encryption)
* `additionalData` - Optional AAD (authenticated but not encrypted)

```typescript theme={null}
const plaintext = new TextEncoder().encode('Secret data');
const key = await AesGcm.generateKey(256);
const nonce = AesGcm.generateNonce();

// Basic encryption
const ciphertext = await AesGcm.encrypt(plaintext, key, nonce);

// With additional authenticated data (AAD)
const metadata = new TextEncoder().encode('version:1.0');
const ciphertextWithAAD = await AesGcm.encrypt(plaintext, key, nonce, metadata);
```

**Output format:**

```
[encrypted_data][16-byte_authentication_tag]
```

#### `decrypt(ciphertext: Uint8Array, key: CryptoKey, nonce: Uint8Array, additionalData?: Uint8Array): Promise<Uint8Array>`

Decrypts AES-GCM ciphertext, verifies authentication tag.

**Parameters:**

* `ciphertext` - Encrypted data with tag
* `key` - Same key used for encryption
* `nonce` - Same nonce used for encryption
* `additionalData` - Same AAD used for encryption (if any)

```typescript theme={null}
try {
  const decrypted = await AesGcm.decrypt(ciphertext, key, nonce);
  const message = new TextDecoder().decode(decrypted);
  console.log(message);
} catch (error) {
  console.error('Decryption failed:', error);
  // Could be: wrong key, tampered data, or corrupted ciphertext
}

// With AAD (must match encryption)
const decryptedWithAAD = await AesGcm.decrypt(
  ciphertextWithAAD,
  key,
  nonce,
  metadata
);
```

**Throws:**

* `InvalidNonceError` - Nonce not 12 bytes
* `DecryptionError` - Authentication tag verification fails (data tampered), wrong key/nonce/AAD used, or corrupted ciphertext

### Nonce Generation

#### `generateNonce(): Uint8Array`

Generates cryptographically secure 12-byte nonce.

```typescript theme={null}
const nonce = AesGcm.generateNonce();
console.log(nonce); // Uint8Array(12)

// Store nonce with ciphertext (not secret, but must be unique)
```

### Constants

```typescript theme={null}
AesGcm.AES128_KEY_SIZE  // 16 bytes (128 bits)
AesGcm.AES256_KEY_SIZE  // 32 bytes (256 bits)
AesGcm.NONCE_SIZE       // 12 bytes (96 bits)
AesGcm.TAG_SIZE         // 16 bytes (128 bits)
```

## Nonce Management

**Critical:** Never reuse a nonce with the same key!

### Safe Nonce Usage

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

const msg1 = new TextEncoder().encode('First message');
const nonce1 = AesGcm.generateNonce();
const ct1 = await AesGcm.encrypt(msg1, key, nonce1);

const msg2 = new TextEncoder().encode('Second message');
const nonce2 = AesGcm.generateNonce(); // New nonce!
const ct2 = await AesGcm.encrypt(msg2, key, nonce2);
```

### Storage Format

Store nonce with ciphertext (nonce is not secret):

```typescript theme={null}
// Encrypt
const key = await AesGcm.generateKey(256);
const nonce = AesGcm.generateNonce();
const ciphertext = await AesGcm.encrypt(plaintext, key, nonce);

// Store together (common format: nonce + ciphertext)
const stored = new Uint8Array(nonce.length + ciphertext.length);
stored.set(nonce, 0);
stored.set(ciphertext, nonce.length);

// Later: Extract and decrypt
const extractedNonce = stored.slice(0, AesGcm.NONCE_SIZE);
const extractedCiphertext = stored.slice(AesGcm.NONCE_SIZE);
const decrypted = await AesGcm.decrypt(extractedCiphertext, key, extractedNonce);
```

### Nonce Collision Risk

With random nonces (12 bytes), collision probability:

* After 2³² encryptions: \~0.005% chance
* After 2⁴⁸ encryptions: 50% chance (birthday paradox)

**Recommendations:**

* **Random nonces**: Safe for up to \~2³² encryptions per key
* **Counter-based**: Increment counter for each encryption (no collisions)
* **Key rotation**: Generate new key periodically to reset nonce space

```typescript theme={null}
// Counter-based nonce (for high-volume scenarios)
import * as Hex from '@tevm/voltaire/Hex';

class NonceCounter {
  constructor() {
    this.counter = 0n;
  }

  next() {
    const counterHex = this.counter.toString(16).padStart(24, '0');
    this.counter++;
    return Hex('0x' + counterHex);
  }
}

const counter = new NonceCounter();
const nonce1 = counter.next();
const nonce2 = counter.next(); // Guaranteed unique
```

## Additional Authenticated Data (AAD)

AAD is authenticated but not encrypted - useful for metadata:

```typescript theme={null}
// Encrypt message with metadata
const message = new TextEncoder().encode('Transfer $100 to Alice');
const metadata = new TextEncoder().encode(JSON.stringify({
  timestamp: Date.now(),
  version: '1.0',
  sender: 'Bob'
}));

const key = await AesGcm.generateKey(256);
const nonce = AesGcm.generateNonce();

const ciphertext = await AesGcm.encrypt(message, key, nonce, metadata);

// Metadata is authenticated (tampering will fail decryption)
// But metadata itself is not encrypted (can be read)
```

**Use cases:**

* Protocol version numbers
* Timestamps
* User IDs
* Packet headers
* Database row IDs

**Security:**

* AAD is authenticated (tampering detected)
* AAD is NOT encrypted (readable by anyone)
* Must provide same AAD for decryption

## Password-Based Encryption

Derive key from user password using PBKDF2:

```typescript theme={null}
import * as AesGcm from '@tevm/voltaire/AesGcm';

async function encryptWithPassword(plaintext, password) {
  // 1. Generate random salt
  import * as Hex from '@tevm/voltaire/Hex';
  const salt = Hex('0x3e4d5c6b7a8910293847566574839201');

  // 2. Derive key from password
  const key = await AesGcm.deriveKey(password, salt, 100000, 256);

  // 3. Encrypt
  const nonce = AesGcm.generateNonce();
  const ciphertext = await AesGcm.encrypt(plaintext, key, nonce);

  // 4. Return salt + nonce + ciphertext
  return {
    salt,
    nonce,
    ciphertext
  };
}

async function decryptWithPassword(encrypted, password) {
  // 1. Derive same key from password + salt
  const key = await AesGcm.deriveKey(
    password,
    encrypted.salt,
    100000,
    256
  );

  // 2. Decrypt
  return await AesGcm.decrypt(encrypted.ciphertext, key, encrypted.nonce);
}

// Usage
const data = new TextEncoder().encode('Secret data');
const encrypted = await encryptWithPassword(data, 'user-password');

// Store: encrypted.salt, encrypted.nonce, encrypted.ciphertext
// Later:
const decrypted = await decryptWithPassword(encrypted, 'user-password');
```

## Key Storage

### Secure Storage Patterns

**1. Environment variables (server-side)**

```typescript theme={null}
// Store base64-encoded key
const key = await AesGcm.generateKey(256);
const keyBytes = await AesGcm.exportKey(key);
const keyBase64 = btoa(String.fromCharCode(...keyBytes));

// Store in .env (keep out of version control!)
// ENCRYPTION_KEY=rK7J...

// Load and use
const storedKeyBytes = Uint8Array(
  atob(process.env.ENCRYPTION_KEY),
  c => c.charCodeAt(0)
);
const storedKey = await AesGcm.importKey(storedKeyBytes);
```

**2. Browser (encrypted with password)**

```typescript theme={null}
import * as Hex from '@tevm/voltaire/Hex';

// Encrypt master key with user password
async function storeEncryptedKey(masterKey, userPassword) {
  const salt = Hex('0x7f8e9d0c1b2a3948576e8d9c0b1a2938');
  const passwordKey = await AesGcm.deriveKey(userPassword, salt, 100000, 256);

  const masterKeyBytes = await AesGcm.exportKey(masterKey);
  const nonce = AesGcm.generateNonce();
  const encryptedKey = await AesGcm.encrypt(masterKeyBytes, passwordKey, nonce);

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

async function loadEncryptedKey(userPassword) {
  const stored = JSON.parse(localStorage.getItem('encryptedKey'));
  const salt = new Uint8Array(stored.salt);
  const nonce = new Uint8Array(stored.nonce);
  const ciphertext = new Uint8Array(stored.ciphertext);

  const passwordKey = await AesGcm.deriveKey(userPassword, salt, 100000, 256);
  const masterKeyBytes = await AesGcm.decrypt(ciphertext, passwordKey, nonce);

  return await AesGcm.importKey(masterKeyBytes);
}
```

**3. Hardware Security Modules (HSM)**

```typescript theme={null}
// Keys never leave secure hardware
// Use HSM APIs to encrypt/decrypt without exposing key
```

## Security

### Critical Warnings

**1. Never reuse nonce with same key**

```typescript theme={null}
// DANGEROUS - Same nonce with same key
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!

// Attacker can XOR ciphertexts to reveal plaintext relationship
```

**2. Use cryptographically secure random**

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

// WRONG - Never use Math.random() for cryptographic values
// Math.random() is predictable and NOT cryptographically secure!
```

**3. Verify authentication tag (automatic)**

```typescript theme={null}
// decrypt() verifies tag automatically
try {
  const plaintext = await AesGcm.decrypt(ciphertext, key, nonce);
  // If we reach here, authentication passed
} catch (error) {
  // Authentication failed - data was tampered or wrong key
  console.error('Tampering detected!');
}
```

**4. Protect keys at rest**

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

// RIGHT - Encrypt key with password or use secure storage
const encryptedKey = await encryptWithPassword(keyBytes, userPassword);
localStorage.setItem('key', JSON.stringify(encryptedKey));
```

**5. Use strong passwords for derivation**

```typescript theme={null}
// Weak password = weak encryption
const weakKey = await AesGcm.deriveKey('12345', salt, 100000, 256);

// Strong password = strong encryption
const strongKey = await AesGcm.deriveKey(
  'correct-horse-battery-staple-2024',
  salt,
  100000,
  256
);
```

### Best Practices

**1. Key size:** Use 256-bit keys for sensitive data

```typescript theme={null}
const key = await AesGcm.generateKey(256); // Recommended
```

**2. PBKDF2 iterations:** Balance security vs performance

```typescript theme={null}
// Minimum: 100,000 iterations
// Recommended: 600,000+ iterations (OWASP 2023)
const key = await AesGcm.deriveKey(password, salt, 600000, 256);
```

**3. Salt randomness:** Use 16+ byte random salt

```typescript theme={null}
import * as Hex from '@tevm/voltaire/Hex';

const salt = Hex('0xa7b6c5d4e3f2a1b0c9d8e7f6a5b4c3d2');
```

**4. Key rotation:** Periodically generate new keys

```typescript theme={null}
// Rotate keys every N encryptions or time period
if (encryptionCount > 1000000 || Date.now() - keyCreatedTime > 30 * 86400000) {
  key = await AesGcm.generateKey(256);
  encryptionCount = 0;
  keyCreatedTime = Date.now();
}
```

**5. Clear sensitive memory (when possible)**

```typescript theme={null}
// After use, zero out key bytes
const keyBytes = await AesGcm.exportKey(key);
// Use key...
keyBytes.fill(0); // Clear memory
```

### Common Attacks

**Nonce Reuse Attack:**

* Same nonce + key reveals XOR of plaintexts
* Protection: Always generate new nonce

**Key Exhaustion:**

* Too many encryptions with same key increases collision risk
* Protection: Rotate keys periodically

**Weak Password:**

* Brute-force PBKDF2-derived keys
* Protection: Strong passwords + high iteration count

**Timing Attacks:**

* Constant-time operations in WebCrypto API
* Protection: Use native crypto.subtle (not hand-rolled crypto)

**Padding Oracle:**

* Not applicable to GCM (no padding)
* GCM uses stream cipher mode

## Performance

### Benchmarks (typical)

**Encryption speed (AES-256-GCM):**

* Modern CPU with AES-NI: 1-5 GB/s
* Without hardware acceleration: 50-200 MB/s

**Key derivation (PBKDF2):**

* 100,000 iterations: \~50-100ms
* 600,000 iterations: \~300-600ms

### Optimization Tips

**1. Batch operations when possible**

```typescript theme={null}
// Encrypt multiple messages
const messages = [...];
const encrypted = await Promise.all(
  messages.map(async msg => {
    const nonce = AesGcm.generateNonce();
    const ct = await AesGcm.encrypt(msg, key, nonce);
    return { nonce, ciphertext: ct };
  })
);
```

**2. Reuse keys (but rotate periodically)**

```typescript theme={null}
// Generate key once
const key = await AesGcm.generateKey(256);

// Reuse for multiple encryptions (with different nonces!)
for (const message of messages) {
  const nonce = AesGcm.generateNonce();
  await AesGcm.encrypt(message, key, nonce);
}
```

**3. Adjust PBKDF2 iterations for use case**

```typescript theme={null}
// High security (cold storage)
const key = await AesGcm.deriveKey(password, salt, 1000000, 256);

// Moderate security (frequent decryption)
const key = await AesGcm.deriveKey(password, salt, 100000, 256);
```

## Use Cases

### File Encryption

```typescript theme={null}
import * as Hex from '@tevm/voltaire/Hex';

async function encryptFile(fileData, password) {
  const salt = Hex('0x9e8d7c6b5a4938271605948372615049');
  const key = await AesGcm.deriveKey(password, salt, 600000, 256);
  const nonce = AesGcm.generateNonce();

  const ciphertext = await AesGcm.encrypt(fileData, key, nonce);

  // Format: salt (16) + nonce (12) + ciphertext + tag (16)
  const encrypted = new Uint8Array(salt.length + nonce.length + ciphertext.length);
  encrypted.set(salt, 0);
  encrypted.set(nonce, 16);
  encrypted.set(ciphertext, 28);

  return encrypted;
}

async function decryptFile(encryptedFile, password) {
  const salt = encryptedFile.slice(0, 16);
  const nonce = encryptedFile.slice(16, 28);
  const ciphertext = encryptedFile.slice(28);

  const key = await AesGcm.deriveKey(password, salt, 600000, 256);
  return await AesGcm.decrypt(ciphertext, key, nonce);
}
```

### Database Field Encryption

```typescript theme={null}
class EncryptedDatabase {
  constructor(key) {
    this.key = key;
  }

  async encryptField(value) {
    const plaintext = new TextEncoder().encode(JSON.stringify(value));
    const nonce = AesGcm.generateNonce();
    const ciphertext = await AesGcm.encrypt(plaintext, this.key, nonce);

    return {
      nonce: Array(nonce),
      ciphertext: Array(ciphertext)
    };
  }

  async decryptField(encrypted) {
    const nonce = new Uint8Array(encrypted.nonce);
    const ciphertext = new Uint8Array(encrypted.ciphertext);

    const plaintext = await AesGcm.decrypt(ciphertext, this.key, nonce);
    return JSON.parse(new TextDecoder().decode(plaintext));
  }
}
```

### Secure Messaging

```typescript theme={null}
async function sendEncryptedMessage(message, recipientPublicKey, senderPrivateKey) {
  // 1. Derive shared secret (ECDH)
  const sharedSecret = await deriveSharedSecret(senderPrivateKey, recipientPublicKey);

  // 2. Use shared secret as key
  const key = await AesGcm.importKey(sharedSecret);

  // 3. Encrypt message
  const plaintext = new TextEncoder().encode(message);
  const nonce = AesGcm.generateNonce();
  const ciphertext = await AesGcm.encrypt(plaintext, key, nonce);

  return { nonce, ciphertext };
}
```

## Error Handling

All AesGcm functions throw typed errors that extend `CryptoError`:

| Error               | Code                | When                                                                      |
| ------------------- | ------------------- | ------------------------------------------------------------------------- |
| `InvalidKeyError`   | `INVALID_KEY`       | Key not 16 or 32 bytes on import                                          |
| `InvalidNonceError` | `INVALID_NONCE`     | Nonce not 12 bytes                                                        |
| `DecryptionError`   | `DECRYPTION_FAILED` | Auth tag verification fails, wrong key/nonce/AAD, or ciphertext too short |
| `AesGcmError`       | `AES_GCM_ERROR`     | Generic encryption failure                                                |

```typescript theme={null}
import * as AesGcm from '@tevm/voltaire/AesGcm';
import { DecryptionError, InvalidNonceError, InvalidKeyError } from '@tevm/voltaire/AesGcm';

try {
  const decrypted = await AesGcm.decrypt(ciphertext, key, nonce);
} catch (e) {
  if (e instanceof DecryptionError) {
    console.error('Authentication failed:', e.message);
    console.error('Code:', e.code); // "DECRYPTION_FAILED"
  } else if (e instanceof InvalidNonceError) {
    console.error('Invalid nonce:', e.message);
  }
}
```

All error classes have:

* `name` - Error class name (e.g., `"DecryptionError"`)
* `code` - Machine-readable error code
* `message` - Human-readable description
* `docsPath` - Link to relevant documentation

## Implementation Notes

* Uses native WebCrypto API (`crypto.subtle`)
* Hardware-accelerated on modern CPUs (AES-NI)
* Constant-time operations (timing attack resistant)
* NIST SP 800-38D compliant
* 128-bit authentication tag (maximum security)
* 96-bit nonce (12 bytes, standard for GCM)

## References

* [NIST SP 800-38D (GCM Specification)](https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf)
* [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API)
* [RFC 5116 (AEAD Algorithms)](https://www.rfc-editor.org/rfc/rfc5116.html)
* [OWASP Password Storage Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html)

## Related

* [AES-GCM (Effect)](https://voltaire-effect.tevm.sh/crypto/aesgcm) - Effect.ts integration with Schema validation
