UNPKG

defarm-sdk

Version:

DeFarm SDK - On-premise blockchain data processing and tokenization engine for agriculture supply chain

403 lines (356 loc) 10.9 kB
/** * Key Derivation Service - Long-term Encryption Solution * * Solves the certificate expiration problem: * - DeFarm maintains a master key (never shared) * - Government receives derived keys * - Keys can be rotated without losing access to old data * - Multiple decryption paths for resilience */ import { createHash, randomBytes, pbkdf2Sync } from 'crypto'; export interface KeyDerivationConfig { masterKeyId: string; governmentId: string; purpose: 'encryption' | 'signing' | 'both'; validFrom: Date; validUntil?: Date; metadata?: Record<string, any>; } export interface DerivedKey { keyId: string; governmentId: string; derivedKey: string; publicKey?: string; validFrom: Date; validUntil?: Date; derivationPath: string; canDecryptHistorical: boolean; metadata?: Record<string, any>; } export interface EncryptionEnvelope { version: number; keyId: string; algorithm: string; encrypted: string; iv?: string; authTag?: string; timestamp: string; fallbackKeyIds?: string[]; // Additional keys that can decrypt } export class KeyDerivationService { private static readonly VERSION = 1; private static readonly ITERATIONS = 100000; /** * Generate derived key from master (DeFarm side) */ static generateDerivedKey( masterKey: string, config: KeyDerivationConfig ): DerivedKey { // Create derivation path const derivationPath = `defarm/v1/${config.governmentId}/${config.purpose}/${config.validFrom.getTime()}`; // Derive key using PBKDF2 const salt = createHash('sha256') .update(derivationPath) .update(config.masterKeyId) .digest(); const derivedKey = pbkdf2Sync( masterKey, salt, this.ITERATIONS, 32, 'sha256' ).toString('hex'); // Generate key ID const keyId = this.generateKeyId(config.governmentId, derivationPath); return { keyId, governmentId: config.governmentId, derivedKey, validFrom: config.validFrom, validUntil: config.validUntil, derivationPath, canDecryptHistorical: true }; } /** * Create time-based derived keys (for automatic rotation) */ static generateTimeBasedKeys( masterKey: string, governmentId: string, startDate: Date, numberOfYears: number = 10 ): DerivedKey[] { const keys: DerivedKey[] = []; for (let year = 0; year < numberOfYears; year++) { const validFrom = new Date(startDate); validFrom.setFullYear(validFrom.getFullYear() + year); const validUntil = new Date(validFrom); validUntil.setFullYear(validUntil.getFullYear() + 1); const config: KeyDerivationConfig = { masterKeyId: 'defarm-master-2024', governmentId, purpose: 'encryption', validFrom, validUntil }; keys.push(this.generateDerivedKey(masterKey, config)); } return keys; } /** * Encrypt with fallback keys for resilience */ static encryptWithFallback( data: string, primaryKey: DerivedKey, fallbackKeys: DerivedKey[] = [] ): EncryptionEnvelope { const crypto = require('crypto'); const algorithm = 'aes-256-gcm'; // Generate IV const iv = randomBytes(16); // Encrypt with primary key const key = Buffer.from(primaryKey.derivedKey, 'hex'); const cipher = crypto.createCipheriv(algorithm, key, iv); let encrypted = cipher.update(data, 'utf8', 'hex'); encrypted += cipher.final('hex'); const authTag = cipher.getAuthTag(); // Create envelope with fallback key IDs const envelope: EncryptionEnvelope = { version: this.VERSION, keyId: primaryKey.keyId, algorithm, encrypted, iv: iv.toString('hex'), authTag: authTag.toString('hex'), timestamp: new Date().toISOString(), fallbackKeyIds: fallbackKeys.map(k => k.keyId) }; return envelope; } /** * Decrypt with key rotation support */ static decrypt( envelope: EncryptionEnvelope, availableKeys: DerivedKey[] ): { success: boolean; data?: string; usedKeyId?: string; error?: string } { // Try primary key first const primaryKey = availableKeys.find(k => k.keyId === envelope.keyId); if (primaryKey) { try { const decrypted = this.decryptWithKey(envelope, primaryKey); return { success: true, data: decrypted, usedKeyId: primaryKey.keyId }; } catch (error) { // Continue to fallbacks } } // Try fallback keys if (envelope.fallbackKeyIds) { for (const fallbackKeyId of envelope.fallbackKeyIds) { const fallbackKey = availableKeys.find(k => k.keyId === fallbackKeyId); if (fallbackKey) { try { const decrypted = this.decryptWithKey(envelope, fallbackKey); return { success: true, data: decrypted, usedKeyId: fallbackKey.keyId }; } catch (error) { // Continue trying } } } } // Try all historical keys if they can decrypt historical data for (const key of availableKeys) { if (key.canDecryptHistorical) { try { const decrypted = this.decryptWithKey(envelope, key); return { success: true, data: decrypted, usedKeyId: key.keyId }; } catch (error) { // Continue } } } return { success: false, error: 'No valid decryption key found' }; } /** * Emergency recovery key generation */ static generateRecoveryKey( masterKey: string, governmentId: string, reason: string ): DerivedKey { const config: KeyDerivationConfig = { masterKeyId: 'defarm-master-recovery', governmentId, purpose: 'both', validFrom: new Date(), metadata: { reason, type: 'emergency_recovery', generatedAt: new Date().toISOString() } }; // Create a recovery-specific derivation path const recoveryPath = `defarm/v1/${governmentId}/recovery/${new Date().getTime()}`; const recoveryKey = this.generateDerivedKey(masterKey, config); recoveryKey.derivationPath = recoveryPath; // Override to include 'recovery' recoveryKey.keyId = this.generateKeyId(governmentId, recoveryPath); // Regenerate keyId with recovery path recoveryKey.canDecryptHistorical = true; // Can decrypt all historical data recoveryKey.metadata = config.metadata; // Preserve metadata return recoveryKey; } /** * Generate hierarchical keys for different security levels */ static generateHierarchicalKeys( masterKey: string, governmentId: string ): { operational: DerivedKey; // Day-to-day operations sensitive: DerivedKey; // Sensitive data critical: DerivedKey; // Critical/classified data recovery: DerivedKey; // Emergency recovery } { const baseConfig: KeyDerivationConfig = { masterKeyId: 'defarm-master-2024', governmentId, purpose: 'encryption', validFrom: new Date() }; return { operational: this.generateDerivedKey(masterKey, { ...baseConfig, purpose: 'encryption', metadata: { level: 'operational' } }), sensitive: this.generateDerivedKey(masterKey, { ...baseConfig, purpose: 'encryption', metadata: { level: 'sensitive' } }), critical: this.generateDerivedKey(masterKey, { ...baseConfig, purpose: 'both', metadata: { level: 'critical' } }), recovery: this.generateRecoveryKey(masterKey, governmentId, 'hierarchical-setup') }; } private static decryptWithKey( envelope: EncryptionEnvelope, key: DerivedKey ): string { const crypto = require('crypto'); const decipher = crypto.createDecipheriv( envelope.algorithm, Buffer.from(key.derivedKey, 'hex'), Buffer.from(envelope.iv!, 'hex') ); decipher.setAuthTag(Buffer.from(envelope.authTag!, 'hex')); let decrypted = decipher.update(envelope.encrypted, 'hex', 'utf8'); decrypted += decipher.final('utf8'); return decrypted; } private static generateKeyId(governmentId: string, derivationPath: string): string { const hash = createHash('sha256') .update(governmentId) .update(derivationPath) .update(Date.now().toString()) .digest('hex'); // Include 'recovery' in keyId for recovery keys if (derivationPath.includes('recovery')) { return `dk_recovery_${governmentId}_${hash.substring(0, 8)}`; } return `dk_${governmentId}_${hash.substring(0, 8)}`; } } /** * Key Management API for Government Integration */ export class KeyManagementAPI { /** * Request new derived key */ static async requestDerivedKey( governmentId: string, purpose: 'operational' | 'sensitive' | 'critical', validityPeriod: number = 365 // days ): Promise<{ keyId: string; encryptedKey: string; // Encrypted with government's public key validFrom: string; validUntil: string; instructions: string; }> { // This would call DeFarm API const response = await fetch('https://api.defarm.com/api/keys/request', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.DEFARM_API_KEY}` }, body: JSON.stringify({ governmentId, purpose, validityDays: validityPeriod }) }); return response.json(); } /** * Rotate existing key */ static async rotateKey( currentKeyId: string, reason: string ): Promise<{ newKeyId: string; encryptedKey: string; migrationInstructions: string; }> { const response = await fetch('https://api.defarm.com/api/keys/rotate', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.DEFARM_API_KEY}` }, body: JSON.stringify({ currentKeyId, reason }) }); return response.json(); } /** * Request emergency recovery */ static async requestEmergencyRecovery( governmentId: string, reason: string, authorizationCode: string // Multi-factor auth ): Promise<{ recoveryKeyId: string; encryptedKey: string; expiresIn: number; // minutes }> { const response = await fetch('https://api.defarm.com/api/keys/emergency-recovery', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.DEFARM_API_KEY}` }, body: JSON.stringify({ governmentId, reason, authorizationCode }) }); return response.json(); } }