[ 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 class Crypto 8 { 9 /** 10 * Encrypts a string with a Key. 11 * 12 * @param string $plaintext 13 * @param Key $key 14 * @param bool $raw_binary 15 * 16 * @throws Ex\EnvironmentIsBrokenException 17 * @throws \TypeError 18 * 19 * @return string 20 */ 21 public static function encrypt($plaintext, $key, $raw_binary = false) 22 { 23 if (!\is_string($plaintext)) { 24 throw new \TypeError( 25 'String expected for argument 1. ' . \ucfirst(\gettype($plaintext)) . ' given instead.' 26 ); 27 } 28 if (!($key instanceof Key)) { 29 throw new \TypeError( 30 'Key expected for argument 2. ' . \ucfirst(\gettype($key)) . ' given instead.' 31 ); 32 } 33 if (!\is_bool($raw_binary)) { 34 throw new \TypeError( 35 'Boolean expected for argument 3. ' . \ucfirst(\gettype($raw_binary)) . ' given instead.' 36 ); 37 } 38 return self::encryptInternal( 39 $plaintext, 40 KeyOrPassword::createFromKey($key), 41 $raw_binary 42 ); 43 } 44 45 /** 46 * Encrypts a string with a password, using a slow key derivation function 47 * to make password cracking more expensive. 48 * 49 * @param string $plaintext 50 * @param string $password 51 * @param bool $raw_binary 52 * 53 * @throws Ex\EnvironmentIsBrokenException 54 * @throws \TypeError 55 * 56 * @return string 57 */ 58 public static function encryptWithPassword($plaintext, $password, $raw_binary = false) 59 { 60 if (!\is_string($plaintext)) { 61 throw new \TypeError( 62 'String expected for argument 1. ' . \ucfirst(\gettype($plaintext)) . ' given instead.' 63 ); 64 } 65 if (!\is_string($password)) { 66 throw new \TypeError( 67 'String expected for argument 2. ' . \ucfirst(\gettype($password)) . ' given instead.' 68 ); 69 } 70 if (!\is_bool($raw_binary)) { 71 throw new \TypeError( 72 'Boolean expected for argument 3. ' . \ucfirst(\gettype($raw_binary)) . ' given instead.' 73 ); 74 } 75 return self::encryptInternal( 76 $plaintext, 77 KeyOrPassword::createFromPassword($password), 78 $raw_binary 79 ); 80 } 81 82 /** 83 * Decrypts a ciphertext to a string with a Key. 84 * 85 * @param string $ciphertext 86 * @param Key $key 87 * @param bool $raw_binary 88 * 89 * @throws \TypeError 90 * @throws Ex\EnvironmentIsBrokenException 91 * @throws Ex\WrongKeyOrModifiedCiphertextException 92 * 93 * @return string 94 */ 95 public static function decrypt($ciphertext, $key, $raw_binary = false) 96 { 97 if (!\is_string($ciphertext)) { 98 throw new \TypeError( 99 'String expected for argument 1. ' . \ucfirst(\gettype($ciphertext)) . ' given instead.' 100 ); 101 } 102 if (!($key instanceof Key)) { 103 throw new \TypeError( 104 'Key expected for argument 2. ' . \ucfirst(\gettype($key)) . ' given instead.' 105 ); 106 } 107 if (!\is_bool($raw_binary)) { 108 throw new \TypeError( 109 'Boolean expected for argument 3. ' . \ucfirst(\gettype($raw_binary)) . ' given instead.' 110 ); 111 } 112 return self::decryptInternal( 113 $ciphertext, 114 KeyOrPassword::createFromKey($key), 115 $raw_binary 116 ); 117 } 118 119 /** 120 * Decrypts a ciphertext to a string with a password, using a slow key 121 * derivation function to make password cracking more expensive. 122 * 123 * @param string $ciphertext 124 * @param string $password 125 * @param bool $raw_binary 126 * 127 * @throws Ex\EnvironmentIsBrokenException 128 * @throws Ex\WrongKeyOrModifiedCiphertextException 129 * @throws \TypeError 130 * 131 * @return string 132 */ 133 public static function decryptWithPassword($ciphertext, $password, $raw_binary = false) 134 { 135 if (!\is_string($ciphertext)) { 136 throw new \TypeError( 137 'String expected for argument 1. ' . \ucfirst(\gettype($ciphertext)) . ' given instead.' 138 ); 139 } 140 if (!\is_string($password)) { 141 throw new \TypeError( 142 'String expected for argument 2. ' . \ucfirst(\gettype($password)) . ' given instead.' 143 ); 144 } 145 if (!\is_bool($raw_binary)) { 146 throw new \TypeError( 147 'Boolean expected for argument 3. ' . \ucfirst(\gettype($raw_binary)) . ' given instead.' 148 ); 149 } 150 return self::decryptInternal( 151 $ciphertext, 152 KeyOrPassword::createFromPassword($password), 153 $raw_binary 154 ); 155 } 156 157 /** 158 * Decrypts a legacy ciphertext produced by version 1 of this library. 159 * 160 * @param string $ciphertext 161 * @param string $key 162 * 163 * @throws Ex\EnvironmentIsBrokenException 164 * @throws Ex\WrongKeyOrModifiedCiphertextException 165 * @throws \TypeError 166 * 167 * @return string 168 */ 169 public static function legacyDecrypt($ciphertext, $key) 170 { 171 if (!\is_string($ciphertext)) { 172 throw new \TypeError( 173 'String expected for argument 1. ' . \ucfirst(\gettype($ciphertext)) . ' given instead.' 174 ); 175 } 176 if (!\is_string($key)) { 177 throw new \TypeError( 178 'String expected for argument 2. ' . \ucfirst(\gettype($key)) . ' given instead.' 179 ); 180 } 181 182 RuntimeTests::runtimeTest(); 183 184 // Extract the HMAC from the front of the ciphertext. 185 if (Core::ourStrlen($ciphertext) <= Core::LEGACY_MAC_BYTE_SIZE) { 186 throw new Ex\WrongKeyOrModifiedCiphertextException( 187 'Ciphertext is too short.' 188 ); 189 } 190 /** 191 * @var string 192 */ 193 $hmac = Core::ourSubstr($ciphertext, 0, Core::LEGACY_MAC_BYTE_SIZE); 194 Core::ensureTrue(\is_string($hmac)); 195 /** 196 * @var string 197 */ 198 $messageCiphertext = Core::ourSubstr($ciphertext, Core::LEGACY_MAC_BYTE_SIZE); 199 Core::ensureTrue(\is_string($messageCiphertext)); 200 201 // Regenerate the same authentication sub-key. 202 $akey = Core::HKDF( 203 Core::LEGACY_HASH_FUNCTION_NAME, 204 $key, 205 Core::LEGACY_KEY_BYTE_SIZE, 206 Core::LEGACY_AUTHENTICATION_INFO_STRING, 207 null 208 ); 209 210 if (self::verifyHMAC($hmac, $messageCiphertext, $akey)) { 211 // Regenerate the same encryption sub-key. 212 $ekey = Core::HKDF( 213 Core::LEGACY_HASH_FUNCTION_NAME, 214 $key, 215 Core::LEGACY_KEY_BYTE_SIZE, 216 Core::LEGACY_ENCRYPTION_INFO_STRING, 217 null 218 ); 219 220 // Extract the IV from the ciphertext. 221 if (Core::ourStrlen($messageCiphertext) <= Core::LEGACY_BLOCK_BYTE_SIZE) { 222 throw new Ex\WrongKeyOrModifiedCiphertextException( 223 'Ciphertext is too short.' 224 ); 225 } 226 /** 227 * @var string 228 */ 229 $iv = Core::ourSubstr($messageCiphertext, 0, Core::LEGACY_BLOCK_BYTE_SIZE); 230 Core::ensureTrue(\is_string($iv)); 231 232 /** 233 * @var string 234 */ 235 $actualCiphertext = Core::ourSubstr($messageCiphertext, Core::LEGACY_BLOCK_BYTE_SIZE); 236 Core::ensureTrue(\is_string($actualCiphertext)); 237 238 // Do the decryption. 239 $plaintext = self::plainDecrypt($actualCiphertext, $ekey, $iv, Core::LEGACY_CIPHER_METHOD); 240 return $plaintext; 241 } else { 242 throw new Ex\WrongKeyOrModifiedCiphertextException( 243 'Integrity check failed.' 244 ); 245 } 246 } 247 248 /** 249 * Encrypts a string with either a key or a password. 250 * 251 * @param string $plaintext 252 * @param KeyOrPassword $secret 253 * @param bool $raw_binary 254 * 255 * @return string 256 */ 257 private static function encryptInternal($plaintext, KeyOrPassword $secret, $raw_binary) 258 { 259 RuntimeTests::runtimeTest(); 260 261 $salt = Core::secureRandom(Core::SALT_BYTE_SIZE); 262 $keys = $secret->deriveKeys($salt); 263 $ekey = $keys->getEncryptionKey(); 264 $akey = $keys->getAuthenticationKey(); 265 $iv = Core::secureRandom(Core::BLOCK_BYTE_SIZE); 266 267 $ciphertext = Core::CURRENT_VERSION . $salt . $iv . self::plainEncrypt($plaintext, $ekey, $iv); 268 $auth = \hash_hmac(Core::HASH_FUNCTION_NAME, $ciphertext, $akey, true); 269 $ciphertext = $ciphertext . $auth; 270 271 if ($raw_binary) { 272 return $ciphertext; 273 } 274 return Encoding::binToHex($ciphertext); 275 } 276 277 /** 278 * Decrypts a ciphertext to a string with either a key or a password. 279 * 280 * @param string $ciphertext 281 * @param KeyOrPassword $secret 282 * @param bool $raw_binary 283 * 284 * @throws Ex\EnvironmentIsBrokenException 285 * @throws Ex\WrongKeyOrModifiedCiphertextException 286 * 287 * @return string 288 */ 289 private static function decryptInternal($ciphertext, KeyOrPassword $secret, $raw_binary) 290 { 291 RuntimeTests::runtimeTest(); 292 293 if (! $raw_binary) { 294 try { 295 $ciphertext = Encoding::hexToBin($ciphertext); 296 } catch (Ex\BadFormatException $ex) { 297 throw new Ex\WrongKeyOrModifiedCiphertextException( 298 'Ciphertext has invalid hex encoding.' 299 ); 300 } 301 } 302 303 if (Core::ourStrlen($ciphertext) < Core::MINIMUM_CIPHERTEXT_SIZE) { 304 throw new Ex\WrongKeyOrModifiedCiphertextException( 305 'Ciphertext is too short.' 306 ); 307 } 308 309 // Get and check the version header. 310 /** @var string $header */ 311 $header = Core::ourSubstr($ciphertext, 0, Core::HEADER_VERSION_SIZE); 312 if ($header !== Core::CURRENT_VERSION) { 313 throw new Ex\WrongKeyOrModifiedCiphertextException( 314 'Bad version header.' 315 ); 316 } 317 318 // Get the salt. 319 /** @var string $salt */ 320 $salt = Core::ourSubstr( 321 $ciphertext, 322 Core::HEADER_VERSION_SIZE, 323 Core::SALT_BYTE_SIZE 324 ); 325 Core::ensureTrue(\is_string($salt)); 326 327 // Get the IV. 328 /** @var string $iv */ 329 $iv = Core::ourSubstr( 330 $ciphertext, 331 Core::HEADER_VERSION_SIZE + Core::SALT_BYTE_SIZE, 332 Core::BLOCK_BYTE_SIZE 333 ); 334 Core::ensureTrue(\is_string($iv)); 335 336 // Get the HMAC. 337 /** @var string $hmac */ 338 $hmac = Core::ourSubstr( 339 $ciphertext, 340 Core::ourStrlen($ciphertext) - Core::MAC_BYTE_SIZE, 341 Core::MAC_BYTE_SIZE 342 ); 343 Core::ensureTrue(\is_string($hmac)); 344 345 // Get the actual encrypted ciphertext. 346 /** @var string $encrypted */ 347 $encrypted = Core::ourSubstr( 348 $ciphertext, 349 Core::HEADER_VERSION_SIZE + Core::SALT_BYTE_SIZE + 350 Core::BLOCK_BYTE_SIZE, 351 Core::ourStrlen($ciphertext) - Core::MAC_BYTE_SIZE - Core::SALT_BYTE_SIZE - 352 Core::BLOCK_BYTE_SIZE - Core::HEADER_VERSION_SIZE 353 ); 354 Core::ensureTrue(\is_string($encrypted)); 355 356 // Derive the separate encryption and authentication keys from the key 357 // or password, whichever it is. 358 $keys = $secret->deriveKeys($salt); 359 360 if (self::verifyHMAC($hmac, $header . $salt . $iv . $encrypted, $keys->getAuthenticationKey())) { 361 $plaintext = self::plainDecrypt($encrypted, $keys->getEncryptionKey(), $iv, Core::CIPHER_METHOD); 362 return $plaintext; 363 } else { 364 throw new Ex\WrongKeyOrModifiedCiphertextException( 365 'Integrity check failed.' 366 ); 367 } 368 } 369 370 /** 371 * Raw unauthenticated encryption (insecure on its own). 372 * 373 * @param string $plaintext 374 * @param string $key 375 * @param string $iv 376 * 377 * @throws Ex\EnvironmentIsBrokenException 378 * 379 * @return string 380 */ 381 protected static function plainEncrypt($plaintext, $key, $iv) 382 { 383 Core::ensureConstantExists('OPENSSL_RAW_DATA'); 384 Core::ensureFunctionExists('openssl_encrypt'); 385 /** @var string $ciphertext */ 386 $ciphertext = \openssl_encrypt( 387 $plaintext, 388 Core::CIPHER_METHOD, 389 $key, 390 OPENSSL_RAW_DATA, 391 $iv 392 ); 393 394 Core::ensureTrue(\is_string($ciphertext), 'openssl_encrypt() failed'); 395 396 return $ciphertext; 397 } 398 399 /** 400 * Raw unauthenticated decryption (insecure on its own). 401 * 402 * @param string $ciphertext 403 * @param string $key 404 * @param string $iv 405 * @param string $cipherMethod 406 * 407 * @throws Ex\EnvironmentIsBrokenException 408 * 409 * @return string 410 */ 411 protected static function plainDecrypt($ciphertext, $key, $iv, $cipherMethod) 412 { 413 Core::ensureConstantExists('OPENSSL_RAW_DATA'); 414 Core::ensureFunctionExists('openssl_decrypt'); 415 416 /** @var string $plaintext */ 417 $plaintext = \openssl_decrypt( 418 $ciphertext, 419 $cipherMethod, 420 $key, 421 OPENSSL_RAW_DATA, 422 $iv 423 ); 424 Core::ensureTrue(\is_string($plaintext), 'openssl_decrypt() failed.'); 425 426 return $plaintext; 427 } 428 429 /** 430 * Verifies an HMAC without leaking information through side-channels. 431 * 432 * @param string $expected_hmac 433 * @param string $message 434 * @param string $key 435 * 436 * @throws Ex\EnvironmentIsBrokenException 437 * 438 * @return bool 439 */ 440 protected static function verifyHMAC($expected_hmac, $message, $key) 441 { 442 $message_hmac = \hash_hmac(Core::HASH_FUNCTION_NAME, $message, $key, true); 443 return Core::hashEquals($message_hmac, $expected_hmac); 444 } 445 }
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 |