PHP 8.1.31 Released!

hash_hmac

(PHP 5 >= 5.1.2, PHP 7, PHP 8, PECL hash >= 1.1)

hash_hmacГенерирует хеш-код на основе ключа через метод HMAC

Описание

hash_hmac(
    string $algo,
    string $data,
    #[\SensitiveParameter] string $key,
    bool $binary = false
): string

Список параметров

algo

Название алгоритма хеширования (например, "sha256"). Список алгоритмов, которые поддерживает функция, приводит описание функции hash_hmac_algos().

Замечание:

Некриптографические хеш-функции не допускаются.

data

Сообщение для хеширования.

key

Общий секретный ключ, используемый для генерации HMAC хеш-кода.

binary

Функция выводит необработанные двоичные данные, если для параметра установили значение true. При значении false функция выводит данные в шестнадцатеричной кодировке в нижнем регистре.

Возвращаемые значения

Функция возвращает строку, которая содержит вычисленный хеш-код в шестнадцатеричной кодировке в нижнем регистре. Функция возвращает хеш-код в виде бинарных данных, если для параметра binary установили значение true.

Ошибки

Функция выбрасывает исключение ValueError, если параметр algo неизвестен или не криптографическая хеш-функция.

Список изменений

Версия Описание
8.0.0 Теперь функция выбрасывает исключение ValueError, если алгоритм algo неизвестен или не криптографическая хеш-функция; раньше вместо этого возвращалось значение false.
7.2.0 Запретили некриптографические хеш-функции (adler32, crc32, crc32b, fnv132, fnv1a32, fnv164, fnv1a64, joaat).

Примеры

Пример #1 Пример использования функции hash_hmac()

<?php

echo hash_hmac('sha256', 'Наглый коричневый лисёнок прыгает вокруг ленивой собаки.', 'secret');

?>

Результат выполнения приведённого примера:

bc83c8fabc807cabbbb087bf90c760888349b223b5ba0a35251f7b37b05bf9c9

Смотрите также

  • hash_hmac_algos() - Возвращает список зарегистрированных алгоритмов хеширования, применимых для hash_hmac
  • hash_hmac_file() - Генерирует хеш-значение на основе ключа через метод HMAC и содержимое файла
  • hash_equals() - Сравнивает строки без риска атаки по времени

Добавить

Примечания пользователей 11 notes

up
123
Korbendallas
6 years ago
Very important notice, if you pass array to $data, php will generate a Warning, return a NULL and continue your application. Which I think is critical vulnerability as this function used to check authorisation typically.

Example:
<?php
var_dump
(hash_hmac('sha256', [], 'secret'));

WARNING hash_hmac() expects parameter 2 to be string, array given on line number 3
NULL
?>
Of course not documented feature.
up
84
Michael
11 years ago
Please be careful when comparing hashes. In certain cases, information can be leaked by using a timing attack. It takes advantage of the == operator only comparing until it finds a difference in the two strings. To prevent it, you have two options.

Option 1: hash both hashed strings first - this doesn't stop the timing difference, but it makes the information useless.

<?php
if (md5($hashed_value) === md5($hashed_expected)) {
echo
"hashes match!";
}
?>

Option 2: always compare the whole string.

<?php
if (hash_compare($hashed_value, $hashed_expected)) {
echo
"hashes match!";
}

function
hash_compare($a, $b) {
if (!
is_string($a) || !is_string($b)) {
return
false;
}

$len = strlen($a);
if (
$len !== strlen($b)) {
return
false;
}

$status = 0;
for (
$i = 0; $i < $len; $i++) {
$status |= ord($a[$i]) ^ ord($b[$i]);
}
return
$status === 0;
}
?>
up
62
Michiel Thalen, Thalent
8 years ago
As Michael uggests we should take care not to compare the hash using == (or ===). Since PHP version 5.6 we can now use hash_equals().

So the example will be:

<?php
if (hash_equals($hashed_expected, $hashed_value) ) {
echo
"hashes match!";
}
?>
up
5
pbuttelli at tutanota dot com
1 year ago
While implementing a TOTP application, please note that hash_hmac() must receive data in binary, not in a hexadecimal string, to generate a valid OTP across platforms.

This problem can be easily fixed by converting a hexadecimal string to its binary form before passing it to hash_hmac().

<?php
$time
= hex2bin('0000000003523f77'); // time must be in this "hexadecimal and padded" form
$key = hex2bin('bb57d1...'); // 160-bits = 40-digit hexadecimal (4 bits) = 32-digit base32 (5 bits)

hash_hmac('sha1', $time, $key);
?>
up
8
KC Cloyd
15 years ago
Sometimes a hosting provider doesn't provide access to the Hash extension. Here is a clone of the hash_hmac function you can use in the event you need an HMAC generator and Hash is not available. It's only usable with MD5 and SHA1 encryption algorithms, but its output is identical to the official hash_hmac function (so far at least).

<?php

function custom_hmac($algo, $data, $key, $raw_output = false)
{
$algo = strtolower($algo);
$pack = 'H'.strlen($algo('test'));
$size = 64;
$opad = str_repeat(chr(0x5C), $size);
$ipad = str_repeat(chr(0x36), $size);

if (
strlen($key) > $size) {
$key = str_pad(pack($pack, $algo($key)), $size, chr(0x00));
} else {
$key = str_pad($key, $size, chr(0x00));
}

for (
$i = 0; $i < strlen($key) - 1; $i++) {
$opad[$i] = $opad[$i] ^ $key[$i];
$ipad[$i] = $ipad[$i] ^ $key[$i];
}

$output = $algo($opad.pack($pack, $algo($ipad.$data)));

return (
$raw_output) ? pack($pack, $output) : $output;
}

?>

Example Use:

<?php

custom_hmac
('sha1', 'Hello, world!', 'secret', true);

?>
up
5
pete dot walker at NOSPAM dot me dot com
12 years ago
A function implementing the algorithm outlined in RFC 6238 (http://tools.ietf.org/html/rfc6238)

<?php
/**
* This function implements the algorithm outlined
* in RFC 6238 for Time-Based One-Time Passwords
*
* @link http://tools.ietf.org/html/rfc6238
* @param string $key the string to use for the HMAC key
* @param mixed $time a value that reflects a time (unix
* time in the example)
* @param int $digits the desired length of the OTP
* @param string $crypto the desired HMAC crypto algorithm
* @return string the generated OTP
*/
function oauth_totp($key, $time, $digits=8, $crypto='sha256')
{
$digits = intval($digits);
$result = null;

// Convert counter to binary (64-bit)
$data = pack('NNC*', $time >> 32, $time & 0xFFFFFFFF);

// Pad to 8 chars (if necessary)
if (strlen ($data) < 8) {
$data = str_pad($data, 8, chr(0), STR_PAD_LEFT);
}

// Get the hash
$hash = hash_hmac($crypto, $data, $key);

// Grab the offset
$offset = 2 * hexdec(substr($hash, strlen($hash) - 1, 1));

// Grab the portion we're interested in
$binary = hexdec(substr($hash, $offset, 8)) & 0x7fffffff;

// Modulus
$result = $binary % pow(10, $digits);

// Pad (if necessary)
$result = str_pad($result, $digits, "0", STR_PAD_LEFT);

return
$result;
}
?>
up
3
havoc at NOSPAM defuse dot ca
12 years ago
Here is an efficient PBDKF2 implementation:

<?php
/*
* PBKDF2 key derivation function as defined by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt
* $algorithm - The hash algorithm to use. Recommended: SHA256
* $password - The password.
* $salt - A salt that is unique to the password.
* $count - Iteration count. Higher is better, but slower. Recommended: At least 1024.
* $key_length - The length of the derived key in bytes.
* $raw_output - If true, the key is returned in raw binary format. Hex encoded otherwise.
* Returns: A $key_length-byte key derived from the password and salt.
*
* Test vectors can be found here: https://www.ietf.org/rfc/rfc6070.txt
*
* This implementation of PBKDF2 was originally created by defuse.ca
* With improvements by variations-of-shadow.com
*/
function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false)
{
$algorithm = strtolower($algorithm);
if(!
in_array($algorithm, hash_algos(), true))
die(
'PBKDF2 ERROR: Invalid hash algorithm.');
if(
$count <= 0 || $key_length <= 0)
die(
'PBKDF2 ERROR: Invalid parameters.');

$hash_length = strlen(hash($algorithm, "", true));
$block_count = ceil($key_length / $hash_length);

$output = "";
for(
$i = 1; $i <= $block_count; $i++) {
// $i encoded as 4 bytes, big endian.
$last = $salt . pack("N", $i);
// first iteration
$last = $xorsum = hash_hmac($algorithm, $last, $password, true);
// perform the other $count - 1 iterations
for ($j = 1; $j < $count; $j++) {
$xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
}
$output .= $xorsum;
}

if(
$raw_output)
return
substr($output, 0, $key_length);
else
return
bin2hex(substr($output, 0, $key_length));
}
?>
up
5
Siann Beck
14 years ago
For signing an Amazon AWS query, base64-encode the binary value:

<?php
$Sig
= base64_encode(hash_hmac('sha256', $Request, $AmazonSecretKey, true));
?>
up
-1
Peter Terence Roux
13 years ago
The Implementation of the PBKDF2 key derivation function as described in RFC 2898 can be used to not only get the hashed KEY but also a specific IV.

To use, one would use it as follows:-

<?php
$p
= str_hash_pbkdf2($pw, $salt, 10, 32, 'sha1');
$p = base64_encode($p);

$iv = str_hash_pbkdf2($pw, $salt, 10, 16, 'sha1', 32);
$iv = base64_encode($iv);
?>

The function should be:-

<?php
// PBKDF2 Implementation (described in RFC 2898)
//
// @param string p password
// @param string s salt
// @param int c iteration count (use 1000 or higher)
// @param int kl derived key length
// @param string a hash algorithm
// @param int st start position of result
//
// @return string derived key
function str_hash_pbkdf2($p, $s, $c, $kl, $a = 'sha256', $st=0)
{
$kb = $start+$kl; // Key blocks to compute
$dk = ''; // Derived key

// Create key
for ($block=1; $block<=$kb; $block++)
{
// Initial hash for this block
$ib = $h = hash_hmac($a, $s . pack('N', $block), $p, true);

// Perform block iterations
for ($i=1; $i<$c; $i++)
{
// XOR each iterate
$ib ^= ($h = hash_hmac($a, $h, $p, true));
}

$dk .= $ib; // Append iterated block
}

// Return derived key of correct length
return substr($dk, $start, $kl);
}
?>
up
-2
josefkoh at hotmail dot com
13 years ago
Simple implementation of hmac sha1

<?php

function hmac_sha1($key, $data)
{
// Adjust key to exactly 64 bytes
if (strlen($key) > 64) {
$key = str_pad(sha1($key, true), 64, chr(0));
}
if (
strlen($key) < 64) {
$key = str_pad($key, 64, chr(0));
}

// Outter and Inner pad
$opad = str_repeat(chr(0x5C), 64);
$ipad = str_repeat(chr(0x36), 64);

// Xor key with opad & ipad
for ($i = 0; $i < strlen($key); $i++) {
$opad[$i] = $opad[$i] ^ $key[$i];
$ipad[$i] = $ipad[$i] ^ $key[$i];
}

return
sha1($opad.sha1($ipad.$data, true));
}
up
-1
Pawel M.
3 years ago
Function for those, who really need to use crc32 algorithm in PHP>7.1

<?php
function hash_hmac_crc32(string $key, string $data): string
{
$b = 4;
if (
strlen($key) > $b) {
$key = pack("H*", hash('crc32', $key));
}
$key = str_pad($key, $b, chr(0x00));
$ipad = str_pad('', $b, chr(0x36));
$opad = str_pad('', $b, chr(0x5c));
$k_ipad = $key ^ $ipad;
$k_opad = $key ^ $opad;
return
hash('crc32', $k_opad . hash('crc32', $k_ipad . $data, true));
}
?>
To Top