Files
e-ticket/src/Service/AnalyticsCryptoService.php
Serreau Jovann 98b0b41064 Use SECRET_ANALYTICS env var, regenerated at each deployment
- New SECRET_ANALYTICS variable replaces kernel.secret for analytics
- Ansible generates a random 32-char secret at each deploy
- Endpoint token and encryption key change with every deployment
- Existing sessions will get new visitor_id after deploy (expected)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 12:27:05 +01:00

75 lines
2.2 KiB
PHP

<?php
namespace App\Service;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
class AnalyticsCryptoService
{
private string $key;
public function __construct(
#[Autowire(env: 'SECRET_ANALYTICS')] private string $analyticsSecret,
) {
$this->key = substr(hash('sha256', $this->analyticsSecret, true), 0, 32);
}
public function encrypt(array $data): string
{
$json = json_encode($data, \JSON_THROW_ON_ERROR);
$iv = random_bytes(12);
$encrypted = openssl_encrypt($json, 'aes-256-gcm', $this->key, \OPENSSL_RAW_DATA, $iv, $tag, '', 16);
// Format compatible with Web Crypto API: iv + ciphertext + tag
return base64_encode($iv.$encrypted.$tag);
}
public function decrypt(string $payload): ?array
{
$raw = base64_decode($payload, true);
if (false === $raw || \strlen($raw) < 28) {
return null;
}
// Try JS format first: iv (12) + ciphertext_with_tag (tag is last 16 bytes)
$iv = substr($raw, 0, 12);
$ciphertextWithTag = substr($raw, 12);
if (\strlen($ciphertextWithTag) >= 16) {
$tag = substr($ciphertextWithTag, -16);
$encrypted = substr($ciphertextWithTag, 0, -16);
$json = openssl_decrypt($encrypted, 'aes-256-gcm', $this->key, \OPENSSL_RAW_DATA, $iv, $tag);
if (false !== $json) {
return json_decode($json, true);
}
}
// Fallback: PHP format iv (12) + tag (16) + ciphertext
$tag = substr($raw, 12, 16);
$encrypted = substr($raw, 28);
$json = openssl_decrypt($encrypted, 'aes-256-gcm', $this->key, \OPENSSL_RAW_DATA, $iv, $tag);
if (false === $json) {
return null;
}
return json_decode($json, true);
}
public function generateVisitorHash(string $uid): string
{
return hash_hmac('sha256', $uid, $this->key);
}
public function verifyVisitorHash(string $uid, string $hash): bool
{
return hash_equals($this->generateVisitorHash($uid), $hash);
}
public function getKeyForJs(): string
{
return base64_encode($this->key);
}
}