Skip to main content

Encryption at Rest

Encryption at Rest

Field-level and full-disk encryption for data at rest.

Overview

All data at rest is encrypted using AES-256-GCM:

  • Algorithm: AES-256-GCM (FIPS 140-2 validated)
  • Key Management: HashiCorp Vault with HSM
  • Key Rotation: Automatic every 90 days
  • Compliance: FedRAMP, NIST 800-53, HIPAA, GDPR

Encryption Layers

graph TB A[Application Data] --> B[Field-Level Encryption] B --> C[Database Encryption] C --> D[Full-Disk Encryption] D --> E[Physical Storage]

Field-Level Encryption

Encrypted Fields

PII/PHI Fields:

  • Email addresses
  • Phone numbers
  • Social Security Numbers
  • Medical record numbers
  • Financial data

Sensitive Data:

  • API keys
  • OAuth tokens
  • Encryption keys
  • Passwords (hashed + encrypted)

Implementation

EncryptionService.ts:

import * as crypto from 'crypto'; interface EncryptedField { ciphertext: string; // Base64 encoded iv: string; // Initialization vector salt: string; // Key derivation salt tag: string; // Authentication tag keyId: string; // Key version algorithm: 'aes-256-gcm'; encryptedAt: Date; } class FieldEncryptionService { private algorithm = 'aes-256-gcm'; private keyDerivation = 'pbkdf2'; private iterations = 100000; private saltLength = 32; private ivLength = 12; private tagLength = 16; async encryptField( fieldName: string, value: string, metadata?: Record<string, any> ): Promise<EncryptedField> { // Get current encryption key from Vault const key = await this.vault.getCurrentKey(); // Generate random IV and salt const iv = crypto.randomBytes(this.ivLength); const salt = crypto.randomBytes(this.saltLength); // Derive key using PBKDF2 const derivedKey = crypto.pbkdf2Sync( key.material, salt, this.iterations, 32, // 256 bits 'sha256' ); // Encrypt with AES-256-GCM const cipher = crypto.createCipheriv( this.algorithm, derivedKey, iv ); const encrypted = Buffer.concat([ cipher.update(value, 'utf8'), cipher.final() ]); const tag = cipher.getAuthTag(); // Audit log await this.auditLog.log({ event: 'field_encrypted', fieldName, keyId: key.id, timestamp: new Date() }); return { ciphertext: encrypted.toString('base64'), iv: iv.toString('base64'), salt: salt.toString('base64'), tag: tag.toString('base64'), keyId: key.id, algorithm: this.algorithm, encryptedAt: new Date() }; } async decryptField( encryptedField: EncryptedField ): Promise<string> { // Get encryption key by ID (supports key rotation) const key = await this.vault.getKey(encryptedField.keyId); // Decode from Base64 const ciphertext = Buffer.from(encryptedField.ciphertext, 'base64'); const iv = Buffer.from(encryptedField.iv, 'base64'); const salt = Buffer.from(encryptedField.salt, 'base64'); const tag = Buffer.from(encryptedField.tag, 'base64'); // Derive key const derivedKey = crypto.pbkdf2Sync( key.material, salt, this.iterations, 32, 'sha256' ); // Decrypt with AES-256-GCM const decipher = crypto.createDecipheriv( this.algorithm, derivedKey, iv ); decipher.setAuthTag(tag); const decrypted = Buffer.concat([ decipher.update(ciphertext), decipher.final() ]); // Audit log await this.auditLog.log({ event: 'field_decrypted', keyId: encryptedField.keyId, timestamp: new Date() }); return decrypted.toString('utf8'); } async reencryptField( encryptedField: EncryptedField ): Promise<EncryptedField> { // Decrypt with old key const plaintext = await this.decryptField(encryptedField); // Encrypt with current key return await this.encryptField( 'reencrypted', plaintext ); } }

Usage Example

const encryption = new FieldEncryptionService(); // Encrypt user email const encryptedEmail = await encryption.encryptField( 'email', 'user@example.com' ); // Store in database await db.query( 'INSERT INTO users (id, email_encrypted) VALUES ($1, $2)', [userId, JSON.stringify(encryptedEmail)] ); // Decrypt when needed const encrypted = JSON.parse(user.email_encrypted); const email = await encryption.decryptField(encrypted);

Database Encryption

PostgreSQL Transparent Data Encryption (TDE)

Configuration:

-- Enable pgcrypto extension CREATE EXTENSION IF NOT EXISTS pgcrypto; -- Encrypted column CREATE TABLE users ( id UUID PRIMARY KEY, email BYTEA, -- Encrypted email_iv BYTEA, email_tag BYTEA, created_at TIMESTAMP ); -- Insert encrypted data INSERT INTO users (id, email, email_iv, email_tag) VALUES ( gen_random_uuid(), pgp_sym_encrypt('user@example.com', 'encryption-key'), gen_random_bytes(12), gen_random_bytes(16) ); -- Query encrypted data SELECT id, pgp_sym_decrypt(email, 'encryption-key') AS email FROM users;

TimescaleDB Encryption

Hypertable Encryption:

-- Create encrypted hypertable CREATE TABLE traces ( trace_id TEXT, timestamp TIMESTAMPTZ, data_encrypted BYTEA, PRIMARY KEY (timestamp, trace_id) ); SELECT create_hypertable('traces', 'timestamp'); -- Encrypted data retention policy SELECT add_retention_policy('traces', INTERVAL '90 days', if_not_exists => TRUE );

Redis Encryption

Redis Configuration:

# redis.conf requirepass your-strong-password # Enable TLS tls-port 6380 tls-cert-file /etc/redis/certs/redis.crt tls-key-file /etc/redis/certs/redis.key tls-ca-cert-file /etc/redis/certs/ca.crt # Encrypt RDB snapshots rdbcompression yes

Application-Level Encryption:

import Redis from 'ioredis'; const redis = new Redis({ port: 6380, host: 'redis.local.bluefly.io', password: process.env.REDIS_PASSWORD, tls: { ca: fs.readFileSync('/etc/redis/certs/ca.crt'), cert: fs.readFileSync('/etc/redis/certs/client.crt'), key: fs.readFileSync('/etc/redis/certs/client.key') } }); // Encrypt before storing const encryptedValue = await encryption.encryptField('cache', value); await redis.set(key, JSON.stringify(encryptedValue)); // Decrypt after retrieval const encrypted = JSON.parse(await redis.get(key)); const decrypted = await encryption.decryptField(encrypted);

Full-Disk Encryption

Linux (LUKS)

Setup:

# Create encrypted volume cryptsetup luksFormat /dev/sdb # Open encrypted volume cryptsetup luksOpen /dev/sdb encrypted_volume # Create filesystem mkfs.ext4 /dev/mapper/encrypted_volume # Mount mount /dev/mapper/encrypted_volume /mnt/encrypted

Docker Volumes

Encrypted Volume:

volumes: encrypted_data: driver: local driver_opts: type: "nfs" o: "addr=nfs.local.bluefly.io,rw,encryption=aes256" device: ":/encrypted/data"

Key Management

HashiCorp Vault Integration

Vault Configuration:

# Enable transit secrets engine vault secrets enable transit # Create encryption key vault write -f transit/keys/bluefly-platform # Configure key rotation vault write transit/keys/bluefly-platform/config \ min_decryption_version=1 \ min_encryption_version=0 \ deletion_allowed=false \ auto_rotate_period=90d

Application Integration:

import Vault from 'node-vault'; class VaultKeyManager { private vault: Vault.client; private mountPoint = 'transit'; private keyName = 'bluefly-platform'; async encrypt(plaintext: string): Promise<string> { const response = await this.vault.write( `${this.mountPoint}/encrypt/${this.keyName}`, { plaintext: Buffer.from(plaintext).toString('base64') } ); return response.data.ciphertext; } async decrypt(ciphertext: string): Promise<string> { const response = await this.vault.write( `${this.mountPoint}/decrypt/${this.keyName}`, { ciphertext } ); return Buffer.from(response.data.plaintext, 'base64').toString('utf8'); } async rotateKey(): Promise<void> { await this.vault.write( `${this.mountPoint}/keys/${this.keyName}/rotate`, {} ); } async getKeyVersion(): Promise<number> { const response = await this.vault.read( `${this.mountPoint}/keys/${this.keyName}` ); return response.data.latest_version; } }

Key Rotation

Automatic Rotation

Schedule: Every 90 days Method: Gradual re-encryption

Rotation Process:

class KeyRotationService { async rotateKeys(): Promise<void> { // 1. Create new key version await this.vault.rotateKey(); // 2. Get all encrypted fields const fields = await this.db.query(` SELECT id, data_encrypted, key_id FROM encrypted_data WHERE key_id != $1 `, [await this.vault.getCurrentKeyVersion()]); // 3. Re-encrypt in batches const batchSize = 1000; for (let i = 0; i < fields.length; i += batchSize) { const batch = fields.slice(i, i + batchSize); await Promise.all(batch.map(async (field) => { const decrypted = await this.encryption.decryptField( field.data_encrypted ); const reencrypted = await this.encryption.encryptField( 'rotated', decrypted ); await this.db.query( 'UPDATE encrypted_data SET data_encrypted = $1 WHERE id = $2', [reencrypted, field.id] ); })); console.log(`Rotated ${i + batch.length} of ${fields.length} fields`); } // 4. Audit log await this.auditLog.log({ event: 'key_rotation_complete', fieldsRotated: fields.length, timestamp: new Date() }); } }

Backup Encryption

Encrypted Backups

PostgreSQL Backup:

# Create encrypted backup pg_dump llm_platform | \ gpg --encrypt --recipient backup@bluefly.io > \ backup-$(date +%Y%m%d).sql.gpg # Restore encrypted backup gpg --decrypt backup-20250115.sql.gpg | \ psql llm_platform

File Backup:

# Create encrypted tar archive tar czf - /data | \ openssl enc -aes-256-cbc -salt -out backup.tar.gz.enc # Extract encrypted archive openssl enc -aes-256-cbc -d -in backup.tar.gz.enc | \ tar xzf -

Compliance

FIPS 140-2 Validation

Crypto Module: OpenSSL FIPS 140-2 validated Validation Certificate: #3953 Algorithms:

  • AES-256-GCM (encryption)
  • PBKDF2-HMAC-SHA256 (key derivation)
  • RSA-2048 (key wrapping)

Audit Requirements

Audit Events:

  • Field encryption/decryption
  • Key access
  • Key rotation
  • Backup creation/restoration
  • Unauthorized access attempts

Log Format:

{ "timestamp": "2025-01-15T10:00:00Z", "event": "field_encrypted", "fieldName": "email", "keyId": "vault-key-v12", "userId": "user-123", "ipAddress": "192.168.1.100" }

Next Steps