Fix AES-GCM format mismatch between JS Web Crypto and PHP openssl

Web Crypto API AES-GCM outputs: iv + ciphertext + tag (tag appended)
PHP openssl was using: iv + tag + ciphertext (tag in middle)

Now both use the same format: iv (12 bytes) + ciphertext + tag (16 bytes).
Decrypt tries JS format first, falls back to PHP format for compatibility.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Serreau Jovann
2026-03-26 12:07:08 +01:00
parent d57669b5b9
commit ce1b153cc1

View File

@@ -20,7 +20,8 @@ class AnalyticsCryptoService
$iv = random_bytes(12);
$encrypted = openssl_encrypt($json, 'aes-256-gcm', $this->key, \OPENSSL_RAW_DATA, $iv, $tag, '', 16);
return base64_encode($iv.$tag.$encrypted);
// Format compatible with Web Crypto API: iv + ciphertext + tag
return base64_encode($iv.$encrypted.$tag);
}
public function decrypt(string $payload): ?array
@@ -30,7 +31,21 @@ class AnalyticsCryptoService
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);