File size: 4,202 Bytes
373c769 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
// 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(); |