import crypto from 'crypto'; /** * Privacy and Anonymity Utilities for LocalGreenChain * Provides tools for anonymous plant tracking while maintaining blockchain integrity */ export interface PrivacySettings { anonymousMode: boolean; locationPrivacy: 'exact' | 'fuzzy' | 'city' | 'country' | 'hidden'; identityPrivacy: 'real' | 'pseudonym' | 'anonymous'; sharePlantDetails: boolean; } export interface FuzzyLocation { latitude: number; longitude: number; accuracy: number; // radius in km displayName: string; } /** * Generate anonymous user ID using cryptographic hash */ export function generateAnonymousId(): string { const randomBytes = crypto.randomBytes(32); return 'anon_' + crypto.createHash('sha256').update(randomBytes).digest('hex').substring(0, 16); } /** * Generate pseudonymous wallet address */ export function generateWalletAddress(): string { const randomBytes = crypto.randomBytes(20); return '0x' + randomBytes.toString('hex'); } /** * Obfuscate location based on privacy level * This prevents exact home address tracking while allowing geographic discovery */ export function obfuscateLocation( latitude: number, longitude: number, privacyLevel: 'exact' | 'fuzzy' | 'city' | 'country' | 'hidden' ): FuzzyLocation { switch (privacyLevel) { case 'exact': return { latitude, longitude, accuracy: 0.1, // ~100m displayName: 'Exact location', }; case 'fuzzy': // Add random offset within 1-5km radius const fuzzRadius = 1 + Math.random() * 4; // 1-5 km const angle = Math.random() * 2 * Math.PI; const latOffset = (fuzzRadius / 111) * Math.cos(angle); // 111 km per degree const lonOffset = (fuzzRadius / (111 * Math.cos(latitude * Math.PI / 180))) * Math.sin(angle); return { latitude: latitude + latOffset, longitude: longitude + lonOffset, accuracy: fuzzRadius, displayName: `Within ${Math.round(fuzzRadius)} km`, }; case 'city': // Round to ~10km grid (0.1 degree ≈ 11km) return { latitude: Math.round(latitude * 10) / 10, longitude: Math.round(longitude * 10) / 10, accuracy: 10, displayName: 'City area', }; case 'country': // Round to ~100km grid (1 degree ≈ 111km) return { latitude: Math.round(latitude), longitude: Math.round(longitude), accuracy: 100, displayName: 'Country/Region', }; case 'hidden': return { latitude: 0, longitude: 0, accuracy: 999999, displayName: 'Location hidden', }; default: return obfuscateLocation(latitude, longitude, 'fuzzy'); } } /** * Generate anonymous plant name */ export function generateAnonymousPlantName(plantType: string, generation: number): string { const hash = crypto.randomBytes(4).toString('hex'); return `${plantType}-Gen${generation}-${hash}`; } /** * Encrypt sensitive data for storage */ export function encryptData(data: string, key: string): string { const algorithm = 'aes-256-cbc'; const keyHash = crypto.createHash('sha256').update(key).digest(); const iv = crypto.randomBytes(16); const cipher = crypto.createCipheriv(algorithm, keyHash, iv); let encrypted = cipher.update(data, 'utf8', 'hex'); encrypted += cipher.final('hex'); return iv.toString('hex') + ':' + encrypted; } /** * Decrypt sensitive data */ export function decryptData(encryptedData: string, key: string): string { const algorithm = 'aes-256-cbc'; const keyHash = crypto.createHash('sha256').update(key).digest(); const parts = encryptedData.split(':'); const iv = Buffer.from(parts[0], 'hex'); const encrypted = parts[1]; const decipher = crypto.createDecipheriv(algorithm, keyHash, iv); let decrypted = decipher.update(encrypted, 'hex', 'utf8'); decrypted += decipher.final('utf8'); return decrypted; } /** * Generate Tor-friendly onion address from plant ID * This creates a deterministic onion-style identifier */ export function generateOnionIdentifier(plantId: string): string { const hash = crypto.createHash('sha256').update(plantId).digest('hex'); return hash.substring(0, 16) + '.onion'; } /** * Create anonymous contact method */ export function createAnonymousContact(realEmail: string, privateKey: string): string { // Hash email with private key to create anonymous identifier const hash = crypto.createHash('sha256') .update(realEmail + privateKey) .digest('hex'); return `anon-${hash.substring(0, 12)}@localgreenchain.onion`; } /** * Validate Tor connection */ export async function isTorConnection(req: any): Promise { // Check if request is coming through Tor const forwardedFor = req.headers['x-forwarded-for']; const realIp = req.headers['x-real-ip']; // Tor exit nodes typically set specific headers // This is a simplified check return ( req.headers['x-tor-connection'] === 'true' || req.socket?.remoteAddress?.includes('127.0.0.1') // Tor proxy ); } /** * Generate privacy report for plant */ export interface PrivacyReport { locationPrivacy: string; identityPrivacy: string; dataEncryption: boolean; torEnabled: boolean; riskLevel: 'low' | 'medium' | 'high'; recommendations: string[]; } export function generatePrivacyReport(settings: PrivacySettings): PrivacyReport { const recommendations: string[] = []; let riskLevel: 'low' | 'medium' | 'high' = 'low'; if (settings.locationPrivacy === 'exact') { recommendations.push('Consider using fuzzy location to protect your privacy'); riskLevel = 'high'; } if (settings.identityPrivacy === 'real') { recommendations.push('Using real identity may compromise anonymity'); if (riskLevel === 'low') riskLevel = 'medium'; } if (settings.sharePlantDetails && !settings.anonymousMode) { recommendations.push('Sharing detailed plant info without anonymous mode enabled'); } if (settings.anonymousMode && settings.locationPrivacy !== 'hidden') { riskLevel = 'low'; recommendations.push('Good privacy settings enabled'); } return { locationPrivacy: settings.locationPrivacy, identityPrivacy: settings.identityPrivacy, dataEncryption: settings.anonymousMode, torEnabled: false, // Will be detected at runtime riskLevel, recommendations: recommendations.length > 0 ? recommendations : ['Privacy settings are optimal'], }; }