summaryrefslogtreecommitdiffstats
path: root/lib/private/security
diff options
context:
space:
mode:
authorLukas Reschke <lukas@owncloud.com>2014-09-03 15:28:42 +0200
committerLukas Reschke <lukas@owncloud.com>2014-09-03 15:28:42 +0200
commit373d1c5e9f4c85e86a0ac1b53b3e54a0d9cdf06e (patch)
tree6b7027fb5bf00d8e7d5d18875e9190ff9f82e062 /lib/private/security
parentd64cacec438e379a39fd2e791020f417b3737d9b (diff)
parentdbbdcff862663373711d968821bb79a10aeb52a6 (diff)
downloadnextcloud-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.php115
-rw-r--r--lib/private/security/securerandom.php79
-rw-r--r--lib/private/security/stringutils.php42
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