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"
}