Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
60.00% |
3 / 5 |
CRAP | |
36.36% |
16 / 44 |
Crypt | |
0.00% |
0 / 1 |
|
60.00% |
3 / 5 |
134.65 | |
36.36% |
16 / 44 |
randomBytes | |
0.00% |
0 / 1 |
30.63 | |
40.91% |
9 / 22 |
|||
hmacBase64 | |
100.00% |
1 / 1 |
3 | |
100.00% |
4 / 4 |
|||
hashBase64 | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
hashEquals | |
0.00% |
0 / 1 |
42 | |
0.00% |
0 / 15 |
|||
randomBytesBase64 | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
<?php | |
/** | |
* @file | |
* Contains \Drupal\Component\Utility\Crypt. | |
*/ | |
namespace Drupal\Component\Utility; | |
/** | |
* Utility class for cryptographically-secure string handling routines. | |
* | |
* @ingroup utility | |
*/ | |
class Crypt { | |
/** | |
* Returns a string of highly randomized bytes (over the full 8-bit range). | |
* | |
* This function is better than simply calling mt_rand() or any other built-in | |
* PHP function because it can return a long string of bytes (compared to < 4 | |
* bytes normally from mt_rand()) and uses the best available pseudo-random | |
* source. | |
* | |
* @param int $count | |
* The number of characters (bytes) to return in the string. | |
* | |
* @return string | |
* A randomly generated string. | |
*/ | |
public static function randomBytes($count) { | |
// $random_state does not use drupal_static as it stores random bytes. | |
static $random_state, $bytes; | |
$missing_bytes = $count - strlen($bytes); | |
if ($missing_bytes > 0) { | |
// openssl_random_pseudo_bytes() will find entropy in a system-dependent | |
// way. | |
if (function_exists('openssl_random_pseudo_bytes')) { | |
$bytes .= openssl_random_pseudo_bytes($missing_bytes); | |
} | |
// If OpenSSL is not available, we can use mcrypt. On Windows, this will | |
// transparently pull from CryptGenRandom. On Unix-based systems, it will | |
// read from /dev/urandom as expected. | |
elseif (function_exists(('mcrypt_create_iv')) && defined('MCRYPT_DEV_URANDOM')) { | |
$bytes .= mcrypt_create_iv($count, MCRYPT_DEV_URANDOM); | |
} | |
// Else, read directly from /dev/urandom, which is available on many *nix | |
// systems and is considered cryptographically secure. | |
elseif ($fh = @fopen('/dev/urandom', 'rb')) { | |
// PHP only performs buffered reads, so in reality it will always read | |
// at least 4096 bytes. Thus, it costs nothing extra to read and store | |
// that much so as to speed any additional invocations. | |
$bytes .= fread($fh, max(4096, $missing_bytes)); | |
fclose($fh); | |
} | |
// If we couldn't get enough entropy, this simple hash-based PRNG will | |
// generate a good set of pseudo-random bytes on any system. | |
// Note that it may be important that our $random_state is passed | |
// through hash() prior to being rolled into $output, that the two hash() | |
// invocations are different, and that the extra input into the first one - | |
// the microtime() - is prepended rather than appended. This is to avoid | |
// directly leaking $random_state via the $output stream, which could | |
// allow for trivial prediction of further "random" numbers. | |
if (strlen($bytes) < $count) { | |
// Initialize on the first call. The contents of $_SERVER includes a mix | |
// of user-specific and system information that varies a little with | |
// each page. | |
if (!isset($random_state)) { | |
$random_state = print_r($_SERVER, TRUE); | |
if (function_exists('getmypid')) { | |
// Further initialize with the somewhat random PHP process ID. | |
$random_state .= getmypid(); | |
} | |
$bytes = ''; | |
} | |
do { | |
$random_state = hash('sha256', microtime() . mt_rand() . $random_state); | |
$bytes .= hash('sha256', mt_rand() . $random_state, TRUE); | |
} while (strlen($bytes) < $count); | |
} | |
} | |
$output = substr($bytes, 0, $count); | |
$bytes = substr($bytes, $count); | |
return $output; | |
} | |
/** | |
* Calculates a base-64 encoded, URL-safe sha-256 hmac. | |
* | |
* @param mixed $data | |
* Scalar value to be validated with the hmac. | |
* @param mixed $key | |
* A secret key, this can be any scalar value. | |
* | |
* @return string | |
* A base-64 encoded sha-256 hmac, with + replaced with -, / with _ and | |
* any = padding characters removed. | |
*/ | |
public static function hmacBase64($data, $key) { | |
// $data and $key being strings here is necessary to avoid empty string | |
// results of the hash function if they are not scalar values. As this | |
// function is used in security-critical contexts like token validation it | |
// is important that it never returns an empty string. | |
if (!is_scalar($data) || !is_scalar($key)) { | |
throw new \InvalidArgumentException('Both parameters passed to \Drupal\Component\Utility\Crypt::hmacBase64 must be scalar values.'); | |
} | |
$hmac = base64_encode(hash_hmac('sha256', $data, $key, TRUE)); | |
// Modify the hmac so it's safe to use in URLs. | |
return str_replace(['+', '/', '='], ['-', '_', ''], $hmac); | |
} | |
/** | |
* Calculates a base-64 encoded, URL-safe sha-256 hash. | |
* | |
* @param string $data | |
* String to be hashed. | |
* | |
* @return string | |
* A base-64 encoded sha-256 hash, with + replaced with -, / with _ and | |
* any = padding characters removed. | |
*/ | |
public static function hashBase64($data) { | |
$hash = base64_encode(hash('sha256', $data, TRUE)); | |
// Modify the hash so it's safe to use in URLs. | |
return str_replace(['+', '/', '='], ['-', '_', ''], $hash); | |
} | |
/** | |
* Compares strings in constant time. | |
* | |
* @param string $known_string | |
* The expected string. | |
* @param string $user_string | |
* The user supplied string to check. | |
* | |
* @return bool | |
* Returns TRUE when the two strings are equal, FALSE otherwise. | |
*/ | |
public static function hashEquals($known_string, $user_string) { | |
if (function_exists('hash_equals')) { | |
return hash_equals($known_string, $user_string); | |
} | |
else { | |
// Backport of hash_equals() function from PHP 5.6 | |
// @see https://github.com/php/php-src/blob/PHP-5.6/ext/hash/hash.c#L739 | |
if (!is_string($known_string)) { | |
trigger_error(sprintf("Expected known_string to be a string, %s given", gettype($known_string)), E_USER_WARNING); | |
return FALSE; | |
} | |
if (!is_string($user_string)) { | |
trigger_error(sprintf("Expected user_string to be a string, %s given", gettype($user_string)), E_USER_WARNING); | |
return FALSE; | |
} | |
$known_len = strlen($known_string); | |
if ($known_len !== strlen($user_string)) { | |
return FALSE; | |
} | |
// This is security sensitive code. Do not optimize this for speed. | |
$result = 0; | |
for ($i = 0; $i < $known_len; $i++) { | |
$result |= (ord($known_string[$i]) ^ ord($user_string[$i])); | |
} | |
return $result === 0; | |
} | |
} | |
/** | |
* Returns a URL-safe, base64 encoded string of highly randomized bytes. | |
* | |
* @param $byte_count | |
* The number of random bytes to fetch and base64 encode. | |
* | |
* @return string | |
* The base64 encoded result will have a length of up to 4 * $byte_count. | |
* | |
* @see \Drupal\Component\Utility\Crypt::randomBytes() | |
*/ | |
public static function randomBytesBase64($count = 32) { | |
return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode(static::randomBytes($count))); | |
} | |
} |