Encryption System Documentation
This directory contains the encryption system for the application, providing secure data encryption/decryption capabilities with support for both personal (per-user) and global (application-level) encryption.
Architecture Overview
The encryption system is built with a modular architecture:
- Base Encryption Service: Generic encryption service that can be used for any type of data
- Specialized Services: Context-specific implementations (e.g., EmojiEncryptionService)
- Utilities: Helper functions for serialization, validation, and data sanitization
Security Features
Dual Encryption Scopes
-
Personal Encryption: Per-user encryption keys for private data
- Each user has their own encryption key
- Only the user can decrypt their personal data
- Keys are derived from user ID + context + master key
-
Global Encryption: Application-level encryption for shared data
- Single key per context (e.g., 'emoji', 'message')
- All authenticated users can decrypt global data
- Used for approved/public content
Key Management
- Key Derivation: Uses PBKDF2 with 100,000 iterations
- Key Caching: In-memory caching for performance
- Key Rotation: Support for rotating encryption keys
- Secure Storage: Keys are never stored in plaintext
Encryption Details
- Algorithm: AES-256-GCM (Galois/Counter Mode)
- Key Size: 256 bits
- IV Size: 128 bits (randomly generated per encryption)
- Authentication: Built-in authentication with GCM mode
- Additional Authenticated Data: Context-specific AAD for integrity
Base Encryption Service
Core Classes
BaseEncryptionService
Generic encryption service that provides the foundation for all encryption operations.
import { BaseEncryptionService } from './base-encryption';
// Encrypt personal data
const encrypted = await BaseEncryptionService.encrypt(data, {
scope: 'personal',
context: 'message',
userId: 123
});
// Encrypt global data
const globalEncrypted = await BaseEncryptionService.encrypt(data, {
scope: 'global',
context: 'announcement'
});
// Decrypt data
const decrypted = await BaseEncryptionService.decrypt(encrypted, {
userId: 123 // Only needed for personal scope
});
EncryptionUtils
Utility functions for encryption operations.
import { EncryptionUtils } from './base-encryption';
// Serialize encrypted payload for storage
const serialized = EncryptionUtils.serializePayload(encrypted);
// Parse serialized payload
const parsed = EncryptionUtils.parsePayload(serialized);
// Generate secure IDs
const id = EncryptionUtils.generateSecureId('prefix');
// Sanitize data for XSS prevention
const clean = EncryptionUtils.sanitizeData(userInput);
Types and Interfaces
// Encryption scope
type EncryptionScope = 'personal' | 'global';
// Context identifier
type EncryptionContext = string; // e.g., 'emoji', 'message', 'file'
// Encryption options
interface EncryptionOptions {
scope: EncryptionScope;
context: EncryptionContext;
userId?: number; // Required for personal scope
additionalData?: string; // Additional authenticated data
}
// Encrypted payload
interface EncryptedPayload {
encryptedData: string;
iv: string;
authTag: string;
scope: EncryptionScope;
context: EncryptionContext;
keyId: string;
}
Emoji Encryption Service
Overview
The EmojiEncryptionService
is a specialized implementation of the base encryption service for emoji data.
Features
- Image Encryption: Specialized methods for encrypting/decrypting emoji images
- Validation: Image format and size validation
- Re-encryption: Convert personal emojis to global when approved
- Metadata Sanitization: XSS protection for emoji metadata
Usage Example
import {
EmojiEncryptionService,
EmojiEncryptionUtils
} from './emoji-encryption';
// Encrypt emoji image (personal)
const encryptedImage = await EmojiEncryptionService.encryptImageData(
imageBuffer,
'personal',
userId
);
// Decrypt emoji image
const decryptedImage = await EmojiEncryptionService.decryptImageData(
encryptedImage,
userId
);
// Re-encrypt for global access (admin approval)
const globalEncrypted = await EmojiEncryptionService.reencryptForGlobalAccess(
personalEncrypted,
originalUserId
);
// Validate image constraints
const validation = EmojiEncryptionService.validateImageConstraints(
imageBuffer,
'png'
);
// Generate emoji ID
const emojiId = EmojiEncryptionUtils.generateEmojiId('happy_face', userId);
// Sanitize emoji metadata
const clean = EmojiEncryptionUtils.sanitizeEmojiData({
name: 'Happy Face',
description: 'A smiling emoji',
aliases: ['smile', 'happy'],
tags: ['emotion', 'positive']
});
Database Schema
User Encryption Keys
CREATE TABLE user_encryption_keys (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
encryption_key_hash TEXT NOT NULL,
key_salt TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(user_id)
);
Application Encryption Keys
CREATE TABLE application_encryption_keys (
id SERIAL PRIMARY KEY,
key_name VARCHAR(100) NOT NULL UNIQUE,
key_purpose VARCHAR(200) NOT NULL,
encryption_key_hash TEXT NOT NULL,
key_salt TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
is_active BOOLEAN DEFAULT true,
UNIQUE(key_name, is_active)
);
Environment Variables
Set these environment variables for production:
# Global encryption master key
GLOBAL_ENCRYPTION_MASTER_KEY=your-secure-global-master-key
# Personal encryption master key
PERSONAL_ENCRYPTION_MASTER_KEY=your-secure-personal-master-key
⚠️ Security Warning: Never use the default keys in production!
Security Considerations
Best Practices
- Key Rotation: Regularly rotate encryption keys
- Environment Variables: Use secure, random master keys in production
- Access Control: Implement proper permission checks before encryption/decryption
- Input Validation: Always validate and sanitize input data
- Audit Logging: Log encryption/decryption operations for security monitoring
Admin Role Security
⚠️ CRITICAL SECURITY: Admin roles can ONLY be assigned through direct database access.
The permission system includes the following security measures:
- Admin role assignment is blocked in all UI/API endpoints
- Role assignment requires
admin.roles.manage
permission - All role operations are logged for audit purposes
- Multiple security checks prevent bypassing admin role restrictions
To assign admin role to a user, use direct database access:
-- Only run this with direct database access
INSERT INTO user_role_assignments (user_id, role_id, assigned_by)
SELECT
(SELECT id FROM users WHERE username = 'target_username'),
(SELECT id FROM user_roles WHERE name = 'admin'),
(SELECT id FROM users WHERE username = 'admin_username');
Data Flow Security
- Personal Data: User uploads → Personal encryption → Database storage
- Admin Approval: Personal data → Decrypt → Re-encrypt with global key
- Global Access: Global encrypted data → Any authenticated user can decrypt
Performance Considerations
Caching
- Keys are cached in memory to avoid repeated database calls
- Cache is automatically cleared on key rotation
- Use
BaseEncryptionService.clearKeyCache()
to manually clear cache
Optimization Tips
- Batch Operations: Process multiple items together when possible
- Key Reuse: Cache frequently used keys
- Async Operations: Use async/await for all encryption operations
- Validation First: Validate data before encryption to avoid unnecessary work
Error Handling
Common Errors
User ID required for personal encryption
: Missing userId for personal scopeInvalid encrypted payload format
: Corrupted or invalid encrypted dataFailed to retrieve encryption key
: Database or key derivation issuesCan only re-encrypt personal data to global
: Attempting to re-encrypt global data
Error Recovery
- Key Issues: Try clearing cache and regenerating keys
- Validation Errors: Check input data format and constraints
- Permission Errors: Verify user has required permissions
- Database Errors: Check database connectivity and schema
Migration Guide
From Old Emoji System
The new system is backward compatible with the old emoji encryption system:
- Existing Data: Old encrypted emojis continue to work
- New Features: Use new encryption methods for new data
- Gradual Migration: Migrate data during admin approval process
API Changes
EncryptionType
remains the same ('personal' | 'global'
)EmojiEncryptedData
extendsEncryptedPayload
with emoji context- All existing API endpoints continue to work
Testing
Unit Tests
import { BaseEncryptionService, EncryptionUtils } from './base-encryption';
describe('BaseEncryptionService', () => {
test('should encrypt and decrypt personal data', async () => {
const data = 'test data';
const encrypted = await BaseEncryptionService.encrypt(data, {
scope: 'personal',
context: 'test',
userId: 123
});
const decrypted = await BaseEncryptionService.decrypt(encrypted, {
userId: 123
});
expect(decrypted).toBe(data);
});
});
Integration Tests
Test the complete flow from API to encryption:
- User uploads emoji
- Emoji is encrypted with personal key
- Admin approves emoji
- Emoji is re-encrypted with global key
- All users can access approved emoji
Troubleshooting
Debug Checklist
- Environment Variables: Verify master keys are set
- Database Schema: Ensure encryption tables exist
- Permissions: Check user has required permissions
- Key Cache: Try clearing cache if key issues persist
- Logs: Check console for encryption error messages
Common Issues
- Performance: Clear key cache if operations are slow
- Permissions: Verify admin permissions are correctly set
- Data Corruption: Validate encrypted payloads before processing
- Key Rotation: Ensure old data is re-encrypted after key rotation
API Reference
BaseEncryptionService Methods
encrypt(data, options)
: Encrypt data with specified optionsdecrypt(payload, options)
: Decrypt encrypted payloadencryptBinary(buffer, options)
: Encrypt binary datadecryptBinary(payload, options)
: Decrypt binary datareencryptPersonalToGlobal(payload, userId)
: Re-encrypt personal to globalgetPersonalKey(userId, context)
: Get/generate personal keygetGlobalKey(context)
: Get/generate global keyrotatePersonalKey(userId, context)
: Rotate personal keyclearKeyCache()
: Clear key cache
EmojiEncryptionService Methods
encryptImageData(buffer, type, userId)
: Encrypt emoji imagedecryptImageData(payload, userId)
: Decrypt emoji imagereencryptForGlobalAccess(payload, userId)
: Re-encrypt for global accessvalidateImageConstraints(buffer, format)
: Validate imagerotateEmojiKey(userId)
: Rotate emoji key for user
EmojiEncryptionUtils Methods
createEncryptedPayload(data)
: Serialize encrypted dataparseEncryptedPayload(payload)
: Parse encrypted datagenerateEmojiId(name, userId)
: Generate unique emoji IDvalidateEmojiMetadata(metadata)
: Validate emoji metadatasanitizeEmojiData(data)
: Sanitize emoji data for XSS prevention