Skip to content
Back to skills

600 Reference 43a68fe5

CSecurity

Comprehensive technical reference for encryption at rest, key management, and compliance standards.

  • 9 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 11, 2026
securityjavascriptpythonrustgojavashellbashsqlnodeaws

Works with

  • cli
  • api

Security analysis

C63/100
  • criticalAccesses sensitive system or user directories
  • criticalAccesses system keychains or credential stores
  • criticalSends environment variables or credentials to an external URL

Pro shows the line behind each finding and how to fix it

Scanned October 11, 2026

npx -y skills add tools-only/X-Skills --skill 600-reference_43a68fe5 --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of 600 Reference 43a68fe5?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for 600 Reference 43a68fe5
[![Security: C — Skills Directory](https://www.skillsdirectory.com/api/skills/tools-only-600-reference-43a68fe5/badge)](https://www.skillsdirectory.com/skills/tools-only-600-reference-43a68fe5)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

SKILL.md
# Encryption at Rest Reference

Comprehensive technical reference for encryption at rest, key management, and compliance standards.

## Table of Contents

1. [Fundamentals](#fundamentals)
2. [Encryption Algorithms](#encryption-algorithms)
3. [Key Management](#key-management)
4. [Envelope Encryption](#envelope-encryption)
5. [Database Encryption](#database-encryption)
6. [File and Disk Encryption](#file-and-disk-encryption)
7. [Cloud KMS Integration](#cloud-kms-integration)
8. [Key Rotation](#key-rotation)
9. [Compliance Standards](#compliance-standards)
10. [Performance Considerations](#performance-considerations)
11. [Common Patterns](#common-patterns)
12. [Anti-Patterns](#anti-patterns)
13. [Security Best Practices](#security-best-practices)
14. [Implementation Examples](#implementation-examples)

---

## Fundamentals

### What is Encryption at Rest?

**Encryption at rest** protects data stored on disk from unauthorized access. Unlike encryption in transit (TLS/SSL), encryption at rest protects data when it's not actively being transmitted.

**Key Differences**:

| Aspect | Encryption at Rest | Encryption in Transit |
|--------|-------------------|----------------------|
| **Protects** | Stored data on disk | Data moving over network |
| **Threat Model** | Physical access, disk theft, backups | Network eavesdropping, MITM |
| **Typical Use** | Databases, files, backups, volumes | HTTPS, TLS, SSH |
| **Performance** | One-time cost on read/write | Continuous overhead during transmission |
| **Key Lifetime** | Long-lived (months/years) | Short-lived (session keys) |

### Threat Model

**What encryption at rest protects against**:
- **Physical theft**: Stolen laptops, hard drives, backup tapes
- **Cloud provider access**: Unauthorized access by cloud staff
- **Forensic recovery**: Data recovery from decommissioned drives
- **Backup compromise**: Stolen or leaked backup files
- **Snapshot access**: Unauthorized access to volume snapshots
- **Insider threats**: Malicious employees with physical access

**What it does NOT protect against**:
- **Application-level attacks**: SQL injection, XSS, CSRF
- **Authorized user access**: Users with valid credentials
- **Memory dumps**: Data in RAM is not encrypted
- **Side-channel attacks**: Timing attacks, cache attacks
- **Compromised encryption keys**: If attacker has keys, encryption is useless

### Encryption Layers

```
┌─────────────────────────────────────────┐
│ Application-Level Encryption            │  ← Column/field encryption (highest control)
├─────────────────────────────────────────┤
│ Database-Level Encryption               │  ← TDE (Transparent Data Encryption)
├─────────────────────────────────────────┤
│ Filesystem-Level Encryption             │  ← eCryptfs, EncFS
├─────────────────────────────────────────┤
│ Volume/Block-Level Encryption           │  ← LUKS, dm-crypt, BitLocker
├─────────────────────────────────────────┤
│ Hardware-Level Encryption               │  ← Self-encrypting drives (SEDs)
└─────────────────────────────────────────┘
```

**Trade-offs**:

| Layer | Granularity | Performance | Key Management | Flexibility |
|-------|-------------|-------------|----------------|-------------|
| Application | Per-field | Slowest | Most complex | Highest |
| Database | Per-database | Fast | Moderate | Moderate |
| Filesystem | Per-file | Moderate | Moderate | Moderate |
| Volume | Per-volume | Fast | Simple | Low |
| Hardware | Per-drive | Fastest | Simple | Lowest |

### When to Use Each Layer

**Application-Level**:
- Need per-field encryption (e.g., credit card numbers)
- Compliance requires end-to-end encryption
- Different users need different keys
- Zero-trust architecture

**Database-Level**:
- Encrypt entire database with minimal code changes
- Compliance requires encryption at rest
- Centralized key management
- Performance is critical

**Filesystem-Level**:
- Encrypt user directories or shared folders
- Protect files from OS-level access
- Need per-user encryption

**Volume-Level**:
- Encrypt entire disk/partition
- Protect against physical theft
- Simplest to implement
- Minimal performance overhead

**Hardware-Level**:
- Best performance
- No OS support required
- Limited key management control

---

## Encryption Algorithms

### Symmetric Encryption

**Definition**: Same key used for encryption and decryption.

**Use Case**: Encryption at rest (fast, efficient).

#### AES (Advanced Encryption Standard)

**Overview**:
- **Standard**: FIPS 197 (2001)
- **Block Size**: 128 bits
- **Key Sizes**: 128, 192, 256 bits
- **Status**: Industry standard, NIST approved

**Modes of Operation**:

| Mode | Properties | Use Case | Security |
|------|-----------|----------|----------|
| **ECB** (Electronic Codebook) | Deterministic, no IV | ❌ NEVER USE | Insecure |
| **CBC** (Cipher Block Chaining) | Requires IV, sequential | Legacy systems | Secure with IV |
| **CTR** (Counter) | Parallel, requires nonce | Performance-critical | Secure with unique nonce |
| **GCM** (Galois/Counter Mode) | AEAD, parallel, auth tag | Modern apps | Best choice |
| **XTS** (XEX-based tweaked-codebook) | Disk encryption | Full-disk encryption | Optimized for storage |

**Recommended**: **AES-256-GCM** (authenticated encryption with associated data).

**Example (OpenSSL)**:
```bash
# Encrypt file with AES-256-GCM
openssl enc -aes-256-gcm -salt -in plaintext.txt -out encrypted.bin -k password

# Decrypt
openssl enc -aes-256-gcm -d -in encrypted.bin -out plaintext.txt -k password
```

**Python Example (cryptography library)**:
```python
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os

# Generate key (256 bits)
key = AESGCM.generate_key(bit_length=256)
aesgcm = AESGCM(key)

# Generate nonce (96 bits recommended for GCM)
nonce = os.urandom(12)

# Encrypt
plaintext = b"Secret data"
ciphertext = aesgcm.encrypt(nonce, plaintext, None)

# Decrypt
decrypted = aesgcm.decrypt(nonce, ciphertext, None)
assert decrypted == plaintext
```

**Go Example**:
```go
package main

import (
    "crypto/aes"
    "crypto/cipher"
    "crypto/rand"
    "io"
)

func encryptAESGCM(plaintext, key []byte) ([]byte, error) {
    block, err := aes.NewCipher(key)
    if err != nil {
        return nil, err
    }

    aesGCM, err := cipher.NewGCM(block)
    if err != nil {
        return nil, err
    }

    nonce := make([]byte, aesGCM.NonceSize())
    if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
        return nil, err
    }

    // Prepend nonce to ciphertext
    ciphertext := aesGCM.Seal(nonce, nonce, plaintext, nil)
    return ciphertext, nil
}
```

**Performance**:
- **Hardware acceleration**: AES-NI (Intel), ARMv8 Crypto Extensions
- **Throughput**: 2-10 GB/s with hardware acceleration
- **Latency**: ~1-5 microseconds per operation

#### ChaCha20-Poly1305

**Overview**:
- **Standard**: RFC 8439 (2018)
- **Algorithm**: Stream cipher (ChaCha20) + MAC (Poly1305)
- **Key Size**: 256 bits
- **Nonce Size**: 96 bits (12 bytes)
- **Use Case**: Mobile devices without AES-NI, software-only systems

**Advantages over AES**:
- Faster on systems without AES-NI
- Constant-time implementation (resistant to timing attacks)
- Simpler to implement correctly

**Python Example**:
```python
from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
import os

# Generate key
key = ChaCha20Poly1305.generate_key()
chacha = ChaCha20Poly1305(key)

# Generate nonce
nonce = os.urandom(12)

# Encrypt
plaintext = b"Secret data"
ciphertext = chacha.encrypt(nonce, plaintext, None)

# Decrypt
decrypted = chacha.decrypt(nonce, ciphertext, None)
assert decrypted == plaintext
```

**When to use ChaCha20-Poly1305**:
- Mobile devices (ARM without crypto extensions)
- Software-only encryption
- Need constant-time implementation
- Alternative to AES-GCM

**When to use AES-GCM**:
- x86/x64 processors with AES-NI
- Hardware acceleration available
- Compliance requires FIPS-approved algorithms

#### Deprecated/Insecure Algorithms

**❌ Never use**:
- **DES** (Data Encryption Standard) - 56-bit key, broken
- **3DES** (Triple DES) - 112-bit effective key, deprecated
- **RC4** - Stream cipher, broken
- **Blowfish** - 64-bit block size, vulnerable to birthday attacks

**Key Takeaways**:
- Use **AES-256-GCM** or **ChaCha20-Poly1305**
- Always use authenticated encryption (AEAD)
- Never reuse nonces
- Use hardware acceleration when available

---

## Key Management

### Key Hierarchy

**Principle**: Separate encryption keys from data encryption keys (DEKs).

```
┌──────────────────────────────────────────┐
│ Root Key (Master Key)                    │  ← Stored in HSM or KMS
│ - Rarely changed                         │
│ - Used to encrypt KEKs                   │
└──────────────┬───────────────────────────┘
               │
               ▼
┌──────────────────────────────────────────┐
│ Key Encryption Key (KEK)                 │  ← Encrypted by root key
│ - Changed periodically (e.g., yearly)    │
│ - Used to encrypt DEKs                   │
└──────────────┬───────────────────────────┘
               │
               ▼
┌──────────────────────────────────────────┐
│ Data Encryption Key (DEK)                │  ← Encrypted by KEK
│ - Changed frequently (e.g., daily)       │
│ - Used to encrypt actual data            │
└──────────────┬───────────────────────────┘
               │
               ▼
       ┌──────────────────┐
       │ Encrypted Data   │  ← Encrypted by DEK
       └──────────────────┘
```

**Benefits**:
- **Key rotation**: Rotate KEKs without re-encrypting all data
- **Performance**: Encrypt DEKs (small) instead of data (large)
- **Security**: Compromise of DEK doesn't expose root key

### Key Generation

**Cryptographically Secure Random Number Generators (CSRNGs)**:

| Language | CSRNG Function | Entropy Source |
|----------|---------------|----------------|
| Python | `os.urandom()`, `secrets` | `/dev/urandom` (Linux), CryptGenRandom (Windows) |
| Go | `crypto/rand.Reader` | `/dev/urandom` |
| Node.js | `crypto.randomBytes()` | OpenSSL |
| OpenSSL | `RAND_bytes()` | `/dev/urandom` |

**❌ Never use**:
- `random.random()` (Python) - Predictable
- `Math.random()` (JavaScript) - Predictable
- `rand()` (C) - Predictable
- Timestamp-based keys
- Hardcoded keys

**✅ Good key generation**:
```python
import os
import secrets

# Generate 256-bit key (32 bytes)
key = os.urandom(32)

# Or use secrets module (Python 3.6+)
key = secrets.token_bytes(32)

# Generate key from password (DO NOT use directly, see KDF section)
```

### Key Derivation Functions (KDFs)

**Purpose**: Derive encryption keys from passwords or passphrases.

**Problem**: Passwords are low-entropy, weak keys.
**Solution**: Use KDF to derive strong keys from passwords.

#### PBKDF2 (Password-Based Key Derivation Function 2)

**Standard**: RFC 8018 (PKCS #5)
**Algorithm**: HMAC + iteration count
**Parameters**:
- Password/passphrase
- Salt (16+ bytes, random)
- Iteration count (100,000+ for HMAC-SHA256)
- Key length (32 bytes for AES-256)

**Python Example**:
```python
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives import hashes
import os

password = b"user-password"
salt = os.urandom(16)

kdf = PBKDF2HMAC(
    algorithm=hashes.SHA256(),
    length=32,  # 256 bits
    salt=salt,
    iterations=480000,  # OWASP recommendation (2023)
)

key = kdf.derive(password)
```

**Pros**: FIPS-approved, widely supported
**Cons**: Vulnerable to GPU/ASIC attacks

#### Argon2

**Standard**: RFC 9106 (2021)
**Algorithm**: Memory-hard KDF
**Variants**:
- **Argon2i**: Optimized for resistance to side-channel attacks
- **Argon2d**: Optimized for resistance to GPU attacks
- **Argon2id**: Hybrid (recommended)

**Parameters**:
- Password
- Salt (16+ bytes)
- Time cost (iterations)
- Memory cost (KB)
- Parallelism (threads)

**Python Example**:
```python
from argon2 import PasswordHasher
import os

ph = PasswordHasher(
    time_cost=3,        # Number of iterations
    memory_cost=65536,  # 64 MB
    parallelism=4,      # 4 threads
    hash_len=32,        # 256-bit key
    salt_len=16,
)

password = "user-password"  # Example only - in production, get from secure input
hash_str = ph.hash(password)

# Verify
try:
    ph.verify(hash_str, password)
    print("Password correct")
except:
    print("Password incorrect")
```

**Pros**: Memory-hard (resistant to GPU/ASIC attacks), winner of Password Hashing Competition (2015)
**Cons**: Not FIPS-approved (yet)

#### scrypt

**Standard**: RFC 7914 (2016)
**Algorithm**: Memory-hard KDF
**Parameters**:
- Password
- Salt (16+ bytes)
- N (CPU/memory cost factor, power of 2, e.g., 2^14)
- r (block size, e.g., 8)
- p (parallelization factor, e.g., 1)

**Python Example**:
```python
from cryptography.hazmat.primitives.kdf.scrypt import Scrypt
import os

password = b"user-password"
salt = os.urandom(16)

kdf = Scrypt(
    salt=salt,
    length=32,
    n=2**14,  # CPU/memory cost factor
    r=8,      # Block size
    p=1,      # Parallelization factor
)

key = kdf.derive(password)
```

**Pros**: Memory-hard, widely supported
**Cons**: Complex parameter tuning

#### Recommendations

**For password hashing (authentication)**:
- **First choice**: Argon2id
- **FIPS compliance**: PBKDF2-HMAC-SHA256 (480,000+ iterations)
- **Legacy systems**: bcrypt

**For key derivation (encryption)**:
- **First choice**: Argon2id or scrypt
- **FIPS compliance**: PBKDF2-HMAC-SHA256

### Key Storage

**❌ Never store keys**:
- Hardcoded in source code
- In configuration files (plaintext)
- In version control (Git)
- In environment variables (for production)
- In application logs
- In databases (unencrypted)

**✅ Secure key storage**:

#### Option 1: Hardware Security Module (HSM)

**What**: Dedicated hardware for key storage and cryptographic operations.

**Features**:
- Keys never leave HSM
- FIPS 140-2 Level 3/4 certified
- Tamper-resistant
- Expensive ($10K-$100K+)

**Use Case**: High-security environments (banks, government).

**Examples**:
- Thales Luna HSM
- AWS CloudHSM
- Google Cloud HSM
- Azure Dedicated HSM

#### Option 2: Cloud Key Management Service (KMS)

**What**: Managed service for key storage and encryption.

**Features**:
- API-based key management
- Automatic key rotation
- Access control (IAM)
- Audit logging
- Lower cost ($1-$10/month per key)

**Use Case**: Cloud-native applications.

**Examples**:
- AWS KMS
- Google Cloud KMS
- Azure Key Vault
- HashiCorp Vault

#### Option 3: Software-Based Key Management

**What**: Store keys encrypted with master key.

**Pattern**:
1. Generate master key (stored securely, e.g., HSM or KMS)
2. Encrypt data encryption keys (DEKs) with master key
3. Store encrypted DEKs alongside encrypted data

**Example (envelope encryption)**:
```python
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os
import json

# Master key (stored in KMS or HSM)
master_key = os.urandom(32)
master_aesgcm = AESGCM(master_key)

# Generate DEK for this data
dek = AESGCM.generate_key(bit_length=256)
dek_nonce = os.urandom(12)

# Encrypt DEK with master key
encrypted_dek = master_aesgcm.encrypt(dek_nonce, dek, None)

# Encrypt data with DEK
data_aesgcm = AESGCM(dek)
data_nonce = os.urandom(12)
plaintext = b"Sensitive data"
ciphertext = data_aesgcm.encrypt(data_nonce, plaintext, None)

# Store encrypted DEK + ciphertext
envelope = {
    "encrypted_dek": encrypted_dek.hex(),
    "dek_nonce": dek_nonce.hex(),
    "ciphertext": ciphertext.hex(),
    "data_nonce": data_nonce.hex(),
}

print(json.dumps(envelope, indent=2))
```

**Decryption**:
```python
# Load envelope
envelope = json.loads(envelope_json)

# Decrypt DEK with master key
dek = master_aesgcm.decrypt(
    bytes.fromhex(envelope["dek_nonce"]),
    bytes.fromhex(envelope["encrypted_dek"]),
    None
)

# Decrypt data with DEK
data_aesgcm = AESGCM(dek)
plaintext = data_aesgcm.decrypt(
    bytes.fromhex(envelope["data_nonce"]),
    bytes.fromhex(envelope["ciphertext"]),
    None
)
```

#### Option 4: Operating System Keystore

**What**: OS-provided secure storage (e.g., macOS Keychain, Windows Credential Manager).

**Use Case**: Desktop applications, local development.

**Python Example (macOS Keychain)**:
```python
import keyring

# Store key
keyring.set_password("myapp", "encryption_key", "base64-encoded-key")

# Retrieve key
key = keyring.get_password("myapp", "encryption_key")
```

### Key Access Control

**Principle of Least Privilege**: Only authorized users/services should access keys.

**AWS KMS Example (IAM Policy)**:
```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "kms:Decrypt",
        "kms:DescribeKey"
      ],
      "Resource": "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012",
      "Condition": {
        "StringEquals": {
          "kms:EncryptionContext:AppName": "myapp"
        }
      }
    }
  ]
}
```

**Best Practices**:
- Use separate keys for different environments (dev, staging, prod)
- Use separate keys for different applications
- Rotate keys regularly
- Audit key access (CloudTrail, Stackdriver, Azure Monitor)
- Use encryption context for additional security

---

## Envelope Encryption

### Concept

**Problem**: Encrypting large amounts of data with KMS is slow and expensive (API calls).

**Solution**:
1. Generate Data Encryption Key (DEK) locally
2. Encrypt data with DEK (fast, local)
3. Encrypt DEK with KMS (small, one API call)
4. Store encrypted DEK + encrypted data together

**Flow**:
```
┌─────────────┐
│  Plaintext  │
└──────┬──────┘
       │
       ▼
┌─────────────────────────────┐
│ Generate DEK (local)        │
│ key = os.urandom(32)        │
└─────────┬───────────────────┘
          │
          ▼
┌─────────────────────────────┐
│ Encrypt data with DEK       │
│ ciphertext = encrypt(       │
│   plaintext, dek            │
│ )                           │
└─────────┬───────────────────┘
          │
          ▼
┌─────────────────────────────┐
│ Encrypt DEK with KMS        │  ← One API call
│ encrypted_dek = kms.encrypt(│
│   dek                       │
│ )                           │
└─────────┬───────────────────┘
          │
          ▼
┌─────────────────────────────┐
│ Store encrypted_dek +       │
│       ciphertext            │
└─────────────────────────────┘
```

**Decryption**:
```
┌─────────────────────────────┐
│ Load encrypted_dek +        │
│      ciphertext             │
└─────────┬───────────────────┘
          │
          ▼
┌─────────────────────────────┐
│ Decrypt DEK with KMS        │  ← One API call
│ dek = kms.decrypt(          │
│   encrypted_dek             │
│ )                           │
└─────────┬───────────────────┘
          │
          ▼
┌─────────────────────────────┐
│ Decrypt data with DEK       │
│ plaintext = decrypt(        │
│   ciphertext, dek           │
│ )                           │
└─────────┬───────────────────┘
          │
          ▼
┌─────────────┐
│  Plaintext  │
└─────────────┘
```

### AWS KMS Envelope Encryption

**Python Example**:
```python
import boto3
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os
import json

kms = boto3.client('kms')
KMS_KEY_ID = 'arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012'

def encrypt_file(plaintext, kms_key_id):
    """Encrypt file using envelope encryption with AWS KMS"""

    # 1. Generate DEK (plaintext key)
    response = kms.generate_data_key(
        KeyId=kms_key_id,
        KeySpec='AES_256'
    )

    dek_plaintext = response['Plaintext']      # DEK (plaintext)
    dek_encrypted = response['CiphertextBlob']  # DEK encrypted by KMS

    # 2. Encrypt data with DEK
    aesgcm = AESGCM(dek_plaintext)
    nonce = os.urandom(12)
    ciphertext = aesgcm.encrypt(nonce, plaintext, None)

    # 3. Return envelope (encrypted DEK + ciphertext)
    return {
        'encrypted_dek': dek_encrypted,
        'ciphertext': ciphertext,
        'nonce': nonce,
    }

def decrypt_file(envelope, kms_key_id):
    """Decrypt file using envelope encryption with AWS KMS"""

    # 1. Decrypt DEK with KMS
    response = kms.decrypt(
        CiphertextBlob=envelope['encrypted_dek']
    )
    dek_plaintext = response['Plaintext']

    # 2. Decrypt data with DEK
    aesgcm = AESGCM(dek_plaintext)
    plaintext = aesgcm.decrypt(
        envelope['nonce'],
        envelope['ciphertext'],
        None
    )

    return plaintext

# Usage
plaintext = b"Sensitive data to encrypt"
envelope = encrypt_file(plaintext, KMS_KEY_ID)
decrypted = decrypt_file(envelope, KMS_KEY_ID)
assert decrypted == plaintext
```

### Google Cloud KMS Envelope Encryption

**Python Example**:
```python
from google.cloud import kms
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os

def encrypt_with_gcp_kms(plaintext, project_id, location_id, key_ring_id, key_id):
    """Encrypt using envelope encryption with GCP KMS"""

    # Initialize KMS client
    client = kms.KeyManagementServiceClient()
    key_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id)

    # 1. Generate DEK locally
    dek = AESGCM.generate_key(bit_length=256)

    # 2. Encrypt data with DEK
    aesgcm = AESGCM(dek)
    nonce = os.urandom(12)
    ciphertext = aesgcm.encrypt(nonce, plaintext, None)

    # 3. Encrypt DEK with KMS
    encrypt_response = client.encrypt(
        request={'name': key_name, 'plaintext': dek}
    )
    encrypted_dek = encrypt_response.ciphertext

    return {
        'encrypted_dek': encrypted_dek,
        'ciphertext': ciphertext,
        'nonce': nonce,
    }

def decrypt_with_gcp_kms(envelope, project_id, location_id, key_ring_id, key_id):
    """Decrypt using envelope encryption with GCP KMS"""

    client = kms.KeyManagementServiceClient()
    key_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id)

    # 1. Decrypt DEK with KMS
    decrypt_response = client.decrypt(
        request={'name': key_name, 'ciphertext': envelope['encrypted_dek']}
    )
    dek = decrypt_response.plaintext

    # 2. Decrypt data with DEK
    aesgcm = AESGCM(dek)
    plaintext = aesgcm.decrypt(
        envelope['nonce'],
        envelope['ciphertext'],
        None
    )

    return plaintext
```

### Benefits of Envelope Encryption

1. **Performance**: Local encryption (fast) + one KMS call (small overhead)
2. **Cost**: Fewer KMS API calls
3. **Scalability**: Can encrypt large files without KMS payload limits
4. **Key rotation**: Rotate master key without re-encrypting data (just re-encrypt DEKs)

---

## Database Encryption

### Transparent Data Encryption (TDE)

**What**: Database-level encryption that encrypts entire database files.

**How it works**:
1. Database generates master key
2. Database encrypts data pages before writing to disk
3. Database decrypts data pages after reading from disk
4. Applications see plaintext (transparent)

**Pros**:
- No application changes required
- Encrypts all data (tables, indexes, logs)
- Protects backups
- Minimal performance overhead (~3-10%)

**Cons**:
- Database has full access to keys
- All-or-nothing (can't encrypt specific columns)
- Doesn't protect against application-level attacks

### PostgreSQL TDE

**Status**: Not natively supported (as of PostgreSQL 16).

**Alternatives**:
1. **pgcrypto extension**: Column-level encryption
2. **Filesystem-level encryption**: LUKS, dm-crypt
3. **Cloud provider TDE**: AWS RDS, Google Cloud SQL

**pgcrypto Example (Column-Level Encryption)**:
```sql
-- Enable pgcrypto extension
CREATE EXTENSION pgcrypto;

-- Encrypt data
INSERT INTO users (email, encrypted_ssn)
VALUES (
    'user@example.com',
    pgp_sym_encrypt('123-45-6789', 'encryption-password')
);

-- Decrypt data
SELECT
    email,
    pgp_sym_decrypt(encrypted_ssn::bytea, 'encryption-password') AS ssn
FROM users;
```

**Best Practice**: Use envelope encryption with KMS instead of hardcoded password:
```sql
-- Store encrypted DEK alongside data
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    email VARCHAR(255),
    encrypted_ssn BYTEA,
    encrypted_dek BYTEA,  -- DEK encrypted by KMS
    dek_nonce BYTEA
);

-- Application encrypts SSN with DEK, encrypts DEK with KMS
-- Store both encrypted_ssn and encrypted_dek
```

### MySQL TDE

**Support**: MySQL 5.7+ (Enterprise), MariaDB 10.1+

**Setup**:
```sql
-- Enable TDE plugin
INSTALL PLUGIN keyring_file SONAME 'keyring_file.so';

-- Configure keyring in my.cnf
[mysqld]
early-plugin-load=keyring_file.so
keyring_file_data=/var/lib/mysql-keyring/keyring

-- Create encrypted table
CREATE TABLE users (
    id INT PRIMARY KEY,
    email VARCHAR(255),
    ssn VARCHAR(11)
) ENCRYPTION='Y';

-- Encrypt existing table
ALTER TABLE users ENCRYPTION='Y';
```

**Key Rotation**:
```sql
-- Rotate master key
ALTER INSTANCE ROTATE INNODB MASTER KEY;
```

### MongoDB Encryption at Rest

**Support**: MongoDB 3.2+ (Enterprise, Atlas)

**Configuration** (mongod.conf):
```yaml
security:
  enableEncryption: true
  encryptionKeyFile: /etc/mongodb-keyfile

# Or use KMIP (Key Management Interoperability Protocol)
security:
  enableEncryption: true
  kmip:
    serverName: kmip.example.com
    port: 5696
    clientCertificateFile: /etc/mongodb-client.pem
```

**Encrypted Storage Engine**:
- Uses AES-256-CBC
- Encrypts data files, journals, logs
- Keys stored in keyfile or KMIP server
- Automatic decryption on read

**Encrypted Collections** (Client-Side Field Level Encryption - CSFLE):
```javascript
const { ClientEncryption } = require('mongodb-client-encryption');

// Setup encryption options
const encryptionOptions = {
  keyVaultNamespace: 'encryption.__keyVault',
  kmsProviders: {
    aws: {
      accessKeyId: process.env.AWS_ACCESS_KEY_ID,
      secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
    }
  }
};

// Create encrypted client
const client = new MongoClient(uri, {
  autoEncryption: encryptionOptions
});

// Create data key
const encryption = new ClientEncryption(client, encryptionOptions);
const dataKeyId = await encryption.createDataKey('aws', {
  masterKey: {
    key: 'arn:aws:kms:us-east-1:123456789012:key/12345678',
    region: 'us-east-1'
  }
});

// Create collection with encrypted fields
await db.createCollection('users', {
  validator: {
    $jsonSchema: {
      properties: {
        ssn: {
          encrypt: {
            keyId: [dataKeyId],
            algorithm: 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'
          }
        }
      }
    }
  }
});

// Insert encrypted data (automatic)
await db.collection('users').insertOne({
  name: 'John Doe',
  ssn: '123-45-6789'  // Automatically encrypted
});
```

### SQLite Encryption

**Native**: SQLite doesn't support encryption natively.

**Extensions**:
1. **SQLCipher**: Open-source SQLite extension
2. **SEE (SQLite Encryption Extension)**: Commercial

**SQLCipher Setup (Python)**:
```python
from pysqlcipher3 import dbapi2 as sqlite3

# Connect to encrypted database
conn = sqlite3.connect('encrypted.db')
conn.execute("PRAGMA key='strong-password'")

# Set encryption algorithm
conn.execute("PRAGMA cipher='aes-256-cbc'")

# Create table and insert data
conn.execute('CREATE TABLE users (id INTEGER PRIMARY KEY, email TEXT)')
conn.execute("INSERT INTO users (email) VALUES ('user@example.com')")
conn.commit()
```

**Key Derivation**: SQLCipher uses PBKDF2-HMAC-SHA512 by default.

**Rekey** (change password):
```python
conn.execute("PRAGMA rekey='new-strong-password'")
```

---

## File and Disk Encryption

### Linux: LUKS (Linux Unified Key Setup)

**What**: Standard disk encryption on Linux.

**Features**:
- Full-disk or partition encryption
- Multiple key slots (up to 8 passphrases/keys)
- Uses dm-crypt kernel module
- Supports AES, XTS mode

**Setup**:
```bash
# Format partition with LUKS
sudo cryptsetup luksFormat /dev/sdb1

# Open encrypted partition
sudo cryptsetup luksOpen /dev/sdb1 encrypted_disk

# Create filesystem
sudo mkfs.ext4 /dev/mapper/encrypted_disk

# Mount
sudo mount /dev/mapper/encrypted_disk /mnt/encrypted

# Auto-mount on boot (add to /etc/crypttab)
echo "encrypted_disk /dev/sdb1 none luks" | sudo tee -a /etc/crypttab

# Add to /etc/fstab
echo "/dev/mapper/encrypted_disk /mnt/encrypted ext4 defaults 0 2" | sudo tee -a /etc/fstab
```

**Key Management**:
```bash
# Add new key slot
sudo cryptsetup luksAddKey /dev/sdb1

# Remove key slot
sudo cryptsetup luksRemoveKey /dev/sdb1

# Backup LUKS header
sudo cryptsetup luksHeaderBackup /dev/sdb1 --header-backup-file luks-header-backup

# Restore LUKS header
sudo cryptsetup luksHeaderRestore /dev/sdb1 --header-backup-file luks-header-backup
```

**Key File** (instead of passphrase):
```bash
# Generate key file
dd if=/dev/urandom of=/root/keyfile bs=1024 count=4
chmod 600 /root/keyfile

# Add key file to LUKS
sudo cryptsetup luksAddKey /dev/sdb1 /root/keyfile

# Open with key file
sudo cryptsetup luksOpen /dev/sdb1 encrypted_disk --key-file /root/keyfile
```

### eCryptfs (Enterprise Cryptographic Filesystem)

**What**: Stacked filesystem encryption (per-directory).

**Use Case**: Encrypt user home directories.

**Setup**:
```bash
# Install eCryptfs
sudo apt-get install ecryptfs-utils

# Setup encrypted directory
mkdir ~/Private
sudo mount -t ecryptfs ~/Private ~/Private

# Configure encryption options (interactive)
# - Key type: passphrase
# - Cipher: aes
# - Key bytes: 32 (AES-256)
# - Plaintext passthrough: no
# - Filename encryption: yes

# Add to /etc/fstab for auto-mount
/home/user/Private /home/user/Private ecryptfs defaults 0 0
```

**Encrypting Home Directory**:
```bash
# Migrate existing home directory to encrypted
ecryptfs-migrate-home -u username
```

### Windows: BitLocker

**What**: Full-disk encryption on Windows (Pro/Enterprise).

**Setup** (via GUI):
1. Control Panel → BitLocker Drive Encryption
2. Turn on BitLocker for drive
3. Choose authentication method (password, smart card, TPM)
4. Save recovery key
5. Encrypt drive

**Setup** (via PowerShell):
```powershell
# Enable BitLocker with password
Enable-BitLocker -MountPoint "C:" -PasswordProtector -Password (ConvertTo-SecureString "password" -AsPlainText -Force)

# Backup recovery key
Backup-BitLockerKeyProtector -MountPoint "C:" -KeyProtectorId (Get-BitLockerVolume -MountPoint "C:").KeyProtector[0].KeyProtectorId -Path "C:\recovery-key.txt"

# Encrypt drive
Resume-BitLocker -MountPoint "C:"
```

**TPM (Trusted Platform Module)**: Hardware chip that stores encryption keys.

**Recovery Key**: 48-digit recovery password, stored in Active Directory or printed.

### macOS: FileVault 2

**What**: Full-disk encryption on macOS.

**Setup** (via GUI):
1. System Preferences → Security & Privacy → FileVault
2. Turn On FileVault
3. Choose recovery method (iCloud or recovery key)
4. Restart to encrypt

**Setup** (via command line):
```bash
# Enable FileVault
sudo fdesetup enable

# Check status
sudo fdesetup status

# List enabled users
sudo fdesetup list
```

**Recovery Key**: 24-character alphanumeric key, store securely.

### File-Level Encryption (GPG)

**What**: Encrypt individual files with GPG (GNU Privacy Guard).

**Setup**:
```bash
# Generate GPG key pair
gpg --full-generate-key

# Encrypt file
gpg --encrypt --recipient user@example.com file.txt

# Decrypt file
gpg --decrypt file.txt.gpg > file.txt
```

**Symmetric Encryption** (password-based):
```bash
# Encrypt
gpg --symmetric --cipher-algo AES256 file.txt

# Decrypt
gpg --decrypt file.txt.gpg > file.txt
```

---

## Cloud KMS Integration

### AWS KMS

**Features**:
- Managed key storage
- FIPS 140-2 Level 2 validated
- Automatic key rotation (annual)
- Audit logging (CloudTrail)
- Integration with AWS services (S3, EBS, RDS)

**Setup**:
```bash
# Create KMS key
aws kms create-key --description "My encryption key"

# Create alias
aws kms create-alias --alias-name alias/my-key --target-key-id 12345678-1234-1234-1234-123456789012

# Enable automatic key rotation
aws kms enable-key-rotation --key-id 12345678-1234-1234-1234-123456789012
```

**Python SDK**:
```python
import boto3

kms = boto3.client('kms')

# Encrypt data directly (max 4 KB)
response = kms.encrypt(
    KeyId='alias/my-key',
    Plaintext=b'Secret data',
    EncryptionContext={'Department': 'Finance'}
)
ciphertext = response['CiphertextBlob']

# Decrypt
response = kms.decrypt(
    CiphertextBlob=ciphertext,
    EncryptionContext={'Department': 'Finance'}
)
plaintext = response['Plaintext']

# Generate data key (for envelope encryption)
response = kms.generate_data_key(
    KeyId='alias/my-key',
    KeySpec='AES_256'
)
dek_plaintext = response['Plaintext']
dek_encrypted = response['CiphertextBlob']
```

**Encryption Context**: Additional authenticated data (AAD) for additional security.

**Key Policies** (IAM-like permissions for keys):
```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Enable IAM User Permissions",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456789012:root"
      },
      "Action": "kms:*",
      "Resource": "*"
    },
    {
      "Sid": "Allow use of the key for encryption",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456789012:role/MyAppRole"
      },
      "Action": [
        "kms:Decrypt",
        "kms:DescribeKey",
        "kms:GenerateDataKey"
      ],
      "Resource": "*"
    }
  ]
}
```

### Google Cloud KMS

**Features**:
- Managed key storage
- FIPS 140-2 Level 3 validated (HSM option)
- Automatic key rotation (configurable)
- Audit logging (Cloud Audit Logs)
- Integration with GCP services (GCS, GCE, BigQuery)

**Setup**:
```bash
# Create key ring
gcloud kms keyrings create my-keyring --location us-east1

# Create key
gcloud kms keys create my-key \
  --location us-east1 \
  --keyring my-keyring \
  --purpose encryption

# Enable automatic rotation (90 days)
gcloud kms keys update my-key \
  --location us-east1 \
  --keyring my-keyring \
  --rotation-period 90d \
  --next-rotation-time 2025-12-01T00:00:00Z
```

**Python SDK**:
```python
from google.cloud import kms

client = kms.KeyManagementServiceClient()

# Key resource name
key_name = 'projects/my-project/locations/us-east1/keyRings/my-keyring/cryptoKeys/my-key'

# Encrypt
plaintext = b'Secret data'
encrypt_response = client.encrypt(
    request={'name': key_name, 'plaintext': plaintext}
)
ciphertext = encrypt_response.ciphertext

# Decrypt
decrypt_response = client.decrypt(
    request={'name': key_name, 'ciphertext': ciphertext}
)
plaintext = decrypt_response.plaintext
```

**IAM Permissions**:
```bash
# Grant encrypt/decrypt permissions
gcloud kms keys add-iam-policy-binding my-key \
  --location us-east1 \
  --keyring my-keyring \
  --member serviceAccount:my-app@my-project.iam.gserviceaccount.com \
  --role roles/cloudkms.cryptoKeyEncrypterDecrypter
```

### Azure Key Vault

**Features**:
- Managed key storage
- FIPS 140-2 Level 2 validated (Premium: HSM)
- Automatic key rotation (configurable)
- Audit logging (Azure Monitor)
- Integration with Azure services (Storage, SQL, VMs)

**Setup**:
```bash
# Create Key Vault
az keyvault create \
  --name my-keyvault \
  --resource-group my-rg \
  --location eastus

# Create key
az keyvault key create \
  --vault-name my-keyvault \
  --name my-key \
  --protection software \
  --kty RSA \
  --size 2048

# Enable automatic rotation
az keyvault key rotation-policy update \
  --vault-name my-keyvault \
  --name my-key \
  --value '{"lifetimeActions":[{"trigger":{"timeAfterCreate":"P90D"},"action":{"type":"Rotate"}}],"attributes":{"expiryTime":"P2Y"}}'
```

**Python SDK**:
```python
from azure.identity import DefaultAzureCredential
from azure.keyvault.keys.crypto import CryptographyClient, EncryptionAlgorithm

credential = DefaultAzureCredential()
key_url = "https://my-keyvault.vault.azure.net/keys/my-key"

crypto_client = CryptographyClient(key_url, credential)

# Encrypt
plaintext = b"Secret data"
result = crypto_client.encrypt(EncryptionAlgorithm.rsa_oaep, plaintext)
ciphertext = result.ciphertext

# Decrypt
result = crypto_client.decrypt(EncryptionAlgorithm.rsa_oaep, ciphertext)
plaintext = result.plaintext
```

**Access Policies**:
```bash
# Grant permissions to application
az keyvault set-policy \
  --name my-keyvault \
  --object-id <app-object-id> \
  --key-permissions encrypt decrypt get list
```

### HashiCorp Vault

**What**: Open-source secrets management and encryption service.

**Features**:
- Encryption as a Service (Transit Secrets Engine)
- Dynamic secrets
- Secrets versioning
- Audit logging
- Self-hosted or managed (HCP Vault)

**Setup**:
```bash
# Start Vault dev server (testing only)
vault server -dev

# Set VAULT_ADDR
export VAULT_ADDR='http://127.0.0.1:8200'

# Enable Transit secrets engine
vault secrets enable transit

# Create encryption key
vault write -f transit/keys/my-key
```

**Encrypt/Decrypt**:
```bash
# Encrypt
vault write transit/encrypt/my-key plaintext=$(echo "Secret data" | base64)

# Output:
# Key           Value
# ciphertext    vault:v1:8SDd3WHDOjf7mq69CyCqYjBXAiQQAVZRkFM96F4qzA==

# Decrypt
vault write transit/decrypt/my-key ciphertext="vault:v1:8SDd3WHDOjf7mq69CyCqYjBXAiQQAVZRkFM96F4qzA=="

# Output (base64 encoded):
# plaintext     U2VjcmV0IGRhdGE=

# Decode
echo "U2VjcmV0IGRhdGE=" | base64 -d
# Output: Secret data
```

**Python SDK (hvac)**:
```python
import hvac
import base64

client = hvac.Client(url='http://127.0.0.1:8200', token='dev-token')

# Encrypt
plaintext = b'Secret data'
response = client.secrets.transit.encrypt_data(
    name='my-key',
    plaintext=base64.b64encode(plaintext).decode('utf-8')
)
ciphertext = response['data']['ciphertext']

# Decrypt
response = client.secrets.transit.decrypt_data(
    name='my-key',
    ciphertext=ciphertext
)
plaintext = base64.b64decode(response['data']['plaintext'])
```

**Key Rotation**:
```bash
# Rotate key (creates new version)
vault write -f transit/keys/my-key/rotate

# Re-encrypt data with new key version
vault write transit/rewrap/my-key ciphertext="vault:v1:8SDd3WHDOjf7mq69CyCqYjBXAiQQAVZRkFM96F4qzA=="
```

---

## Key Rotation

### Why Rotate Keys?

**Reasons**:
1. **Limit blast radius**: Compromise of old key doesn't affect new data
2. **Compliance**: Many standards require periodic rotation (e.g., PCI-DSS)
3. **Cryptographic hygiene**: Reduce ciphertext exposure
4. **Mitigate key exposure**: Employee departure, system compromise

**How Often?**:
- **Master keys**: Annually
- **Data encryption keys (DEKs)**: Monthly or per dataset
- **Secrets (passwords, API keys)**: 90 days
- **Emergency rotation**: Immediately on suspected compromise

### Rotation Strategies

#### Strategy 1: Re-encrypt All Data (Complete Rotation)

**How**:
1. Generate new key
2. Decrypt all data with old key
3. Encrypt all data with new key
4. Update key references
5. Delete old key (after grace period)

**Pros**: Clean, simple
**Cons**: Expensive (re-encrypt all data), downtime, risky

**Use Case**: Small datasets, infrequent rotation.

**Example**:
```python
def rotate_complete(old_key, new_key, data_files):
    for file_path in data_files:
        # Read encrypted data
        with open(file_path, 'rb') as f:
            ciphertext = f.read()

        # Decrypt with old key
        plaintext = decrypt(ciphertext, old_key)

        # Encrypt with new key
        new_ciphertext = encrypt(plaintext, new_key)

        # Write back
        with open(file_path, 'wb') as f:
            f.write(new_ciphertext)
```

#### Strategy 2: Envelope Encryption (Zero-Downtime Rotation)

**How** (using key hierarchy):
1. Generate new KEK (Key Encryption Key)
2. Re-encrypt all DEKs with new KEK
3. Data stays encrypted with DEKs (no change)
4. Update KEK references
5. Delete old KEK

**Pros**: Fast (only re-encrypt DEKs, not data), no downtime
**Cons**: Requires key hierarchy

**Use Case**: Large datasets, cloud KMS.

**Flow**:
```
Before Rotation:
┌────────────────┐
│  Old KEK       │
└───────┬────────┘
        │
        ▼
┌────────────────┐
│ Encrypted DEKs │ ← Re-encrypt these (small)
└───────┬────────┘
        │
        ▼
┌────────────────┐
│ Encrypted Data │ ← Leave unchanged (large)
└────────────────┘

After Rotation:
┌────────────────┐
│  New KEK       │
└───────┬────────┘
        │
        ▼
┌────────────────┐
│ Encrypted DEKs │ ← Re-encrypted with new KEK
└───────┬────────┘
        │
        ▼
┌────────────────┐
│ Encrypted Data │ ← Unchanged
└────────────────┘
```

**Example**:
```python
def rotate_envelope(old_kek, new_kek, encrypted_deks):
    """Rotate KEK without touching data"""
    for record in encrypted_deks:
        # Decrypt DEK with old KEK
        dek = decrypt(record['encrypted_dek'], old_kek)

        # Re-encrypt DEK with new KEK
        new_encrypted_dek = encrypt(dek, new_kek)

        # Update database
        update_dek(record['id'], new_encrypted_dek)

        # Data encrypted with DEK remains unchanged
```

#### Strategy 3: Versioned Keys (Multi-Version)

**How**:
1. Keep both old and new keys active
2. Write new data with new key
3. Read old data with old key
4. Gradually re-encrypt old data (background job)
5. Retire old key after all data migrated

**Pros**: Zero downtime, gradual migration
**Cons**: Complex (need to track key versions)

**Use Case**: Large datasets, continuous writes.

**Schema Example**:
```sql
CREATE TABLE encrypted_data (
    id SERIAL PRIMARY KEY,
    ciphertext BYTEA,
    key_version INTEGER,  -- Track which key encrypted this row
    created_at TIMESTAMP
);
```

**Application Logic**:
```python
def read_data(record_id):
    record = db.query("SELECT * FROM encrypted_data WHERE id = ?", record_id)

    # Use key version to select correct key
    if record['key_version'] == 1:
        key = old_key
    elif record['key_version'] == 2:
        key = new_key
    else:
        raise ValueError(f"Unknown key version: {record['key_version']}")

    plaintext = decrypt(record['ciphertext'], key)
    return plaintext

def write_data(plaintext):
    # Always use latest key for new data
    ciphertext = encrypt(plaintext, new_key)
    db.execute(
        "INSERT INTO encrypted_data (ciphertext, key_version) VALUES (?, ?)",
        ciphertext, 2
    )

def background_reencryption():
    """Gradually re-encrypt old data"""
    while True:
        # Get batch of old records
        old_records = db.query(
            "SELECT * FROM encrypted_data WHERE key_version = 1 LIMIT 100"
        )

        if not old_records:
            break  # All done

        for record in old_records:
            # Decrypt with old key
            plaintext = decrypt(record['ciphertext'], old_key)

            # Re-encrypt with new key
            new_ciphertext = encrypt(plaintext, new_key)

            # Update record
            db.execute(
                "UPDATE encrypted_data SET ciphertext = ?, key_version = ? WHERE id = ?",
                new_ciphertext, 2, record['id']
            )

        time.sleep(1)  # Rate limit
```

### AWS KMS Automatic Key Rotation

**How**:
- KMS automatically rotates key material annually
- Old key versions retained (for decryption)
- New encryptions use new key version
- Application code unchanged (KMS handles versioning)

**Enable**:
```bash
aws kms enable-key-rotation --key-id 12345678-1234-1234-1234-123456789012
```

**How It Works Internally**:
```
Key ARN: arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012

Key Versions:
- v1 (2024-01-01): Used for old ciphertext
- v2 (2025-01-01): Used for new encryptions (current)

Encryption: Uses v2
Decryption: KMS automatically detects version and uses correct key material
```

**No Application Changes Required**.

### Manual Key Rotation (Non-KMS)

**Procedure**:
1. Generate new key
2. Update application config to use new key for encryptions
3. Keep old key for decryptions
4. Re-encrypt data in background (optional)
5. Remove old key after grace period

**Configuration Example**:
```yaml
# keys.yaml
encryption_keys:
  - id: key-v1
    key: <base64-encoded-key>
    created_at: 2024-01-01
    deprecated: true  # Don't use for new encryptions
  - id: key-v2
    key: <base64-encoded-key>
    created_at: 2025-01-01
    deprecated: false  # Active key
```

**Application Logic**:
```python
def get_active_key():
    """Get current key for encryptions"""
    for key in config['encryption_keys']:
        if not key['deprecated']:
            return key
    raise ValueError("No active key found")

def get_key_by_id(key_id):
    """Get key by ID for decryptions"""
    for key in config['encryption_keys']:
        if key['id'] == key_id:
            return key
    raise ValueError(f"Key not found: {key_id}")

def encrypt_with_rotation(plaintext):
    """Encrypt with active key, store key ID"""
    key = get_active_key()
    ciphertext = encrypt(plaintext, key['key'])
    return {
        'key_id': key['id'],
        'ciphertext': ciphertext
    }

def decrypt_with_rotation(envelope):
    """Decrypt using key ID"""
    key = get_key_by_id(envelope['key_id'])
    plaintext = decrypt(envelope['ciphertext'], key['key'])
    return plaintext
```

---

## Compliance Standards

### FIPS 140-2 (Federal Information Processing Standards)

**What**: US government standard for cryptographic modules.

**Levels**:
- **Level 1**: Basic security (software only)
- **Level 2**: Tamper-evident seals, role-based authentication
- **Level 3**: Tamper-resistant hardware (HSM), identity-based authentication
- **Level 4**: Environmental failure protection (temperature, voltage)

**Approved Algorithms**:
- **Encryption**: AES, Triple-DES
- **Hashing**: SHA-256, SHA-384, SHA-512
- **Key Exchange**: Diffie-Hellman, ECDH
- **Signatures**: RSA, ECDSA

**NOT Approved**:
- ChaCha20-Poly1305 (not FIPS-approved, but secure)
- MD5, SHA-1 (deprecated)

**Cloud KMS FIPS Compliance**:
- AWS KMS: FIPS 140-2 Level 2 (standard), Level 3 (CloudHSM)
- Google Cloud KMS: FIPS 140-2 Level 3 (HSM)
- Azure Key Vault: FIPS 140-2 Level 2 (standard), Level 3 (Premium HSM)

### GDPR (General Data Protection Regulation)

**Requirement**: Article 32 - "Appropriate technical and organizational measures" including encryption.

**Key Points**:
- Encrypt personal data at rest and in transit
- Pseudonymization and encryption recommended
- No specific algorithms mandated (risk-based approach)
- Data breach notification required (encryption reduces risk)

**Best Practices**:
- Use AES-256-GCM or stronger
- Encrypt all personal data (names, emails, etc.)
- Key management separate from data storage
- Regular key rotation
- Audit logging

### HIPAA (Health Insurance Portability and Accountability Act)

**Requirement**: "Encryption and decryption" (addressable safeguard under Security Rule).

**Standards**:
- Use NIST-approved algorithms
- Encryption at rest for ePHI (electronic Protected Health Information)
- Encryption in transit (TLS 1.2+)
- Access controls for encryption keys
- Audit logging

**Recommended**:
- AES-256
- Key management via KMS or HSM
- Separate keys for different data types
- Annual key rotation

### PCI-DSS (Payment Card Industry Data Security Standard)

**Requirement**: 3.4 - "Render PAN [Primary Account Number] unreadable anywhere it is stored."

**Methods**:
1. **Encryption**: AES-128 or stronger
2. **Truncation**: Store only last 4 digits
3. **Tokenization**: Replace PAN with token
4. **Hashing**: One-way hash (with salt)

**Key Management Requirements** (3.5-3.6):
- Split knowledge of keys (no single person has full key)
- Dual control (two people required for key operations)
- Key rotation at least annually
- Secure key storage (HSM or KMS)
- Audit logging of key access

**Example (Tokenization)**:
```python
import secrets
import hashlib

# Store token → encrypted PAN mapping in secure vault
token_vault = {}

def tokenize_pan(pan):
    """Replace PAN with random token"""
    token = secrets.token_urlsafe(16)

    # Encrypt PAN before storing
    encrypted_pan = encrypt(pan, kms_key)
    token_vault[token] = encrypted_pan

    return token

def detokenize(token):
    """Retrieve PAN from token"""
    encrypted_pan = token_vault.get(token)
    if not encrypted_pan:
        raise ValueError("Invalid token")

    pan = decrypt(encrypted_pan, kms_key)
    return pan

# Usage
pan = "4111111111111111"
token = tokenize_pan(pan)  # "xYz123..."
print(f"Store token: {token}")

# Retrieve
original_pan = detokenize(token)
assert original_pan == pan
```

### SOC 2 (System and Organization Controls 2)

**Requirement**: Trust Services Criteria (TSC) - Confidentiality.

**Key Points**:
- Encrypt sensitive data at rest
- Encrypt data in transit
- Key management procedures documented
- Access controls for keys
- Audit logging and monitoring

**Compliance Checklist**:
- [ ] Encryption policy documented
- [ ] Encryption algorithms approved (AES-256, etc.)
- [ ] Key management procedures (generation, storage, rotation, destruction)
- [ ] Access controls (who can access keys)
- [ ] Audit logging enabled
- [ ] Incident response plan (key compromise)
- [ ] Annual review of encryption practices

---

## Performance Considerations

### Encryption Overhead

**Typical Performance Impact**:

| Layer | Overhead | Mitigation |
|-------|----------|------------|
| Application-level | 10-30% | Hardware acceleration, caching |
| Database TDE | 3-10% | Hardware acceleration, async encryption |
| Filesystem | 5-15% | Hardware acceleration, kernel optimization |
| Volume/disk | 2-5% | Hardware acceleration (AES-NI), SSD |
| Hardware (SED) | <1% | Built-in encryption |

### Hardware Acceleration

#### AES-NI (Intel/AMD)

**What**: CPU instruction set for AES encryption.

**Check Support** (Linux):
```bash
grep -o aes /proc/cpuinfo
```

**Performance**:
- **Without AES-NI**: ~100-500 MB/s
- **With AES-NI**: 2-10 GB/s (10-20x faster)

**Enable in Code** (automatic with modern libraries):
```python
# cryptography library automatically uses AES-NI if available
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
# No configuration needed - hardware acceleration automatic
```

**OpenSSL** (check):
```bash
openssl speed aes-256-gcm
```

#### ARM Crypto Extensions

**What**: ARMv8 Crypto Extensions (similar to AES-NI).

**Check Support**:
```bash
grep -o aes /proc/cpuinfo
```

**Performance**: Similar to x86 AES-NI (2-5 GB/s).

### Caching Decrypted Data

**Problem**: Decrypting on every read is expensive.

**Solution**: Cache decrypted data in memory (with TTL).

**Example**:
```python
from functools import lru_cache
import time

# Cache decrypted data (max 1000 items)
@lru_cache(maxsize=1000)
def decrypt_cached(ciphertext, key):
    return decrypt(ciphertext, key)

# Time-based cache (TTL = 5 minutes)
cache = {}

def decrypt_with_ttl(ciphertext, key, ttl=300):
    cache_key = hashlib.sha256(ciphertext).hexdigest()

    if cache_key in cache:
        plaintext, timestamp = cache[cache_key]
        if time.time() - timestamp < ttl:
            return plaintext  # Cache hit

    # Cache miss - decrypt
    plaintext = decrypt(ciphertext, key)
    cache[cache_key] = (plaintext, time.time())
    return plaintext
```

**Trade-offs**:
- **Pros**: Faster reads
- **Cons**: Memory usage, cache invalidation complexity, data in RAM (unencrypted)

### Encryption Parallelization

**GCM Mode**: Supports parallel encryption/decryption (unlike CBC).

**Example (Multi-threaded Encryption)**:
```python
from concurrent.futures import ThreadPoolExecutor
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os

def encrypt_chunk(chunk, key):
    aesgcm = AESGCM(key)
    nonce = os.urandom(12)
    ciphertext = aesgcm.encrypt(nonce, chunk, None)
    return (nonce, ciphertext)

def encrypt_large_file_parallel(file_path, key, chunk_size=1024*1024):
    """Encrypt large file in parallel chunks"""
    with open(file_path, 'rb') as f:
        chunks = []
        while True:
            chunk = f.read(chunk_size)
            if not chunk:
                break
            chunks.append(chunk)

    # Encrypt chunks in parallel
    with ThreadPoolExecutor(max_workers=8) as executor:
        encrypted_chunks = list(executor.map(
            lambda chunk: encrypt_chunk(chunk, key),
            chunks
        ))

    return encrypted_chunks
```

### Benchmarking Encryption Performance

**OpenSSL Benchmark**:
```bash
# Benchmark AES-256-GCM
openssl speed -evp aes-256-gcm

# Benchmark multiple algorithms
openssl speed aes-256-gcm chacha20-poly1305 aes-128-cbc
```

**Python Benchmark**:
```python
import time
from cryptography.hazmat.primitives.ciphers.aead import AESGCM, ChaCha20Poly1305
import os

def benchmark_encryption(algorithm, plaintext, iterations=10000):
    if algorithm == 'aes-gcm':
        key = AESGCM.generate_key(bit_length=256)
        cipher = AESGCM(key)
    elif algorithm == 'chacha20':
        key = ChaCha20Poly1305.generate_key()
        cipher = ChaCha20Poly1305(key)

    nonce = os.urandom(12)

    start = time.time()
    for _ in range(iterations):
        ciphertext = cipher.encrypt(nonce, plaintext, None)
    elapsed = time.time() - start

    throughput = (len(plaintext) * iterations) / elapsed / 1024 / 1024
    print(f"{algorithm}: {throughput:.2f} MB/s")

# Test
plaintext = b"A" * 1024  # 1 KB
benchmark_encryption('aes-gcm', plaintext)
benchmark_encryption('chacha20', plaintext)
```

---

## Common Patterns

### Pattern 1: Column-Level Encryption (Application-Level)

**Use Case**: Encrypt specific sensitive columns (e.g., SSN, credit card).

**Schema**:
```sql
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    email VARCHAR(255),
    encrypted_ssn BYTEA,  -- Encrypted SSN
    encrypted_dek BYTEA,  -- DEK encrypted by KMS
    dek_nonce BYTEA
);
```

**Application**:
```python
def insert_user(email, ssn, kms_key):
    # Generate DEK
    dek = AESGCM.generate_key(bit_length=256)
    dek_nonce = os.urandom(12)

    # Encrypt DEK with KMS
    encrypted_dek = kms.encrypt(KeyId=kms_key, Plaintext=dek)['CiphertextBlob']

    # Encrypt SSN with DEK
    aesgcm = AESGCM(dek)
    ssn_nonce = os.urandom(12)
    encrypted_ssn = aesgcm.encrypt(ssn_nonce, ssn.encode(), None)

    # Store
    db.execute(
        "INSERT INTO users (email, encrypted_ssn, encrypted_dek, dek_nonce) VALUES (?, ?, ?, ?)",
        email, encrypted_ssn, encrypted_dek, dek_nonce
    )

def get_user_ssn(user_id, kms_key):
    row = db.query("SELECT * FROM users WHERE id = ?", user_id)

    # Decrypt DEK with KMS
    dek = kms.decrypt(CiphertextBlob=row['encrypted_dek'])['Plaintext']

    # Decrypt SSN with DEK
    aesgcm = AESGCM(dek)
    ssn = aesgcm.decrypt(row['dek_nonce'], row['encrypted_ssn'], None)
    return ssn.decode()
```

### Pattern 2: File Encryption with Metadata

**Use Case**: Encrypt files, store metadata alongside.

**File Structure**:
```
encrypted_file.bin:
  - Header (256 bytes):
    - Version (4 bytes)
    - Algorithm (16 bytes, e.g., "aes-256-gcm")
    - Key ID (32 bytes)
    - Nonce (12 bytes)
    - Encrypted DEK (varies)
  - Ciphertext (remaining bytes)
```

**Implementation**:
```python
import struct
import json

def encrypt_file_with_metadata(input_path, output_path, kms_key_id):
    # Read plaintext
    with open(input_path, 'rb') as f:
        plaintext = f.read()

    # Generate DEK
    dek = AESGCM.generate_key(bit_length=256)
    nonce = os.urandom(12)

    # Encrypt DEK with KMS
    kms_response = kms.encrypt(KeyId=kms_key_id, Plaintext=dek)
    encrypted_dek = kms_response['CiphertextBlob']

    # Encrypt file with DEK
    aesgcm = AESGCM(dek)
    ciphertext = aesgcm.encrypt(nonce, plaintext, None)

    # Build header
    header = {
        'version': 1,
        'algorithm': 'aes-256-gcm',
        'key_id': kms_key_id,
        'nonce': nonce.hex(),
        'encrypted_dek': encrypted_dek.hex(),
    }
    header_json = json.dumps(header).encode('utf-8')
    header_len = len(header_json)

    # Write encrypted file
    with open(output_path, 'wb') as f:
        f.write(struct.pack('<I', header_len))  # Header length (4 bytes)
        f.write(header_json)
        f.write(ciphertext)

def decrypt_file_with_metadata(input_path, output_path):
    with open(input_path, 'rb') as f:
        # Read header length
        header_len = struct.unpack('<I', f.read(4))[0]

        # Read header
        header_json = f.read(header_len)
        header = json.loads(header_json)

        # Read ciphertext
        ciphertext = f.read()

    # Decrypt DEK with KMS
    dek = kms.decrypt(
        CiphertextBlob=bytes.fromhex(header['encrypted_dek'])
    )['Plaintext']

    # Decrypt file
    aesgcm = AESGCM(dek)
    plaintext = aesgcm.decrypt(
        bytes.fromhex(header['nonce']),
        ciphertext,
        None
    )

    # Write plaintext
    with open(output_path, 'wb') as f:
        f.write(plaintext)
```

### Pattern 3: Searchable Encryption (Deterministic)

**Problem**: Can't search encrypted data (ciphertext is random).

**Solution**: Deterministic encryption (same plaintext → same ciphertext).

**⚠️ Security Trade-off**: Reveals equality (can see if two values are same).

**Use Case**: Encrypt email addresses, but allow searching by email.

**Algorithm**: AES-SIV (Synthetic IV) - deterministic AEAD.

**Python Example**:
```python
from cryptography.hazmat.primitives.ciphers.aead import AESSIV

# Generate key (2x key size for SIV)
key = AESSIV.generate_key(bit_length=512)  # AES-256-SIV needs 512-bit key
aessiv = AESSIV(key)

# Encrypt (deterministic)
email1 = b"user@example.com"
ciphertext1 = aessiv.encrypt(email1, None)

email2 = b"user@example.com"
ciphertext2 = aessiv.encrypt(email2, None)

# Same plaintext → same ciphertext
assert ciphertext1 == ciphertext2  # Allows searching

# Store
db.execute(
    "INSERT INTO users (email_encrypted) VALUES (?)",
    ciphertext1
)

# Search
search_email = b"user@example.com"
search_ciphertext = aessiv.encrypt(search_email, None)
results = db.query(
    "SELECT * FROM users WHERE email_encrypted = ?",
    search_ciphertext
)
```

**⚠️ Warning**: Don't use deterministic encryption for high-entropy data (e.g., SSNs). Use only for low-sensitivity, searchable fields.

### Pattern 4: Key Per Tenant (Multi-Tenancy)

**Use Case**: SaaS application with multiple customers.

**Goal**: Each tenant has separate encryption key (data isolation).

**Schema**:
```sql
CREATE TABLE tenants (
    id SERIAL PRIMARY KEY,
    name VARCHAR(255),
    kms_key_id VARCHAR(255)  -- Separate KMS key per tenant
);

CREATE TABLE tenant_data (
    id SERIAL PRIMARY KEY,
    tenant_id INTEGER REFERENCES tenants(id),
    encrypted_data BYTEA,
    nonce BYTEA
);
```

**Application**:
```python
def insert_tenant_data(tenant_id, plaintext):
    # Get tenant's KMS key
    tenant = db.query("SELECT kms_key_id FROM tenants WHERE id = ?", tenant_id)
    kms_key_id = tenant['kms_key_id']

    # Encrypt with tenant's key
    envelope = encrypt_with_kms(plaintext, kms_key_id)

    db.execute(
        "INSERT INTO tenant_data (tenant_id, encrypted_data, nonce) VALUES (?, ?, ?)",
        tenant_id, envelope['ciphertext'], envelope['nonce']
    )

def get_tenant_data(tenant_id, record_id):
    # Get tenant's KMS key
    tenant = db.query("SELECT kms_key_id FROM tenants WHERE id = ?", tenant_id)
    kms_key_id = tenant['kms_key_id']

    # Get encrypted data
    record = db.query(
        "SELECT * FROM tenant_data WHERE id = ? AND tenant_id = ?",
        record_id, tenant_id
    )

    # Decrypt with tenant's key
    plaintext = decrypt_with_kms(record, kms_key_id)
    return plaintext
```

**Benefits**:
- Data isolation (key per tenant)
- Tenant-specific key rotation
- Compliance (some customers require dedicated keys)
- Key revocation (disable tenant's key)

---

## Anti-Patterns

### ❌ Hardcoded Keys

**Bad**:
```python
# NEVER DO THIS
AES_KEY = b'my-secret-key-12345678901234567890'

def encrypt(plaintext):
    aesgcm = AESGCM(AES_KEY)
    nonce = os.urandom(12)
    return aesgcm.encrypt(nonce, plaintext, None)
```

**Why**: Keys in source code → Git history → compromised.

**Fix**: Use environment variables (dev) or KMS (production).

```python
import os

# Load from environment variable
AES_KEY = os.environ['AES_KEY'].encode()

# Or load from KMS
kms_response = kms.decrypt(CiphertextBlob=encrypted_key)
AES_KEY = kms_response['Plaintext']
```

### ❌ Weak Algorithms

**Bad**:
```python
from Crypto.Cipher import DES  # NEVER USE DES

key = b'12345678'  # 56-bit key (too weak)
cipher = DES.new(key, DES.MODE_ECB)
ciphertext = cipher.encrypt(plaintext)
```

**Why**: DES is broken (can be cracked in hours).

**Fix**: Use AES-256-GCM.

```python
from cryptography.hazmat.primitives.ciphers.aead import AESGCM

key = AESGCM.generate_key(bit_length=256)
aesgcm = AESGCM(key)
nonce = os.urandom(12)
ciphertext = aesgcm.encrypt(nonce, plaintext, None)
```

### ❌ ECB Mode

**Bad**:
```python
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes

cipher = Cipher(algorithms.AES(key), modes.ECB())
encryptor = cipher.encryptor()
ciphertext = encryptor.update(plaintext) + encryptor.finalize()
```

**Why**: ECB reveals patterns (same plaintext → same ciphertext block).

**Famous Example**: [ECB Penguin](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_Codebook_(ECB))

**Fix**: Use GCM mode.

### ❌ Reusing Nonces

**Bad**:
```python
nonce = b'12345678'  # Fixed nonce
aesgcm = AESGCM(key)

ciphertext1 = aesgcm.encrypt(nonce, plaintext1, None)  # Same nonce
ciphertext2 = aesgcm.encrypt(nonce, plaintext2, None)  # Same nonce (BAD!)
```

**Why**: Nonce reuse breaks GCM security (can recover key).

**Fix**: Generate new nonce for each encryption.

```python
nonce1 = os.urandom(12)
ciphertext1 = aesgcm.encrypt(nonce1, plaintext1, None)

nonce2 = os.urandom(12)
ciphertext2 = aesgcm.encrypt(nonce2, plaintext2, None)
```

### ❌ No Authentication (Encryption Without MAC)

**Bad**:
```python
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes

cipher = Cipher(algorithms.AES(key), modes.CTR(nonce))
encryptor = cipher.encryptor()
ciphertext = encryptor.update(plaintext) + encryptor.finalize()
# No authentication - attacker can modify ciphertext
```

**Why**: Unauthenticated encryption is vulnerable to tampering (e.g., bit flipping).

**Fix**: Use authenticated encryption (AEAD) like GCM.

```python
aesgcm = AESGCM(key)
ciphertext = aesgcm.encrypt(nonce, plaintext, None)  # Includes auth tag
```

### ❌ Storing Keys with Data

**Bad**:
```python
# Store key in same database as encrypted data
db.execute(
    "INSERT INTO users (name, encrypted_ssn, encryption_key) VALUES (?, ?, ?)",
    name, encrypted_ssn, key
)
```

**Why**: If attacker gets database, they get both data and keys.

**Fix**: Store keys separately (KMS, HSM, separate database).

### ❌ No Key Rotation

**Bad**:
```python
# Use same key forever
KEY = load_key_from_config()  # Never changes
```

**Why**: Long-lived keys increase blast radius of compromise.

**Fix**: Implement key rotation strategy (see Key Rotation section).

---

## Security Best Practices

### 1. Use Authenticated Encryption (AEAD)

**Always use**:
- AES-GCM
- ChaCha20-Poly1305
- AES-SIV (deterministic)

**Never use**:
- AES-CBC without HMAC
- AES-CTR without HMAC
- AES-ECB

### 2. Generate Keys Properly

**✅ Use CSRNGs**:
```python
import os
import secrets

key = os.urandom(32)  # Good
key = secrets.token_bytes(32)  # Good
```

**❌ Never use**:
```python
import random

key = random.randbytes(32)  # BAD (predictable)
key = hashlib.sha256(b"password").digest()  # BAD (no KDF)
```

### 3. Never Hardcode Keys

**✅ Environment variables (dev)**:
```python
key = os.environ['ENCRYPTION_KEY']
```

**✅ KMS (production)**:
```python
response = kms.decrypt(CiphertextBlob=encrypted_key)
key = response['Plaintext']
```

### 4. Use Key Hierarchies

**Pattern**:
```
Root Key (KMS/HSM)
  └─> KEK (Key Encryption Key)
       └─> DEK (Data Encryption Key)
            └─> Data
```

### 5. Rotate Keys Regularly

**Schedule**:
- Master keys: Annually
- DEKs: Monthly or per dataset
- Secrets: 90 days

### 6. Audit Key Access

**Enable logging**:
- AWS CloudTrail (KMS key usage)
- Google Cloud Audit Logs
- Azure Monitor

**Monitor for**:
- Unusual key access patterns
- Failed decryption attempts
- Key deletion attempts

### 7. Separate Keys by Environment

**Good**:
```
keys/
├── dev/encryption-key
├── staging/encryption-key
└── prod/encryption-key
```

**Bad**:
```
# Same key for all environments
ENCRYPTION_KEY=abc123...
```

### 8. Encrypt Backups

**Always encrypt**:
- Database backups
- File backups
- Volume snapshots

**Example**:
```bash
# Encrypt backup with GPG
tar czf - /var/lib/postgresql | gpg --encrypt --recipient admin@example.com > backup.tar.gz.gpg

# Decrypt
gpg --decrypt backup.tar.gz.gpg | tar xzf -
```

### 9. Use Encryption Context (AAD)

**What**: Additional Authenticated Data - authenticated but not encrypted.

**Use Case**: Bind ciphertext to specific context (user ID, resource ID).

**AWS KMS Example**:
```python
response = kms.encrypt(
    KeyId='alias/my-key',
    Plaintext=plaintext,
    EncryptionContext={
        'UserId': '12345',
        'ResourceId': 'file-abc'
    }
)

# Decryption requires same context
plaintext = kms.decrypt(
    CiphertextBlob=ciphertext,
    EncryptionContext={
        'UserId': '12345',
        'ResourceId': 'file-abc'
    }
)
```

**Benefit**: Prevents ciphertext from being decrypted in wrong context.

### 10. Secure Key Deletion

**Process**:
1. Disable key (prevent new encryptions)
2. Grace period (30 days)
3. Delete key
4. Audit logging

**AWS KMS**:
```bash
# Schedule key deletion (7-30 day waiting period)
aws kms schedule-key-deletion --key-id 12345678-1234-1234-1234-123456789012 --pending-window-in-days 30

# Cancel deletion (if needed)
aws kms cancel-key-deletion --key-id 12345678-1234-1234-1234-123456789012
```

---

## Implementation Examples

### Example 1: Encrypting Files with AWS KMS

```python
import boto3
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os
import json

kms = boto3.client('kms')
KMS_KEY_ID = 'arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012'

def encrypt_file(input_path, output_path):
    """Encrypt file using envelope encryption"""
    # Read file
    with open(input_path, 'rb') as f:
        plaintext = f.read()

    # Generate DEK
    response = kms.generate_data_key(KeyId=KMS_KEY_ID, KeySpec='AES_256')
    dek_plaintext = response['Plaintext']
    dek_encrypted = response['CiphertextBlob']

    # Encrypt file with DEK
    aesgcm = AESGCM(dek_plaintext)
    nonce = os.urandom(12)
    ciphertext = aesgcm.encrypt(nonce, plaintext, None)

    # Save encrypted file + metadata
    envelope = {
        'encrypted_dek': dek_encrypted.hex(),
        'nonce': nonce.hex(),
        'ciphertext': ciphertext.hex(),
    }

    with open(output_path, 'w') as f:
        json.dump(envelope, f)

def decrypt_file(input_path, output_path):
    """Decrypt file"""
    # Load envelope
    with open(input_path, 'r') as f:
        envelope = json.load(f)

    # Decrypt DEK
    dek_plaintext = kms.decrypt(
        CiphertextBlob=bytes.fromhex(envelope['encrypted_dek'])
    )['Plaintext']

    # Decrypt file
    aesgcm = AESGCM(dek_plaintext)
    plaintext = aesgcm.decrypt(
        bytes.fromhex(envelope['nonce']),
        bytes.fromhex(envelope['ciphertext']),
        None
    )

    # Save decrypted file
    with open(output_path, 'wb') as f:
        f.write(plaintext)

# Usage
encrypt_file('secret.txt', 'secret.txt.encrypted')
decrypt_file('secret.txt.encrypted', 'secret.txt.decrypted')
```

### Example 2: Database Column Encryption

```python
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os
import base64

class EncryptedField:
    """SQLAlchemy custom type for encrypted fields"""

    def __init__(self, key):
        self.key = key
        self.aesgcm = AESGCM(key)

    def encrypt(self, plaintext):
        """Encrypt plaintext"""
        if plaintext is None:
            return None

        nonce = os.urandom(12)
        ciphertext = self.aesgcm.encrypt(nonce, plaintext.encode(), None)

        # Prepend nonce to ciphertext
        encrypted_data = nonce + ciphertext
        return base64.b64encode(encrypted_data).decode('utf-8')

    def decrypt(self, encrypted_data):
        """Decrypt ciphertext"""
        if encrypted_data is None:
            return None

        encrypted_bytes = base64.b64decode(encrypted_data)

        # Extract nonce and ciphertext
        nonce = encrypted_bytes[:12]
        ciphertext = encrypted_bytes[12:]

        plaintext = self.aesgcm.decrypt(nonce, ciphertext, None)
        return plaintext.decode('utf-8')

# Usage with SQLAlchemy
from sqlalchemy import Column, Integer, String, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

Base = declarative_base()

# Load encryption key
ENCRYPTION_KEY = os.urandom(32)  # In production, load from KMS
encrypted_field = EncryptedField(ENCRYPTION_KEY)

class User(Base):
    __tablename__ = 'users'

    id = Column(Integer, primary_key=True)
    email = Column(String)
    _encrypted_ssn = Column('encrypted_ssn', String)

    @property
    def ssn(self):
        return encrypted_field.decrypt(self._encrypted_ssn)

    @ssn.setter
    def ssn(self, value):
        self._encrypted_ssn = encrypted_field.encrypt(value)

# Create database
engine = create_engine('sqlite:///encrypted.db')
Base.metadata.create_all(engine)

Session = sessionmaker(bind=engine)
session = Session()

# Insert encrypted data
user = User(email='user@example.com', ssn='123-45-6789')
session.add(user)
session.commit()

# Retrieve and decrypt
user = session.query(User).first()
print(f"Email: {user.email}")
print(f"SSN: {user.ssn}")  # Automatically decrypted
```

### Example 3: Zero-Downtime Key Rotation

```python
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os
import time

class KeyRotator:
    def __init__(self, old_key, new_key):
        self.old_key = old_key
        self.new_key = new_key
        self.old_aesgcm = AESGCM(old_key)
        self.new_aesgcm = AESGCM(new_key)

    def re_encrypt_deks(self, db):
        """Re-encrypt all DEKs with new key"""
        records = db.query("SELECT id, encrypted_dek, dek_nonce FROM encrypted_data")

        for record in records:
            # Decrypt DEK with old key
            dek = self.old_aesgcm.decrypt(
                record['dek_nonce'],
                record['encrypted_dek'],
                None
            )

            # Re-encrypt DEK with new key
            new_nonce = os.urandom(12)
            new_encrypted_dek = self.new_aesgcm.encrypt(new_nonce, dek, None)

            # Update database
            db.execute(
                "UPDATE encrypted_data SET encrypted_dek = ?, dek_nonce = ? WHERE id = ?",
                new_encrypted_dek, new_nonce, record['id']
            )

            print(f"Re-encrypted DEK for record {record['id']}")

        print("Key rotation complete!")

# Usage
old_key = os.urandom(32)  # Current key
new_key = os.urandom(32)  # New key

rotator = KeyRotator(old_key, new_key)
rotator.re_encrypt_deks(db)
```

---

## References

### Standards and Specifications

1. **NIST SP 800-175B**: Guideline for Using Cryptographic Standards
   - https://csrc.nist.gov/publications/detail/sp/800-175b/rev-1/final

2. **NIST SP 800-57**: Recommendation for Key Management
   - https://csrc.nist.gov/publications/detail/sp/800-57-part-1/rev-5/final

3. **FIPS 140-2**: Security Requirements for Cryptographic Modules
   - https://csrc.nist.gov/publications/detail/fips/140/2/final

4. **RFC 5116**: An Interface and Algorithms for Authenticated Encryption
   - https://tools.ietf.org/html/rfc5116

5. **RFC 8439**: ChaCha20 and Poly1305 for IETF Protocols
   - https://tools.ietf.org/html/rfc8439

6. **RFC 8018**: PKCS #5: Password-Based Cryptography Specification (PBKDF2)
   - https://tools.ietf.org/html/rfc8018

7. **RFC 9106**: Argon2 Memory-Hard Function
   - https://tools.ietf.org/html/rfc9106

### Compliance Resources

1. **GDPR Article 32**: Security of processing
   - https://gdpr-info.eu/art-32-gdpr/

2. **HIPAA Security Rule**: Encryption and Decryption (§ 164.312(a)(2)(iv))
   - https://www.hhs.gov/hipaa/for-professionals/security/index.html

3. **PCI-DSS Requirement 3**: Protect stored cardholder data
   - https://www.pcisecuritystandards.org/

4. **SOC 2 Trust Services Criteria**: Confidentiality
   - https://www.aicpa.org/interestareas/frc/assuranceadvisoryservices/sorhome.html

### Tools and Libraries

1. **cryptography** (Python): https://cryptography.io/
2. **OpenSSL**: https://www.openssl.org/
3. **AWS KMS**: https://aws.amazon.com/kms/
4. **Google Cloud KMS**: https://cloud.google.com/kms
5. **Azure Key Vault**: https://azure.microsoft.com/en-us/services/key-vault/
6. **HashiCorp Vault**: https://www.vaultproject.io/
7. **SQLCipher**: https://www.zetetic.net/sqlcipher/

### Further Reading

1. **Cryptographic Right Answers**: https://latacora.micro.blog/2018/04/03/cryptographic-right-answers.html
2. **OWASP Cryptographic Storage Cheat Sheet**: https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html
3. **Encryption in Use**: https://en.wikipedia.org/wiki/Encryption#Encryption_in_use
4. **Key Management Best Practices**: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-57pt1r5.pdf

---

**Last Updated**: 2025-10-27
**Version**: 1.0

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.