[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 3 namespace Defuse\Crypto; 4 5 use Defuse\Crypto\Exception as Ex; 6 7 final class Core 8 { 9 const HEADER_VERSION_SIZE = 4; 10 const MINIMUM_CIPHERTEXT_SIZE = 84; 11 12 const CURRENT_VERSION = "\xDE\xF5\x02\x00"; 13 14 const CIPHER_METHOD = 'aes-256-ctr'; 15 const BLOCK_BYTE_SIZE = 16; 16 const KEY_BYTE_SIZE = 32; 17 const SALT_BYTE_SIZE = 32; 18 const MAC_BYTE_SIZE = 32; 19 const HASH_FUNCTION_NAME = 'sha256'; 20 const ENCRYPTION_INFO_STRING = 'DefusePHP|V2|KeyForEncryption'; 21 const AUTHENTICATION_INFO_STRING = 'DefusePHP|V2|KeyForAuthentication'; 22 const BUFFER_BYTE_SIZE = 1048576; 23 24 const LEGACY_CIPHER_METHOD = 'aes-128-cbc'; 25 const LEGACY_BLOCK_BYTE_SIZE = 16; 26 const LEGACY_KEY_BYTE_SIZE = 16; 27 const LEGACY_HASH_FUNCTION_NAME = 'sha256'; 28 const LEGACY_MAC_BYTE_SIZE = 32; 29 const LEGACY_ENCRYPTION_INFO_STRING = 'DefusePHP|KeyForEncryption'; 30 const LEGACY_AUTHENTICATION_INFO_STRING = 'DefusePHP|KeyForAuthentication'; 31 32 /* 33 * V2.0 Format: VERSION (4 bytes) || SALT (32 bytes) || IV (16 bytes) || 34 * CIPHERTEXT (varies) || HMAC (32 bytes) 35 * 36 * V1.0 Format: HMAC (32 bytes) || IV (16 bytes) || CIPHERTEXT (varies). 37 */ 38 39 /** 40 * Adds an integer to a block-sized counter. 41 * 42 * @param string $ctr 43 * @param int $inc 44 * 45 * @throws Ex\EnvironmentIsBrokenException 46 * 47 * @return string 48 * 49 * @psalm-suppress RedundantCondition - It's valid to use is_int to check for overflow. 50 */ 51 public static function incrementCounter($ctr, $inc) 52 { 53 Core::ensureTrue( 54 Core::ourStrlen($ctr) === Core::BLOCK_BYTE_SIZE, 55 'Trying to increment a nonce of the wrong size.' 56 ); 57 58 Core::ensureTrue( 59 \is_int($inc), 60 'Trying to increment nonce by a non-integer.' 61 ); 62 63 // The caller is probably re-using CTR-mode keystream if they increment by 0. 64 Core::ensureTrue( 65 $inc > 0, 66 'Trying to increment a nonce by a nonpositive amount' 67 ); 68 69 Core::ensureTrue( 70 $inc <= PHP_INT_MAX - 255, 71 'Integer overflow may occur' 72 ); 73 74 /* 75 * We start at the rightmost byte (big-endian) 76 * So, too, does OpenSSL: http://stackoverflow.com/a/3146214/2224584 77 */ 78 for ($i = Core::BLOCK_BYTE_SIZE - 1; $i >= 0; --$i) { 79 $sum = \ord($ctr[$i]) + $inc; 80 81 /* Detect integer overflow and fail. */ 82 Core::ensureTrue(\is_int($sum), 'Integer overflow in CTR mode nonce increment'); 83 84 $ctr[$i] = \pack('C', $sum & 0xFF); 85 $inc = $sum >> 8; 86 } 87 return $ctr; 88 } 89 90 /** 91 * Returns a random byte string of the specified length. 92 * 93 * @param int $octets 94 * 95 * @throws Ex\EnvironmentIsBrokenException 96 * 97 * @return string 98 */ 99 public static function secureRandom($octets) 100 { 101 self::ensureFunctionExists('random_bytes'); 102 try { 103 return \random_bytes($octets); 104 } catch (\Exception $ex) { 105 throw new Ex\EnvironmentIsBrokenException( 106 'Your system does not have a secure random number generator.' 107 ); 108 } 109 } 110 111 /** 112 * Computes the HKDF key derivation function specified in 113 * http://tools.ietf.org/html/rfc5869. 114 * 115 * @param string $hash Hash Function 116 * @param string $ikm Initial Keying Material 117 * @param int $length How many bytes? 118 * @param string $info What sort of key are we deriving? 119 * @param string $salt 120 * 121 * @throws Ex\EnvironmentIsBrokenException 122 * @psalm-suppress UndefinedFunction - We're checking if the function exists first. 123 * 124 * @return string 125 */ 126 public static function HKDF($hash, $ikm, $length, $info = '', $salt = null) 127 { 128 static $nativeHKDF = null; 129 if ($nativeHKDF === null) { 130 $nativeHKDF = \is_callable('\\hash_hkdf'); 131 } 132 if ($nativeHKDF) { 133 if (\is_null($salt)) { 134 $salt = ''; 135 } 136 return \hash_hkdf($hash, $ikm, $length, $info, $salt); 137 } 138 139 $digest_length = Core::ourStrlen(\hash_hmac($hash, '', '', true)); 140 141 // Sanity-check the desired output length. 142 Core::ensureTrue( 143 !empty($length) && \is_int($length) && $length >= 0 && $length <= 255 * $digest_length, 144 'Bad output length requested of HDKF.' 145 ); 146 147 // "if [salt] not provided, is set to a string of HashLen zeroes." 148 if (\is_null($salt)) { 149 $salt = \str_repeat("\x00", $digest_length); 150 } 151 152 // HKDF-Extract: 153 // PRK = HMAC-Hash(salt, IKM) 154 // The salt is the HMAC key. 155 $prk = \hash_hmac($hash, $ikm, $salt, true); 156 157 // HKDF-Expand: 158 159 // This check is useless, but it serves as a reminder to the spec. 160 Core::ensureTrue(Core::ourStrlen($prk) >= $digest_length); 161 162 // T(0) = '' 163 $t = ''; 164 $last_block = ''; 165 for ($block_index = 1; Core::ourStrlen($t) < $length; ++$block_index) { 166 // T(i) = HMAC-Hash(PRK, T(i-1) | info | 0x??) 167 $last_block = \hash_hmac( 168 $hash, 169 $last_block . $info . \chr($block_index), 170 $prk, 171 true 172 ); 173 // T = T(1) | T(2) | T(3) | ... | T(N) 174 $t .= $last_block; 175 } 176 177 // ORM = first L octets of T 178 /** @var string $orm */ 179 $orm = Core::ourSubstr($t, 0, $length); 180 Core::ensureTrue(\is_string($orm)); 181 return $orm; 182 } 183 184 /** 185 * Checks if two equal-length strings are the same without leaking 186 * information through side channels. 187 * 188 * @param string $expected 189 * @param string $given 190 * 191 * @throws Ex\EnvironmentIsBrokenException 192 * 193 * @return bool 194 */ 195 public static function hashEquals($expected, $given) 196 { 197 static $native = null; 198 if ($native === null) { 199 $native = \function_exists('hash_equals'); 200 } 201 if ($native) { 202 return \hash_equals($expected, $given); 203 } 204 205 // We can't just compare the strings with '==', since it would make 206 // timing attacks possible. We could use the XOR-OR constant-time 207 // comparison algorithm, but that may not be a reliable defense in an 208 // interpreted language. So we use the approach of HMACing both strings 209 // with a random key and comparing the HMACs. 210 211 // We're not attempting to make variable-length string comparison 212 // secure, as that's very difficult. Make sure the strings are the same 213 // length. 214 Core::ensureTrue(Core::ourStrlen($expected) === Core::ourStrlen($given)); 215 216 $blind = Core::secureRandom(32); 217 $message_compare = \hash_hmac(Core::HASH_FUNCTION_NAME, $given, $blind); 218 $correct_compare = \hash_hmac(Core::HASH_FUNCTION_NAME, $expected, $blind); 219 return $correct_compare === $message_compare; 220 } 221 /** 222 * Throws an exception if the constant doesn't exist. 223 * 224 * @param string $name 225 * @return void 226 * 227 * @throws Ex\EnvironmentIsBrokenException 228 */ 229 public static function ensureConstantExists($name) 230 { 231 Core::ensureTrue( 232 \defined($name), 233 'Constant '.$name.' does not exists' 234 ); 235 } 236 237 /** 238 * Throws an exception if the function doesn't exist. 239 * 240 * @param string $name 241 * @return void 242 * 243 * @throws Ex\EnvironmentIsBrokenException 244 */ 245 public static function ensureFunctionExists($name) 246 { 247 Core::ensureTrue( 248 \function_exists($name), 249 'function '.$name.' does not exists' 250 ); 251 } 252 253 /** 254 * Throws an exception if the condition is false. 255 * 256 * @param bool $condition 257 * @param string $message 258 * @return void 259 * 260 * @throws Ex\EnvironmentIsBrokenException 261 */ 262 public static function ensureTrue($condition, $message = '') 263 { 264 if (!$condition) { 265 throw new Ex\EnvironmentIsBrokenException($message); 266 } 267 } 268 269 /* 270 * We need these strlen() and substr() functions because when 271 * 'mbstring.func_overload' is set in php.ini, the standard strlen() and 272 * substr() are replaced by mb_strlen() and mb_substr(). 273 */ 274 275 /** 276 * Computes the length of a string in bytes. 277 * 278 * @param string $str 279 * 280 * @throws Ex\EnvironmentIsBrokenException 281 * 282 * @return int 283 */ 284 public static function ourStrlen($str) 285 { 286 static $exists = null; 287 if ($exists === null) { 288 $exists = \extension_loaded('mbstring') && \ini_get('mbstring.func_overload') !== false && (int)\ini_get('mbstring.func_overload') & MB_OVERLOAD_STRING; 289 } 290 if ($exists) { 291 $length = \mb_strlen($str, '8bit'); 292 Core::ensureTrue($length !== false); 293 return $length; 294 } else { 295 return \strlen($str); 296 } 297 } 298 299 /** 300 * Behaves roughly like the function substr() in PHP 7 does. 301 * 302 * @param string $str 303 * @param int $start 304 * @param int $length 305 * 306 * @throws Ex\EnvironmentIsBrokenException 307 * 308 * @return string|bool 309 */ 310 public static function ourSubstr($str, $start, $length = null) 311 { 312 static $exists = null; 313 if ($exists === null) { 314 $exists = \extension_loaded('mbstring') && \ini_get('mbstring.func_overload') !== false && (int)\ini_get('mbstring.func_overload') & MB_OVERLOAD_STRING; 315 } 316 317 // This is required to make mb_substr behavior identical to substr. 318 // Without this, mb_substr() would return false, contra to what the 319 // PHP documentation says (it doesn't say it can return false.) 320 $input_len = Core::ourStrlen($str); 321 if ($start === $input_len && !$length) { 322 return ''; 323 } 324 325 if ($start > $input_len) { 326 return false; 327 } 328 329 // mb_substr($str, 0, NULL, '8bit') returns an empty string on PHP 5.3, 330 // so we have to find the length ourselves. Also, substr() doesn't 331 // accept null for the length. 332 if (! isset($length)) { 333 if ($start >= 0) { 334 $length = $input_len - $start; 335 } else { 336 $length = -$start; 337 } 338 } 339 340 if ($length < 0) { 341 throw new \InvalidArgumentException( 342 "Negative lengths are not supported with ourSubstr." 343 ); 344 } 345 346 if ($exists) { 347 $substr = \mb_substr($str, $start, $length, '8bit'); 348 // At this point there are two cases where mb_substr can 349 // legitimately return an empty string. Either $length is 0, or 350 // $start is equal to the length of the string (both mb_substr and 351 // substr return an empty string when this happens). It should never 352 // ever return a string that's longer than $length. 353 if (Core::ourStrlen($substr) > $length || (Core::ourStrlen($substr) === 0 && $length !== 0 && $start !== $input_len)) { 354 throw new Ex\EnvironmentIsBrokenException( 355 'Your version of PHP has bug #66797. Its implementation of 356 mb_substr() is incorrect. See the details here: 357 https://bugs.php.net/bug.php?id=66797' 358 ); 359 } 360 return $substr; 361 } 362 363 return \substr($str, $start, $length); 364 } 365 366 /** 367 * Computes the PBKDF2 password-based key derivation function. 368 * 369 * The PBKDF2 function is defined in RFC 2898. Test vectors can be found in 370 * RFC 6070. This implementation of PBKDF2 was originally created by Taylor 371 * Hornby, with improvements from http://www.variations-of-shadow.com/. 372 * 373 * @param string $algorithm The hash algorithm to use. Recommended: SHA256 374 * @param string $password The password. 375 * @param string $salt A salt that is unique to the password. 376 * @param int $count Iteration count. Higher is better, but slower. Recommended: At least 1000. 377 * @param int $key_length The length of the derived key in bytes. 378 * @param bool $raw_output If true, the key is returned in raw binary format. Hex encoded otherwise. 379 * 380 * @throws Ex\EnvironmentIsBrokenException 381 * 382 * @return string A $key_length-byte key derived from the password and salt. 383 */ 384 public static function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false) 385 { 386 // Type checks: 387 if (! \is_string($algorithm)) { 388 throw new \InvalidArgumentException( 389 'pbkdf2(): algorithm must be a string' 390 ); 391 } 392 if (! \is_string($password)) { 393 throw new \InvalidArgumentException( 394 'pbkdf2(): password must be a string' 395 ); 396 } 397 if (! \is_string($salt)) { 398 throw new \InvalidArgumentException( 399 'pbkdf2(): salt must be a string' 400 ); 401 } 402 // Coerce strings to integers with no information loss or overflow 403 $count += 0; 404 $key_length += 0; 405 406 $algorithm = \strtolower($algorithm); 407 Core::ensureTrue( 408 \in_array($algorithm, \hash_algos(), true), 409 'Invalid or unsupported hash algorithm.' 410 ); 411 412 // Whitelist, or we could end up with people using CRC32. 413 $ok_algorithms = [ 414 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', 415 'ripemd160', 'ripemd256', 'ripemd320', 'whirlpool', 416 ]; 417 Core::ensureTrue( 418 \in_array($algorithm, $ok_algorithms, true), 419 'Algorithm is not a secure cryptographic hash function.' 420 ); 421 422 Core::ensureTrue($count > 0 && $key_length > 0, 'Invalid PBKDF2 parameters.'); 423 424 if (\function_exists('hash_pbkdf2')) { 425 // The output length is in NIBBLES (4-bits) if $raw_output is false! 426 if (! $raw_output) { 427 $key_length = $key_length * 2; 428 } 429 return \hash_pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output); 430 } 431 432 $hash_length = Core::ourStrlen(\hash($algorithm, '', true)); 433 $block_count = \ceil($key_length / $hash_length); 434 435 $output = ''; 436 for ($i = 1; $i <= $block_count; $i++) { 437 // $i encoded as 4 bytes, big endian. 438 $last = $salt . \pack('N', $i); 439 // first iteration 440 $last = $xorsum = \hash_hmac($algorithm, $last, $password, true); 441 // perform the other $count - 1 iterations 442 for ($j = 1; $j < $count; $j++) { 443 /** 444 * @psalm-suppress InvalidOperand 445 */ 446 $xorsum ^= ($last = \hash_hmac($algorithm, $last, $password, true)); 447 } 448 $output .= $xorsum; 449 } 450 451 if ($raw_output) { 452 return (string) Core::ourSubstr($output, 0, $key_length); 453 } else { 454 return Encoding::binToHex((string) Core::ourSubstr($output, 0, $key_length)); 455 } 456 } 457 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |