From d26a9c3c5819be48b76586c2fa60da9a7a9829dd Mon Sep 17 00:00:00 2001
From: Lukas Reschke <lukas@owncloud.com>
Date: Tue, 26 Aug 2014 19:02:40 +0200
Subject: [PATCH] Add some security utilities

This adds some security utilities to core including:
- A library for basic crypto operations (e.g. to encrypt passwords)
- A better library for cryptographic actions which allows you to specify the charset
- A library for secure string comparisions

Remove .htaccess

Remove .htaccess

Fix typo

Add public API

Use timing constant comparision

Remove CBC constant

Adjust code

Remove confusing $this
---
 config/config.sample.php              |   3 +
 core/setup/controller.php             |   1 -
 core/templates/installation.php       |   7 --
 lib/private/repair.php                |   1 +
 lib/private/security/crypto.php       | 104 ++++++++++++++++++++++++++
 lib/private/security/securerandom.php |  79 +++++++++++++++++++
 lib/private/security/stringutils.php  |  38 ++++++++++
 lib/private/server.php                |  26 +++++++
 lib/private/setup.php                 |  17 +++--
 lib/private/util.php                  |  54 ++-----------
 lib/public/security/icrypto.php       |  46 ++++++++++++
 lib/public/security/isecurerandom.php |  53 +++++++++++++
 lib/public/security/stringutils.php   |  25 +++++++
 lib/public/util.php                   |   1 +
 lib/repair/repairconfig.php           |  37 +++++++++
 tests/lib/security/crypto.php         |  63 ++++++++++++++++
 tests/lib/security/securerandom.php   |  51 +++++++++++++
 tests/lib/security/stringutils.php    |  21 ++++++
 18 files changed, 565 insertions(+), 62 deletions(-)
 create mode 100644 lib/private/security/crypto.php
 create mode 100644 lib/private/security/securerandom.php
 create mode 100644 lib/private/security/stringutils.php
 create mode 100644 lib/public/security/icrypto.php
 create mode 100644 lib/public/security/isecurerandom.php
 create mode 100644 lib/public/security/stringutils.php
 create mode 100644 lib/repair/repairconfig.php
 create mode 100644 tests/lib/security/crypto.php
 create mode 100644 tests/lib/security/securerandom.php
 create mode 100644 tests/lib/security/stringutils.php

diff --git a/config/config.sample.php b/config/config.sample.php
index 96565556910..02dbb1fcf75 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 e764b232e89..c72f06fc2df 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 f934e3a86c2..b74d4caf107 100644
--- a/core/templates/installation.php
+++ b/core/templates/installation.php
@@ -27,13 +27,6 @@
 		<?php p($l->t('Please update your PHP installation to use %s securely.', $theme->getName() )); ?></p>
 	</fieldset>
 	<?php endif; ?>
-	<?php if(!$_['secureRNG']): ?>
-	<fieldset class="warning">
-		<legend><strong><?php p($l->t('Security Warning'));?></strong></legend>
-		<p><?php p($l->t('No secure random number generator is available, please enable the PHP OpenSSL extension.'));?><br/>
-		<?php p($l->t('Without a secure random number generator an attacker may be able to predict password reset tokens and take over your account.'));?></p>
-	</fieldset>
-	<?php endif; ?>
 	<?php if(!$_['htaccessWorking']): ?>
 	<fieldset class="warning">
 		<legend><strong><?php p($l->t('Security Warning'));?></strong></legend>
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();
 		});
@@ -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 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);
+		}
+	}
+}
diff --git a/tests/lib/security/crypto.php b/tests/lib/security/crypto.php
new file mode 100644
index 00000000000..e07a60267e8
--- /dev/null
+++ b/tests/lib/security/crypto.php
@@ -0,0 +1,63 @@
+<?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.
+ */
+
+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 00000000000..75f8f56fb9f
--- /dev/null
+++ b/tests/lib/security/securerandom.php
@@ -0,0 +1,51 @@
+<?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.
+ */
+
+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 00000000000..72293124eb9
--- /dev/null
+++ b/tests/lib/security/stringutils.php
@@ -0,0 +1,21 @@
+<?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.
+ */
+
+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('', ''));
+	}
+
+}
-- 
GitLab