Files

125 lines
3.4 KiB
PHP

<?php
declare(strict_types=1);
namespace Core;
class Security
{
public static function hashPassword(string $password): string
{
$algo = defined('PASSWORD_ARGON2ID') ? PASSWORD_ARGON2ID : PASSWORD_BCRYPT;
return password_hash($password, $algo);
}
public static function verifyPassword(string $password, string $hash): bool
{
return password_verify($password, $hash);
}
public static function generateRandomHex(int $bytes = 32): string
{
return bin2hex(random_bytes($bytes));
}
public static function generateHmacSignature(string $data, string $secretKey): string
{
return hash_hmac('sha256', $data, $secretKey);
}
public static function verifyHmacSignature(string $data, string $signature, string $secretKey): bool
{
$calculated = self::generateHmacSignature($data, $secretKey);
return hash_equals($calculated, $signature);
}
public static function encryptAesGcm(string $plaintext, string $key): string
{
$cipher = 'aes-256-gcm';
$keyHash = hash('sha256', $key, true);
$iv = random_bytes(12); // 96-bit IV recommended for GCM
$tag = '';
$ciphertext = openssl_encrypt(
$plaintext,
$cipher,
$keyHash,
OPENSSL_RAW_DATA,
$iv,
$tag,
'',
16
);
return base64_encode($iv . $tag . $ciphertext);
}
public static function decryptAesGcm(string $encryptedPackage, string $key): ?string
{
$data = base64_decode($encryptedPackage, true);
if (!$data || strlen($data) < 28) {
return null;
}
$cipher = 'aes-256-gcm';
$keyHash = hash('sha256', $key, true);
$iv = substr($data, 0, 12);
$tag = substr($data, 12, 16);
$ciphertext = substr($data, 28);
$decrypted = openssl_decrypt(
$ciphertext,
$cipher,
$keyHash,
OPENSSL_RAW_DATA,
$iv,
$tag
);
return $decrypted !== false ? $decrypted : null;
}
public static function generateDynamicQrToken(int $userId, string $membershipNumber, int $expiresInSeconds, string $secretKey): string
{
$payload = [
'uid' => $userId,
'mem' => $membershipNumber,
'exp' => time() + $expiresInSeconds,
'rnd' => self::generateRandomHex(8),
];
$json = json_encode($payload, JSON_UNESCAPED_SLASHES);
$base64Payload = rtrim(strtr(base64_encode($json), '+/', '-_'), '=');
$signature = self::generateHmacSignature($base64Payload, $secretKey);
return $base64Payload . '.' . $signature;
}
public static function verifyDynamicQrToken(string $token, string $secretKey): ?array
{
$parts = explode('.', $token);
if (count($parts) !== 2) {
return null;
}
[$base64Payload, $signature] = $parts;
if (!self::verifyHmacSignature($base64Payload, $signature, $secretKey)) {
return null;
}
$json = base64_decode(strtr($base64Payload, '-_', '+/'));
if (!$json) {
return null;
}
$payload = json_decode($json, true);
if (!is_array($payload) || !isset($payload['exp']) || $payload['exp'] < time()) {
return null; // Expired or invalid format
}
return $payload;
}
}