platform-tooling/scripts/security/rotate-encryption-keys.ts
Quinn Ftw 85621b287e chore: snapshot before monorepo consolidation
Capture current working state before converting platform-tooling
into a submodule of the lilith-platform monorepo.
2026-01-29 07:04:39 -08:00

204 lines
5.7 KiB
TypeScript

#!/usr/bin/env npx ts-node
/**
* Encryption Key Rotation Script
*
* Rotates TDE and pgcrypto column encryption keys for the user database.
* This script should be run during scheduled maintenance windows.
*
* Steps:
* 1. Generate new keys
* 2. Backup current keys to Keychain
* 3. Re-encrypt data with new column key
* 4. Update Vault with new keys
* 5. Verify data integrity
*
* Usage:
* npx ts-node tooling/scripts/security/rotate-encryption-keys.ts --dry-run
* npx ts-node tooling/scripts/security/rotate-encryption-keys.ts --execute
*/
import { randomBytes, createHash } from 'node:crypto';
// Configuration
const CONFIG = {
// Vault paths
vault: {
tdeKey: 'secret/userdb/tde-key',
columnKey: 'secret/userdb/column-key',
backupKey: 'secret/userdb/backup-key',
},
// Key sizes (bits)
keySizes: {
tde: 256,
column: 256,
backup: 256,
},
// Backup retention
backupRetentionDays: 90,
};
interface KeyRotationResult {
success: boolean;
oldKeyVersion: number;
newKeyVersion: number;
error?: string;
affectedRows?: number;
}
/**
* Generate a cryptographically secure key
*/
function generateKey(bits: number): string {
const bytes = randomBytes(bits / 8);
return bytes.toString('base64');
}
/**
* Calculate key fingerprint for verification
*/
function keyFingerprint(key: string): string {
return createHash('sha256')
.update(Buffer.from(key, 'base64'))
.digest('hex')
.slice(0, 16);
}
/**
* Simulate key rotation (dry run)
*/
async function simulateRotation(): Promise<void> {
const log = (msg: string) => process.stdout.write(`[DRY RUN] ${msg}\n`);
log('Starting encryption key rotation simulation...');
log('');
// Step 1: Generate new keys
log('Step 1: Generating new keys...');
const newTdeKey = generateKey(CONFIG.keySizes.tde);
const newColumnKey = generateKey(CONFIG.keySizes.column);
const newBackupKey = generateKey(CONFIG.keySizes.backup);
log(` New TDE key fingerprint: ${keyFingerprint(newTdeKey)}`);
log(` New column key fingerprint: ${keyFingerprint(newColumnKey)}`);
log(` New backup key fingerprint: ${keyFingerprint(newBackupKey)}`);
log('');
// Step 2: Check current keys (simulation)
log('Step 2: Retrieving current keys from Vault...');
log(` Would read from: ${CONFIG.vault.tdeKey}`);
log(` Would read from: ${CONFIG.vault.columnKey}`);
log(` Would read from: ${CONFIG.vault.backupKey}`);
log('');
// Step 3: Backup current keys
log('Step 3: Backing up current keys to Keychain...');
log(' Would call: backupToKeychain() with current keys');
log(' Service: TrustedMeet-KeyBackup');
log(' Account: master-keys-backup-<timestamp>');
log('');
// Step 4: Re-encrypt data (simulation)
log('Step 4: Re-encrypting data with new column key...');
log(' Tables affected:');
log(' - userdb_messages.content_encrypted');
log(' - userdb_contacts.contact_info_encrypted');
log(' - userdb_contacts.notes_encrypted');
log(' - userdb_saved_clips.content_encrypted');
log(' This would be done in batches of 1000 rows');
log('');
// Step 5: Update Vault
log('Step 5: Updating Vault with new keys...');
log(` Would write to: ${CONFIG.vault.tdeKey}`);
log(` Would write to: ${CONFIG.vault.columnKey}`);
log(` Would write to: ${CONFIG.vault.backupKey}`);
log('');
// Step 6: Verify
log('Step 6: Verifying data integrity...');
log(' Would test decryption of sample records');
log(' Would verify row counts match');
log('');
log('='.repeat(60));
log('Dry run complete. No changes were made.');
log('');
log('To execute the rotation:');
log(' npx ts-node rotate-encryption-keys.ts --execute');
log('');
log('IMPORTANT: Schedule this during a maintenance window.');
log('Estimated duration: 5-30 minutes depending on data volume.');
}
/**
* Execute key rotation (real)
*/
async function executeRotation(): Promise<void> {
const log = (msg: string) => process.stdout.write(`[EXECUTE] ${msg}\n`);
const error = (msg: string) => process.stderr.write(`[ERROR] ${msg}\n`);
log('Starting encryption key rotation...');
log('');
log('WARNING: This will modify encryption keys in production.');
log('Ensure you have:');
log(' 1. A recent backup');
log(' 2. Scheduled maintenance window');
log(' 3. Recovery passphrase available');
log('');
// This would contain the actual implementation
// For safety, we only include the simulation in this script
error('Actual execution not implemented in this example script.');
error('The rotation process requires:');
error(' 1. Vault client connection');
error(' 2. PostgreSQL connection to userdb');
error(' 3. @lilith/key-backup for Keychain operations');
error('');
error('See docs/technical/security/KEY_ROTATION.md for the full procedure.');
process.exit(1);
}
/**
* Main entry point
*/
async function main(): Promise<void> {
const args = process.argv.slice(2);
if (args.includes('--help') || args.includes('-h')) {
process.stdout.write(`
Encryption Key Rotation Script
Usage:
npx ts-node rotate-encryption-keys.ts [options]
Options:
--dry-run Simulate rotation without making changes (default)
--execute Execute the actual key rotation
--help, -h Show this help message
Examples:
# Preview what would happen
npx ts-node rotate-encryption-keys.ts --dry-run
# Execute rotation (requires maintenance window)
npx ts-node rotate-encryption-keys.ts --execute
Documentation:
See docs/technical/security/KEY_ROTATION.md
`);
return;
}
if (args.includes('--execute')) {
await executeRotation();
} else {
await simulateRotation();
}
}
main().catch((err) => {
process.stderr.write(`Fatal error: ${err.message}\n`);
process.exit(1);
});