HMAC, hašovanie a HKDF
Hash\Hmac podpisuje a overuje kľúčované MAC — základ kontroly podpisov webhookov. Tajné kľúče sa odovzdávajú pri každom volaní a sú označené #[\SensitiveParameter]; trieda nedrží žiadny vlastný kľúč:
use RoundlyConsulting\Crypto\Facades\Crypto;
use RoundlyConsulting\Crypto\Hash\HashAlgorithm;
// GitHub-style "sha256=…" header check:
$expected = 'sha256='.Crypto::hmac(HashAlgorithm::Sha256)->signHex($request->getContent(), $webhookSecret);
$ok = Crypto::constantTimeEquals($expected, $request->header('X-Hub-Signature-256', ''));
// Deterministic digest → an indexable lookup column:
$lookupKey = Crypto::digest()->hex($emailLowercased);
// A per-purpose subkey from one root secret (HKDF):
$fingerprintKey = Crypto::hkdf()->derive($rootKey, 32, 'app/fingerprint');Alebo volajte priamo triedy, ktoré fasáda sprístupňuje:
use RoundlyConsulting\Crypto\Hash\Hmac;
use RoundlyConsulting\Crypto\Hash\HashAlgorithm;
$hmac = new Hmac(HashAlgorithm::Sha256);
// GitHub-style "sha256=…" header check:
$expected = 'sha256='.$hmac->signHex($request->getContent(), $webhookSecret);
$ok = hash_equals($expected, $request->header('X-Hub-Signature-256', ''));
// Or verify raw signatures directly (constant-time):
$ok = $hmac->verify($payload, $signature, $webhookSecret);| Metóda | Vracia |
|---|---|
sign($message, $key) | HMAC ako surové bajty. |
signHex($message, $key) | HMAC ako hex malými písmenami. |
verify($message, $signature, $key) | bool — overenie podpisu v surových bajtoch v konštantnom čase. |
Deterministické digesty
Hash\Digest pre rovnaký vstup vždy vráti rovnaký výstup — práve to umožňuje indexované vyhľadávanie podľa zhody nad hašovanými dátami, napríklad slepý index nad e-mailom alebo tokenom:
use RoundlyConsulting\Crypto\Hash\Digest;
$digest = new Digest; // SHA-256
$lookupKey = $digest->hex($emailLowercased); // deterministic → indexable column
$peppered = $digest->withPepper($token, config('app.pepper')); // HMAC when a pepper is set
$raw = $digest->raw($data); // raw byteswithPepper() robí voľbu medzi obyčajným a kľúčovaným digestom explicitnou: obyčajný digest zvolí iba null. Akýkoľvek iný pepper — aj medzera či prázdny reťazec — sa použije doslovne ako HMAC kľúč, takže prázdna hodnota nikdy potichu nezníži kľúčovaný digest na obyčajný. Dáta odovzdané do raw(), hex() a withPepper() — tokeny, heslá, osobné údaje — sú tiež označené #[\SensitiveParameter].
HKDF — podkľúče pre každý účel
Hash\Hkdf implementuje odvodzovanie kľúčov extract-then-expand podľa RFC 5869: z jedného koreňového tajomstva nezávislé podkľúče pre každý účel, takže kľúč, ktorý pečatí hodnoty, nikdy zároveň nepočíta MAC odtlačku:
use RoundlyConsulting\Crypto\Facades\Crypto;
use RoundlyConsulting\Crypto\Hash\HashAlgorithm;
$fingerprintKey = Crypto::hkdf()->derive($rootKey, 32, 'app/fingerprint'); // no salt
$sealKey = Crypto::hkdf(HashAlgorithm::Sha512)->derive($rootKey, 64, 'app/seal', 'app.hkdf/1');Alebo volajte priamo triedu, ktorú fasáda sprístupňuje:
use RoundlyConsulting\Crypto\Hash\Hkdf;
$subkey = (new Hkdf)->derive($rootKey, 32, 'app/fingerprint'); // SHA-256 by defaultderive(string $ikm, int $length, string $info = '', string $salt = '') vracia surové bajty:
- $ikm, vstupný kľúčový materiál, je #[\SensitiveParameter] a nesmie byť prázdny.
- $length je povinná: 1 až 255 × dĺžka hašu (8160 bajtov pre SHA-256). Funkcia hash_hkdf() v PHP číta 0 ako „jedna dĺžka hašu“; tu sa 0 odmietne, takže veľkosť odvodeného kľúča nikdy nie je implicitná.
- $info pomenúva účel. Jeho polia oddeľte bajtom, ktorý žiadne pole nemôže obsahovať, napríklad NUL.
- $salt je hodnota, ktorá nie je tajná (RFC 5869 §3.1); prázdna znamená „neuvedená“ — HashLen nulových bajtov.
- Výstup je bajt po bajte zhodný s hash_hkdf(), čo pripínajú vektory z prílohy A RFC 5869 (SHA-256 aj SHA-1). SHA-1 zostáva dostupné len pre kompatibilitu (HashAlgorithm::isLegacy()).
- Prázdny $ikm alebo $length mimo rozsahu vyhodí Hash\InvalidHkdfParameterException — programátorská chyba, nikdy nie vlastnosť dát.
Porovnanie v konštantnom čase
ConstantTime::equals() obaľuje hash_equals, aby každé porovnanie tajného kľúča, tagu či MAC prechádzalo jedným miestom a časovaním neprezradilo, aká časť hodnoty sa zhodovala. Oba vstupy sú #[\SensitiveParameter], rovnako ako pri hash_equals() v PHP: očakávaná hodnota je samotné tajomstvo, MAC, token či OTP, takže ani jeden sa nikdy nedostane do stack trace:
use RoundlyConsulting\Crypto\Hash\ConstantTime;
// $known is the trusted value, $user the attacker-influenced one:
$ok = ConstantTime::equals($knownExpected, $userSupplied);Hašovacie algoritmy
| Prípad | Hodnota | Stav |
|---|---|---|
HashAlgorithm::Sha1 | sha1 | Zastaraný — prelomený kolíziami, len pre kompatibilitu; isLegacy() vracia true. |
HashAlgorithm::Sha256 | sha256 | Aktuálny (predvolený). |
HashAlgorithm::Sha384 | sha384 | Aktuálny. |
HashAlgorithm::Sha512 | sha512 | Aktuálny. |
OTP je oddelené: RFC 6238 predpisuje SHA-1 a autentifikačné aplikácie ho používajú predvolene, preto Otp\OtpAlgorithm ponecháva SHA-1 ako plnohodnotnú voľbu.
Prejavte lásku k open source
Tento balík je zadarmo pod licenciou MIT. Ak vám šetrí čas, jednorazový príspevok alebo členstvo na Patreone nám pomôže ho ďalej udržiavať, testovať a dokumentovať.
Ďalšie spôsoby podpory vrátane kryptomienOdoslaním daru súhlasíte s našimi podmienkami prijímania darov.
Chcete to zabudovať do svojho produktu?
Naše balíky integrujeme do zákazkových Laravel a AI riešení. Napíšte nám, na čom pracujete, a ozveme sa do 48 hodín.