defarm-sdk
Version:
DeFarm SDK - On-premise blockchain data processing and tokenization engine for agriculture supply chain
403 lines (356 loc) • 10.9 kB
text/typescript
/**
* 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();
}
}