diff options
author | Lukas Reschke <lukas@owncloud.com> | 2014-09-03 15:28:42 +0200 |
---|---|---|
committer | Lukas Reschke <lukas@owncloud.com> | 2014-09-03 15:28:42 +0200 |
commit | 373d1c5e9f4c85e86a0ac1b53b3e54a0d9cdf06e (patch) | |
tree | 6b7027fb5bf00d8e7d5d18875e9190ff9f82e062 /lib/private/security | |
parent | d64cacec438e379a39fd2e791020f417b3737d9b (diff) | |
parent | dbbdcff862663373711d968821bb79a10aeb52a6 (diff) | |
download | nextcloud-server-373d1c5e9f4c85e86a0ac1b53b3e54a0d9cdf06e.tar.gz nextcloud-server-373d1c5e9f4c85e86a0ac1b53b3e54a0d9cdf06e.zip |
Merge pull request #10642 from owncloud/securityutils
Add some security utilities
Diffstat (limited to 'lib/private/security')
-rw-r--r-- | lib/private/security/crypto.php | 115 | ||||
-rw-r--r-- | lib/private/security/securerandom.php | 79 | ||||
-rw-r--r-- | lib/private/security/stringutils.php | 42 |
3 files changed, 236 insertions, 0 deletions
diff --git a/lib/private/security/crypto.php b/lib/private/security/crypto.php new file mode 100644 index 00000000000..6fdff8d92a2 --- /dev/null +++ b/lib/private/security/crypto.php @@ -0,0 +1,115 @@ +<?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\ISecureRandom; +use OCP\Security\StringUtils; +use OCP\IConfig; + +/** + * 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; + /** @var IConfig */ + private $config; + /** @var ISecureRandom */ + private $random; + + function __construct(IConfig $config, ISecureRandom $random) { + $this->cipher = new Crypt_AES(); + $this->config = $config; + $this->random = $random; + } + + /** + * @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 = $this->config->getSystemValue('secret'); + } + + // Append an "a" behind the password and hash it to prevent reusing the same password as for encryption + $password = hash('sha512', $password . 'a'); + + $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 = $this->config->getSystemValue('secret'); + } + $this->cipher->setPassword($password); + + $iv = $this->random->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 = $this->config->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..33a3a708012 --- /dev/null +++ b/lib/private/security/stringutils.php @@ -0,0 +1,42 @@ +<?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. + * + * Be aware that this function may leak whether the string to compare have a different + * length. + * + * @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()->getLowStrengthGenerator()->generate(10); + + if(hash('sha512', $expected.$randomString) === hash('sha512', $input.$randomString)) { + if($expected === $input) { + return true; + } + } + + return false; + } +}
\ No newline at end of file |