/** * @fileoverview Encrypted Unified Storage Manager (IndexedDB + Storage Layer + SIS Seal Protection) * @module storage/storage-manager * @description مدير التخزين الموحد المشفر عبر IndexedDB مع طابور تسلسلي للكتابة وأختام أمان رقمية لمقاومة التلاعب. */ (global => { 'use strict'; const DB_NAME = global.FritreeAppConstants?.STORAGE_CONSTANTS?.DB_NAME || "9a8b7c6d5e4f3a2b1c0"; const DB_VERSION = global.FritreeAppConstants?.STORAGE_CONSTANTS?.DB_VERSION || 6; const STORES = global.FritreeAppConstants?.STORAGE_CONSTANTS?.STORES || Object.freeze({ MEDIA: "t7a8b9c0d", HISTORY: "q1e2f3a4b", SYSTEM: "a5c6d7e8f" }); const SECURE_SIS_KEYS = new Set( global.FritreeAppConstants?.STORAGE_CONSTANTS?.SECURE_SIS_KEYS || [ "acc_userLevel", "acc_userXP", "acc_lifetimeFb", "acc_lifetimeWa", "acc_lifetimeTasks", "local_previous_posts", "wa_connected", "wa_sent_today", "acc_fbPoints", "acc_waPoints", "usdPoints", "local_contacts_database", "local_calendar_events_db", "local_post_groups", "scheduledCampaignsData" ] ); const INTEGRITY_SEAL_SALT = global.FritreeAppConstants?.SECURITY_SALTS?.STORAGE_SEAL_SALT || "FritreeStorageIntegritySealSalt_V5_AEAD_AES256"; const HASH_KEY_SALT = global.FritreeAppConstants?.SECURITY_SALTS?.HASH_KEY_SALT || "FritreeStorageKeyObfuscationSalt_2026_StrictSHA256_AEADMasterLock"; let dbInstance = null; let isDbInitializing = false; const dbInitPromiseResolvers = []; /** * معالجة الأخطاء الصامتة */ function silentTrap(err) { if (err && console && console.warn) { console.warn("[Fritree Storage Core SafeTrap]", err); } return null; } /** * طابور الكتابة المتسلسل غير المتزامن (Async Write Queue) * يضمن تنفيذ عمليات الكتابة بالترتيب ومنع تضارب الـ IndexedDB Transactions */ 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) { silentTrap(err); resolve(false); } finally { this.isProcessing = false; this.processNext(); } } } const storageWriteQueue = new AsyncWriteQueue(); /** * تهيئة وفتح قاعدة بيانات IndexedDB */ 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) => { try { const request = indexedDB.open(DB_NAME, DB_VERSION); request.onupgradeneeded = event => { try { const db = event.target.result; Object.values(STORES).forEach(storeName => { if (!db.objectStoreNames.contains(storeName)) { db.createObjectStore(storeName, { keyPath: "id" }); } }); } catch (e) { silentTrap(e); } }; 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; resolve(null); dbInitPromiseResolvers.forEach(r => r.resolve(null)); dbInitPromiseResolvers.length = 0; }; } catch (err) { isDbInitializing = false; resolve(null); } }); } /** * تحويل Base64 إلى Blob */ function base64ToBlob(base64Data, mimeType = 'image/jpeg') { try { if (!base64Data || typeof base64Data !== 'string') { return new Blob([], { type: mimeType }); } const parts = base64Data.split(","); const rawB64 = parts.length > 1 ? parts[1] : parts[0]; const detectedMime = parts.length > 1 ? parts[0].split(":")[1].split(";")[0] : mimeType; const byteCharacters = atob(rawB64); 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: detectedMime }); } catch (e) { silentTrap(e); return new Blob([], { type: mimeType }); } } /** * تحويل Blob إلى Base64 */ async function blobToBase64(blob) { try { if (!(blob instanceof Blob)) return null; if (typeof FileReader !== 'undefined') { return new Promise((resolve) => { const reader = new FileReader(); reader.onloadend = () => resolve(reader.result); reader.onerror = () => resolve(null); reader.readAsDataURL(blob); }); } const buffer = await blob.arrayBuffer(); const bytes = new Uint8Array(buffer); let binary = ''; const len = bytes.byteLength; const chunk = 8192; for (let i = 0; i < len; i += chunk) { const slice = bytes.subarray(i, i + chunk); binary += String.fromCharCode.apply(null, slice); } return `data:${blob.type || 'image/jpeg'};base64,${btoa(binary)}`; } catch (err) { silentTrap(err); return null; } } /** * تشويش وتعمية مفاتيح التخزين */ async function asyncHashKey(key) { try { if (global.FritreeCrypto && typeof global.FritreeCrypto.sha256 === 'function') { const hashHex = await global.FritreeCrypto.sha256(String(key) + HASH_KEY_SALT); return "fte_sh_v5_" + hashHex.substring(0, 32); } let hash = 0; const input = String(key) + HASH_KEY_SALT; for (let i = 0; i < input.length; i++) { hash = ((hash << 5) - hash) + input.charCodeAt(i); hash |= 0; } return "fte_sh_v5_" + Math.abs(hash).toString(16); } catch (e) { silentTrap(e); return "fallback_hash_" + String(key).length; } } function syncHashKey(key) { try { let hash = 0; const input = String(key) + HASH_KEY_SALT; for (let i = 0; i < input.length; i++) { hash = ((hash << 5) - hash) + input.charCodeAt(i); hash |= 0; } return "fte_sh_v5_" + Math.abs(hash).toString(16); } catch (e) { silentTrap(e); return "fallback_sync_" + String(key).length; } } /** * كتابة سجل داخل IndexedDB */ async function writeIndexedDBEntry(storeName, key, encryptedValue) { try { const db = await initIndexedDB(); if (!db) return false; const obfuscatedKey = await asyncHashKey(key); return new Promise((resolve) => { const transaction = db.transaction([storeName], "readwrite"); const store = transaction.objectStore(storeName); const putRequest = store.put({ id: obfuscatedKey, rawKeyName: key, payload: encryptedValue, updated_at: Date.now() }); putRequest.onsuccess = () => resolve(true); putRequest.onerror = () => resolve(false); }); } catch (e) { silentTrap(e); return false; } } /** * قراءة سجل من IndexedDB */ async function readIndexedDBEntry(storeName, key) { try { const db = await initIndexedDB(); if (!db) return null; const obfuscatedKey = await asyncHashKey(key); return new Promise((resolve) => { const transaction = db.transaction([storeName], "readonly"); const store = transaction.objectStore(storeName); const getRequest = store.get(obfuscatedKey); getRequest.onsuccess = e => { const record = e.target.result; resolve(record ? record.payload : null); }; getRequest.onerror = () => resolve(null); }); } catch (e) { silentTrap(e); return null; } } /** * حذف سجل من IndexedDB */ async function deleteIndexedDBEntry(storeName, key) { try { const db = await initIndexedDB(); if (!db) return false; const obfuscatedKey = await asyncHashKey(key); return new Promise((resolve) => { const transaction = db.transaction([storeName], "readwrite"); const store = transaction.objectStore(storeName); const deleteRequest = store.delete(obfuscatedKey); deleteRequest.onsuccess = () => resolve(true); deleteRequest.onerror = () => resolve(false); }); } catch (e) { silentTrap(e); return false; } } /** * توجيه المفتاح لمخزن الـ IndexedDB المناسب */ function routeIndexedDBStore(key) { try { if (key === "campaignHistoryData") { return STORES.HISTORY; } else if (key === "local_previous_posts" || key.startsWith("media_") || key.startsWith("rot_media_") || key.startsWith("wa_composer_")) { return STORES.MEDIA; } return STORES.SYSTEM; } catch (e) { return STORES.SYSTEM; } } /** * حساب فحص الأمان المتناظر لأختام SIS */ async function computeSecureSymmetricChecksum(key, data) { try { let stringData = ""; if (key === 'usdPoints') { stringData = parseFloat(data || 0).toFixed(2); } else if (typeof data === 'object' && data !== null) { stringData = JSON.stringify(data); } else { stringData = String(data !== undefined && data !== null ? data : ""); } const payloadString = `${key}:${stringData}:${INTEGRITY_SEAL_SALT}`; if (global.FritreeCrypto && typeof global.FritreeCrypto.sha256 === 'function') { return await global.FritreeCrypto.sha256(payloadString); } let hash = 0; for (let i = 0; i < payloadString.length; i++) { hash = ((hash << 5) - hash) + payloadString.charCodeAt(i); hash |= 0; } return "fallback_sha256_" + Math.abs(hash).toString(16); } catch (e) { silentTrap(e); return "hash_error"; } } async function writeSecondarySignatureAnchor(key, signature) { try { 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; } } catch (e) { silentTrap(e); return false; } } async function readSecondarySignatureAnchor(key) { try { 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 ? payload[obfuscatedSigKey] : null); }); }); } else { return localStorage.getItem(syncHashKey(`sis_anchor_sig_${key}`)) || null; } } catch (e) { silentTrap(e); return null; } } async function deleteSecondarySignatureAnchor(key) { try { 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; } } catch (e) { silentTrap(e); return false; } } /** * كائن إدارة التخزين الموحد النهائي (Unified Storage API) */ const UnifiedStorageManager = { /** * تخزين عنصر مشفر * @param {string} key * @param {*} value * @returns {Promise} */ set: function(key, value) { return storageWriteQueue.enqueue(async () => { try { if (global.FritreeCrypto && typeof global.FritreeCrypto.validateData === 'function') { if (!global.FritreeCrypto.validateData(value, key)) { console.error("[Fritree Storage] Data validation failed for key:", key); return false; } } 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); await writeIndexedDBEntry(STORES.SYSTEM, `local_sis_backup_${key}`, value); } if (value instanceof Blob) { try { const targetStore = STORES.MEDIA; await writeIndexedDBEntry(targetStore, key, value); return true; } catch (dbErr) { silentTrap(dbErr); return false; } } const obfuscatedKey = await asyncHashKey(key); const encryptedValue = await global.FritreeCrypto.encryptAndSignPayload(value, key); if (!encryptedValue) { console.error("[Fritree Storage] Encryption failed for key:", key); return false; } if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) { const payload = {}; payload[obfuscatedKey] = encryptedValue; await new Promise(resolve => chrome.storage.local.set(payload, resolve)); } else { localStorage.setItem(obfuscatedKey, encryptedValue); } const targetStore = routeIndexedDBStore(key); await writeIndexedDBEntry(targetStore, key, encryptedValue); return true; } catch (ex) { silentTrap(ex); return false; } }); }, /** * جلب وفك تشفير عنصر مع التحقق من الأختام واستعادة النسخ التلقائية * @param {string} key * @param {*} defaultValue * @returns {Promise<*>} */ get: function(key, defaultValue = null) { return new Promise(async resolve => { try { let decryptedValue = null; let cipherPacketFound = false; const obfuscatedKey = await asyncHashKey(key); let cipherPacket = null; if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) { cipherPacket = await new Promise(r => { chrome.storage.local.get(obfuscatedKey, result => { r(result ? result[obfuscatedKey] : null); }); }); } else { cipherPacket = localStorage.getItem(obfuscatedKey); } if (cipherPacket !== null && cipherPacket !== undefined) { cipherPacketFound = true; } else { const targetStore = routeIndexedDBStore(key); cipherPacket = await readIndexedDBEntry(targetStore, key); if (cipherPacket !== null && cipherPacket !== undefined) { cipherPacketFound = true; } } if (cipherPacket instanceof Blob) { resolve(cipherPacket); return; } if (cipherPacketFound && typeof cipherPacket === 'string') { decryptedValue = await global.FritreeCrypto.verifyAndDecryptPayload(cipherPacket, key); } // تدقيق أختام الحماية (SIS Seals) if (SECURE_SIS_KEYS.has(key)) { if (decryptedValue === null) { if (cipherPacketFound) { console.warn("[Fritree Storage] Primary storage corrupted for key:", key, "- Restoring uncorrupted backup..."); } const restoredBackup = await readIndexedDBEntry(STORES.SYSTEM, `local_sis_backup_${key}`); if (restoredBackup !== null) { decryptedValue = restoredBackup; const restoredSig = await computeSecureSymmetricChecksum(key, restoredBackup); await writeIndexedDBEntry(STORES.SYSTEM, `local_sis_sig_${key}`, restoredSig); await writeSecondarySignatureAnchor(key, restoredSig); await this.set(key, restoredBackup); } } else { const actualComputedSig = await computeSecureSymmetricChecksum(key, decryptedValue); const savedAnchorASig = await readIndexedDBEntry(STORES.SYSTEM, `local_sis_sig_${key}`); const savedAnchorBSig = await readSecondarySignatureAnchor(key); if (!savedAnchorASig && !savedAnchorBSig) { await writeIndexedDBEntry(STORES.SYSTEM, `local_sis_sig_${key}`, actualComputedSig); await writeSecondarySignatureAnchor(key, actualComputedSig); await writeIndexedDBEntry(STORES.SYSTEM, `local_sis_backup_${key}`, decryptedValue); } else if (savedAnchorASig && savedAnchorBSig) { const isAValid = savedAnchorASig === actualComputedSig; const isBValid = savedAnchorBSig === actualComputedSig; if (!isAValid || !isBValid) { console.error("[Fritree Storage] TAMPER DETECTED via SIS Seals for key:", key, "- Restoring uncorrupted backup."); const restoredValue = await readIndexedDBEntry(STORES.SYSTEM, `local_sis_backup_${key}`); if (restoredValue !== null) { decryptedValue = restoredValue; const restoredSig = await computeSecureSymmetricChecksum(key, restoredValue); await writeIndexedDBEntry(STORES.SYSTEM, `local_sis_sig_${key}`, restoredSig); await writeSecondarySignatureAnchor(key, restoredSig); await this.set(key, restoredValue); } } } } } resolve(decryptedValue !== null ? decryptedValue : defaultValue); } catch (e) { silentTrap(e); resolve(defaultValue); } }); }, /** * حذف عنصر مشفر */ remove: function(key) { return storageWriteQueue.enqueue(async () => { try { 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 targetStore = routeIndexedDBStore(key); await deleteIndexedDBEntry(targetStore, key); if (SECURE_SIS_KEYS.has(key)) { await deleteIndexedDBEntry(STORES.SYSTEM, `local_sis_sig_${key}`); await deleteSecondarySignatureAnchor(key); await deleteIndexedDBEntry(STORES.SYSTEM, `local_sis_backup_${key}`); } return true; } catch (e) { silentTrap(e); return false; } }); }, /** * مسح كامل قاعدة البيانات */ clear: function() { return storageWriteQueue.enqueue(async () => { try { 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); }); } catch (e) { silentTrap(e); return false; } }); }, b64ToBlob: base64ToBlob, blobToB64: blobToBase64 }; Object.freeze(UnifiedStorageManager); Object.defineProperty(global, 'FritreeStorage', { value: UnifiedStorageManager, writable: false, configurable: false }); initIndexedDB().catch(err => silentTrap(err)); })(typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : this);