key = $key; // IV القديم للتوافقية أثناء مرحلة المايغريشن $this->cbcIv = $cbcIv ?: getenv('initializationVector') ?: str_repeat('0', 16); $mode = strtolower($writeMode ?: (getenv('ENCRYPTION_MODE') ?: 'cbc')); $this->writeMode = $mode === 'gcm' ? 'gcm' : 'cbc'; } public function writeMode(): string { return $this->writeMode; } /** * نقطة التشفير الموحّدة لكل التطبيق. * * حتى الآن كانت CBC بـ IV ثابت، أي حتمية: نفس النص ينتج نفس التشفير، وهو * ما كان يسمح بالبحث عبر مقارنة النص المشفّر، لكنه يسرّب المساواة * والبادئات المشتركة. مع ENCRYPTION_MODE=gcm يصبح التشفير عشوائياً * وموثَّقاً، ويتكفّل الفهرس الأعمى (BlindIndex) بالبحث. */ public function encryptData(string $plainText): string { if ($this->writeMode === 'gcm') { return $this->encryptDataGCM($plainText); } return $this->encryptDataCBC($plainText); } // ─── تشفير نص باستخدام AES-256-CBC الحتمي (للتوافقية والرجوع) ── public function encryptDataCBC(string $plainText): string { $plainText = mb_convert_encoding($plainText, 'UTF-8'); $padded = $this->addPadding($plainText); $encrypted = openssl_encrypt($padded, self::ALGO_CBC, $this->key, OPENSSL_RAW_DATA, $this->cbcIv); return base64_encode($encrypted); } // ─── تشفير نص باستخدام AES-256-GCM العشوائي (عالي الأمان) ── public function encryptDataGCM(string $plainText): string { $plainText = mb_convert_encoding($plainText, 'UTF-8'); $iv = random_bytes(self::IV_LEN_GCM); $tag = ''; $encrypted = openssl_encrypt($plainText, self::ALGO_GCM, $this->key, OPENSSL_RAW_DATA, $iv, $tag, "", self::TAG_LEN); return self::PREFIX_GCM . base64_encode($iv . $tag . $encrypted); } // ─── فك تشفير نص (يدعم CBC والـ GCM المستقبلي) ─────────── public function decryptData(?string $cipherText): string|false { if (empty($cipherText)) return ''; // تحقق إن كان مشفر بالنظام الجديد if (str_starts_with($cipherText, self::PREFIX_GCM)) { $raw = base64_decode(substr($cipherText, strlen(self::PREFIX_GCM)), true); if ($raw === false || strlen($raw) < self::IV_LEN_GCM + self::TAG_LEN) return false; $iv = substr($raw, 0, self::IV_LEN_GCM); $tag = substr($raw, self::IV_LEN_GCM, self::TAG_LEN); $cipher = substr($raw, self::IV_LEN_GCM + self::TAG_LEN); $plain = openssl_decrypt($cipher, self::ALGO_GCM, $this->key, OPENSSL_RAW_DATA, $iv, $tag); return $plain !== false ? $plain : false; } // وإلا استخدم CBC القديم $decoded = base64_decode($cipherText, true); if ($decoded === false) return false; $decrypted = openssl_decrypt($decoded, self::ALGO_CBC, $this->key, OPENSSL_RAW_DATA, $this->cbcIv); if ($decrypted === false) return false; $pad = ord($decrypted[strlen($decrypted) - 1]); if ($pad < 1 || $pad > 16) return false; return substr($decrypted, 0, -$pad); } // ─── تشفير/فك تشفير Binary (صور، ملفات) ─────────────── // تُستخدم الـ GCM مع IV عشوائي (كما في encryptData) public function encryptBinary(string $data): string { $iv = random_bytes(self::IV_LEN_GCM); $tag = ''; $encrypted = openssl_encrypt($data, self::ALGO_GCM, $this->key, OPENSSL_RAW_DATA, $iv, $tag, "", self::TAG_LEN); return base64_encode($iv . $tag . $encrypted); } public function decryptBinary(string $data): string|false { $raw = base64_decode($data, true); if ($raw === false || strlen($raw) < self::IV_LEN_GCM + self::TAG_LEN) return false; $iv = substr($raw, 0, self::IV_LEN_GCM); $tag = substr($raw, self::IV_LEN_GCM, self::TAG_LEN); $cipher = substr($raw, self::IV_LEN_GCM + self::TAG_LEN); return openssl_decrypt($cipher, self::ALGO_GCM, $this->key, OPENSSL_RAW_DATA, $iv, $tag); } // --------- دوال الـ Padding للـ CBC ---------- private function addPadding($data, $blockSize = 16) { $pad = $blockSize - (strlen($data) % $blockSize); return $data . str_repeat(chr($pad), $pad); } private function removePadding($data) { $pad = ord($data[strlen($data) - 1]); return substr($data, 0, -$pad); } }