// Secure encryption utility for API key storage // Uses device-specific salt and multiple encryption layers class ApiKeyEncryption { constructor() { // Generate a device-specific salt based on browser fingerprint this.salt = this.generateDeviceSalt(); this.additionalSalt = 'Luna-OCR-2025-Security-Salt'; } // Generate a consistent salt based on device characteristics generateDeviceSalt() { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); ctx.textBaseline = 'top'; ctx.font = '14px Arial'; ctx.fillText('Luna OCR Device Salt', 2, 2); const fingerprint = [ navigator.userAgent, navigator.language, window.screen.width + 'x' + window.screen.height, new Date().getTimezoneOffset(), canvas.toDataURL() ].join('|'); return this.simpleHash(fingerprint); } // Simple hash function simpleHash(str) { let hash = 0; for (let i = 0; i < str.length; i++) { const char = str.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash = hash & hash; // Convert to 32-bit integer } return Math.abs(hash).toString(36); } // Multi-layer encryption with salt and additional security encrypt(text) { if (!text || text.trim() === '') return ''; // Layer 1: Add timestamp and random padding const timestamp = Date.now().toString(36); const randomPadding = Math.random().toString(36).substring(2); const paddedText = `${timestamp}:${text}:${randomPadding}`; // Layer 2: XOR with device salt const saltKey = this.salt + this.additionalSalt; let encrypted = ''; for (let i = 0; i < paddedText.length; i++) { const textChar = paddedText.charCodeAt(i); const saltChar = saltKey.charCodeAt(i % saltKey.length); const encryptedChar = textChar ^ saltChar; encrypted += String.fromCharCode(encryptedChar); } // Layer 3: Base64 encode with additional obfuscation const base64 = btoa(encrypted); const obfuscated = base64.split('').reverse().join(''); return obfuscated; } // Multi-layer decryption decrypt(encryptedText) { if (!encryptedText || encryptedText.trim() === '') return ''; try { // Layer 1: Reverse obfuscation and Base64 decode const deobfuscated = encryptedText.split('').reverse().join(''); const encrypted = atob(deobfuscated); // Layer 2: XOR decryption with device salt const saltKey = this.salt + this.additionalSalt; let decrypted = ''; for (let i = 0; i < encrypted.length; i++) { const encryptedChar = encrypted.charCodeAt(i); const saltChar = saltKey.charCodeAt(i % saltKey.length); const decryptedChar = encryptedChar ^ saltChar; decrypted += String.fromCharCode(decryptedChar); } // Layer 3: Extract original text from padded format const parts = decrypted.split(':'); if (parts.length >= 3) { // Remove timestamp and random padding, return original text return parts.slice(1, -1).join(':'); } return decrypted; // Fallback for old format } catch (error) { console.warn('Failed to decrypt API key:', error); return ''; } } // Store encrypted API key with obfuscated key name storeApiKey(apiKey) { if (!apiKey || apiKey.trim() === '') { localStorage.removeItem('luna_secure_config_v2'); return; } const encrypted = this.encrypt(apiKey); localStorage.setItem('luna_secure_config_v2', encrypted); } // Retrieve and decrypt API key retrieveApiKey() { const encrypted = localStorage.getItem('luna_secure_config_v2'); if (!encrypted) return ''; return this.decrypt(encrypted); } // Clear stored API key clearApiKey() { localStorage.removeItem('luna_secure_config_v2'); } // Check if API key exists hasStoredApiKey() { return !!localStorage.getItem('luna_secure_config_v2'); } } export default new ApiKeyEncryption();