Files
intaleq_v2/app/Services/PayloadCrypto.php
2026-04-22 23:16:23 +03:00

106 lines
3.2 KiB
PHP

<?php
namespace App\Services;
/**
* خدمة تشفير حمولة البيانات (Payload Crypto Service)
*
* الغرض من الملف:
* تشفير وفك تشفير البيانات المتبادلة بين التطبيق (Flutter) والخادم (API) باستخدام تقنية AES-256-GCM.
*
* كيفية العمل:
* 1. يولد مفتاح تشفير ديناميكي وفريد لكل طلب (IV).
* 2. يضمن خصوصية البيانات (تشفير) وسلامتها (منع التلاعب عبر الـ Authentication Tag).
* 3. يختلف عن التشفير القديم (Legacy) بأنه أكثر أماناً ويستخدم معايير تشفير حديثة.
*/
class PayloadCrypto
{
private string $key;
private const CIPHER = 'aes-256-gcm';
private const IV_LENGTH = 12;
private const TAG_LENGTH = 16;
public function __construct()
{
$keyPath = config('intaleq.legacy_enc_key_path');
if (!file_exists($keyPath)) {
throw new \RuntimeException('Encryption key not found');
}
// Derive a 32-byte key from the stored key using HKDF
$rawKey = trim(file_get_contents($keyPath));
$this->key = hash_hkdf('sha256', $rawKey, 32, 'intaleq-v2-gcm');
}
/**
* Encrypt payload for sending to Flutter app
*
* @param array|string $data Data to encrypt
* @return string Base64 encoded (IV + ciphertext + tag)
*/
public function encrypt($data): string
{
$plaintext = is_array($data) ? json_encode($data) : $data;
$iv = random_bytes(self::IV_LENGTH);
$tag = '';
$ciphertext = openssl_encrypt(
$plaintext,
self::CIPHER,
$this->key,
OPENSSL_RAW_DATA,
$iv,
$tag,
'', // Additional Authenticated Data (AAD)
self::TAG_LENGTH
);
if ($ciphertext === false) {
throw new \RuntimeException('Encryption failed');
}
// Pack: IV (12) + ciphertext (variable) + tag (16)
return base64_encode($iv . $ciphertext . $tag);
}
/**
* Decrypt payload received from Flutter app
*
* @param string $encoded Base64 encoded (IV + ciphertext + tag)
* @return string|null Decrypted plaintext or null on failure
*/
public function decrypt(string $encoded): ?string
{
$raw = base64_decode($encoded, true);
if ($raw === false || strlen($raw) < self::IV_LENGTH + self::TAG_LENGTH + 1) {
return null;
}
$iv = substr($raw, 0, self::IV_LENGTH);
$tag = substr($raw, -self::TAG_LENGTH);
$ciphertext = substr($raw, self::IV_LENGTH, -self::TAG_LENGTH);
$plaintext = openssl_decrypt(
$ciphertext,
self::CIPHER,
$this->key,
OPENSSL_RAW_DATA,
$iv,
$tag
);
return $plaintext !== false ? $plaintext : null;
}
/**
* Decrypt and decode JSON payload
*/
public function decryptJson(string $encoded): ?array
{
$plaintext = $this->decrypt($encoded);
if (!$plaintext) return null;
$data = json_decode($plaintext, true);
return is_array($data) ? $data : null;
}
}