<?php
if (PHP_VERSION_ID < 70000) {
die('no');
}
/**
* Use HKDF to derive multiple keys from one.
* http://tools.ietf.org/html/rfc5869
*
* @param string $hash Hash Function
* @param string $ikm Initial Keying Material
* @param int $length How many bytes?
* @param string $info What sort of key are we deriving?
* @param string $salt
* @return string
* @throws Exception
*/
function hash_hkdf(string $algo, string $ikm, int $length, string $info = '', $salt = null)
{
$digest_length = mb_strlen(hash_hmac($algo, '', '', true), '8bit');
// Sanity-check the desired output length.
if (empty($length) || !is_int($length) ||
$length < 0 || $length > 255 * $digest_length) {
throw new Exception("Bad output length requested of HKDF.");
}
// "if [salt] not provided, is set to a string of HashLen zeroes."
if ($salt === null) {
$salt = str_repeat("\x00", $digest_length);
}
// HKDF-Extract:
// PRK = HMAC-Hash(salt, IKM)
// The salt is the HMAC key.
$prk = hash_hmac($algo, $ikm, $salt, true);
// HKDF-Expand:
// This check is useless, but it serves as a reminder to the spec.
if (mb_strlen($prk, '8bit') < $digest_length) {
throw new Exception('HKDF-Expand failed');
}
// T(0) = ''
$t = '';
$last_block = '';
for ($block_index = 1; mb_strlen($t, '8bit') < $length; ++$block_index) {
// T(i) = HMAC-Hash(PRK, T(i-1) | info | 0x??)
$last_block = hash_hmac(
$algo,
$last_block . $info . chr($block_index),
$prk,
true
);
// T = T(1) | T(2) | T(3) | ... | T(N)
$t .= $last_block;
}
// ORM = first L octets of T
$orm = mb_substr($t, 0, $length, '8bit');
if ($orm === false) {
throw new Exception('Unexpected error');
}
return $orm;
}
/**
* Encrypt a message with AES-256-CTR + HMAC-SHA256
* @param string $message
* @param string $key
* @return string
*/
function aes256ctr_hmacsha256_encrypt(string $message, string $key)
{
$iv = random_bytes(16);
$salt = random_bytes(16);
$eKey = hash_hkdf('sha256', $key, 32, 'Encryption Key', $salt);
$aKey = hash_hkdf('sha256', $key, 32, 'Authentication Key', $salt);
$ciphertext = $iv . $salt . openssl_encrypt(
$message,
'aes-256-ctr',
$eKey,
OPENSSL_RAW_DATA,
$iv
);
$mac = hash_hmac('sha256', $ciphertext, $aKey, true);
return base64_encode($mac . $ciphertext);
}
/**
* Decrypt a message with AES-256-CTR + HMAC-SHA256
* @param string $message
* @param string $key
* @return string
* @throws Exception
*/
function aes256ctr_hmacsha256_decrypt(string $ciphertext, string $key)
{
$decode = base64_decode($ciphertext);
if ($decode === false) {
throw new Exception("Encoding error");
}
$mac = mb_substr($decode, 0, 32, '8bit');
$iv = mb_substr($decode, 32, 16, '8bit');
$salt = mb_substr($decode, 48, 16, '8bit');
$ciphertext = mb_substr($decode, 64, null, '8bit');
$aKey = hash_hkdf('sha256', $key, 32, 'Authentication Key', $salt);
$calcMac = hash_hmac('sha256', $iv . $salt . $ciphertext, $aKey, true);
if (!hash_equals($calcMac, $mac)) {
throw new Exception("Invalid message");
}
$eKey = hash_hkdf('sha256', $key, 32, 'Encryption Key', $salt);
return openssl_decrypt(
$ciphertext,
'aes-256-ctr',
$eKey,
OPENSSL_RAW_DATA,
$iv
);
}
$key = random_bytes(32);
$message = "This code rocks";
$ciphertext = aes256ctr_hmacsha256_encrypt($message, $key);
var_dump($ciphertext);
$plaintext = aes256ctr_hmacsha256_decrypt($ciphertext, $key);
var_dump($plaintext);
- Output for 7.4.0
- Fatal error: Cannot redeclare hash_hkdf() in /in/5Nebh on line 18
Process exited with code 255. - Output for 7.1.5 - 7.1.33, 7.2.17 - 7.2.25, 7.3.0 - 7.3.12
- Fatal error: Cannot redeclare hash_hkdf() in /in/5Nebh on line 59
Process exited with code 255. - Output for 7.1.0
- string(108) "zggLjMpdLcjp7Sjc04q0XNU6UpAwHYT25IcidWqdnK9Iis67sYJdtFxU+cm3DSJ0dc38vxl+1Uere17cvvOGXa18WeePSTV1opk4opPILA=="
string(15) "This code rocks"
- Output for 7.0.20
- string(108) "z4Cn7tGilfi73cetI95+XtOwF9xjXx0ETyz7yEplCkxLdI8zuRl9GkLYsiwLFpt7CpQ+RnzqKVFISgQtUsethhzfdBGwm1ob4rAk5H2SHg=="
string(15) "This code rocks"
- Output for 7.0.10
- string(108) "bKowJSv5tLYCDwj5oB0B3WtOA/RzmtSGZgXJhthpdMsmUeNKAdpS+4K/IcyoHSgt7IbZz2nbcUbir0JBKFdo/GZ3zmVxyhqxJdTSU6K/9g=="
string(15) "This code rocks"
- Output for 7.0.9
- string(108) "Z/8hXQvqHsDec21F5If90RGCZM0YUslYLlIe++V0iZm1HNeaE8i7nWRexXfCAVMyPDalNO9qt+TbQatMIJfOEs4WRrC8k5JOMk98+Vfl3A=="
string(15) "This code rocks"
- Output for 7.0.8
- string(108) "pUDsC+n3lA1W5djt/vhYHkNXj6TrTOZNqnH/EJLKAE9jQ1dEo27eFbaq5VkUfOfS8/+OXiIjSCqPWkAuahblcvt+9GofiqDDjE9EVhQCsQ=="
string(15) "This code rocks"
- Output for 7.0.7
- string(108) "Q3aNDDVsqQiWZrWLn5qZKvMJsqmxd2b5BsnkRosJbmYpAlpBcXP+m3ixSW9WiFCQUNBdLBrYAtTBdUHZBGxsgOPITVBXawnpJLvMpGfl8w=="
string(15) "This code rocks"
- Output for 7.0.6
- string(108) "tBeo1pPegcsrC2DQyJGnxmtOUj22tXV+ch+yD01FueJPKY+AKYxm0x90N1XrALVrSvCM52An2gKmtF+i0DVsk0/JbYbENPAIJlFtHRoi2w=="
string(15) "This code rocks"
- Output for 7.0.5
- string(108) "p4RljddC/1XbMx+t6JsAePprEW5jXhGQeePNRO0I0cRiz7e0LgqDP8m1OCmnQxICfJdMMwh/aGEHIBM0nmdSi0oUY7ALGEUR+GglzRJl6A=="
string(15) "This code rocks"
- Output for 7.0.4
- string(108) "myWKQENiacpd+l/3hjc09iSvQUm7DIm4e5GsWnzsI91HZutha2J2ysaPIPJioUGFu4Erh/3oVbS4zf6pVdk92jm6Rvsf+JhoqIni0XC11A=="
string(15) "This code rocks"
- Output for 7.0.3
- string(108) "Mq8tqnz+ns4y20QvcmhFPMP3pugYiXFZNNm0nHhp40/2TNGv7d77EHHwyZm7ohg46FqTtNjpQV09OwVkhrz157YahmgA1TlJAQZkuTWDRg=="
string(15) "This code rocks"
- Output for 7.0.2
- string(108) "jZhRpuxMlZi9Btt7cETz3FEDcVK8vrJ6DsdWfc/X8JPL0rLfrciEMew/66Zm/j5th+N6e7QztAcrG9jYCio0RVyDmxLX1z2DcUm+i38A1Q=="
string(15) "This code rocks"
- Output for 7.0.1
- string(108) "QSHCQel8l4vdqI0+E/FHS1iJFy8XXnVJmJ/QQJcCHny7n940WrP50nwS6X31oaasnynVpd/HVJhvCoCWtJnSBf8wQVHU2+owtcVFtE1JOg=="
string(15) "This code rocks"
- Output for 7.0.0
- string(108) "RAyL3f3TaX8cJarwuXoAQ1HFaDEOIiBc7e/3ktleJ7Lk37YMkczJKVWYwy5mD7is1+QH7OsHPhELHSFixqXuDqpL4nZ1r06qJa+SkYXVKQ=="
string(15) "This code rocks"
- Output for 5.5.0 - 5.5.38, 5.6.0 - 5.6.28
- Fatal error: Default value for parameters with a class type hint can only be NULL in /in/5Nebh on line 18
Process exited with code 255.
preferences:
49.2 ms | 427 KiB | 5 Q