diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/private/repair.php | 1 | ||||
-rw-r--r-- | lib/private/security/crypto.php | 104 | ||||
-rw-r--r-- | lib/private/security/securerandom.php | 79 | ||||
-rw-r--r-- | lib/private/security/stringutils.php | 38 | ||||
-rw-r--r-- | lib/private/server.php | 26 | ||||
-rw-r--r-- | lib/private/setup.php | 17 | ||||
-rwxr-xr-x | lib/private/util.php | 54 | ||||
-rw-r--r-- | lib/public/security/icrypto.php | 46 | ||||
-rw-r--r-- | lib/public/security/isecurerandom.php | 53 | ||||
-rw-r--r-- | lib/public/security/stringutils.php | 25 | ||||
-rw-r--r-- | lib/public/util.php | 1 | ||||
-rw-r--r-- | lib/repair/repairconfig.php | 37 |
12 files changed, 427 insertions, 54 deletions
diff --git a/lib/private/repair.php b/lib/private/repair.php index 46b5ae46399..4ff446f8608 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 00000000000..659b0170ecf --- /dev/null +++ b/lib/private/security/crypto.php @@ -0,0 +1,104 @@ +<?php +/** + * Copyright (c) 2014 Lukas Reschke <lukas@owncloud.com> + * 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 00000000000..2402e863fb0 --- /dev/null +++ b/lib/private/security/securerandom.php @@ -0,0 +1,79 @@ +<?php +/** + * Copyright (c) 2014 Lukas Reschke <lukas@owncloud.com> + * 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 00000000000..32dff50fa8b --- /dev/null +++ b/lib/private/security/stringutils.php @@ -0,0 +1,38 @@ +<?php +/** + * Copyright (c) 2014 Lukas Reschke <lukas@owncloud.com> + * 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 aab3c82bfeb..86fead1daf1 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(); }); @@ -456,6 +464,24 @@ class Server extends SimpleContainer implements IServerContainer { } /** + * 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 * * @return \OCP\IDb diff --git a/lib/private/setup.php b/lib/private/setup.php index fdf98ab0959..9ea1690b6d9 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 4307560a928..b2a9aecb5d0 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 00000000000..e55eea8dd66 --- /dev/null +++ b/lib/public/security/icrypto.php @@ -0,0 +1,46 @@ +<?php +/** + * Copyright (c) 2014 Lukas Reschke <lukas@owncloud.com> + * 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 00000000000..ae6e1d58451 --- /dev/null +++ b/lib/public/security/isecurerandom.php @@ -0,0 +1,53 @@ +<?php +/** + * Copyright (c) 2014 Lukas Reschke <lukas@owncloud.com> + * 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 00000000000..8e7b132724e --- /dev/null +++ b/lib/public/security/stringutils.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright (c) 2014 Lukas Reschke <lukas@owncloud.com> + * 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 87b7a4f19db..83a6155685b 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 00000000000..e09d8e8fe7a --- /dev/null +++ b/lib/repair/repairconfig.php @@ -0,0 +1,37 @@ +<?php +/** + * Copyright (c) 2014 Lukas Reschke <lukas@owncloud.com> + * 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); + } + } +} |