← Documentation Home

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