@firebase-backup-platform/encryption
The encryption package provides secure AES-256-GCM encryption for FireBackup. It handles key management, encryption/decryption operations, and secure key derivation.
Installation
npm install @firebase-backup-platform/encryption
# or
yarn add @firebase-backup-platform/encryption
Overview
This package implements industry-standard encryption:
- AES-256-GCM - Authenticated encryption with associated data (AEAD)
- Random IV - Unique initialization vector per encryption operation
- Authentication tags - Integrity verification for encrypted data
- Key derivation - PBKDF2-HMAC-SHA256 for password-based keys
Quick Start
Basic Encryption
import { encrypt, decrypt, generateKey } from '@firebase-backup-platform/encryption';
// Generate a new encryption key
const key = generateKey();
console.log('Key (store securely):', key.toString('base64'));
// Encrypt data
const plaintext = Buffer.from('Sensitive backup data');
const encrypted = await encrypt(plaintext, key);
console.log('Ciphertext:', encrypted.ciphertext.toString('base64'));
console.log('IV:', encrypted.iv.toString('base64'));
console.log('Auth Tag:', encrypted.authTag.toString('base64'));
// Decrypt data
const decrypted = await decrypt(encrypted, key);
console.log('Decrypted:', decrypted.toString());
Using the EncryptionService
import { EncryptionService } from '@firebase-backup-platform/encryption';
const encryptionService = new EncryptionService({
key: process.env.ENCRYPTION_KEY
});
// Encrypt
const encrypted = await encryptionService.encrypt(data);
// Decrypt
const decrypted = await encryptionService.decrypt(encrypted);
API Reference
Key Generation
generateKey()
Generates a cryptographically secure 256-bit key.
function generateKey(): Buffer
Example:
import { generateKey } from '@firebase-backup-platform/encryption';
const key = generateKey();
// Store as base64 string
const keyString = key.toString('base64');
console.log('Key:', keyString); // Store in secrets manager
// Load from base64 string
const loadedKey = Buffer.from(keyString, 'base64');
deriveKey(password, salt, options)
Derives a key from a password using PBKDF2.
function deriveKey(
password: string,
salt: Buffer | string,
options?: KeyDerivationOptions
): Promise<Buffer>
KeyDerivationOptions:
| Property | Type | Default | Description |
|---|---|---|---|
iterations | number | 100000 | PBKDF2 iterations |
keyLength | number | 32 | Output key length (bytes) |
digest | string | 'sha256' | Hash algorithm |
Example:
import { deriveKey, generateSalt } from '@firebase-backup-platform/encryption';
// Generate a random salt (store alongside encrypted data)
const salt = generateSalt();
// Derive key from password
const key = await deriveKey('user-password', salt, {
iterations: 100000
});
// Store salt with encrypted data for decryption
console.log('Salt:', salt.toString('base64'));
generateSalt(length)
Generates a random salt for key derivation.
function generateSalt(length?: number): Buffer
Example:
const salt = generateSalt(16); // 16 bytes
console.log('Salt:', salt.toString('hex'));
Encryption/Decryption
encrypt(data, key, options)
Encrypts data using AES-256-GCM.
function encrypt(
data: Buffer | string,
key: Buffer | string,
options?: EncryptOptions
): Promise<EncryptedData>
EncryptOptions:
| Property | Type | Default | Description |
|---|---|---|---|
aad | Buffer | - | Additional authenticated data |
encoding | string | 'utf8' | String encoding |
EncryptedData:
interface EncryptedData {
ciphertext: Buffer;
iv: Buffer; // 12 bytes
authTag: Buffer; // 16 bytes
aad?: Buffer;
}
Example:
const encrypted = await encrypt(
Buffer.from('Sensitive data'),
key,
{ aad: Buffer.from('backup-metadata') }
);
decrypt(encryptedData, key)
Decrypts data encrypted with AES-256-GCM.
function decrypt(
encryptedData: EncryptedData,
key: Buffer | string
): Promise<Buffer>
Example:
const decrypted = await decrypt(
{
ciphertext: encrypted.ciphertext,
iv: encrypted.iv,
authTag: encrypted.authTag,
aad: encrypted.aad
},
key
);
console.log('Decrypted:', decrypted.toString());
EncryptionService Class
A high-level service for encryption operations.
Constructor
new EncryptionService(config: EncryptionServiceConfig)
EncryptionServiceConfig:
| Property | Type | Required | Description |
|---|---|---|---|
key | string | Buffer | Yes | Encryption key |
keyDerivation | KeyDerivationConfig | No | Password-based key derivation |
Example:
// Using a pre-generated key
const service = new EncryptionService({
key: process.env.ENCRYPTION_KEY
});
// Using password-based key derivation
const service = new EncryptionService({
key: 'master-password',
keyDerivation: {
enabled: true,
salt: process.env.KEY_SALT,
iterations: 100000
}
});
Methods
encrypt(data, options)
async encrypt(data: Buffer | string, options?: EncryptOptions): Promise<EncryptedPayload>
EncryptedPayload:
interface EncryptedPayload {
data: Buffer; // IV + Ciphertext + AuthTag combined
metadata: {
algorithm: string;
ivLength: number;
authTagLength: number;
};
}
decrypt(payload)
async decrypt(payload: EncryptedPayload): Promise<Buffer>
encryptStream(inputStream, outputStream)
Encrypts a stream of data.
async encryptStream(
inputStream: Readable,
outputStream: Writable
): Promise<StreamEncryptionResult>
decryptStream(inputStream, outputStream)
Decrypts a stream of data.
async decryptStream(
inputStream: Readable,
outputStream: Writable
): Promise<void>
Serialization
Pack and Unpack
Helper functions for serializing encrypted data:
import { pack, unpack } from '@firebase-backup-platform/encryption';
// Pack encrypted data into a single buffer
const encrypted = await encrypt(data, key);
const packed = pack(encrypted);
console.log('Packed length:', packed.length);
// Unpack to get individual components
const unpacked = unpack(packed);
const decrypted = await decrypt(unpacked, key);
Format
The packed format is:
+--------+------------+----------+
| IV | AuthTag | Ciphertext|
| 12 B | 16 B | Variable |
+--------+------------+----------+
JSON Serialization
import { toJSON, fromJSON } from '@firebase-backup-platform/encryption';
// Serialize to JSON-safe format
const encrypted = await encrypt(data, key);
const json = toJSON(encrypted);
console.log(json);
// {
// ciphertext: "base64...",
// iv: "base64...",
// authTag: "base64..."
// }
// Deserialize
const restored = fromJSON(json);
const decrypted = await decrypt(restored, key);
Stream Encryption
For large files, use streaming encryption:
import { createEncryptStream, createDecryptStream } from '@firebase-backup-platform/encryption';
import { pipeline } from 'stream/promises';
import fs from 'fs';
// Encrypt a file
const encryptStream = createEncryptStream(key);
await pipeline(
fs.createReadStream('large-backup.json'),
encryptStream,
fs.createWriteStream('large-backup.encrypted')
);
// Get encryption metadata (needed for decryption)
const { iv, authTag } = encryptStream.getMetadata();
// Decrypt a file
const decryptStream = createDecryptStream(key, { iv, authTag });
await pipeline(
fs.createReadStream('large-backup.encrypted'),
decryptStream,
fs.createWriteStream('large-backup.decrypted.json')
);
Key Rotation
Support for key rotation:
import { KeyRotator } from '@firebase-backup-platform/encryption';
const rotator = new KeyRotator({
currentKey: process.env.CURRENT_ENCRYPTION_KEY,
previousKeys: [
process.env.PREVIOUS_KEY_1,
process.env.PREVIOUS_KEY_2
]
});
// Decrypt with any key (tries current, then previous)
const decrypted = await rotator.decrypt(encryptedData);
// Re-encrypt with current key
const reencrypted = await rotator.reencrypt(encryptedData);
// Check if data needs re-encryption
const needsRotation = await rotator.needsReencryption(encryptedData);
Error Handling
import {
encrypt,
decrypt,
EncryptionError,
DecryptionError,
InvalidKeyError,
AuthenticationError,
KeyDerivationError
} from '@firebase-backup-platform/encryption';
try {
const decrypted = await decrypt(encryptedData, key);
} catch (error) {
if (error instanceof InvalidKeyError) {
console.error('Invalid encryption key format');
} else if (error instanceof AuthenticationError) {
console.error('Data integrity check failed - data may be tampered');
} else if (error instanceof DecryptionError) {
console.error('Decryption failed:', error.message);
} else if (error instanceof EncryptionError) {
console.error('Encryption error:', error.message);
}
}
Security Specifications
Algorithm Details
| Property | Value |
|---|---|
| Algorithm | AES-256-GCM |
| Key Size | 256 bits (32 bytes) |
| IV Size | 96 bits (12 bytes) |
| Auth Tag Size | 128 bits (16 bytes) |
| Mode | Galois/Counter Mode (GCM) |
Key Derivation (PBKDF2)
| Property | Default Value |
|---|---|
| Algorithm | PBKDF2 |
| Hash | HMAC-SHA256 |
| Iterations | 100,000 |
| Salt Size | 128 bits (16 bytes) |
| Key Length | 256 bits (32 bytes) |
Best Practices
Security Best Practices
-
✅ Generate unique IV for each encryption operation
- Never reuse IV with the same key
-
✅ Store keys in a secrets manager
- AWS Secrets Manager
- HashiCorp Vault
- Google Cloud Secret Manager
-
✅ Rotate keys periodically
- Recommended: annually
-
✅ Verify auth tag before processing decrypted data
- Prevents tampering attacks
-
✅ Use secure random for key generation
crypto.randomBytes()in Node.js
-
✅ Keep backups of encryption keys
- Separate from encrypted data
TypeScript Support
import {
encrypt,
decrypt,
generateKey,
deriveKey,
generateSalt,
pack,
unpack,
toJSON,
fromJSON,
EncryptionService,
KeyRotator,
createEncryptStream,
createDecryptStream,
EncryptedData,
EncryptedPayload,
EncryptOptions,
KeyDerivationOptions,
EncryptionServiceConfig,
EncryptionError,
DecryptionError,
InvalidKeyError,
AuthenticationError
} from '@firebase-backup-platform/encryption';
// Typed operations
const key: Buffer = generateKey();
const encrypted: EncryptedData = await encrypt(data, key);
const decrypted: Buffer = await decrypt(encrypted, key);
// Typed service
const config: EncryptionServiceConfig = {
key: process.env.ENCRYPTION_KEY!
};
const service = new EncryptionService(config);
Integration with Backup Core
import { BackupCore } from '@firebase-backup-platform/backup-core';
import { generateKey } from '@firebase-backup-platform/encryption';
// Generate a key (store securely!)
const encryptionKey = generateKey();
const backupCore = new BackupCore({
firebase: { ... },
storage: { ... },
encryption: {
enabled: true,
key: encryptionKey
}
});
// All backups are now encrypted with AES-256-GCM
const result = await backupCore.backup({
collections: ['users']
});
Related Packages
- backup-core - Uses encryption for secure backups
- compression - Compress before encrypting
- storage-connectors - Storage with encryption