Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
50.00% |
8 / 16 |
|
50.00% |
2 / 4 |
CRAP | |
0.00% |
0 / 1 |
| MWCryptHash | |
50.00% |
8 / 16 |
|
50.00% |
2 / 4 |
19.12 | |
0.00% |
0 / 1 |
| hashAlgo | |
30.00% |
3 / 10 |
|
0.00% |
0 / 1 |
9.49 | |||
| hashLength | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
| hash | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| hmac | |
66.67% |
2 / 3 |
|
0.00% |
0 / 1 |
2.15 | |||
| 1 | <?php |
| 2 | /** |
| 3 | * @license GPL-2.0-or-later |
| 4 | * @file |
| 5 | */ |
| 6 | |
| 7 | /** |
| 8 | * Utility functions for generating hashes |
| 9 | * |
| 10 | * This is based in part on Drupal code as well as what we used in our own code |
| 11 | * prior to introduction of this class, by way of MWCryptRand. |
| 12 | */ |
| 13 | class MWCryptHash { |
| 14 | /** |
| 15 | * The hash algorithm being used |
| 16 | */ |
| 17 | protected static ?string $algo = null; |
| 18 | |
| 19 | /** |
| 20 | * The number of bytes outputted by the hash algorithm |
| 21 | */ |
| 22 | protected static int $hashLength; |
| 23 | |
| 24 | /** |
| 25 | * Decide on the best acceptable hash algorithm we have available for hash() |
| 26 | * @return string A hash algorithm |
| 27 | */ |
| 28 | public static function hashAlgo() { |
| 29 | $algorithm = self::$algo; |
| 30 | if ( $algorithm !== null ) { |
| 31 | return $algorithm; |
| 32 | } |
| 33 | |
| 34 | $algos = hash_hmac_algos(); |
| 35 | $preference = [ 'whirlpool', 'sha256' ]; |
| 36 | |
| 37 | foreach ( $preference as $algorithm ) { |
| 38 | if ( in_array( $algorithm, $algos, true ) ) { |
| 39 | self::$algo = $algorithm; |
| 40 | return $algorithm; |
| 41 | } |
| 42 | } |
| 43 | |
| 44 | throw new DomainException( 'Could not find an acceptable hashing function.' ); |
| 45 | } |
| 46 | |
| 47 | /** |
| 48 | * Return the byte-length output of the hash algorithm we are |
| 49 | * using in self::hash and self::hmac. |
| 50 | * |
| 51 | * @param bool $raw True to return the length for binary data, false to |
| 52 | * return for hex-encoded |
| 53 | * @return int Number of bytes the hash outputs |
| 54 | */ |
| 55 | public static function hashLength( $raw = true ) { |
| 56 | self::$hashLength ??= strlen( self::hash( '', true ) ); |
| 57 | // Optimisation: Skip computing the length of non-raw hashes. |
| 58 | // The algos in hashAlgo() all produce a digest that is a multiple |
| 59 | // of 8 bits, where hex is always twice the length of binary byte length. |
| 60 | return $raw ? self::$hashLength : self::$hashLength * 2; |
| 61 | } |
| 62 | |
| 63 | /** |
| 64 | * Generate a cryptographic hash value (message digest) for a string, |
| 65 | * making use of the best hash algorithm that we have available. |
| 66 | * |
| 67 | * @param string $data |
| 68 | * @param bool $raw True to return binary data, false to return it hex-encoded |
| 69 | * @return string A hash of the data |
| 70 | */ |
| 71 | public static function hash( $data, $raw = true ) { |
| 72 | return hash( self::hashAlgo(), $data, $raw ); |
| 73 | } |
| 74 | |
| 75 | /** |
| 76 | * Generate a keyed cryptographic hash value (HMAC) for a string, |
| 77 | * making use of the best hash algorithm that we have available. |
| 78 | * |
| 79 | * @param string $data |
| 80 | * @param string $key |
| 81 | * @param bool $raw True to return binary data, false to return it hex-encoded |
| 82 | * @return string An HMAC hash of the data + key |
| 83 | */ |
| 84 | public static function hmac( $data, $key, $raw = true ) { |
| 85 | if ( !is_string( $key ) ) { |
| 86 | // hash_hmac tolerates non-string (would return null with warning) |
| 87 | throw new InvalidArgumentException( 'Invalid key type: ' . get_debug_type( $key ) ); |
| 88 | } |
| 89 | return hash_hmac( self::hashAlgo(), $data, $key, $raw ); |
| 90 | } |
| 91 | |
| 92 | } |