diff options
author | Lukas Reschke <lukas@owncloud.com> | 2014-11-20 18:02:32 +0300 |
---|---|---|
committer | Lukas Reschke <lukas@owncloud.com> | 2014-11-20 18:05:42 +0300 |
commit | ec853da5ad54af7e6eabb40923784a56330b095a (patch) | |
tree | 923772b3571478dc4d5a7d3a6f4d94c8ab487a51 /lib/private/security | |
parent | f64c6c9c9cb48ce2291c5c613e80794e6130a85d (diff) |
Backport \OC\Security\Crypto to ownCloud 7
Conflicts:
lib/repair/repairconfig.php
Diffstat (limited to 'lib/private/security')
-rw-r--r-- | lib/private/security/crypto.php | 130 | ||||
-rw-r--r-- | lib/private/security/stringutils.php | 46 |
2 files changed, 176 insertions, 0 deletions
diff --git a/lib/private/security/crypto.php b/lib/private/security/crypto.php new file mode 100644 index 00000000000..44fe2fc0326 --- /dev/null +++ b/lib/private/security/crypto.php @@ -0,0 +1,130 @@ +<?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; +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; + + /** + * @param IConfig $config + */ + function __construct(IConfig $config) { + $this->cipher = new Crypt_AES(); + $this->config = $config; + } + + /** + * Custom implementation of hex2bin since the function is only available starting + * with PHP 5.4 + * + * @TODO Remove this once 5.3 support for ownCloud is dropped + * @param $message + * @return string + */ + protected static function hexToBin($message) { + if (function_exists('hex2bin')) { + return hex2bin($message); + } + + return pack("H*", $message); + } + + /** + * @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 = \OC_Util::generateRandomBytes($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 = self::hexToBin($parts[0]); + $iv = $parts[1]; + $hmac = self::hexToBin($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); + } + +}
\ No newline at end of file diff --git a/lib/private/security/stringutils.php b/lib/private/security/stringutils.php new file mode 100644 index 00000000000..449883b634f --- /dev/null +++ b/lib/private/security/stringutils.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 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(!is_string($expected) || !is_string($input)) { + return false; + } + + if(function_exists('hash_equals')) { + return hash_equals($expected, $input); + } + + $randomString = \OC_Util::generateRandomBytes(10); + + if(hash('sha512', $expected.$randomString) === hash('sha512', $input.$randomString)) { + if($expected === $input) { + return true; + } + } + + return false; + } +}
\ No newline at end of file |