| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566 |
- <?php
- namespace Libs;
- /**
- * Assinatura e verificação HMAC-SHA256.
- *
- * Usado para autenticar webhooks máquina-a-máquina: quem envia calcula
- * HMAC_SHA256(corpo, segredo) e manda no header; quem recebe recalcula com o
- * mesmo segredo e compara. Como o segredo nunca trafega, só quem o possui
- * consegue gerar uma assinatura válida — e qualquer alteração no corpo
- * invalida a assinatura.
- *
- * O segredo é compartilhado entre os dois lados por um canal seguro e, neste
- * projeto, é armazenado por empresa (company.company_hmac_secret).
- */
- final class Hmac
- {
- private const ALGO = 'sha256';
- /**
- * Prefixo opcional aceito no header de assinatura (ex.: "sha256=ab12...").
- * Muitos emissores usam esse formato; aceitamos com ou sem prefixo.
- */
- private const PREFIX = 'sha256=';
- /**
- * Gera um novo segredo HMAC forte (32 bytes -> 64 caracteres hexadecimais).
- * Deve ser usado ao provisionar a integração de CRM de uma empresa.
- */
- public static function generateSecret(): string
- {
- return bin2hex(random_bytes(32));
- }
- /**
- * Calcula a assinatura hexadecimal do corpo com o segredo informado.
- */
- public static function sign(string $body, string $secret): string
- {
- return hash_hmac(self::ALGO, $body, $secret);
- }
- /**
- * Verifica se a assinatura recebida corresponde ao corpo, usando o segredo.
- *
- * - Rejeita quando segredo ou assinatura estão vazios (config ausente).
- * - Aceita a assinatura com ou sem o prefixo "sha256=".
- * - Compara em tempo constante (hash_equals) para evitar timing attacks.
- */
- public static function verify(string $body, string $secret, string $providedSignature): bool
- {
- if ($secret === '' || $providedSignature === '') {
- return false;
- }
- $provided = $providedSignature;
- if (str_starts_with($provided, self::PREFIX)) {
- $provided = substr($provided, strlen(self::PREFIX));
- }
- $expected = self::sign($body, $secret);
- return hash_equals($expected, $provided);
- }
- }
|