summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/private/repair.php4
-rw-r--r--lib/private/security/crypto.php130
-rw-r--r--lib/private/security/stringutils.php46
-rw-r--r--lib/private/server.php13
-rw-r--r--lib/private/setup.php1
-rw-r--r--lib/public/security/icrypto.php46
-rw-r--r--lib/public/security/stringutils.php25
-rw-r--r--lib/repair/repairconfig.php11
8 files changed, 275 insertions, 1 deletions
diff --git a/lib/private/repair.php b/lib/private/repair.php
index 64c50bb0f43..936657ed7d5 100644
--- a/lib/private/repair.php
+++ b/lib/private/repair.php
@@ -10,6 +10,7 @@ namespace OC;
use OC\Hooks\BasicEmitter;
use OC\Hooks\Emitter;
+use OC\Repair\RepairConfig;
class Repair extends BasicEmitter {
/**
@@ -69,7 +70,8 @@ class Repair extends BasicEmitter {
*/
public static function getRepairSteps() {
return array(
- new \OC\Repair\RepairMimeTypes()
+ new \OC\Repair\RepairMimeTypes(),
+ new RepairConfig(),
);
}
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
diff --git a/lib/private/server.php b/lib/private/server.php
index 790edfc2103..07f80311aea 100644
--- a/lib/private/server.php
+++ b/lib/private/server.php
@@ -10,6 +10,7 @@ use OC\DB\ConnectionWrapper;
use OC\Files\Node\Root;
use OC\Files\View;
use OCP\IServerContainer;
+use OC\Security\Crypto;
/**
* Class Server
@@ -199,6 +200,9 @@ class Server extends SimpleContainer implements IServerContainer {
$this->registerService('Search', function ($c) {
return new Search();
});
+ $this->registerService('Crypto', function ($c) {
+ return new Crypto(\OC::$server->getConfig());
+ });
$this->registerService('Db', function ($c) {
return new Db();
});
@@ -480,6 +484,15 @@ class Server extends SimpleContainer implements IServerContainer {
}
/**
+ * 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 6fb5ac2d62d..a18c72db334 100644
--- a/lib/private/setup.php
+++ b/lib/private/setup.php
@@ -181,6 +181,7 @@ 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);
+ OC_Config::setValue('secret', OC_Util::generateRandomBytes(96));
//write the config file
OC_Config::setValue('trusted_domains', $trustedDomains);
diff --git a/lib/public/security/icrypto.php b/lib/public/security/icrypto.php
new file mode 100644
index 00000000000..204935d73ac
--- /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 = '');
+} \ No newline at end of file
diff --git a/lib/public/security/stringutils.php b/lib/public/security/stringutils.php
new file mode 100644
index 00000000000..e74efec4fde
--- /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);
+ }
+} \ No newline at end of file
diff --git a/lib/repair/repairconfig.php b/lib/repair/repairconfig.php
index db119b4a25a..e14294b1074 100644
--- a/lib/repair/repairconfig.php
+++ b/lib/repair/repairconfig.php
@@ -31,6 +31,7 @@ class RepairConfig extends BasicEmitter implements RepairStep {
*/
public function run() {
$this->removePortsFromTrustedDomains();
+ $this->addSecret();
}
/**
@@ -51,4 +52,14 @@ class RepairConfig extends BasicEmitter implements RepairStep {
}
\OC::$server->getConfig()->setSystemValue('trusted_domains', $newTrustedDomains);
}
+
+ /**
+ * Adds a secret to config.php
+ */
+ private function addSecret() {
+ if(\OC::$server->getConfig()->getSystemValue('secret', null) === null) {
+ $secret = \OC_Util::generateRandomBytes(96);
+ \OC::$server->getConfig()->setSystemValue('secret', $secret);
+ }
+ }
}