// ============================================================================ // File: modules/storage.js // ============================================================================ (global => { 'use strict'; const DB_NAME = "FritreeEnterpriseDB"; const DB_VERSION = 3; const STORES = { MEDIA: "media_assets", HISTORY: "campaign_history", SYSTEM: "system_metadata" }; // Keys monitored under the Double-Anchor Signature Integrity Verification (SIS) // Enforces strict anti-tampering verification for level metrics, cards, and USD balances const SECURE_SIS_KEYS = new Set([ "acc_userLevel", "acc_userXP", "acc_lifetimeFb", "acc_lifetimeWa", "acc_lifetimeTasks", "local_previous_posts", "wa_connected", "wa_sent_today", "acc_fbShares", "acc_waShares", "userPoints" // Represents USD balance ]); // Constant validation salt tied to SHA-256 integrity seal const INTEGRITY_SEAL_SALT = "FritreeSymmetricIntegritySeal_WebCrypto_Strict_SHA256_Production_Salt_2026"; // Configuration keys stored via standard WebCrypto GCM encryption envelopes const LIGHTWEIGHT_CONFIG_KEYS = new Set([ "userPoints", "userPointsSig", "fritree_account_id", "acc_fbShares", "acc_fbShares_sig", "acc_waShares", "acc_waShares_sig", "acc_userLevel", "acc_userXP", "acc_lifetimeFb", "acc_lifetimeWa", "acc_lifetimeTasks", "acc_activeSub", "acc_subExpiry", "local_sleep_intervals", "local_protection_settings", "local_shield_config_matrix", "wa_connected", "wa_phone_number", "wa_profile_name", "wa_sent_today" ]); let dbInstance = null; let isDbInitializing = false; const dbInitPromiseResolvers = []; // ============================================================================ // Asynchronous Execution Queue to Mitigate Database Write Collisions // ============================================================================ class AsyncWriteQueue { constructor() { this.queue = []; this.isProcessing = false; } enqueue(task) { return new Promise((resolve, reject) => { this.queue.push({ task, resolve, reject }); this.processNext(); }); } async processNext() { if (this.isProcessing || this.queue.length === 0) return; this.isProcessing = true; const { task, resolve, reject } = this.queue.shift(); try { const result = await task(); resolve(result); } catch (err) { reject(err); } finally { this.isProcessing = false; this.processNext(); } } } const storageWriteQueue = new AsyncWriteQueue(); // ============================================================================ // IndexedDB Connection Lifecycles // ============================================================================ function initIndexedDB() { if (dbInstance) return Promise.resolve(dbInstance); if (isDbInitializing) { return new Promise((resolve, reject) => { dbInitPromiseResolvers.push({ resolve, reject }); }); } isDbInitializing = true; return new Promise((resolve, reject) => { const request = indexedDB.open(DB_NAME, DB_VERSION); request.onupgradeneeded = event => { const db = event.target.result; Object.values(STORES).forEach(storeName => { if (!db.objectStoreNames.contains(storeName)) { db.createObjectStore(storeName, { keyPath: "id" }); } }); }; request.onsuccess = event => { dbInstance = event.target.result; isDbInitializing = false; resolve(dbInstance); dbInitPromiseResolvers.forEach(r => r.resolve(dbInstance)); dbInitPromiseResolvers.length = 0; }; request.onerror = event => { isDbInitializing = false; reject(event.target.error); dbInitPromiseResolvers.forEach(r => r.reject(event.target.error)); dbInitPromiseResolvers.length = 0; }; }); } function base64ToBlob(base64Data, mimeType) { const byteCharacters = atob(base64Data.split(",")[1] || base64Data); const byteArrays = []; for (let offset = 0; offset < byteCharacters.length; offset += 512) { const slice = byteCharacters.slice(offset, offset + 512); const byteNumbers = new Array(slice.length); for (let i = 0; i < slice.length; i++) { byteNumbers[i] = slice.charCodeAt(i); } const byteArray = new Uint8Array(byteNumbers); byteArrays.push(byteArray); } return new Blob(byteArrays, { type: mimeType }); } function blobToBase64(blob) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onloadend = () => resolve(reader.result); reader.onerror = reject; reader.readAsDataURL(blob); }); } async function writeIndexedDBEntry(storeName, key, encryptedValue) { const db = await initIndexedDB(); return new Promise((resolve, reject) => { const transaction = db.transaction([storeName], "readwrite"); const store = transaction.objectStore(storeName); const putRequest = store.put({ id: key, payload: encryptedValue, updated_at: Date.now() }); putRequest.onsuccess = () => resolve(true); putRequest.onerror = e => reject(e.target.error); }); } async function readIndexedDBEntry(storeName, key) { const db = await initIndexedDB(); return new Promise(resolve => { const transaction = db.transaction([storeName], "readonly"); const store = transaction.objectStore(storeName); const getRequest = store.get(key); getRequest.onsuccess = e => { const record = e.target.result; resolve(record ? record.payload : null); }; getRequest.onerror = () => { resolve(null); }; }); } async function deleteIndexedDBEntry(storeName, key) { const db = await initIndexedDB(); return new Promise((resolve, reject) => { const transaction = db.transaction([storeName], "readwrite"); const store = transaction.objectStore(storeName); const deleteRequest = store.delete(key); deleteRequest.onsuccess = () => resolve(true); deleteRequest.onerror = e => reject(e.target.error); }); } function routeIndexedDBStore(key) { if (key === "campaignHistoryData") { return STORES.HISTORY; } else if (key === "local_previous_posts") { return STORES.MEDIA; } return STORES.SYSTEM; } // ============================================================================ // Double-Anchor Secondary Verification Anchors // ============================================================================ async function computeSecureSymmetricChecksum(key, data) { let stringData = ""; if (typeof data === 'number') { // Precise floating-point serialization lock for USD to avoid numeric evaluation rounding errors stringData = key === 'userPoints' ? data.toFixed(2) : String(data); } else if (typeof data === 'object') { stringData = JSON.stringify(data); } else { stringData = String(data); } const payloadString = `${key}:${stringData}:${INTEGRITY_SEAL_SALT}`; if (typeof FritreeCrypto !== 'undefined' && typeof FritreeCrypto.sha256 === 'function') { return await FritreeCrypto.sha256(payloadString); } let hash = 0; const inputStr = payloadString; for (let i = 0; i < inputStr.length; i++) { const char = inputStr.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash |= 0; } return "fallback_sha256_" + Math.abs(hash).toString(16); } async function writeSecondarySignatureAnchor(key, signature) { const obfuscatedSigKey = await asyncHashKey(`sis_anchor_sig_${key}`); if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) { const payload = {}; payload[obfuscatedSigKey] = signature; return new Promise(res => chrome.storage.local.set(payload, () => res(true))); } else { localStorage.setItem(syncHashKey(`sis_anchor_sig_${key}`), signature); return true; } } async function readSecondarySignatureAnchor(key) { const obfuscatedSigKey = await asyncHashKey(`sis_anchor_sig_${key}`); if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) { return new Promise(res => { chrome.storage.local.get(obfuscatedSigKey, payload => { res(payload[obfuscatedSigKey] || null); }); }); } else { return localStorage.getItem(syncHashKey(`sis_anchor_sig_${key}`)) || null; } } async function deleteSecondarySignatureAnchor(key) { const obfuscatedSigKey = await asyncHashKey(`sis_anchor_sig_${key}`); if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) { return new Promise(res => chrome.storage.local.remove(obfuscatedSigKey, () => res(true))); } else { localStorage.removeItem(syncHashKey(`sis_anchor_sig_${key}`)); return true; } } // ============================================================================ // Storage Interface Operations // ============================================================================ const UnifiedStorageManager = { set: function(key, value) { return storageWriteQueue.enqueue(async () => { if (typeof FritreeCrypto !== 'undefined' && typeof FritreeCrypto.validateData === 'function') { if (!FritreeCrypto.validateData(value, key)) { return false; } } // Compile and seal state signatures across local stores if (SECURE_SIS_KEYS.has(key)) { const dynamicSig = await computeSecureSymmetricChecksum(key, value); await writeIndexedDBEntry(STORES.SYSTEM, `local_sis_sig_${key}`, dynamicSig); await writeSecondarySignatureAnchor(key, dynamicSig); // Secure dual backup points to facilitate self-healing recovery await writeIndexedDBEntry(STORES.SYSTEM, `local_sis_backup_${key}`, value); if (typeof FritreeCrypto !== 'undefined' && typeof FritreeCrypto.setStorage === 'function') { await FritreeCrypto.setStorage(`sis_backup_${key}`, value); } } // Directly route raw Blob files to the IndexedDB media store if (value instanceof Blob) { try { const targetStore = STORES.MEDIA; await writeIndexedDBEntry(targetStore, key, value); return true; } catch (dbErr) { console.error("[Fritree Storage] Binary media write failure:", dbErr); return false; } } // Handle standard extension configurations via Chrome Local Storage if (LIGHTWEIGHT_CONFIG_KEYS.has(key)) { if (typeof FritreeCrypto !== 'undefined' && typeof FritreeCrypto.setStorage === 'function') { return await FritreeCrypto.setStorage(key, value); } const localBackupKey = syncHashKey(key); const plainSerialized = JSON.stringify(value); const localMockCipher = btoa(unescape(encodeURIComponent(plainSerialized))); localStorage.setItem(localBackupKey, `fte_fallback_enc:${localMockCipher}`); return true; } // Process high-capacity campaign histories and template registries const targetStore = routeIndexedDBStore(key); try { if (typeof FritreeCrypto !== 'undefined' && typeof FritreeCrypto.setStorage === 'function') { await FritreeCrypto.setStorage(key, value); } else { throw new Error("Cryptographic module is offline."); } const obfuscatedKey = await asyncHashKey(key); const freshCipher = await new Promise(res => { if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) { chrome.storage.local.get(obfuscatedKey, payload => res(payload[obfuscatedKey])); } else { res(localStorage.getItem(obfuscatedKey)); } }); if (freshCipher) { await writeIndexedDBEntry(targetStore, key, freshCipher); if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) { chrome.storage.local.remove(obfuscatedKey); } else { localStorage.removeItem(obfuscatedKey); } return true; } } catch (dbErr) { console.warn("[Fritree Storage] IndexedDB route missed. Fallback to LocalStorage:", dbErr); if (typeof FritreeCrypto !== 'undefined' && typeof FritreeCrypto.setStorage === 'function') { return await FritreeCrypto.setStorage(key, value); } } return false; }); }, get: function(key, defaultValue = null) { return new Promise(async resolve => { try { let decryptedValue = null; if (LIGHTWEIGHT_CONFIG_KEYS.has(key)) { if (typeof FritreeCrypto !== 'undefined' && typeof FritreeCrypto.getStorage === 'function') { decryptedValue = await FritreeCrypto.getStorage(key, defaultValue); } else { const raw = localStorage.getItem(syncHashKey(key)); if (raw) { if (raw.startsWith("fte_fallback_enc:")) { const cipherPart = raw.slice("fte_fallback_enc:".length); const plain = decodeURIComponent(escape(atob(cipherPart))); decryptedValue = JSON.parse(plain); } else { decryptedValue = JSON.parse(raw); } } else { decryptedValue = defaultValue; } } } else { const isMediaKey = key.startsWith("media_blob_") || key.startsWith("media_thumb_") || key.startsWith("media_") || key.startsWith("rot_media_") || key.startsWith("wa_composer_"); const targetStore = isMediaKey ? STORES.MEDIA : routeIndexedDBStore(key); const cipherPacket = await readIndexedDBEntry(targetStore, key); if (cipherPacket === null) { if (typeof FritreeCrypto !== 'undefined' && typeof FritreeCrypto.getStorage === 'function') { decryptedValue = await FritreeCrypto.getStorage(key, defaultValue); } else { decryptedValue = defaultValue; } } else { if (cipherPacket instanceof Blob) { resolve(cipherPacket); return; } const obfuscatedKey = await asyncHashKey(key); const tempPayload = {}; tempPayload[obfuscatedKey] = cipherPacket; if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) { await new Promise(r => chrome.storage.local.set(tempPayload, r)); decryptedValue = await FritreeCrypto.getStorage(key, defaultValue); chrome.storage.local.remove(obfuscatedKey); } else { localStorage.setItem(obfuscatedKey, cipherPacket); decryptedValue = await FritreeCrypto.getStorage(key, defaultValue); localStorage.removeItem(obfuscatedKey); } } } // Run the Double-Anchor verification and reject external settings tampering if (SECURE_SIS_KEYS.has(key) && decryptedValue !== null && decryptedValue !== defaultValue) { const actualComputedSig = await computeSecureSymmetricChecksum(key, decryptedValue); const savedAnchorASig = await readIndexedDBEntry(STORES.SYSTEM, `local_sis_sig_${key}`); const savedAnchorBSig = await readSecondarySignatureAnchor(key); const isAnchorAValid = savedAnchorASig === actualComputedSig; const isAnchorBValid = savedAnchorBSig === actualComputedSig; if (!isAnchorAValid || !isAnchorBValid) { console.warn(`[Fritree Storage] Security Guard: Tampering detected for key: "${key}"! Attempting secure recovery...`); let recoveredValue = null; // Try to recover from IndexedDB Backup (Anchor A) const backupA = await readIndexedDBEntry(STORES.SYSTEM, `local_sis_backup_${key}`); if (backupA !== null) { const sigA = await computeSecureSymmetricChecksum(key, backupA); if (sigA === savedAnchorASig) { recoveredValue = backupA; console.log(`[Fritree Storage] Restored tampered "${key}" from secure IndexedDB backup.`); } } // Try to recover from LocalStorage Backup (Anchor B) if Anchor A was altered if (recoveredValue === null) { if (typeof FritreeCrypto !== 'undefined' && typeof FritreeCrypto.getStorage === 'function') { const backupB = await FritreeCrypto.getStorage(`sis_backup_${key}`, null); if (backupB !== null) { const sigB = await computeSecureSymmetricChecksum(key, backupB); if (sigB === savedAnchorBSig) { recoveredValue = backupB; console.log(`[Fritree Storage] Restored tampered "${key}" from secure LocalStorage backup.`); } } } } if (recoveredValue !== null) { decryptedValue = recoveredValue; // Re-sync signatures const restoredSig = await computeSecureSymmetricChecksum(key, recoveredValue); await writeIndexedDBEntry(STORES.SYSTEM, `local_sis_sig_${key}`, restoredSig); await writeSecondarySignatureAnchor(key, restoredSig); } else { // If both backups are compromised, reject manipulation and revert to safe default state console.error(`[Fritree Storage] Critical Violation: Recovery failed for "${key}". Reverting to safe default.`); decryptedValue = defaultValue; const defaultSig = await computeSecureSymmetricChecksum(key, defaultValue); await writeIndexedDBEntry(STORES.SYSTEM, `local_sis_sig_${key}`, defaultSig); await writeSecondarySignatureAnchor(key, defaultSig); await writeIndexedDBEntry(STORES.SYSTEM, `local_sis_backup_${key}`, defaultValue); if (typeof FritreeCrypto !== 'undefined' && typeof FritreeCrypto.setStorage === 'function') { await FritreeCrypto.setStorage(`sis_backup_${key}`, defaultValue); } } } } resolve(decryptedValue !== null ? decryptedValue : defaultValue); } catch (e) { console.error(`[Fritree Storage] Key retrieval failure for "${key}":`, e); resolve(defaultValue); } }); }, remove: function(key) { return storageWriteQueue.enqueue(async () => { const obfuscatedKey = await asyncHashKey(key); localStorage.removeItem(syncHashKey(key)); localStorage.removeItem(obfuscatedKey); if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) { chrome.storage.local.remove(obfuscatedKey); } const isMediaKey = key.startsWith("media_blob_") || key.startsWith("media_thumb_") || key.startsWith("media_") || key.startsWith("rot_media_") || key.startsWith("wa_composer_"); const targetStore = isMediaKey ? STORES.MEDIA : routeIndexedDBStore(key); try { await deleteIndexedDBEntry(targetStore, key); } catch (e) { console.error("[Fritree Storage] Store deletion error:", e); } if (SECURE_SIS_KEYS.has(key)) { try { await deleteIndexedDBEntry(STORES.SYSTEM, `local_sis_sig_${key}`); await deleteSecondarySignatureAnchor(key); await deleteIndexedDBEntry(STORES.SYSTEM, `local_sis_backup_${key}`); if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) { const obfuscatedBackupKey = await asyncHashKey(`sis_backup_${key}`); chrome.storage.local.remove(obfuscatedBackupKey); } } catch (e) { console.error("[Fritree Storage] Anchor removal error:", e); } } if (!isMediaKey) { try { await deleteIndexedDBEntry(STORES.MEDIA, key); } catch (e) { console.error("[Fritree Storage] Fallback store removal error:", e); } } return true; }); }, clear: function() { return storageWriteQueue.enqueue(async () => { localStorage.clear(); if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) { await new Promise(r => chrome.storage.local.clear(r)); } if (dbInstance) { dbInstance.close(); dbInstance = null; } return new Promise(resolve => { const deleteReq = indexedDB.deleteDatabase(DB_NAME); deleteReq.onsuccess = () => resolve(true); deleteReq.onerror = () => resolve(false); }); }); }, b64ToBlob: base64ToBlob, blobToB64: blobToBase64 }; async function asyncHashKey(key) { if (typeof FritreeCrypto !== 'undefined' && typeof FritreeCrypto.sha256 === 'function') { return "" + await FritreeCrypto.sha256(key + "FritreeKeySalt_2026_StrictSHA256_Hashed_Production_WebCrypto_Salt"); } const input = key + "FritreeKeySalt_2026_StrictSHA256_Hashed_Production_WebCrypto_Salt"; let hash = 0; for (let i = 0; i < input.length; i++) { const char = input.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash |= 0; } return "fte_sh_sha256_" + Math.abs(hash).toString(16); } function syncHashKey(key) { const input = key + "FritreeKeySalt_2026_StrictSHA256_Hashed_Production_WebCrypto_Salt"; let hash = 0; for (let i = 0; i < input.length; i++) { const char = input.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash |= 0; } return "fte_sh_sha256_" + Math.abs(hash).toString(16); } global.FritreeStorage = UnifiedStorageManager; initIndexedDB().then(() => { console.log("[Fritree Storage] Unified IndexedDB system online."); }).catch(err => { console.error("[Fritree Storage] Database upgrade/initialization failed:", err); }); })(typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : this);