luna_ocr / src /utils /encryption.js
veela4's picture
Add files using upload-large-folder tool
373c769 verified
// 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();