. src/RuntimeTests.phpݲW^Dsrc/KeyProtectedByPassword.phpݲWRʶsrc/KeyOrPassword.phpݲW src/Key.phpݲWN`p src/File.php,ݲW,G7src/Exception/WrongKeyOrModifiedCiphertextException.phpݲWd+Msrc/Exception/IOException.phprݲWr"i&ɶ.src/Exception/EnvironmentIsBrokenException.phpݲW !src/Exception/CryptoException.phpXݲWX$src/Exception/BadFormatException.phpyݲWy=*src/Encoding.php ݲW õsrc/DerivedKeys.phpJݲWJƕ7src/Crypto.phpݲWG src/Core.phpݲW3vendor/composer/ClassLoader.phpݲW k!vendor/composer/autoload_real.php>ݲW>0b#vendor/composer/autoload_static.phpݲWά"vendor/composer/autoload_files.phpݲW%vendor/composer/autoload_classmap.phpݲW!vendor/composer/autoload_psr4.phpdݲWdZH'vendor/composer/autoload_namespaces.phpdݲWdZH1vendor/paragonie/random_compat/lib/random_int.phpݲWhp:vendor/paragonie/random_compat/lib/random_bytes_mcrypt.phpݲW"E vDvendor/paragonie/random_compat/lib/random_bytes_libsodium_legacy.phpݲWW!X=vendor/paragonie/random_compat/lib/random_bytes_libsodium.phpݲW ̶?vendor/paragonie/random_compat/lib/random_bytes_dev_urandom.phpݲWU%>vendor/paragonie/random_compat/lib/random_bytes_com_dotnet.phpvݲWv?-vendor/paragonie/random_compat/lib/random.php ݲW h5vendor/paragonie/random_compat/lib/error_polyfill.phpݲWb(Ŷ2vendor/paragonie/random_compat/lib/cast_to_int.phpݲW"v8vendor/paragonie/random_compat/lib/byte_safe_strings.phpݲWacvendor/autoload.phpݲWD}getRawBytes()) != Core::KEY_BYTE_SIZE) { throw new Ex\EnvironmentIsBrokenException(); } if (Core::ENCRYPTION_INFO_STRING == Core::AUTHENTICATION_INFO_STRING) { throw new Ex\EnvironmentIsBrokenException(); } } catch (Ex\EnvironmentIsBrokenException $ex) { $test_state = 3; throw $ex; } $test_state = 1; } private static function testEncryptDecrypt() { $key = Key::createNewRandomKey(); $data = "EnCrYpT EvErYThInG\x00\x00"; $ciphertext = Crypto::encrypt($data, $key, true); try { $decrypted = Crypto::decrypt($ciphertext, $key, true); } catch (Ex\WrongKeyOrModifiedCiphertextException $ex) { throw new Ex\EnvironmentIsBrokenException(); } if ($decrypted !== $data) { throw new Ex\EnvironmentIsBrokenException(); } try { Crypto::decrypt($ciphertext . 'a', $key, true); throw new Ex\EnvironmentIsBrokenException(); } catch (Ex\WrongKeyOrModifiedCiphertextException $e) { } $indices_to_change = [ 0, Core::HEADER_VERSION_SIZE + 1, Core::HEADER_VERSION_SIZE + Core::SALT_BYTE_SIZE + 1, Core::HEADER_VERSION_SIZE + Core::SALT_BYTE_SIZE + Core::BLOCK_BYTE_SIZE + 1, ]; foreach ($indices_to_change as $index) { try { $ciphertext[$index] = \chr((\ord($ciphertext[$index]) + 1) % 256); Crypto::decrypt($ciphertext, $key, true); throw new Ex\EnvironmentIsBrokenException(); } catch (Ex\WrongKeyOrModifiedCiphertextException $e) { } } $key = Key::createNewRandomKey(); $data = 'abcdef'; $ciphertext = Crypto::encrypt($data, $key, true); $wrong_key = Key::createNewRandomKey(); try { Crypto::decrypt($ciphertext, $wrong_key, true); throw new Ex\EnvironmentIsBrokenException(); } catch (Ex\WrongKeyOrModifiedCiphertextException $e) { } $key = Key::createNewRandomKey(); $ciphertext = \str_repeat('A', Core::MINIMUM_CIPHERTEXT_SIZE - 1); try { Crypto::decrypt($ciphertext, $key, true); throw new Ex\EnvironmentIsBrokenException(); } catch (Ex\WrongKeyOrModifiedCiphertextException $e) { } } private static function HKDFTestVector() { $ikm = \str_repeat("\x0b", 22); $salt = Encoding::hexToBin('000102030405060708090a0b0c'); $info = Encoding::hexToBin('f0f1f2f3f4f5f6f7f8f9'); $length = 42; $okm = Encoding::hexToBin( '3cb25f25faacd57a90434f64d0362f2a' . '2d2d0a90cf1a5a4c5db02d56ecc4c5bf' . '34007208d5b887185865' ); $computed_okm = Core::HKDF('sha256', $ikm, $length, $info, $salt); if ($computed_okm !== $okm) { throw new Ex\EnvironmentIsBrokenException(); } $ikm = \str_repeat("\x0c", 22); $length = 42; $okm = Encoding::hexToBin( '2c91117204d745f3500d636a62f64f0a' . 'b3bae548aa53d423b0d1f27ebba6f5e5' . '673a081d70cce7acfc48' ); $computed_okm = Core::HKDF('sha1', $ikm, $length, '', null); if ($computed_okm !== $okm) { throw new Ex\EnvironmentIsBrokenException(); } } private static function HMACTestVector() { $key = \str_repeat("\x0b", 20); $data = 'Hi There'; $correct = 'b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7'; if (\hash_hmac(Core::HASH_FUNCTION_NAME, $data, $key) !== $correct) { throw new Ex\EnvironmentIsBrokenException(); } } private static function AESTestVector() { $key = Encoding::hexToBin( '603deb1015ca71be2b73aef0857d7781' . '1f352c073b6108d72d9810a30914dff4' ); $iv = Encoding::hexToBin('f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff'); $plaintext = Encoding::hexToBin( '6bc1bee22e409f96e93d7e117393172a' . 'ae2d8a571e03ac9c9eb76fac45af8e51' . '30c81c46a35ce411e5fbc1191a0a52ef' . 'f69f2445df4f9b17ad2b417be66c3710' ); $ciphertext = Encoding::hexToBin( '601ec313775789a5b7a7f504bbf3d228' . 'f443e3ca4d62b59aca84e990cacaf5c5' . '2b0930daa23de94ce87017ba2d84988d' . 'dfc9c58db67aada613c2dd08457941a6' ); $computed_ciphertext = Crypto::plainEncrypt($plaintext, $key, $iv); if ($computed_ciphertext !== $ciphertext) { echo \str_repeat("\n", 30); echo \bin2hex($computed_ciphertext); echo "\n---\n"; echo \bin2hex($ciphertext); echo \str_repeat("\n", 30); throw new Ex\EnvironmentIsBrokenException(); } $computed_plaintext = Crypto::plainDecrypt($ciphertext, $key, $iv, Core::CIPHER_METHOD); if ($computed_plaintext !== $plaintext) { throw new Ex\EnvironmentIsBrokenException(); } } } saveToAsciiSafeString(), \hash(Core::HASH_FUNCTION_NAME, $password, true), true ); return new KeyProtectedByPassword($encrypted_key); } public static function loadFromAsciiSafeString($saved_key_string) { $encrypted_key = Encoding::loadBytesFromChecksummedAsciiSafeString( self::PASSWORD_KEY_CURRENT_VERSION, $saved_key_string ); return new KeyProtectedByPassword($encrypted_key); } public function saveToAsciiSafeString() { return Encoding::saveBytesToChecksummedAsciiSafeString( self::PASSWORD_KEY_CURRENT_VERSION, $this->encrypted_key ); } public function unlockKey($password) { try { $inner_key_encoded = Crypto::decryptWithPassword( $this->encrypted_key, \hash(Core::HASH_FUNCTION_NAME, $password, true), true ); return Key::loadFromAsciiSafeString($inner_key_encoded); } catch (Ex\BadFormatException $ex) { throw new Ex\WrongKeyOrModifiedCiphertextException( "The decrypted key was found to be in an invalid format. " . "This very likely indicates it was modified by an attacker." ); } } private function __construct($encrypted_key) { $this->encrypted_key = $encrypted_key; } } secret_type === self::SECRET_TYPE_KEY) { $akey = Core::HKDF( Core::HASH_FUNCTION_NAME, $this->secret->getRawBytes(), Core::KEY_BYTE_SIZE, Core::AUTHENTICATION_INFO_STRING, $salt ); $ekey = Core::HKDF( Core::HASH_FUNCTION_NAME, $this->secret->getRawBytes(), Core::KEY_BYTE_SIZE, Core::ENCRYPTION_INFO_STRING, $salt ); return new DerivedKeys($akey, $ekey); } elseif ($this->secret_type === self::SECRET_TYPE_PASSWORD) { $prehash = \hash(Core::HASH_FUNCTION_NAME, $this->secret, true); $prekey = Core::pbkdf2( Core::HASH_FUNCTION_NAME, $prehash, $salt, self::PBKDF2_ITERATIONS, Core::KEY_BYTE_SIZE, true ); $akey = Core::HKDF( Core::HASH_FUNCTION_NAME, $prekey, Core::KEY_BYTE_SIZE, Core::AUTHENTICATION_INFO_STRING, $salt ); $ekey = Core::HKDF( Core::HASH_FUNCTION_NAME, $prekey, Core::KEY_BYTE_SIZE, Core::ENCRYPTION_INFO_STRING, $salt ); return new DerivedKeys($akey, $ekey); } else { throw new Ex\EnvironmentIsBrokenException('Bad secret type.'); } } private function __construct($secret_type, $secret) { $this->secret_type = $secret_type; $this->secret = $secret; } } key_bytes ); } public function getRawBytes() { return $this->key_bytes; } private function __construct($bytes) { if (Core::ourStrlen($bytes) !== self::KEY_BYTE_SIZE) { throw new Ex\EnvironmentIsBrokenException( 'Bad key length.' ); } $this->key_bytes = $bytes; } } deriveKeys($file_salt); $ekey = $keys->getEncryptionKey(); $akey = $keys->getAuthenticationKey(); $ivsize = Core::BLOCK_BYTE_SIZE; $iv = Core::secureRandom($ivsize); $hmac = \hash_init(Core::HASH_FUNCTION_NAME, HASH_HMAC, $akey); if ($hmac === false) { throw new Ex\EnvironmentIsBrokenException( 'Cannot initialize a hash context' ); } self::writeBytes( $outputHandle, Core::CURRENT_VERSION . $file_salt . $iv, Core::HEADER_VERSION_SIZE + Core::SALT_BYTE_SIZE + $ivsize ); \hash_update($hmac, Core::CURRENT_VERSION); \hash_update($hmac, $file_salt); \hash_update($hmac, $iv); $thisIv = $iv; $inc = Core::BUFFER_BYTE_SIZE / Core::BLOCK_BYTE_SIZE; $at_file_end = false; while (! (\feof($inputHandle) || $at_file_end)) { $pos = \ftell($inputHandle); if ($pos === false) { throw new Ex\IOException( 'Could not get current position in input file during encryption' ); } if ($pos + Core::BUFFER_BYTE_SIZE >= $inputSize) { $at_file_end = true; $read = self::readBytes( $inputHandle, $inputSize - $pos ); } else { $read = self::readBytes( $inputHandle, Core::BUFFER_BYTE_SIZE ); } $encrypted = \openssl_encrypt( $read, Core::CIPHER_METHOD, $ekey, OPENSSL_RAW_DATA, $thisIv ); if ($encrypted === false) { throw new Ex\EnvironmentIsBrokenException( 'OpenSSL encryption error' ); } self::writeBytes($outputHandle, $encrypted, Core::ourStrlen($encrypted)); \hash_update($hmac, $encrypted); $thisIv = Core::incrementCounter($thisIv, $inc); } $final_mac = \hash_final($hmac, true); self::writeBytes($outputHandle, $final_mac, Core::MAC_BYTE_SIZE); } public static function decryptResourceInternal($inputHandle, $outputHandle, KeyOrPassword $secret) { if (! \is_resource($inputHandle)) { throw new Ex\IOException( 'Input handle must be a resource!' ); } if (! \is_resource($outputHandle)) { throw new Ex\IOException( 'Output handle must be a resource!' ); } $stat = \fstat($inputHandle); if ($stat['size'] < Core::MINIMUM_CIPHERTEXT_SIZE) { throw new Ex\WrongKeyOrModifiedCiphertextException( 'Input file is too small to have been created by this library.' ); } $header = self::readBytes($inputHandle, Core::HEADER_VERSION_SIZE); if ($header !== Core::CURRENT_VERSION) { throw new Ex\WrongKeyOrModifiedCiphertextException( 'Bad version header.' ); } $file_salt = self::readBytes($inputHandle, Core::SALT_BYTE_SIZE); $ivsize = Core::BLOCK_BYTE_SIZE; $iv = self::readBytes($inputHandle, $ivsize); $keys = $secret->deriveKeys($file_salt); $ekey = $keys->getEncryptionKey(); $akey = $keys->getAuthenticationKey(); $macs = []; $thisIv = $iv; $inc = Core::BUFFER_BYTE_SIZE / Core::BLOCK_BYTE_SIZE; if (\fseek($inputHandle, (-1 * Core::MAC_BYTE_SIZE), SEEK_END) === false) { throw new Ex\IOException( 'Cannot seek to beginning of MAC within input file' ); } $cipher_end = \ftell($inputHandle); if ($cipher_end === false) { throw new Ex\IOException( 'Cannot read input file' ); } --$cipher_end; $stored_mac = self::readBytes($inputHandle, Core::MAC_BYTE_SIZE); $hmac = \hash_init(Core::HASH_FUNCTION_NAME, HASH_HMAC, $akey); if ($hmac === false) { throw new Ex\EnvironmentIsBrokenException( 'Cannot initialize a hash context' ); } if (\fseek($inputHandle, Core::HEADER_VERSION_SIZE, SEEK_SET) === false) { throw new Ex\IOException( 'Cannot read seek within input file' ); } if (\fseek($inputHandle, Core::SALT_BYTE_SIZE + $ivsize, SEEK_CUR) === false) { throw new Ex\IOException( 'Cannot seek input file to beginning of ciphertext' ); } \hash_update($hmac, $header); \hash_update($hmac, $file_salt); \hash_update($hmac, $iv); $hmac2 = \hash_copy($hmac); $break = false; while (! $break) { $pos = \ftell($inputHandle); if ($pos === false) { throw new Ex\IOException( 'Could not get current position in input file during decryption' ); } if ($pos + Core::BUFFER_BYTE_SIZE >= $cipher_end) { $break = true; $read = self::readBytes( $inputHandle, $cipher_end - $pos + 1 ); } else { $read = self::readBytes( $inputHandle, Core::BUFFER_BYTE_SIZE ); } \hash_update($hmac, $read); $chunk_mac = \hash_copy($hmac); if ($chunk_mac === false) { throw new Ex\EnvironmentIsBrokenException( 'Cannot duplicate a hash context' ); } $macs []= \hash_final($chunk_mac); } $final_mac = \hash_final($hmac, true); if (! Core::hashEquals($final_mac, $stored_mac)) { throw new Ex\WrongKeyOrModifiedCiphertextException( 'Integrity check failed.' ); } if (\fseek($inputHandle, Core::SALT_BYTE_SIZE + $ivsize + Core::HEADER_VERSION_SIZE, SEEK_SET) === false) { throw new Ex\IOException( 'Could not move the input file pointer during decryption' ); } $at_file_end = false; while (! $at_file_end) { $pos = \ftell($inputHandle); if ($pos === false) { throw new Ex\IOException( 'Could not get current position in input file during decryption' ); } if ($pos + Core::BUFFER_BYTE_SIZE >= $cipher_end) { $at_file_end = true; $read = self::readBytes( $inputHandle, $cipher_end - $pos + 1 ); } else { $read = self::readBytes( $inputHandle, Core::BUFFER_BYTE_SIZE ); } \hash_update($hmac2, $read); $calc_mac = \hash_copy($hmac2); if ($calc_mac === false) { throw new Ex\EnvironmentIsBrokenException( 'Cannot duplicate a hash context' ); } $calc = \hash_final($calc_mac); if (empty($macs)) { throw new Ex\WrongKeyOrModifiedCiphertextException( 'File was modified after MAC verification' ); } elseif (! Core::hashEquals(\array_shift($macs), $calc)) { throw new Ex\WrongKeyOrModifiedCiphertextException( 'File was modified after MAC verification' ); } $decrypted = \openssl_decrypt( $read, Core::CIPHER_METHOD, $ekey, OPENSSL_RAW_DATA, $thisIv ); if ($decrypted === false) { throw new Ex\EnvironmentIsBrokenException( 'OpenSSL decryption error' ); } self::writeBytes( $outputHandle, $decrypted, Core::ourStrlen($decrypted) ); $thisIv = Core::incrementCounter($thisIv, $inc); } } public static function readBytes($stream, $num_bytes) { if ($num_bytes < 0) { throw new Ex\EnvironmentIsBrokenException( 'Tried to read less than 0 bytes' ); } elseif ($num_bytes === 0) { return ''; } $buf = ''; $remaining = $num_bytes; while ($remaining > 0 && ! \feof($stream)) { $read = \fread($stream, $remaining); if ($read === false) { throw new Ex\IOException( 'Could not read from the file' ); } $buf .= $read; $remaining -= Core::ourStrlen($read); } if (Core::ourStrlen($buf) !== $num_bytes) { throw new Ex\IOException( 'Tried to read past the end of the file' ); } return $buf; } public static function writeBytes($stream, $buf, $num_bytes = null) { $bufSize = Core::ourStrlen($buf); if ($num_bytes === null) { $num_bytes = $bufSize; } if ($num_bytes > $bufSize) { throw new Ex\IOException( 'Trying to write more bytes than the buffer contains.' ); } if ($num_bytes < 0) { throw new Ex\IOException( 'Tried to write less than 0 bytes' ); } $remaining = $num_bytes; while ($remaining > 0) { $written = \fwrite($stream, $buf, $remaining); if ($written === false) { throw new Ex\IOException( 'Could not write to the file' ); } $buf = Core::ourSubstr($buf, $written, null); $remaining -= $written; } return $num_bytes; } private static function getLastErrorMessage() { $error = error_get_last(); if ($error === null) { return '[no PHP error]'; } else { return $error['message']; } } } > 4; $hex .= \pack( 'CC', 87 + $b + ((($b - 10) >> 8) & ~38), 87 + $c + ((($c - 10) >> 8) & ~38) ); } return $hex; } public static function hexToBin($hex_string) { $hex_pos = 0; $bin = ''; $hex_len = Core::ourStrlen($hex_string); $state = 0; $c_acc = 0; while ($hex_pos < $hex_len) { $c = \ord($hex_string[$hex_pos]); $c_num = $c ^ 48; $c_num0 = ($c_num - 10) >> 8; $c_alpha = ($c & ~32) - 55; $c_alpha0 = (($c_alpha - 10) ^ ($c_alpha - 16)) >> 8; if (($c_num0 | $c_alpha0) === 0) { throw new Ex\BadFormatException( 'Encoding::hexToBin() input is not a hex string.' ); } $c_val = ($c_num0 & $c_num) | ($c_alpha & $c_alpha0); if ($state === 0) { $c_acc = $c_val * 16; } else { $bin .= \pack('C', $c_acc | $c_val); } $state ^= 1; ++$hex_pos; } return $bin; } public static function saveBytesToChecksummedAsciiSafeString($header, $bytes) { if (Core::ourStrlen($header) !== self::SERIALIZE_HEADER_BYTES) { throw new Ex\EnvironmentIsBrokenException( 'Header must be ' . self::SERIALIZE_HEADER_BYTES . ' bytes.' ); } return Encoding::binToHex( $header . $bytes . \hash( self::CHECKSUM_HASH_ALGO, $header . $bytes, true ) ); } public static function loadBytesFromChecksummedAsciiSafeString($expected_header, $string) { if (Core::ourStrlen($expected_header) !== self::SERIALIZE_HEADER_BYTES) { throw new Ex\EnvironmentIsBrokenException( 'Header must be 4 bytes.' ); } $bytes = Encoding::hexToBin($string); if (Core::ourStrlen($bytes) < self::SERIALIZE_HEADER_BYTES + self::CHECKSUM_BYTE_SIZE) { throw new Ex\BadFormatException( 'Encoded data is shorter than expected.' ); } $actual_header = Core::ourSubstr($bytes, 0, self::SERIALIZE_HEADER_BYTES); if ($actual_header !== $expected_header) { throw new Ex\BadFormatException( 'Invalid header.' ); } $checked_bytes = Core::ourSubstr( $bytes, 0, Core::ourStrlen($bytes) - self::CHECKSUM_BYTE_SIZE ); $checksum_a = Core::ourSubstr( $bytes, Core::ourStrlen($bytes) - self::CHECKSUM_BYTE_SIZE, self::CHECKSUM_BYTE_SIZE ); $checksum_b = \hash(self::CHECKSUM_HASH_ALGO, $checked_bytes, true); if (! Core::hashEquals($checksum_a, $checksum_b)) { throw new Ex\BadFormatException( "Data is corrupted, the checksum doesn't match" ); } return Core::ourSubstr( $bytes, self::SERIALIZE_HEADER_BYTES, Core::ourStrlen($bytes) - self::SERIALIZE_HEADER_BYTES - self::CHECKSUM_BYTE_SIZE ); } } akey; } public function getEncryptionKey() { return $this->ekey; } public function __construct($akey, $ekey) { $this->akey = $akey; $this->ekey = $ekey; } } deriveKeys($salt); $ekey = $keys->getEncryptionKey(); $akey = $keys->getAuthenticationKey(); $iv = Core::secureRandom(Core::BLOCK_BYTE_SIZE); $ciphertext = Core::CURRENT_VERSION . $salt . $iv . self::plainEncrypt($plaintext, $ekey, $iv); $auth = \hash_hmac(Core::HASH_FUNCTION_NAME, $ciphertext, $akey, true); $ciphertext = $ciphertext . $auth; if ($raw_binary) { return $ciphertext; } return Encoding::binToHex($ciphertext); } private static function decryptInternal($ciphertext, KeyOrPassword $secret, $raw_binary) { RuntimeTests::runtimeTest(); if (! $raw_binary) { try { $ciphertext = Encoding::hexToBin($ciphertext); } catch (Ex\BadFormatException $ex) { throw new Ex\WrongKeyOrModifiedCiphertextException( 'Ciphertext has invalid hex encoding.' ); } } if (Core::ourStrlen($ciphertext) < Core::MINIMUM_CIPHERTEXT_SIZE) { throw new Ex\WrongKeyOrModifiedCiphertextException( 'Ciphertext is too short.' ); } $header = Core::ourSubstr($ciphertext, 0, Core::HEADER_VERSION_SIZE); if ($header !== Core::CURRENT_VERSION) { throw new Ex\WrongKeyOrModifiedCiphertextException( 'Bad version header.' ); } $salt = Core::ourSubstr( $ciphertext, Core::HEADER_VERSION_SIZE, Core::SALT_BYTE_SIZE ); if ($salt === false) { throw new Ex\EnvironmentIsBrokenException(); } $iv = Core::ourSubstr( $ciphertext, Core::HEADER_VERSION_SIZE + Core::SALT_BYTE_SIZE, Core::BLOCK_BYTE_SIZE ); if ($iv === false) { throw new Ex\EnvironmentIsBrokenException(); } $hmac = Core::ourSubstr( $ciphertext, Core::ourStrlen($ciphertext) - Core::MAC_BYTE_SIZE, Core::MAC_BYTE_SIZE ); if ($hmac === false) { throw new Ex\EnvironmentIsBrokenException(); } $encrypted = Core::ourSubstr( $ciphertext, Core::HEADER_VERSION_SIZE + Core::SALT_BYTE_SIZE + Core::BLOCK_BYTE_SIZE, Core::ourStrlen($ciphertext) - Core::MAC_BYTE_SIZE - Core::SALT_BYTE_SIZE - Core::BLOCK_BYTE_SIZE - Core::HEADER_VERSION_SIZE ); if ($encrypted === false) { throw new Ex\EnvironmentIsBrokenException(); } $keys = $secret->deriveKeys($salt); if (self::verifyHMAC($hmac, $header . $salt . $iv . $encrypted, $keys->getAuthenticationKey())) { $plaintext = self::plainDecrypt($encrypted, $keys->getEncryptionKey(), $iv, Core::CIPHER_METHOD); return $plaintext; } else { throw new Ex\WrongKeyOrModifiedCiphertextException( 'Integrity check failed.' ); } } protected static function plainEncrypt($plaintext, $key, $iv) { Core::ensureConstantExists('OPENSSL_RAW_DATA'); Core::ensureFunctionExists('openssl_encrypt'); $ciphertext = \openssl_encrypt( $plaintext, Core::CIPHER_METHOD, $key, OPENSSL_RAW_DATA, $iv ); if ($ciphertext === false) { throw new Ex\EnvironmentIsBrokenException( 'openssl_encrypt() failed.' ); } return $ciphertext; } protected static function plainDecrypt($ciphertext, $key, $iv, $cipherMethod) { Core::ensureConstantExists('OPENSSL_RAW_DATA'); Core::ensureFunctionExists('openssl_decrypt'); $plaintext = \openssl_decrypt( $ciphertext, $cipherMethod, $key, OPENSSL_RAW_DATA, $iv ); if ($plaintext === false) { throw new Ex\EnvironmentIsBrokenException( 'openssl_decrypt() failed.' ); } return $plaintext; } protected static function verifyHMAC($correct_hmac, $message, $key) { $message_hmac = \hash_hmac(Core::HASH_FUNCTION_NAME, $message, $key, true); return Core::hashEquals($correct_hmac, $message_hmac); } } PHP_INT_MAX - 255) { throw new Ex\EnvironmentIsBrokenException( 'Integer overflow may occur.' ); } for ($i = Core::BLOCK_BYTE_SIZE - 1; $i >= 0; --$i) { $sum = \ord($ctr[$i]) + $inc; if (! \is_int($sum)) { throw new Ex\EnvironmentIsBrokenException( 'Integer overflow in CTR mode nonce increment.' ); } $ctr[$i] = \pack('C', $sum & 0xFF); $inc = $sum >> 8; } return $ctr; } public static function secureRandom($octets) { self::ensureFunctionExists('random_bytes'); try { return \random_bytes($octets); } catch (\Exception $ex) { throw new Ex\EnvironmentIsBrokenException( 'Your system does not have a secure random number generator.' ); } } public static function HKDF($hash, $ikm, $length, $info = '', $salt = null) { $digest_length = Core::ourStrlen(\hash_hmac($hash, '', '', true)); if (empty($length) || ! \is_int($length) || $length < 0 || $length > 255 * $digest_length) { throw new Ex\EnvironmentIsBrokenException( 'Bad output length requested of HKDF.' ); } if (\is_null($salt)) { $salt = \str_repeat("\x00", $digest_length); } $prk = \hash_hmac($hash, $ikm, $salt, true); if (Core::ourStrlen($prk) < $digest_length) { throw new Ex\EnvironmentIsBrokenException(); } $t = ''; $last_block = ''; for ($block_index = 1; Core::ourStrlen($t) < $length; ++$block_index) { $last_block = \hash_hmac( $hash, $last_block . $info . \chr($block_index), $prk, true ); $t .= $last_block; } $orm = Core::ourSubstr($t, 0, $length); if ($orm === false) { throw new Ex\EnvironmentIsBrokenException(); } return $orm; } public static function hashEquals($expected, $given) { static $native = null; if ($native === null) { $native = \function_exists('hash_equals'); } if ($native) { return \hash_equals($expected, $given); } if (Core::ourStrlen($expected) !== Core::ourStrlen($given)) { throw new Ex\EnvironmentIsBrokenException(); } $blind = Core::secureRandom(32); $message_compare = \hash_hmac(Core::HASH_FUNCTION_NAME, $given, $blind); $correct_compare = \hash_hmac(Core::HASH_FUNCTION_NAME, $expected, $blind); return $correct_compare === $message_compare; } public static function ensureConstantExists($name) { if (! \defined($name)) { throw new Ex\EnvironmentIsBrokenException(); } } public static function ensureFunctionExists($name) { if (! \function_exists($name)) { throw new Ex\EnvironmentIsBrokenException(); } } public static function ourStrlen($str) { static $exists = null; if ($exists === null) { $exists = \function_exists('mb_strlen'); } if ($exists) { $length = \mb_strlen($str, '8bit'); if ($length === false) { throw new Ex\EnvironmentIsBrokenException(); } return $length; } else { return \strlen($str); } } public static function ourSubstr($str, $start, $length = null) { static $exists = null; if ($exists === null) { $exists = \function_exists('mb_substr'); } if ($exists) { if (! isset($length)) { if ($start >= 0) { $length = Core::ourStrlen($str) - $start; } else { $length = -$start; } } if ($start === Core::ourStrlen($str) && $length === 0) { return ''; } if ($start > Core::ourStrlen($str)) { return false; } $substr = \mb_substr($str, $start, $length, '8bit'); if (Core::ourStrlen($substr) !== $length) { throw new Ex\EnvironmentIsBrokenException( 'Your version of PHP has bug #66797. Its implementation of mb_substr() is incorrect. See the details here: https://bugs.php.net/bug.php?id=66797' ); } return $substr; } if (isset($length)) { return \substr($str, $start, $length); } else { return \substr($str, $start); } } public static function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false) { if (! \is_string($algorithm)) { throw new \InvalidArgumentException( 'pbkdf2(): algorithm must be a string' ); } if (! \is_string($password)) { throw new \InvalidArgumentException( 'pbkdf2(): password must be a string' ); } if (! \is_string($salt)) { throw new \InvalidArgumentException( 'pbkdf2(): salt must be a string' ); } $count += 0; $key_length += 0; $algorithm = \strtolower($algorithm); if (! \in_array($algorithm, \hash_algos(), true)) { throw new Ex\EnvironmentIsBrokenException( 'Invalid or unsupported hash algorithm.' ); } $ok_algorithms = [ 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', 'ripemd160', 'ripemd256', 'ripemd320', 'whirlpool', ]; if (! \in_array($algorithm, $ok_algorithms, true)) { throw new Ex\EnvironmentIsBrokenException( 'Algorithm is not a secure cryptographic hash function.' ); } if ($count <= 0 || $key_length <= 0) { throw new Ex\EnvironmentIsBrokenException( 'Invalid PBKDF2 parameters.' ); } if (\function_exists('hash_pbkdf2')) { if (! $raw_output) { $key_length = $key_length * 2; } return \hash_pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output); } $hash_length = Core::ourStrlen(\hash($algorithm, '', true)); $block_count = \ceil($key_length / $hash_length); $output = ''; for ($i = 1; $i <= $block_count; $i++) { $last = $salt . \pack('N', $i); $last = $xorsum = \hash_hmac($algorithm, $last, $password, true); for ($j = 1; $j < $count; $j++) { $xorsum ^= ($last = \hash_hmac($algorithm, $last, $password, true)); } $output .= $xorsum; } if ($raw_output) { return Core::ourSubstr($output, 0, $key_length); } else { return Encoding::binToHex(Core::ourSubstr($output, 0, $key_length)); } } } prefixesPsr0)) { return call_user_func_array('array_merge', $this->prefixesPsr0); } return array(); } public function getPrefixesPsr4() { return $this->prefixDirsPsr4; } public function getFallbackDirs() { return $this->fallbackDirsPsr0; } public function getFallbackDirsPsr4() { return $this->fallbackDirsPsr4; } public function getClassMap() { return $this->classMap; } public function addClassMap(array $classMap) { if ($this->classMap) { $this->classMap = array_merge($this->classMap, $classMap); } else { $this->classMap = $classMap; } } public function add($prefix, $paths, $prepend = false) { if (!$prefix) { if ($prepend) { $this->fallbackDirsPsr0 = array_merge( (array) $paths, $this->fallbackDirsPsr0 ); } else { $this->fallbackDirsPsr0 = array_merge( $this->fallbackDirsPsr0, (array) $paths ); } return; } $first = $prefix[0]; if (!isset($this->prefixesPsr0[$first][$prefix])) { $this->prefixesPsr0[$first][$prefix] = (array) $paths; return; } if ($prepend) { $this->prefixesPsr0[$first][$prefix] = array_merge( (array) $paths, $this->prefixesPsr0[$first][$prefix] ); } else { $this->prefixesPsr0[$first][$prefix] = array_merge( $this->prefixesPsr0[$first][$prefix], (array) $paths ); } } public function addPsr4($prefix, $paths, $prepend = false) { if (!$prefix) { if ($prepend) { $this->fallbackDirsPsr4 = array_merge( (array) $paths, $this->fallbackDirsPsr4 ); } else { $this->fallbackDirsPsr4 = array_merge( $this->fallbackDirsPsr4, (array) $paths ); } } elseif (!isset($this->prefixDirsPsr4[$prefix])) { $length = strlen($prefix); if ('\\' !== $prefix[$length - 1]) { throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); } $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; $this->prefixDirsPsr4[$prefix] = (array) $paths; } elseif ($prepend) { $this->prefixDirsPsr4[$prefix] = array_merge( (array) $paths, $this->prefixDirsPsr4[$prefix] ); } else { $this->prefixDirsPsr4[$prefix] = array_merge( $this->prefixDirsPsr4[$prefix], (array) $paths ); } } public function set($prefix, $paths) { if (!$prefix) { $this->fallbackDirsPsr0 = (array) $paths; } else { $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; } } public function setPsr4($prefix, $paths) { if (!$prefix) { $this->fallbackDirsPsr4 = (array) $paths; } else { $length = strlen($prefix); if ('\\' !== $prefix[$length - 1]) { throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); } $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; $this->prefixDirsPsr4[$prefix] = (array) $paths; } } public function setUseIncludePath($useIncludePath) { $this->useIncludePath = $useIncludePath; } public function getUseIncludePath() { return $this->useIncludePath; } public function setClassMapAuthoritative($classMapAuthoritative) { $this->classMapAuthoritative = $classMapAuthoritative; } public function isClassMapAuthoritative() { return $this->classMapAuthoritative; } public function register($prepend = false) { spl_autoload_register(array($this, 'loadClass'), true, $prepend); } public function unregister() { spl_autoload_unregister(array($this, 'loadClass')); } public function loadClass($class) { if ($file = $this->findFile($class)) { includeFile($file); return true; } } public function findFile($class) { if ('\\' == $class[0]) { $class = substr($class, 1); } if (isset($this->classMap[$class])) { return $this->classMap[$class]; } if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { return false; } $file = $this->findFileWithExtension($class, '.php'); if (false === $file && defined('HHVM_VERSION')) { $file = $this->findFileWithExtension($class, '.hh'); } if (false === $file) { $this->missingClasses[$class] = true; } return $file; } private function findFileWithExtension($class, $ext) { $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; $first = $class[0]; if (isset($this->prefixLengthsPsr4[$first])) { foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) { if (0 === strpos($class, $prefix)) { foreach ($this->prefixDirsPsr4[$prefix] as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { return $file; } } } } } foreach ($this->fallbackDirsPsr4 as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { return $file; } } if (false !== $pos = strrpos($class, '\\')) { $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); } else { $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; } if (isset($this->prefixesPsr0[$first])) { foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { if (0 === strpos($class, $prefix)) { foreach ($dirs as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { return $file; } } } } } foreach ($this->fallbackDirsPsr0 as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { return $file; } } if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { return $file; } return false; } } function includeFile($file) { include $file; } = 50600 && !defined('HHVM_VERSION'); if ($useStaticLoader) { require_once __DIR__ . '/autoload_static.php'; call_user_func(\Composer\Autoload\ComposerStaticInit48409120b72c151f52194d87e7437450::getInitializer($loader)); } else { $map = require __DIR__ . '/autoload_namespaces.php'; foreach ($map as $namespace => $path) { $loader->set($namespace, $path); } $map = require __DIR__ . '/autoload_psr4.php'; foreach ($map as $namespace => $path) { $loader->setPsr4($namespace, $path); } $classMap = require __DIR__ . '/autoload_classmap.php'; if ($classMap) { $loader->addClassMap($classMap); } } $loader->register(true); if ($useStaticLoader) { $includeFiles = Composer\Autoload\ComposerStaticInit48409120b72c151f52194d87e7437450::$files; } else { $includeFiles = require __DIR__ . '/autoload_files.php'; } foreach ($includeFiles as $fileIdentifier => $file) { composerRequire48409120b72c151f52194d87e7437450($fileIdentifier, $file); } return $loader; } } function composerRequire48409120b72c151f52194d87e7437450($fileIdentifier, $file) { if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { require $file; $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; } } __DIR__ . '/..' . '/paragonie/random_compat/lib/random.php', ); public static $classMap = array ( 'Defuse\\Crypto\\Core' => __DIR__ . '/../..' . '/src/Core.php', 'Defuse\\Crypto\\Crypto' => __DIR__ . '/../..' . '/src/Crypto.php', 'Defuse\\Crypto\\DerivedKeys' => __DIR__ . '/../..' . '/src/DerivedKeys.php', 'Defuse\\Crypto\\Encoding' => __DIR__ . '/../..' . '/src/Encoding.php', 'Defuse\\Crypto\\Exception\\BadFormatException' => __DIR__ . '/../..' . '/src/Exception/BadFormatException.php', 'Defuse\\Crypto\\Exception\\CryptoException' => __DIR__ . '/../..' . '/src/Exception/CryptoException.php', 'Defuse\\Crypto\\Exception\\EnvironmentIsBrokenException' => __DIR__ . '/../..' . '/src/Exception/EnvironmentIsBrokenException.php', 'Defuse\\Crypto\\Exception\\IOException' => __DIR__ . '/../..' . '/src/Exception/IOException.php', 'Defuse\\Crypto\\Exception\\WrongKeyOrModifiedCiphertextException' => __DIR__ . '/../..' . '/src/Exception/WrongKeyOrModifiedCiphertextException.php', 'Defuse\\Crypto\\File' => __DIR__ . '/../..' . '/src/File.php', 'Defuse\\Crypto\\Key' => __DIR__ . '/../..' . '/src/Key.php', 'Defuse\\Crypto\\KeyOrPassword' => __DIR__ . '/../..' . '/src/KeyOrPassword.php', 'Defuse\\Crypto\\KeyProtectedByPassword' => __DIR__ . '/../..' . '/src/KeyProtectedByPassword.php', 'Defuse\\Crypto\\RuntimeTests' => __DIR__ . '/../..' . '/src/RuntimeTests.php', ); public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { $loader->classMap = ComposerStaticInit48409120b72c151f52194d87e7437450::$classMap; }, null, ClassLoader::class); } } $vendorDir . '/paragonie/random_compat/lib/random.php', ); $baseDir . '/src/Core.php', 'Defuse\\Crypto\\Crypto' => $baseDir . '/src/Crypto.php', 'Defuse\\Crypto\\DerivedKeys' => $baseDir . '/src/DerivedKeys.php', 'Defuse\\Crypto\\Encoding' => $baseDir . '/src/Encoding.php', 'Defuse\\Crypto\\Exception\\BadFormatException' => $baseDir . '/src/Exception/BadFormatException.php', 'Defuse\\Crypto\\Exception\\CryptoException' => $baseDir . '/src/Exception/CryptoException.php', 'Defuse\\Crypto\\Exception\\EnvironmentIsBrokenException' => $baseDir . '/src/Exception/EnvironmentIsBrokenException.php', 'Defuse\\Crypto\\Exception\\IOException' => $baseDir . '/src/Exception/IOException.php', 'Defuse\\Crypto\\Exception\\WrongKeyOrModifiedCiphertextException' => $baseDir . '/src/Exception/WrongKeyOrModifiedCiphertextException.php', 'Defuse\\Crypto\\File' => $baseDir . '/src/File.php', 'Defuse\\Crypto\\Key' => $baseDir . '/src/Key.php', 'Defuse\\Crypto\\KeyOrPassword' => $baseDir . '/src/KeyOrPassword.php', 'Defuse\\Crypto\\KeyProtectedByPassword' => $baseDir . '/src/KeyProtectedByPassword.php', 'Defuse\\Crypto\\RuntimeTests' => $baseDir . '/src/RuntimeTests.php', ); $max) { throw new Error( 'Minimum value must be less than or equal to the maximum value' ); } if ($max === $min) { return $min; } $attempts = $bits = $bytes = $mask = $valueShift = 0; $range = $max - $min; if (!is_int($range)) { $bytes = PHP_INT_SIZE; $mask = ~0; } else { while ($range > 0) { if ($bits % 8 === 0) { ++$bytes; } ++$bits; $range >>= 1; $mask = $mask << 1 | 1; } $valueShift = $min; } do { if ($attempts > 128) { throw new Exception( 'random_int: RNG is broken - too many rejections' ); } $randomByteString = random_bytes($bytes); if ($randomByteString === false) { throw new Exception( 'Random number generator failure' ); } $val = 0; for ($i = 0; $i < $bytes; ++$i) { $val |= ord($randomByteString[$i]) << ($i * 8); } $val &= $mask; $val += $valueShift; ++$attempts; } while (!is_int($val) || $val > $max || $val < $min); return (int) $val; } 2147483647) { $buf = ''; for ($i = 0; $i < $bytes; $i += 1073741824) { $n = ($bytes - $i) > 1073741824 ? 1073741824 : $bytes - $i; $buf .= Sodium::randombytes_buf($n); } } else { $buf = Sodium::randombytes_buf($bytes); } if ($buf !== false) { if (RandomCompat_strlen($buf) === $bytes) { return $buf; } } throw new Exception( 'Could not gather sufficient random data' ); } 2147483647) { $buf = ''; for ($i = 0; $i < $bytes; $i += 1073741824) { $n = ($bytes - $i) > 1073741824 ? 1073741824 : $bytes - $i; $buf .= \Sodium\randombytes_buf($n); } } else { $buf = \Sodium\randombytes_buf($bytes); } if ($buf !== false) { if (RandomCompat_strlen($buf) === $bytes) { return $buf; } } throw new Exception( 'Could not gather sufficient random data' ); } 0); if ($buf !== false) { if (RandomCompat_strlen($buf) === $bytes) { return $buf; } } } throw new Exception( 'Error reading from source device' ); } GetRandom($bytes, 0)); if (RandomCompat_strlen($buf) >= $bytes) { return RandomCompat_substr($buf, 0, $bytes); } ++$execCount; } while ($execCount < $bytes); throw new Exception( 'Could not gather sufficient random data' ); } = 50300 && function_exists('\\Sodium\\randombytes_buf')) { require_once $RandomCompatDIR.'/random_bytes_libsodium.php'; } elseif (method_exists('Sodium', 'randombytes_buf')) { require_once $RandomCompatDIR.'/random_bytes_libsodium_legacy.php'; } } if (DIRECTORY_SEPARATOR === '/') { $RandomCompatUrandom = true; $RandomCompat_basedir = ini_get('open_basedir'); if (!empty($RandomCompat_basedir)) { $RandomCompat_open_basedir = explode( PATH_SEPARATOR, strtolower($RandomCompat_basedir) ); $RandomCompatUrandom = (array() !== array_intersect( array('/dev', '/dev/', '/dev/urandom'), $RandomCompat_open_basedir )); $RandomCompat_open_basedir = null; } if ( !function_exists('random_bytes') && $RandomCompatUrandom && @is_readable('/dev/urandom') ) { require_once $RandomCompatDIR.'/random_bytes_dev_urandom.php'; } $RandomCompat_basedir = null; } else { $RandomCompatUrandom = false; } if ( !function_exists('random_bytes') && PHP_VERSION_ID >= 50307 && extension_loaded('mcrypt') && (DIRECTORY_SEPARATOR !== '/' || $RandomCompatUrandom) ) { if ( DIRECTORY_SEPARATOR !== '/' || (PHP_VERSION_ID <= 50609 || PHP_VERSION_ID >= 50613) ) { require_once $RandomCompatDIR.'/random_bytes_mcrypt.php'; } } $RandomCompatUrandom = null; if ( !function_exists('random_bytes') && extension_loaded('com_dotnet') && class_exists('COM') ) { $RandomCompat_disabled_classes = preg_split( '#\s*,\s*#', strtolower(ini_get('disable_classes')) ); if (!in_array('com', $RandomCompat_disabled_classes)) { try { $RandomCompatCOMtest = new COM('CAPICOM.Utilities.1'); if (method_exists($RandomCompatCOMtest, 'GetRandom')) { require_once $RandomCompatDIR.'/random_bytes_com_dotnet.php'; } } catch (com_exception $e) { } } $RandomCompat_disabled_classes = null; $RandomCompatCOMtest = null; } if (!function_exists('random_bytes')) { function random_bytes($length) { throw new Exception( 'There is no suitable CSPRNG installed on your system' ); } } } if (!function_exists('random_int')) { require_once $RandomCompatDIR.'/random_int.php'; } $RandomCompatDIR = null; } ~PHP_INT_MAX && $number < PHP_INT_MAX ) { $number = (int) $number; } if (is_int($number) || $fail_open) { return $number; } throw new TypeError( 'Expected an integer.' ); } } RandomCompat_strlen($binary_string)) { return false; } return mb_substr($binary_string, $start, $length, '8bit'); } } else { function RandomCompat_substr($binary_string, $start, $length = null) { if (!is_string($binary_string)) { throw new TypeError( 'RandomCompat_substr(): First argument should be a string' ); } if (!is_int($start)) { throw new TypeError( 'RandomCompat_substr(): Second argument should be an integer' ); } if ($length !== null) { if (!is_int($length)) { throw new TypeError( 'RandomCompat_substr(): Third argument should be an integer, or omitted' ); } return substr($binary_string, $start, $length); } return substr($binary_string, $start); } } }