diff --git a/config/config.sample.php b/config/config.sample.php index 9656555691..02dbb1fcf7 100755 --- a/config/config.sample.php +++ b/config/config.sample.php @@ -35,6 +35,9 @@ $CONFIG = array( /* Define the salt used to hash the user passwords. All your user passwords are lost if you lose this string. */ "passwordsalt" => "", +/* Secret used by ownCloud for various purposes, e.g. to encrypt data. If you lose this string there will be data corruption. */ +"secret" => "", + /* Force use of HTTPS connection (true = use HTTPS) */ "forcessl" => false, diff --git a/core/setup/controller.php b/core/setup/controller.php index e764b232e8..c72f06fc2d 100644 --- a/core/setup/controller.php +++ b/core/setup/controller.php @@ -153,7 +153,6 @@ class Controller { 'hasMSSQL' => $hasMSSQL, 'databases' => $databases, 'directory' => $datadir, - 'secureRNG' => \OC_Util::secureRNGAvailable(), 'htaccessWorking' => $htaccessWorking, 'vulnerableToNullByte' => $vulnerableToNullByte, 'errors' => $errors, diff --git a/core/templates/installation.php b/core/templates/installation.php index f934e3a86c..b74d4caf10 100644 --- a/core/templates/installation.php +++ b/core/templates/installation.php @@ -27,13 +27,6 @@ t('Please update your PHP installation to use %s securely.', $theme->getName() )); ?>

- -
- t('Security Warning'));?> -

t('No secure random number generator is available, please enable the PHP OpenSSL extension.'));?>
- t('Without a secure random number generator an attacker may be able to predict password reset tokens and take over your account.'));?>

-
-
t('Security Warning'));?> diff --git a/lib/private/repair.php b/lib/private/repair.php index 46b5ae4639..4ff446f860 100644 --- a/lib/private/repair.php +++ b/lib/private/repair.php @@ -71,6 +71,7 @@ class Repair extends BasicEmitter { return array( new \OC\Repair\RepairMimeTypes(), new \OC\Repair\RepairLegacyStorages(\OC::$server->getConfig(), \OC_DB::getConnection()), + new \OC\Repair\RepairConfig(), ); } diff --git a/lib/private/security/crypto.php b/lib/private/security/crypto.php new file mode 100644 index 0000000000..659b0170ec --- /dev/null +++ b/lib/private/security/crypto.php @@ -0,0 +1,104 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + + +namespace OC\Security; + +use Crypt_AES; +use Crypt_Hash; +use OCP\Security\ICrypto; +use OCP\Security\StringUtils; + +/** + * Class Crypto provides a high-level encryption layer using AES-CBC. If no key has been provided + * it will use the secret defined in config.php as key. Additionally the message will be HMAC'd. + * + * Usage: + * $encryptWithDefaultPassword = \OC::$server->getCrypto()->encrypt('EncryptedText'); + * $encryptWithCustompassword = \OC::$server->getCrypto()->encrypt('EncryptedText', 'password'); + * + * @package OC\Security + */ +class Crypto implements ICrypto { + /** @var Crypt_AES $cipher */ + private $cipher; + /** @var int */ + private $ivLength = 16; + + function __construct() { + $this->cipher = new Crypt_AES(); + } + + /** + * @param string $message The message to authenticate + * @param string $password Password to use (defaults to `secret` in config.php) + * @return string Calculated HMAC + */ + public function calculateHMAC($message, $password = '') { + if($password === '') { + $password = \OC::$server->getConfig()->getSystemValue('secret'); + } + + $hash = new Crypt_Hash('sha512'); + $hash->setKey($password); + return $hash->hash($message); + } + + /** + * Encrypts a value and adds an HMAC (Encrypt-Then-MAC) + * @param string $plaintext + * @param string $password Password to encrypt, if not specified the secret from config.php will be taken + * @return string Authenticated ciphertext + */ + public function encrypt($plaintext, $password = '') { + if($password === '') { + $password = \OC::$server->getConfig()->getSystemValue('secret'); + } + $this->cipher->setPassword($password); + + $iv = \OC::$server->getSecureRandom()->getLowStrengthGenerator()->generate($this->ivLength); + $this->cipher->setIV($iv); + + $ciphertext = bin2hex($this->cipher->encrypt($plaintext)); + $hmac = bin2hex($this->calculateHMAC($ciphertext.$iv, $password)); + + return $ciphertext.'|'.$iv.'|'.$hmac; + } + + /** + * Decrypts a value and verifies the HMAC (Encrypt-Then-Mac) + * @param string $authenticatedCiphertext + * @param string $password Password to encrypt, if not specified the secret from config.php will be taken + * @return string plaintext + * @throws \Exception If the HMAC does not match + */ + public function decrypt($authenticatedCiphertext, $password = '') { + if($password === '') { + $password = \OC::$server->getConfig()->getSystemValue('secret'); + } + $this->cipher->setPassword($password); + + $parts = explode('|', $authenticatedCiphertext); + if(sizeof($parts) !== 3) { + throw new \Exception('Authenticated ciphertext could not be decoded.'); + } + + $ciphertext = hex2bin($parts[0]); + $iv = $parts[1]; + $hmac = hex2bin($parts[2]); + + $this->cipher->setIV($iv); + + if(!StringUtils::equals($this->calculateHMAC($parts[0].$parts[1], $password), $hmac)) { + throw new \Exception('HMAC does not match.'); + } + + return $this->cipher->decrypt($ciphertext); + } + +} diff --git a/lib/private/security/securerandom.php b/lib/private/security/securerandom.php new file mode 100644 index 0000000000..2402e863fb --- /dev/null +++ b/lib/private/security/securerandom.php @@ -0,0 +1,79 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Security; + +use RandomLib; +use Sabre\DAV\Exception; +use OCP\Security\ISecureRandom; + +/** + * Class SecureRandom provides a layer around RandomLib to generate + * secure random strings. + * + * Usage: + * \OC::$server->getSecureRandom()->getMediumStrengthGenerator()->generate(10); + * + * @package OC\Security + */ +class SecureRandom implements ISecureRandom { + + /** @var \RandomLib\Factory */ + var $factory; + /** @var \RandomLib\Generator */ + var $generator; + + function __construct() { + $this->factory = new RandomLib\Factory; + } + + /** + * Convenience method to get a low strength random number generator. + * + * Low Strength should be used anywhere that random strings are needed + * in a non-cryptographical setting. They are not strong enough to be + * used as keys or salts. They are however useful for one-time use tokens. + * + * @return $this + */ + public function getLowStrengthGenerator() { + $this->generator = $this->factory->getLowStrengthGenerator(); + return $this; + } + + /** + * Convenience method to get a medium strength random number generator. + * + * Medium Strength should be used for most needs of a cryptographic nature. + * They are strong enough to be used as keys and salts. However, they do + * take some time and resources to generate, so they should not be over-used + * + * @return $this + */ + public function getMediumStrengthGenerator() { + $this->generator = $this->factory->getMediumStrengthGenerator(); + return $this; + } + + /** + * Generate a random string of specified length. + * @param string $length The length of the generated string + * @param string $characters An optional list of characters to use if no characterlist is + * specified 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ./ + * is used. + * @return string + * @throws \Exception If the generator is not initialized. + */ + public function generate($length, $characters = '') { + if(is_null($this->generator)) { + throw new \Exception('Generator is not initialized.'); + } + + return $this->generator->generateString($length, $characters); + } +} diff --git a/lib/private/security/stringutils.php b/lib/private/security/stringutils.php new file mode 100644 index 0000000000..32dff50fa8 --- /dev/null +++ b/lib/private/security/stringutils.php @@ -0,0 +1,38 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Security; + +class StringUtils { + + /** + * Compares whether two strings are equal. To prevent guessing of the string + * length this is done by comparing two hashes against each other and afterwards + * a comparison of the real string to prevent against the unlikely chance of + * collisions. + * @param string $expected The expected value + * @param string $input The input to compare against + * @return bool True if the two strings are equal, otherwise false. + */ + public static function equals($expected, $input) { + + if(function_exists('hash_equals')) { + return hash_equals($expected, $input); + } + + $randomString = \OC::$server->getSecureRandom()->getMediumStrengthGenerator()->generate(10); + + if(hash('sha512', $expected.$randomString) === hash('sha512', $input.$randomString)) { + if($expected === $input) { + return true; + } + } + + return false; + } +} \ No newline at end of file diff --git a/lib/private/server.php b/lib/private/server.php index aab3c82bfe..86fead1daf 100644 --- a/lib/private/server.php +++ b/lib/private/server.php @@ -9,6 +9,8 @@ use OC\Cache\UserCache; use OC\DB\ConnectionWrapper; use OC\Files\Node\Root; use OC\Files\View; +use OC\Security\Crypto; +use OC\Security\SecureRandom; use OCP\IServerContainer; /** @@ -199,6 +201,12 @@ class Server extends SimpleContainer implements IServerContainer { $this->registerService('Search', function ($c) { return new Search(); }); + $this->registerService('SecureRandom', function($c) { + return new SecureRandom(); + }); + $this->registerService('Crypto', function($c) { + return new Crypto(); + }); $this->registerService('Db', function ($c) { return new Db(); }); @@ -455,6 +463,24 @@ class Server extends SimpleContainer implements IServerContainer { return $this->query('Search'); } + /** + * Returns a SecureRandom instance + * + * @return \OCP\Security\ISecureRandom + */ + function getSecureRandom() { + return $this->query('SecureRandom'); + } + + /** + * Returns a Crypto instance + * + * @return \OCP\Security\ICrypto + */ + function getCrypto() { + return $this->query('Crypto'); + } + /** * Returns an instance of the db facade * diff --git a/lib/private/setup.php b/lib/private/setup.php index fdf98ab095..9ea1690b6d 100644 --- a/lib/private/setup.php +++ b/lib/private/setup.php @@ -67,14 +67,19 @@ class OC_Setup { } //generate a random salt that is used to salt the local user passwords - $salt = OC_Util::generateRandomBytes(30); - OC_Config::setValue('passwordsalt', $salt); + $salt = \OC::$server->getSecureRandom()->getLowStrengthGenerator()->generate(30); + \OC::$server->getConfig()->setSystemValue('passwordsalt', $salt); + + // generate a secret + $secret = \OC::$server->getSecureRandom()->getMediumStrengthGenerator()->generate(48); + \OC::$server->getConfig()->setSystemValue('secret', $secret); //write the config file - OC_Config::setValue('trusted_domains', $trustedDomains); - OC_Config::setValue('datadirectory', $datadir); - OC_Config::setValue('dbtype', $dbtype); - OC_Config::setValue('version', implode('.', OC_Util::getVersion())); + \OC::$server->getConfig()->setSystemValue('trusted_domains', $trustedDomains); + \OC::$server->getConfig()->setSystemValue('datadirectory', $datadir); + \OC::$server->getConfig()->setSystemValue('dbtype', $dbtype); + \OC::$server->getConfig()->setSystemValue('version', implode('.', OC_Util::getVersion())); + try { $dbSetup->initialize($options); $dbSetup->setupDatabase($username); diff --git a/lib/private/util.php b/lib/private/util.php index 4307560a92..b2a9aecb5d 100755 --- a/lib/private/util.php +++ b/lib/private/util.php @@ -905,7 +905,7 @@ class OC_Util { $id = OC_Config::getValue('instanceid', null); if (is_null($id)) { // We need to guarantee at least one letter in instanceid so it can be used as the session_name - $id = 'oc' . self::generateRandomBytes(10); + $id = 'oc' . \OC::$server->getSecureRandom()->getLowStrengthGenerator()->generate(10); OC_Config::$object->setValue('instanceid', $id); } return $id; @@ -1208,62 +1208,20 @@ class OC_Util { * * @param int $length of the random string * @return string - * Please also update secureRNGAvailable if you change something here + * @deprecated Use \OC::$server->getSecureRandom()->getMediumStrengthGenerator()->generate($length); instead */ public static function generateRandomBytes($length = 30) { - // Try to use openssl_random_pseudo_bytes - if (function_exists('openssl_random_pseudo_bytes')) { - $pseudoByte = bin2hex(openssl_random_pseudo_bytes($length, $strong)); - if ($strong == true) { - return substr($pseudoByte, 0, $length); // Truncate it to match the length - } - } - - // Try to use /dev/urandom - if (!self::runningOnWindows()) { - $fp = @file_get_contents('/dev/urandom', false, null, 0, $length); - if ($fp !== false) { - $string = substr(bin2hex($fp), 0, $length); - return $string; - } - } - - // Fallback to mt_rand() - $characters = '0123456789'; - $characters .= 'abcdefghijklmnopqrstuvwxyz'; - $charactersLength = strlen($characters) - 1; - $pseudoByte = ""; - - // Select some random characters - for ($i = 0; $i < $length; $i++) { - $pseudoByte .= $characters[mt_rand(0, $charactersLength)]; - } - return $pseudoByte; + return \OC::$server->getSecureRandom()->getMediumStrengthGenerator()->generate($length); } /** * Checks if a secure random number generator is available * - * @return bool + * @return true + * @deprecated Function will be removed in the future and does only return true. */ public static function secureRNGAvailable() { - // Check openssl_random_pseudo_bytes - if (function_exists('openssl_random_pseudo_bytes')) { - openssl_random_pseudo_bytes(1, $strong); - if ($strong == true) { - return true; - } - } - - // Check /dev/urandom - if (!self::runningOnWindows()) { - $fp = @file_get_contents('/dev/urandom', false, null, 0, 1); - if ($fp !== false) { - return true; - } - } - - return false; + return true; } /** diff --git a/lib/public/security/icrypto.php b/lib/public/security/icrypto.php new file mode 100644 index 0000000000..e55eea8dd6 --- /dev/null +++ b/lib/public/security/icrypto.php @@ -0,0 +1,46 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OCP\Security; + +/** + * Class Crypto provides a high-level encryption layer using AES-CBC. If no key has been provided + * it will use the secret defined in config.php as key. Additionally the message will be HMAC'd. + * + * Usage: + * $encryptWithDefaultPassword = \OC::$server->getCrypto()->encrypt('EncryptedText'); + * $encryptWithCustomPassword = \OC::$server->getCrypto()->encrypt('EncryptedText', 'password'); + * + * @package OCP\Security + */ +interface ICrypto { + + /** + * @param string $message The message to authenticate + * @param string $password Password to use (defaults to `secret` in config.php) + * @return string Calculated HMAC + */ + public function calculateHMAC($message, $password = ''); + + /** + * Encrypts a value and adds an HMAC (Encrypt-Then-MAC) + * @param string $plaintext + * @param string $password Password to encrypt, if not specified the secret from config.php will be taken + * @return string Authenticated ciphertext + */ + public function encrypt($plaintext, $password = ''); + + /** + * Decrypts a value and verifies the HMAC (Encrypt-Then-Mac) + * @param string $authenticatedCiphertext + * @param string $password Password to encrypt, if not specified the secret from config.php will be taken + * @return string plaintext + * @throws \Exception If the HMAC does not match + */ + public function decrypt($authenticatedCiphertext, $password = ''); +} diff --git a/lib/public/security/isecurerandom.php b/lib/public/security/isecurerandom.php new file mode 100644 index 0000000000..ae6e1d5845 --- /dev/null +++ b/lib/public/security/isecurerandom.php @@ -0,0 +1,53 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OCP\Security; + +/** + * Class SecureRandom provides a layer around RandomLib to generate + * secure random numbers. + * + * Usage: + * $rng = new \OC\Security\SecureRandom(); + * $randomString = $rng->getMediumStrengthGenerator()->generateString(30); + * + * @package OCP\Security + */ +interface ISecureRandom { + + /** + * Convenience method to get a low strength random number generator. + * + * Low Strength should be used anywhere that random strings are needed + * in a non-cryptographical setting. They are not strong enough to be + * used as keys or salts. They are however useful for one-time use tokens. + * + * @return $this + */ + public function getLowStrengthGenerator(); + + /** + * Convenience method to get a medium strength random number generator. + * + * Medium Strength should be used for most needs of a cryptographic nature. + * They are strong enough to be used as keys and salts. However, they do + * take some time and resources to generate, so they should not be over-used + * + * @return $this + */ + public function getMediumStrengthGenerator(); + + /** + * Generate a random string of specified length. + * @param string $length The length of the generated string + * @param string $characters An optional list of characters to use + * @return string + * @throws \Exception + */ + public function generate($length, $characters = ''); +} diff --git a/lib/public/security/stringutils.php b/lib/public/security/stringutils.php new file mode 100644 index 0000000000..8e7b132724 --- /dev/null +++ b/lib/public/security/stringutils.php @@ -0,0 +1,25 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + + +namespace OCP\Security; + +class StringUtils { + /** + * Compares whether two strings are equal. To prevent guessing of the string + * length this is done by comparing two hashes against each other and afterwards + * a comparison of the real string to prevent against the unlikely chance of + * collisions. + * @param string $expected The expected value + * @param string $input The input to compare against + * @return bool True if the two strings are equal, otherwise false. + */ + public static function equals($expected, $input) { + return \OC\Security\StringUtils::equals($expected, $input); + } +} diff --git a/lib/public/util.php b/lib/public/util.php index 87b7a4f19d..83a6155685 100644 --- a/lib/public/util.php +++ b/lib/public/util.php @@ -505,6 +505,7 @@ class Util { * Generates a cryptographic secure pseudo-random string * @param int $length of the random string * @return string + * @deprecated Use \OC::$server->getSecureRandom()->getMediumStrengthGenerator()->generate($length); instead */ public static function generateRandomBytes($length = 30) { return \OC_Util::generateRandomBytes($length); diff --git a/lib/repair/repairconfig.php b/lib/repair/repairconfig.php new file mode 100644 index 0000000000..e09d8e8fe7 --- /dev/null +++ b/lib/repair/repairconfig.php @@ -0,0 +1,37 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Repair; + +use OC\Hooks\BasicEmitter; +use OC\RepairStep; +use Sabre\DAV\Exception; + +class RepairConfig extends BasicEmitter implements RepairStep { + + public function getName() { + return 'Repair config'; + } + + /** + * Updates the configuration after running an update + */ + public function run() { + $this->addSecret(); + } + + /** + * Adds a secret to config.php + */ + private function addSecret() { + if(\OC::$server->getConfig()->getSystemValue('secret', null) === null) { + $secret = \OC::$server->getSecureRandom()->getMediumStrengthGenerator()->generate(48); + \OC::$server->getConfig()->setSystemValue('secret', $secret); + } + } +} diff --git a/tests/lib/security/crypto.php b/tests/lib/security/crypto.php new file mode 100644 index 0000000000..e07a60267e --- /dev/null +++ b/tests/lib/security/crypto.php @@ -0,0 +1,63 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +use \OC\Security\Crypto; + +class CryptoTest extends \PHPUnit_Framework_TestCase { + + function testDefaultEncrypt() { + $stringToEncrypt = 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.'; + $crypto = new Crypto(); + $ciphertext = $crypto->encrypt($stringToEncrypt); + $this->assertEquals($stringToEncrypt, $crypto->decrypt($ciphertext)); + + $stringToEncrypt = ''; + $ciphertext = $crypto->encrypt($stringToEncrypt); + $this->assertEquals($stringToEncrypt, $crypto->decrypt($ciphertext)); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage HMAC does not match. + */ + function testWrongPassword() { + $stringToEncrypt = 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.'; + $encryptCrypto = new Crypto(); + $ciphertext = $encryptCrypto->encrypt($stringToEncrypt); + + $decryptCrypto = new Crypto(); + $this->assertFalse($decryptCrypto->decrypt($ciphertext, 'A wrong password!')); + } + + function testLaterDecryption() { + $stringToEncrypt = 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.'; + $encryptedString = '560f5436ba864b9f12f7f7ca6d41c327554a6f2c0a160a03316b202af07c65163274993f3a46e7547c07ba89304f00594a2f3bd99f83859097c58049c39d0d4ade10e0de914ff0604961e7c849d0271ed6c0b23f984ba16e7d033e3305fb0910e7b6a2a65c988d17dbee71d8f953684d|d2kdFUspVjC0Y0sr|1a5feacf87eaa6869a6abdfba9a296e7bbad45b6ad89f7dce67cdc98e2da5dc4379cc672cc655e52bbf19599bf59482fbea13a73937697fa656bf10f3fc4f1aa'; + $crypto = new Crypto(); + $this->assertEquals($stringToEncrypt, $crypto->decrypt($encryptedString, 'ThisIsAVeryS3cur3P4ssw0rd')); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage HMAC does not match. + */ + function testWrongIV() { + $encryptedString = '560f5436ba864b9f12f7f7ca6d41c327554a6f2c0a160a03316b202af07c65163274993f3a46e7547c07ba89304f00594a2f3bd99f83859097c58049c39d0d4ade10e0de914ff0604961e7c849d0271ed6c0b23f984ba16e7d033e3305fb0910e7b6a2a65c988d17dbee71d8f953684d|d2kdFUspVjC0o0sr|1a5feacf87eaa6869a6abdfba9a296e7bbad45b6ad89f7dce67cdc98e2da5dc4379cc672cc655e52bbf19599bf59482fbea13a73937697fa656bf10f3fc4f1aa'; + $crypto = new Crypto(); + $crypto->decrypt($encryptedString, 'ThisIsAVeryS3cur3P4ssw0rd'); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage Authenticated ciphertext could not be decoded. + */ + function testWrongParameters() { + $encryptedString = '1|2'; + $crypto = new Crypto(); + $crypto->decrypt($encryptedString, 'ThisIsAVeryS3cur3P4ssw0rd'); + } +} diff --git a/tests/lib/security/securerandom.php b/tests/lib/security/securerandom.php new file mode 100644 index 0000000000..75f8f56fb9 --- /dev/null +++ b/tests/lib/security/securerandom.php @@ -0,0 +1,51 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class SecureRandomTest extends \PHPUnit_Framework_TestCase { + + public function stringGenerationProvider() { + return array( + array(0, 0), + array(1, 1), + array(128, 128), + array(256, 256), + array(1024, 1024), + array(2048, 2048), + array(64000, 64000), + ); + } + + /** + * @dataProvider stringGenerationProvider + */ + function testGetLowStrengthGeneratorLength($length, $expectedLength) { + $rng = new \OC\Security\SecureRandom(); + $generator = $rng->getLowStrengthGenerator(); + + $this->assertEquals($expectedLength, strlen($generator->generate($length))); + } + + /** + * @dataProvider stringGenerationProvider + */ + function testMediumLowStrengthGeneratorLength($length, $expectedLength) { + $rng = new \OC\Security\SecureRandom(); + $generator = $rng->getMediumStrengthGenerator(); + + $this->assertEquals($expectedLength, strlen($generator->generate($length))); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage Generator is not initialized + */ + function testUninitializedGenerate() { + $rng = new \OC\Security\SecureRandom(); + $rng->generate(30); + } +} diff --git a/tests/lib/security/stringutils.php b/tests/lib/security/stringutils.php new file mode 100644 index 0000000000..72293124eb --- /dev/null +++ b/tests/lib/security/stringutils.php @@ -0,0 +1,21 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +use \OC\Security\StringUtils; + +class StringUtilsTest extends \PHPUnit_Framework_TestCase { + + function testEquals() { + $this->assertTrue(StringUtils::equals('GpKY9fSnWRaeFNJbES99zVGvA', 'GpKY9fSnWRaeFNJbES99zVGvA')); + $this->assertFalse(StringUtils::equals('GpKY9fSnWNJbES99zVGvA', 'GpKY9fSnWRaeFNJbES99zVGvA')); + $this->assertFalse(StringUtils::equals('', 'GpKY9fSnWRaeFNJbES99zVGvA')); + $this->assertFalse(StringUtils::equals('GpKY9fSnWRaeFNJbES99zVGvA', '')); + $this->assertTrue(StringUtils::equals('', '')); + } + +}