From 0c2f9ca849ef41232511cf576cc9a9de2caa43f0 Mon Sep 17 00:00:00 2001
From: Clark Tomlinson <fallen013@gmail.com>
Date: Tue, 24 Mar 2015 17:29:10 -0400
Subject: Updating keystorage movement and fixing hooks

---
 apps/encryption/appinfo/encryption.php       |  43 +--
 apps/encryption/hooks/userhooks.php          | 213 ++++-----------
 apps/encryption/lib/crypto/Encryption.php    |  17 +-
 apps/encryption/lib/crypto/crypt.php         | 153 +++++++++--
 apps/encryption/lib/hookmanager.php          |   2 +
 apps/encryption/lib/keymanager.php           | 324 +++++++++++++++++-----
 apps/encryption/lib/migrator.php             | 123 ---------
 apps/encryption/lib/recovery.php             |  58 +++-
 apps/encryption/lib/users/setup.php          |  20 +-
 apps/encryption/lib/util.php                 | 394 +++++++++++++++++++++++++++
 apps/encryption/settings/settings-admin.php  |   8 +-
 apps/encryption/tests/lib/KeyManagerTest.php |  39 ++-
 apps/encryption/tests/lib/MigratorTest.php   |   3 +-
 apps/files_encryption/lib/hooks.php          | 226 +++++++++++++++
 lib/private/server.php                       |   2 +-
 15 files changed, 1202 insertions(+), 423 deletions(-)
 delete mode 100644 apps/encryption/lib/migrator.php
 create mode 100644 apps/encryption/lib/util.php

diff --git a/apps/encryption/appinfo/encryption.php b/apps/encryption/appinfo/encryption.php
index f2ab89aadef..1a4c17eb60a 100644
--- a/apps/encryption/appinfo/encryption.php
+++ b/apps/encryption/appinfo/encryption.php
@@ -21,16 +21,16 @@
 namespace OCA\Encryption\AppInfo;
 
 
+use OC\Files\Filesystem;
+use OC\Files\View;
 use OCA\Encryption\Crypto\Crypt;
 use OCA\Encryption\HookManager;
-use OCA\Encryption\Hooks\AppHooks;
-use OCA\Encryption\Hooks\FileSystemHooks;
-use OCA\Encryption\Hooks\ShareHooks;
 use OCA\Encryption\Hooks\UserHooks;
 use OCA\Encryption\KeyManager;
 use OCA\Encryption\Migrator;
 use OCA\Encryption\Recovery;
 use OCA\Encryption\Users\Setup;
+use OCA\Encryption\Util;
 use OCP\App;
 use OCP\AppFramework\IAppContainer;
 use OCP\Encryption\IManager;
@@ -81,14 +81,7 @@ class Encryption extends \OCP\AppFramework\App {
 			$hookManager = new HookManager();
 
 			$hookManager->registerHook([
-				new UserHooks($container->query('KeyManager'),
-					$server->getLogger(),
-					$container->query('UserSetup'),
-					$container->query('Migrator'),
-					$server->getUserSession()),
-//				new ShareHooks(),
-//				new FileSystemHooks(),
-//				new AppHooks()
+				new UserHooks($container->query('KeyManager'), $server->getLogger(), $container->query('UserSetup'), $server->getUserSession(), new \OCP\Util(), $container->query('Util')),
 			]);
 
 			$hookManager->fireHooks();
@@ -103,7 +96,7 @@ class Encryption extends \OCP\AppFramework\App {
 	 *
 	 */
 	public function registerEncryptionModule() {
-//		$this->encryptionManager->registerEncryptionModule(new \OCA\Encryption\Crypto\Encryption());
+		$this->encryptionManager->registerEncryptionModule(new \OCA\Encryption\Crypto\Encryption());
 	}
 
 	/**
@@ -124,10 +117,13 @@ class Encryption extends \OCP\AppFramework\App {
 			function (IAppContainer $c) {
 				$server = $c->getServer();
 
-				return new KeyManager($server->getEncryptionKeyStorage(),
+				return new KeyManager($server->getEncryptionKeyStorage('encryption'),
 					$c->query('Crypt'),
 					$server->getConfig(),
-					$server->getUserSession());
+					$server->getUserSession(),
+					$server->getMemCacheFactory(),
+					$server->getLogger()
+				);
 			});
 
 
@@ -141,7 +137,7 @@ class Encryption extends \OCP\AppFramework\App {
 					$server->getSecureRandom(),
 					$c->query('KeyManager'),
 					$server->getConfig(),
-					$server->getEncryptionKeyStorage());
+					$server->getEncryptionKeyStorage('encryption'));
 			});
 
 		$container->registerService('UserSetup',
@@ -157,13 +153,26 @@ class Encryption extends \OCP\AppFramework\App {
 			function (IAppContainer $c) {
 				$server = $c->getServer();
 
-				return new Migrator($server->getUserSession(),
-					$server->getConfig(),
+				return new Migrator($server->getConfig(),
 					$server->getUserManager(),
 					$server->getLogger(),
 					$c->query('Crypt'));
 			});
 
+		$container->registerService('Util',
+			function (IAppContainer $c) {
+				$server = $c->getServer();
+
+				return new Util(new View(),
+					new Filesystem(),
+					$c->query('Crypt'),
+					$c->query('KeyManager'),
+					$server->getLogger(),
+					$server->getUserSession(),
+					$server->getConfig()
+				);
+			});
+
 	}
 
 	/**
diff --git a/apps/encryption/hooks/userhooks.php b/apps/encryption/hooks/userhooks.php
index 79de70a6d02..096fd3beb93 100644
--- a/apps/encryption/hooks/userhooks.php
+++ b/apps/encryption/hooks/userhooks.php
@@ -22,15 +22,14 @@
 namespace OCA\Encryption\Hooks;
 
 
+use OCP\Util as OCUtil;
 use OCA\Encryption\Hooks\Contracts\IHook;
 use OCA\Encryption\KeyManager;
-use OCA\Encryption\Migrator;
-use OCA\Encryption\RequirementsChecker;
 use OCA\Encryption\Users\Setup;
 use OCP\App;
 use OCP\ILogger;
 use OCP\IUserSession;
-use OCP\Util;
+use OCA\Encryption\Util;
 use Test\User;
 
 class UserHooks implements IHook {
@@ -46,14 +45,14 @@ class UserHooks implements IHook {
 	 * @var Setup
 	 */
 	private $userSetup;
-	/**
-	 * @var Migrator
-	 */
-	private $migrator;
 	/**
 	 * @var IUserSession
 	 */
 	private $user;
+	/**
+	 * @var Util
+	 */
+	private $util;
 
 	/**
 	 * UserHooks constructor.
@@ -61,17 +60,19 @@ class UserHooks implements IHook {
 	 * @param KeyManager $keyManager
 	 * @param ILogger $logger
 	 * @param Setup $userSetup
-	 * @param Migrator $migrator
 	 * @param IUserSession $user
+	 * @param OCUtil $ocUtil
+	 * @param Util $util
+	 * @internal param Migrator $migrator
 	 */
 	public function __construct(
-		KeyManager $keyManager, ILogger $logger, Setup $userSetup, Migrator $migrator, IUserSession $user) {
+		KeyManager $keyManager, ILogger $logger, Setup $userSetup, IUserSession $user, OCUtil $ocUtil, Util $util) {
 
 		$this->keyManager = $keyManager;
 		$this->logger = $logger;
 		$this->userSetup = $userSetup;
-		$this->migrator = $migrator;
 		$this->user = $user;
+		$this->util = $util;
 	}
 
 	/**
@@ -80,12 +81,24 @@ class UserHooks implements IHook {
 	 * @return null
 	 */
 	public function addHooks() {
-		Util::connectHook('OC_User', 'post_login', $this, 'login');
-		Util::connectHook('OC_User', 'logout', $this, 'logout');
-		Util::connectHook('OC_User', 'post_setPassword', $this, 'setPassphrase');
-		Util::connectHook('OC_User', 'pre_setPassword', $this, 'preSetPassphrase');
-		Util::connectHook('OC_User', 'post_createUser', $this, 'postCreateUser');
-		Util::connectHook('OC_User', 'post_deleteUser', $this, 'postDeleteUser');
+		OCUtil::connectHook('OC_User', 'post_login', $this, 'login');
+		OCUtil::connectHook('OC_User', 'logout', $this, 'logout');
+		OCUtil::connectHook('OC_User',
+			'post_setPassword',
+			$this,
+			'setPassphrase');
+		OCUtil::connectHook('OC_User',
+			'pre_setPassword',
+			$this,
+			'preSetPassphrase');
+		OCUtil::connectHook('OC_User',
+			'post_createUser',
+			$this,
+			'postCreateUser');
+		OCUtil::connectHook('OC_User',
+			'post_deleteUser',
+			$this,
+			'postDeleteUser');
 	}
 
 
@@ -93,6 +106,8 @@ class UserHooks implements IHook {
 	 * Startup encryption backend upon user login
 	 *
 	 * @note This method should never be called for users using client side encryption
+	 * @param array $params
+	 * @return bool
 	 */
 	public function login($params) {
 
@@ -107,198 +122,76 @@ class UserHooks implements IHook {
 		}
 
 		// setup user, if user not ready force relogin
-		if (!$this->userSetup->setupUser($params['password'])) {
-			return false;
-		}
-
-		$cache = $this->keyManager->init();
-
-		// Check if first-run file migration has already been performed
-		$ready = false;
-		$migrationStatus = $this->migrator->getStatus($params['uid']);
-		if ($migrationStatus === Migrator::$migrationOpen && $cache !== false) {
-			$ready = $this->migrator->beginMigration();
-		} elseif ($migrationStatus === Migrator::$migrationInProgress) {
-			// refuse login as long as the initial encryption is running
-			sleep(5);
-			$this->user->logout();
+		if (!$this->userSetup->setupUser($params['uid'], $params['password'])) {
 			return false;
 		}
 
-		$result = true;
-
-		// If migration not yet done
-		if ($ready) {
-
-			// Encrypt existing user files
-			try {
-				$result = $util->encryptAll('/' . $params['uid'] . '/' . 'files');
-			} catch (\Exception $ex) {
-				\OCP\Util::writeLog('Encryption library', 'Initial encryption failed! Error: ' . $ex->getMessage(), \OCP\Util::FATAL);
-				$result = false;
-			}
-
-			if ($result) {
-				\OC_Log::write(
-					'Encryption library', 'Encryption of existing files belonging to "' . $params['uid'] . '" completed'
-					, \OC_Log::INFO
-				);
-				// Register successful migration in DB
-				$util->finishMigration();
-			} else {
-				\OCP\Util::writeLog('Encryption library', 'Initial encryption failed!', \OCP\Util::FATAL);
-				$util->resetMigrationStatus();
-				\OCP\User::logout();
-			}
-		}
-
-		return $result;
+		$this->keyManager->init($params['uid'], $params['password']);
 	}
 
 	/**
 	 * remove keys from session during logout
 	 */
 	public function logout() {
-		$session = new Session(new \OC\Files\View());
-		$session->removeKeys();
+		KeyManager::$cacheFactory->clear();
 	}
 
 	/**
 	 * setup encryption backend upon user created
 	 *
 	 * @note This method should never be called for users using client side encryption
+	 * @param array $params
 	 */
 	public function postCreateUser($params) {
 
-		if (App::isEnabled('files_encryption')) {
-			$view = new \OC\Files\View('/');
-			$util = new Util($view, $params['uid']);
-			Helper::setupUser($util, $params['password']);
+		if (App::isEnabled('encryption')) {
+			$this->userSetup->setupUser($params['uid'], $params['password']);
 		}
 	}
 
 	/**
 	 * cleanup encryption backend upon user deleted
 	 *
+	 * @param array $params : uid, password
 	 * @note This method should never be called for users using client side encryption
 	 */
 	public function postDeleteUser($params) {
 
-		if (App::isEnabled('files_encryption')) {
-			Keymanager::deletePublicKey(new \OC\Files\View(), $params['uid']);
+		if (App::isEnabled('encryption')) {
+			$this->keyManager->deletePublicKey($params['uid']);
 		}
 	}
 
 	/**
 	 * If the password can't be changed within ownCloud, than update the key password in advance.
-	 */
-	public function preSetPassphrase($params) {
-		if (App::isEnabled('files_encryption')) {
-			if (!\OC_User::canUserChangePassword($params['uid'])) {
-				self::setPassphrase($params);
-			}
-		}
-	}
-
-	/**
-	 * Change a user's encryption passphrase
 	 *
-	 * @param array $params keys: uid, password
+	 * @param array $params : uid, password
+	 * @return bool
 	 */
-	public function setPassphrase($params) {
-		if (App::isEnabled('files_encryption') === false) {
-			return true;
-		}
-
-		// Only attempt to change passphrase if server-side encryption
-		// is in use (client-side encryption does not have access to
-		// the necessary keys)
-		if (Crypt::mode() === 'server') {
-
-			$view = new \OC\Files\View('/');
-			$session = new Session($view);
-
-			// Get existing decrypted private key
-			$privateKey = $session->getPrivateKey();
-
-			if ($params['uid'] === \OCP\User::getUser() && $privateKey) {
-
-				// Encrypt private key with new user pwd as passphrase
-				$encryptedPrivateKey = Crypt::symmetricEncryptFileContent($privateKey, $params['password'], Helper::getCipher());
-
-				// Save private key
-				if ($encryptedPrivateKey) {
-					Keymanager::setPrivateKey($encryptedPrivateKey, \OCP\User::getUser());
-				} else {
-					\OCP\Util::writeLog('files_encryption', 'Could not update users encryption password', \OCP\Util::ERROR);
-				}
-
-				// NOTE: Session does not need to be updated as the
-				// private key has not changed, only the passphrase
-				// used to decrypt it has changed
-
-
-			} else { // admin changed the password for a different user, create new keys and reencrypt file keys
-
-				$user = $params['uid'];
-				$util = new Util($view, $user);
-				$recoveryPassword = isset($params['recoveryPassword']) ? $params['recoveryPassword'] : null;
-
-				// we generate new keys if...
-				// ...we have a recovery password and the user enabled the recovery key
-				// ...encryption was activated for the first time (no keys exists)
-				// ...the user doesn't have any files
-				if (($util->recoveryEnabledForUser() && $recoveryPassword)
-					|| !$util->userKeysExists()
-					|| !$view->file_exists($user . '/files')
-				) {
-
-					// backup old keys
-					$util->backupAllKeys('recovery');
-
-					$newUserPassword = $params['password'];
-
-					// make sure that the users home is mounted
-					\OC\Files\Filesystem::initMountPoints($user);
-
-					$keypair = Crypt::createKeypair();
-
-					// Disable encryption proxy to prevent recursive calls
-					$proxyStatus = \OC_FileProxy::$enabled;
-					\OC_FileProxy::$enabled = false;
-
-					// Save public key
-					Keymanager::setPublicKey($keypair['publicKey'], $user);
-
-					// Encrypt private key with new password
-					$encryptedKey = Crypt::symmetricEncryptFileContent($keypair['privateKey'], $newUserPassword, Helper::getCipher());
-					if ($encryptedKey) {
-						Keymanager::setPrivateKey($encryptedKey, $user);
-
-						if ($recoveryPassword) { // if recovery key is set we can re-encrypt the key files
-							$util = new Util($view, $user);
-							$util->recoverUsersFiles($recoveryPassword);
-						}
-					} else {
-						\OCP\Util::writeLog('files_encryption', 'Could not update users encryption password', \OCP\Util::ERROR);
-					}
+	public function preSetPassphrase($params) {
+		if (App::isEnabled('encryption')) {
 
-					\OC_FileProxy::$enabled = $proxyStatus;
+			if (!$this->user->getUser()->canChangePassword()) {
+				if (App::isEnabled('encryption') === false) {
+					return true;
 				}
+				$this->keyManager->setPassphrase($params,
+					$this->user,
+					$this->util);
 			}
 		}
 	}
 
+
 	/**
 	 * after password reset we create a new key pair for the user
 	 *
 	 * @param array $params
 	 */
 	public function postPasswordReset($params) {
-		$uid = $params['uid'];
 		$password = $params['password'];
 
-		$util = new Util(new \OC\Files\View(), $uid);
-		$util->replaceUserKeys($password);
+		$this->keyManager->replaceUserKeys($params['uid']);
+		$this->userSetup->setupServerSide($params['uid'], $password);
 	}
 }
diff --git a/apps/encryption/lib/crypto/Encryption.php b/apps/encryption/lib/crypto/Encryption.php
index 123581b83ac..b8429d7124e 100644
--- a/apps/encryption/lib/crypto/Encryption.php
+++ b/apps/encryption/lib/crypto/Encryption.php
@@ -18,7 +18,7 @@ class Encryption extends Crypt implements IEncryptionModule {
    * @return string defining the technical unique id
    */
   public function getId() {
-    // TODO: Implement getId() method.
+    return md5($this->getDisplayName());
   }
 
   /**
@@ -27,7 +27,7 @@ class Encryption extends Crypt implements IEncryptionModule {
    * @return string
    */
   public function getDisplayName() {
-    // TODO: Implement getDisplayName() method.
+    return 'ownCloud Default Encryption';
   }
 
   /**
@@ -44,7 +44,9 @@ class Encryption extends Crypt implements IEncryptionModule {
    *                       or if no additional data is needed return a empty array
    */
   public function begin($path, $header, $accessList) {
+
     // TODO: Implement begin() method.
+    // return additional header information that needs to be written i.e. cypher used
   }
 
   /**
@@ -68,6 +70,7 @@ class Encryption extends Crypt implements IEncryptionModule {
    */
   public function encrypt($data) {
     // Todo: xxx Update Signature and usages
+    // passphrase is file key decrypted with user private/share key
     $this->symmetricEncryptFileContent($data);
   }
 
@@ -113,4 +116,14 @@ class Encryption extends Crypt implements IEncryptionModule {
   public function calculateUnencryptedSize($path) {
     // TODO: Implement calculateUnencryptedSize() method.
   }
+
+  /**
+   * get size of the unencrypted payload per block.
+   * ownCloud read/write files with a block size of 8192 byte
+   *
+   * @return integer
+   */
+  public function getUnencryptedBlockSize() {
+    // TODO: Implement getUnencryptedBlockSize() method.
+  }
 }
diff --git a/apps/encryption/lib/crypto/crypt.php b/apps/encryption/lib/crypto/crypt.php
index 8018f11a370..f9fe4f9bece 100644
--- a/apps/encryption/lib/crypto/crypt.php
+++ b/apps/encryption/lib/crypto/crypt.php
@@ -25,6 +25,8 @@ namespace OCA\Encryption\Crypto;
 use OC\Encryption\Exceptions\DecryptionFailedException;
 use OC\Encryption\Exceptions\EncryptionFailedException;
 use OC\Encryption\Exceptions\GenericEncryptionException;
+use OCA\Files_Encryption\Exception\MultiKeyDecryptException;
+use OCA\Files_Encryption\Exception\MultiKeyEncryptException;
 use OCP\IConfig;
 use OCP\ILogger;
 use OCP\IUser;
@@ -83,12 +85,17 @@ class Crypt {
 		$res = $this->getOpenSSLPKey();
 
 		if (!$res) {
-			$log->error("Encryption Library could'nt generate users key-pair for {$this->user->getUID()}", ['app' => 'encryption']);
+			$log->error("Encryption Library could'nt generate users key-pair for {$this->user->getUID()}",
+				['app' => 'encryption']);
 
 			if (openssl_error_string()) {
-				$log->error('Encryption library openssl_pkey_new() fails: ' . openssl_error_string(), ['app' => 'encryption']);
+				$log->error('Encryption library openssl_pkey_new() fails: ' . openssl_error_string(),
+					['app' => 'encryption']);
 			}
-		} elseif (openssl_pkey_export($res, $privateKey, null, $this->getOpenSSLConfig())) {
+		} elseif (openssl_pkey_export($res,
+			$privateKey,
+			null,
+			$this->getOpenSSLConfig())) {
 			$keyDetails = openssl_pkey_get_details($res);
 			$publicKey = $keyDetails['key'];
 
@@ -97,9 +104,11 @@ class Crypt {
 				'privateKey' => $privateKey
 			];
 		}
-		$log->error('Encryption library couldn\'t export users private key, please check your servers openSSL configuration.' . $user->getUID(), ['app' => 'encryption']);
+		$log->error('Encryption library couldn\'t export users private key, please check your servers openSSL configuration.' . $user->getUID(),
+			['app' => 'encryption']);
 		if (openssl_error_string()) {
-			$log->error('Encryption Library:' . openssl_error_string(), ['app' => 'encryption']);
+			$log->error('Encryption Library:' . openssl_error_string(),
+				['app' => 'encryption']);
 		}
 
 		return false;
@@ -118,7 +127,9 @@ class Crypt {
 	 */
 	private function getOpenSSLConfig() {
 		$config = ['private_key_bits' => 4096];
-		$config = array_merge(\OC::$server->getConfig()->getSystemValue('openssl', []), $config);
+		$config = array_merge(\OC::$server->getConfig()->getSystemValue('openssl',
+			[]),
+			$config);
 		return $config;
 	}
 
@@ -131,14 +142,18 @@ class Crypt {
 	public function symmetricEncryptFileContent($plainContent, $passphrase) {
 
 		if (!$plainContent) {
-			$this->logger->error('Encryption Library, symmetrical encryption failed no content given', ['app' => 'encryption']);
+			$this->logger->error('Encryption Library, symmetrical encryption failed no content given',
+				['app' => 'encryption']);
 			return false;
 		}
 
 		$iv = $this->generateIv();
 
 		try {
-			$encryptedContent = $this->encrypt($plainContent, $iv, $passphrase, $this->getCipher());
+			$encryptedContent = $this->encrypt($plainContent,
+				$iv,
+				$passphrase,
+				$this->getCipher());
 			// combine content to encrypt the IV identifier and actual IV
 			$catFile = $this->concatIV($encryptedContent, $iv);
 			$padded = $this->addPadding($catFile);
@@ -146,7 +161,8 @@ class Crypt {
 			return $padded;
 		} catch (EncryptionFailedException $e) {
 			$message = 'Could not encrypt file content (code: ' . $e->getCode() . '): ';
-			$this->logger->error('files_encryption' . $message . $e->getMessage(), ['app' => 'encryption']);
+			$this->logger->error('files_encryption' . $message . $e->getMessage(),
+				['app' => 'encryption']);
 			return false;
 		}
 
@@ -161,11 +177,16 @@ class Crypt {
 	 * @throws EncryptionFailedException
 	 */
 	private function encrypt($plainContent, $iv, $passphrase = '', $cipher = self::DEFAULT_CIPHER) {
-		$encryptedContent = openssl_encrypt($plainContent, $cipher, $passphrase, false, $iv);
+		$encryptedContent = openssl_encrypt($plainContent,
+			$cipher,
+			$passphrase,
+			false,
+			$iv);
 
 		if (!$encryptedContent) {
 			$error = 'Encryption (symmetric) of content failed';
-			$this->logger->error($error . openssl_error_string(), ['app' => 'encryption']);
+			$this->logger->error($error . openssl_error_string(),
+				['app' => 'encryption']);
 			throw new EncryptionFailedException($error);
 		}
 
@@ -177,8 +198,9 @@ class Crypt {
 	 */
 	public function getCipher() {
 		$cipher = $this->config->getSystemValue('cipher', self::DEFAULT_CIPHER);
-		if ($cipher !== 'AES-256-CFB' || $cipher !== 'AES-128-CFB') {
-			$this->logger->warning('Wrong cipher defined in config.php only AES-128-CFB and AES-256-CFB are supported. Fall back' . self::DEFAULT_CIPHER, ['app' => 'encryption']);
+		if ($cipher !== 'AES-256-CFB' && $cipher !== 'AES-128-CFB') {
+			$this->logger->warning('Wrong cipher defined in config.php only AES-128-CFB and AES-256-CFB are supported. Fall back' . self::DEFAULT_CIPHER,
+				['app' => 'encryption']);
 			$cipher = self::DEFAULT_CIPHER;
 		}
 
@@ -214,10 +236,14 @@ class Crypt {
 
 		// If we found a header we need to remove it from the key we want to decrypt
 		if (!empty($header)) {
-			$recoveryKey = substr($recoveryKey, strpos($recoveryKey, self::HEADEREND) + strlen(self::HEADERSTART));
+			$recoveryKey = substr($recoveryKey,
+				strpos($recoveryKey,
+					self::HEADEREND) + strlen(self::HEADERSTART));
 		}
 
-		$plainKey = $this->symmetricDecryptFileContent($recoveryKey, $password, $cipher);
+		$plainKey = $this->symmetricDecryptFileContent($recoveryKey,
+			$password,
+			$cipher);
 
 		// Check if this is a valid private key
 		$res = openssl_get_privatekey($plainKey);
@@ -246,7 +272,10 @@ class Crypt {
 
 		$catFile = $this->splitIv($noPadding);
 
-		$plainContent = $this->decrypt($catFile['encrypted'], $catFile['iv'], $passphrase, $cipher);
+		$plainContent = $this->decrypt($catFile['encrypted'],
+			$catFile['iv'],
+			$passphrase,
+			$cipher);
 
 		if ($plainContent) {
 			return $plainContent;
@@ -296,7 +325,11 @@ class Crypt {
 	 * @throws DecryptionFailedException
 	 */
 	private function decrypt($encryptedContent, $iv, $passphrase = '', $cipher = self::DEFAULT_CIPHER) {
-		$plainContent = openssl_decrypt($encryptedContent, $cipher, $passphrase, false, $iv);
+		$plainContent = openssl_decrypt($encryptedContent,
+			$cipher,
+			$passphrase,
+			false,
+			$iv);
 
 		if ($plainContent) {
 			return $plainContent;
@@ -317,7 +350,8 @@ class Crypt {
 			$header = substr($data, 0, $endAt + strlen(self::HEADEREND));
 
 			// +1 not to start with an ':' which would result in empty element at the beginning
-			$exploded = explode(':', substr($header, strlen(self::HEADERSTART) + 1));
+			$exploded = explode(':',
+				substr($header, strlen(self::HEADERSTART) + 1));
 
 			$element = array_shift($exploded);
 
@@ -339,7 +373,8 @@ class Crypt {
 		if ($random) {
 			if (!$strong) {
 				// If OpenSSL indicates randomness is insecure log error
-				$this->logger->error('Encryption Library: Insecure symmetric key was generated using openssl_random_psudo_bytes()', ['app' => 'encryption']);
+				$this->logger->error('Encryption Library: Insecure symmetric key was generated using openssl_random_psudo_bytes()',
+					['app' => 'encryption']);
 			}
 
 			/*
@@ -351,5 +386,85 @@ class Crypt {
 		// If we ever get here we've failed anyway no need for an else
 		throw new GenericEncryptionException('Generating IV Failed');
 	}
+
+	/**
+	 * Check if a file's contents contains an IV and is symmetrically encrypted
+	 *
+	 * @param $content
+	 * @return bool
+	 */
+	public function isCatFileContent($content) {
+		if (!$content) {
+			return false;
+		}
+
+		$noPadding = $this->removePadding($content);
+
+		// Fetch encryption metadata from end of file
+		$meta = substr($noPadding, -22);
+
+		// Fetch identifier from start of metadata
+		$identifier = substr($meta, 0, 6);
+
+		if ($identifier === '00iv00') {
+			return true;
+		}
+		return false;
+	}
+
+	/**
+	 * @param $encKeyFile
+	 * @param $shareKey
+	 * @param $privateKey
+	 * @return mixed
+	 * @throws MultiKeyDecryptException
+	 */
+	public function multiKeyDecrypt($encKeyFile, $shareKey, $privateKey) {
+		if (!$encKeyFile) {
+			throw new MultiKeyDecryptException('Cannot multikey decrypt empty plain content');
+		}
+
+		if (openssl_open($encKeyFile, $plainContent, $shareKey, $privateKey)) {
+			return $plainContent;
+		} else {
+			throw new MultiKeyDecryptException('multikeydecrypt with share key failed');
+		}
+	}
+
+	/**
+	 * @param $plainContent
+	 * @param array $keyFiles
+	 * @return array
+	 * @throws MultiKeyEncryptException
+	 */
+	public function multiKeyEncrypt($plainContent, array $keyFiles) {
+		// openssl_seal returns false without errors if plaincontent is empty
+		// so trigger our own error
+		if (empty($plainContent)) {
+			throw new MultiKeyEncryptException('Cannot multikeyencrypt empty plain content');
+		}
+
+		// Set empty vars to be set by openssl by reference
+		$sealed = '';
+		$shareKeys = [];
+		$mappedShareKeys = [];
+
+		if (openssl_seal($plainContent, $sealed, $shareKeys, $keyFiles)) {
+			$i = 0;
+
+			// Ensure each shareKey is labelled with its coreesponding keyid
+			foreach ($keyFiles as $userId => $publicKey) {
+				$mappedShareKeys[$userId] = $shareKeys[$i];
+				$i++;
+			}
+
+			return [
+				'keys' => $mappedShareKeys,
+				'data' => $sealed
+			];
+		} else {
+			throw new MultiKeyEncryptException('multikeyencryption failed ' . openssl_error_string());
+		}
+	}
 }
 
diff --git a/apps/encryption/lib/hookmanager.php b/apps/encryption/lib/hookmanager.php
index a535230a6a7..9e6d9cd855c 100644
--- a/apps/encryption/lib/hookmanager.php
+++ b/apps/encryption/lib/hookmanager.php
@@ -54,6 +54,8 @@ class HookManager {
 	public function fireHooks() {
 		foreach ($this->hookInstances as $instance) {
 			/**
+			 * Fire off the add hooks method of each instance stored in cache
+			 *
 			 * @var $instance IHook
 			 */
 			$instance->addHooks();
diff --git a/apps/encryption/lib/keymanager.php b/apps/encryption/lib/keymanager.php
index 272bf0849c2..83b24c79b8c 100644
--- a/apps/encryption/lib/keymanager.php
+++ b/apps/encryption/lib/keymanager.php
@@ -22,21 +22,27 @@
 namespace OCA\Encryption;
 
 
+use OC\Encryption\Exceptions\DecryptionFailedException;
 use OC\Encryption\Exceptions\PrivateKeyMissingException;
 use OC\Encryption\Exceptions\PublicKeyMissingException;
 use OCA\Encryption\Crypto\Crypt;
-use OCP\Encryption\IKeyStorage;
+use OCP\Encryption\Keys\IStorage;
+use OCP\ICache;
+use OCP\ICacheFactory;
 use OCP\IConfig;
-use OCP\IUser;
+use OCP\ILogger;
 use OCP\IUserSession;
 
 class KeyManager {
 
 	/**
-	 * @var IKeyStorage
+	 * @var ICache
+	 */
+	public static $cacheFactory;
+	/**
+	 * @var IStorage
 	 */
 	private $keyStorage;
-
 	/**
 	 * @var Crypt
 	 */
@@ -53,35 +59,143 @@ class KeyManager {
 	 * @var string UserID
 	 */
 	private $keyId;
+	/**
+	 * @var string
+	 */
+	private $publicKeyId = 'public';
+	/**
+	 * @var string
+	 */
+	private $privateKeyId = 'private';
 
 	/**
 	 * @var string
 	 */
-	private $publicKeyId = '.public';
+	private $shareKeyId = 'sharekey';
+
 	/**
 	 * @var string
 	 */
-	private $privateKeyId = '.private';
+	private $fileKeyId = 'filekey';
 	/**
 	 * @var IConfig
 	 */
 	private $config;
+	/**
+	 * @var ILogger
+	 */
+	private $log;
 
 	/**
-	 * @param IKeyStorage $keyStorage
+	 * @param IStorage $keyStorage
 	 * @param Crypt $crypt
 	 * @param IConfig $config
 	 * @param IUserSession $userSession
+	 * @param ICacheFactory $cacheFactory
+	 * @param ILogger $log
 	 */
-	public function __construct(IKeyStorage $keyStorage, Crypt $crypt, IConfig $config, IUserSession $userSession) {
+	public function __construct(IStorage $keyStorage, Crypt $crypt, IConfig $config, IUserSession $userSession, ICacheFactory $cacheFactory, ILogger $log) {
 
 		$this->keyStorage = $keyStorage;
 		$this->crypt = $crypt;
 		$this->config = $config;
-		$this->recoveryKeyId = $this->config->getAppValue('encryption', 'recoveryKeyId');
-		$this->publicShareKeyId = $this->config->getAppValue('encryption', 'publicShareKeyId');
+		$this->recoveryKeyId = $this->config->getAppValue('encryption',
+			'recoveryKeyId');
+		$this->publicShareKeyId = $this->config->getAppValue('encryption',
+			'publicShareKeyId');
 		$this->keyId = $userSession && $userSession->isLoggedIn() ? $userSession->getUser()->getUID() : false;
 
+		self::$cacheFactory = $cacheFactory;
+		self::$cacheFactory = self::$cacheFactory->create('encryption');
+		$this->log = $log;
+	}
+
+	/**
+	 * @return bool
+	 */
+	public function recoveryKeyExists() {
+		return (strlen($this->keyStorage->getSystemUserKey($this->recoveryKeyId)) !== 0);
+	}
+
+	/**
+	 * @param $password
+	 * @return bool
+	 */
+	public function checkRecoveryPassword($password) {
+		$recoveryKey = $this->keyStorage->getSystemUserKey($this->recoveryKeyId);
+		$decryptedRecoveryKey = $this->crypt->decryptPrivateKey($recoveryKey,
+			$password);
+
+		if ($decryptedRecoveryKey) {
+			return true;
+		}
+		return false;
+	}
+
+	/**
+	 * @param string $uid
+	 * @param string $password
+	 * @param string $keyPair
+	 * @return bool
+	 */
+	public function storeKeyPair($uid, $password, $keyPair) {
+		// Save Public Key
+		$this->setPublicKey($uid, $keyPair['publicKey']);
+
+		$encryptedKey = $this->crypt->symmetricEncryptFileContent($keyPair['privateKey'],
+			$password);
+
+		if ($encryptedKey) {
+			$this->setPrivateKey($uid, $encryptedKey);
+			$this->config->setAppValue('encryption', 'recoveryAdminEnabled', 1);
+			return true;
+		}
+		return false;
+	}
+
+	/**
+	 * @param $userId
+	 * @param $key
+	 * @return bool
+	 */
+	public function setPublicKey($userId, $key) {
+		return $this->keyStorage->setUserKey($userId, $this->publicKeyId, $key);
+	}
+
+	/**
+	 * @param $userId
+	 * @param $key
+	 * @return bool
+	 */
+	public function setPrivateKey($userId, $key) {
+		return $this->keyStorage->setUserKey($userId,
+			$this->privateKeyId,
+			$key);
+	}
+
+	/**
+	 * Decrypt private key and store it
+	 *
+	 * @param string $uid userid
+	 * @param string $passPhrase users password
+	 * @return ICache
+	 */
+	public function init($uid, $passPhrase) {
+		try {
+			$privateKey = $this->getPrivateKey($uid);
+			$privateKey = $this->crypt->decryptPrivateKey($privateKey,
+				$passPhrase);
+		} catch (PrivateKeyMissingException $e) {
+			return false;
+		} catch (DecryptionFailedException $e) {
+			return false;
+		}
+
+		self::$cacheFactory->set('privateKey', $privateKey);
+		self::$cacheFactory->set('initStatus', true);
+
+
+		return self::$cacheFactory;
 	}
 
 	/**
@@ -90,7 +204,8 @@ class KeyManager {
 	 * @throws PrivateKeyMissingException
 	 */
 	public function getPrivateKey($userId) {
-		$privateKey = $this->keyStorage->getUserKey($userId, $this->privateKeyId);
+		$privateKey = $this->keyStorage->getUserKey($userId,
+			$this->privateKeyId);
 
 		if (strlen($privateKey) !== 0) {
 			return $privateKey;
@@ -99,24 +214,105 @@ class KeyManager {
 	}
 
 	/**
-	 * @param $userId
+	 * @param $path
 	 * @return mixed
-	 * @throws PublicKeyMissingException
 	 */
-	public function getPublicKey($userId) {
-		$publicKey = $this->keyStorage->getUserKey($userId, $this->publicKeyId);
+	public function getFileKey($path) {
+		return $this->keyStorage->getFileKey($path, $this->fileKeyId);
+	}
 
-		if (strlen($publicKey) !== 0) {
-			return $publicKey;
-		}
-		throw new PublicKeyMissingException();
+	/**
+	 * @param $path
+	 * @return mixed
+	 */
+	public function getShareKey($path) {
+		return $this->keyStorage->getFileKey($path, $this->keyId . $this->shareKeyId);
 	}
 
 	/**
+	 * Change a user's encryption passphrase
+	 *
+	 * @param array $params keys: uid, password
+	 * @param IUserSession $user
+	 * @param Util $util
 	 * @return bool
 	 */
-	public function recoveryKeyExists() {
-		return (strlen($this->keyStorage->getSystemUserKey($this->recoveryKeyId)) !== 0);
+	public function setPassphrase($params, IUserSession $user, Util $util) {
+
+		// Only attempt to change passphrase if server-side encryption
+		// is in use (client-side encryption does not have access to
+		// the necessary keys)
+		if ($this->crypt->mode() === 'server') {
+
+			// Get existing decrypted private key
+			$privateKey = self::$cacheFactory->get('privateKey');
+
+			if ($params['uid'] === $user->getUser()->getUID() && $privateKey) {
+
+				// Encrypt private key with new user pwd as passphrase
+				$encryptedPrivateKey = $this->crypt->symmetricEncryptFileContent($privateKey,
+					$params['password']);
+
+				// Save private key
+				if ($encryptedPrivateKey) {
+					$this->setPrivateKey($user->getUser()->getUID(),
+						$encryptedPrivateKey);
+				} else {
+					$this->log->error('Encryption could not update users encryption password');
+				}
+
+				// NOTE: Session does not need to be updated as the
+				// private key has not changed, only the passphrase
+				// used to decrypt it has changed
+
+
+			} else { // admin changed the password for a different user, create new keys and reencrypt file keys
+
+				$user = $params['uid'];
+				$recoveryPassword = isset($params['recoveryPassword']) ? $params['recoveryPassword'] : null;
+
+				// we generate new keys if...
+				// ...we have a recovery password and the user enabled the recovery key
+				// ...encryption was activated for the first time (no keys exists)
+				// ...the user doesn't have any files
+				if (($util->recoveryEnabledForUser() && $recoveryPassword)
+
+					|| !$this->userHasKeys($user)
+					|| !$util->userHasFiles($user)
+				) {
+
+					// backup old keys
+					$this->backupAllKeys('recovery');
+
+					$newUserPassword = $params['password'];
+
+					$keypair = $this->crypt->createKeyPair();
+
+					// Disable encryption proxy to prevent recursive calls
+					$proxyStatus = \OC_FileProxy::$enabled;
+					\OC_FileProxy::$enabled = false;
+
+					// Save public key
+					$this->setPublicKey($user, $keypair['publicKey']);
+
+					// Encrypt private key with new password
+					$encryptedKey = $this->crypt->symmetricEncryptFileContent($keypair['privateKey'],
+						$newUserPassword);
+
+					if ($encryptedKey) {
+						$this->setPrivateKey($user, $encryptedKey);
+
+						if ($recoveryPassword) { // if recovery key is set we can re-encrypt the key files
+							$util->recoverUsersFiles($recoveryPassword);
+						}
+					} else {
+						$this->log->error('Encryption Could not update users encryption password');
+					}
+
+					\OC_FileProxy::$enabled = $proxyStatus;
+				}
+			}
+		}
 	}
 
 	/**
@@ -136,82 +332,70 @@ class KeyManager {
 	}
 
 	/**
-	 * @param $password
-	 * @return bool
+	 * @param $userId
+	 * @return mixed
+	 * @throws PublicKeyMissingException
 	 */
-	public function checkRecoveryPassword($password) {
-		$recoveryKey = $this->keyStorage->getSystemUserKey($this->recoveryKeyId);
-		$decryptedRecoveryKey = $this->crypt->decryptPrivateKey($recoveryKey, $password);
+	public function getPublicKey($userId) {
+		$publicKey = $this->keyStorage->getUserKey($userId, $this->publicKeyId);
 
-		if ($decryptedRecoveryKey) {
-			return true;
+		if (strlen($publicKey) !== 0) {
+			return $publicKey;
 		}
-		return false;
+		throw new PublicKeyMissingException();
 	}
 
 	/**
-	 * @param $userId
-	 * @param $key
-	 * @return bool
+	 * @param $purpose
+	 * @param bool $timestamp
+	 * @param bool $includeUserKeys
 	 */
-	public function setPublicKey($userId, $key) {
-		return $this->keyStorage->setUserKey($userId, $this->publicKeyId, $key);
+	public function backupAllKeys($purpose, $timestamp = true, $includeUserKeys = true) {
+//		$backupDir = $this->keyStorage->;
 	}
 
 	/**
-	 * @param $userId
-	 * @param $key
-	 * @return bool
+	 * @param string $uid
 	 */
-	public function setPrivateKey($userId, $key) {
-		return $this->keyStorage->setUserKey($userId, $this->privateKeyId, $key);
+	public function replaceUserKeys($uid) {
+		$this->backupAllKeys('password_reset');
+		$this->deletePublicKey($uid);
+		$this->deletePrivateKey($uid);
 	}
 
-
 	/**
-	 * @param $password
-	 * @param $keyPair
+	 * @param $uid
 	 * @return bool
 	 */
-	public function storeKeyPair($password, $keyPair) {
-		// Save Public Key
-		$this->setPublicKey($this->keyId, $keyPair['publicKey']);
-
-		$encryptedKey = $this->crypt->symmetricEncryptFileContent($keyPair['privateKey'], $password);
-
-		if ($encryptedKey) {
-			$this->setPrivateKey($this->keyId, $encryptedKey);
-			$this->config->setAppValue('encryption', 'recoveryAdminEnabled', 1);
-			return true;
-		}
-		return false;
+	public function deletePublicKey($uid) {
+		return $this->keyStorage->deleteUserKey($uid, $this->publicKeyId);
 	}
 
 	/**
+	 * @param $uid
 	 * @return bool
 	 */
-	public function ready() {
-		return $this->keyStorage->ready();
+	private function deletePrivateKey($uid) {
+		return $this->keyStorage->deleteUserKey($uid, $this->privateKeyId);
 	}
 
-
 	/**
-	 * @return \OCP\ICache
-	 * @throws PrivateKeyMissingException
+	 * @param array $userIds
+	 * @return array
+	 * @throws PublicKeyMissingException
 	 */
-	public function init() {
-		try {
-			$privateKey = $this->getPrivateKey($this->keyId);
-		} catch (PrivateKeyMissingException $e) {
-			return false;
-		}
+	public function getPublicKeys(array $userIds) {
+		$keys = [];
 
-		$cache = \OC::$server->getMemCacheFactory();
+		foreach ($userIds as $userId) {
+			try {
+				$keys[$userId] = $this->getPublicKey($userId);
+			} catch (PublicKeyMissingException $e) {
+				continue;
+			}
+		}
 
-		$cacheInstance = $cache->create('Encryption');
-		$cacheInstance->set('privateKey', $privateKey);
+		return $keys;
 
-		return $cacheInstance;
 	}
-
 }
diff --git a/apps/encryption/lib/migrator.php b/apps/encryption/lib/migrator.php
deleted file mode 100644
index 8f7823cb1ae..00000000000
--- a/apps/encryption/lib/migrator.php
+++ /dev/null
@@ -1,123 +0,0 @@
-<?php
-/**
- * @author Clark Tomlinson  <fallen013@gmail.com>
- * @since 3/9/15, 2:44 PM
- * @link http:/www.clarkt.com
- * @copyright Clark Tomlinson © 2015
- *
- */
-
-namespace OCA\Encryption;
-
-
-use OCA\Encryption\Crypto\Crypt;
-use OCP\IConfig;
-use OCP\ILogger;
-use OCP\IUserManager;
-use OCP\IUserSession;
-use OCP\PreConditionNotMetException;
-
-class Migrator {
-
-	/**
-	 * @var bool
-	 */
-	private $status = false;
-	/**
-	 * @var IUserManager
-	 */
-	private $user;
-	/**
-	 * @var IConfig
-	 */
-	private $config;
-	/**
-	 * @var string
-	 */
-	public static $migrationOpen = '0';
-	/**
-	 * @var string
-	 */
-	public static $migrationInProgress = '-1';
-	/**
-	 * @var string
-	 */
-	public static $migrationComplete = '1';
-	/**
-	 * @var IUserManager
-	 */
-	private $userManager;
-	/**
-	 * @var ILogger
-	 */
-	private $log;
-	/**
-	 * @var Crypt
-	 */
-	private $crypt;
-
-	/**
-	 * Migrator constructor.
-	 *
-	 * @param IUserSession $userSession
-	 * @param IConfig $config
-	 * @param IUserManager $userManager
-	 * @param ILogger $log
-	 * @param Crypt $crypt
-	 */
-	public function __construct(IUserSession $userSession, IConfig $config, IUserManager $userManager, ILogger $log, Crypt $crypt) {
-		$this->user = $userSession && $userSession->isLoggedIn() ? $userSession->getUser() : false;
-		$this->config = $config;
-		$this->userManager = $userManager;
-		$this->log = $log;
-		$this->crypt = $crypt;
-	}
-
-	/**
-	 * @param $userId
-	 * @return bool|string
-	 */
-	public function getStatus($userId) {
-		if ($this->userManager->userExists($userId)) {
-			$this->status = $this->config->getUserValue($userId, 'encryption', 'migrationStatus', false);
-
-			if (!$this->status) {
-				$this->config->setUserValue($userId, 'encryption', 'migrationStatus', self::$migrationOpen);
-				$this->status = self::$migrationOpen;
-			}
-		}
-
-		return $this->status;
-	}
-
-	/**
-	 * @return bool
-	 */
-	public function beginMigration() {
-		$status = $this->setMigrationStatus(self::$migrationInProgress, self::$migrationOpen);
-
-		if ($status) {
-			$this->log->info('Encryption Library Start migration to encrypt for ' . $this->user->getUID());
-			return $status;
-		}
-		$this->log->warning('Encryption Library Could not activate migration for ' . $this->user->getUID() . '. Probably another process already started the inital encryption');
-		return $status;
-	}
-
-	/**
-	 * @param $status
-	 * @param bool $preCondition
-	 * @return bool
-	 */
-	private function setMigrationStatus($status, $preCondition = false) {
-		// Convert to string if preCondition is set
-		$preCondition = ($preCondition === false) ? false : (string)$preCondition;
-
-		try {
-			$this->config->setUserValue($this->user->getUID(), 'encryption', 'migrationStatus', (string)$status, $preCondition);
-			return true;
-		} catch (PreConditionNotMetException $e) {
-			return false;
-		}
-	}
-}
diff --git a/apps/encryption/lib/recovery.php b/apps/encryption/lib/recovery.php
index 88350e96c53..457184b4b96 100644
--- a/apps/encryption/lib/recovery.php
+++ b/apps/encryption/lib/recovery.php
@@ -22,11 +22,12 @@
 namespace OCA\Encryption;
 
 
-use OC\Files\View;
 use OCA\Encryption\Crypto\Crypt;
-use OCP\Encryption\IKeyStorage;
+use OCP\Encryption\Keys\IStorage;
 use OCP\IConfig;
 use OCP\IUser;
+use OCP\IUserSession;
+use OCP\PreConditionNotMetException;
 use OCP\Security\ISecureRandom;
 
 class Recovery {
@@ -58,20 +59,20 @@ class Recovery {
 	private $keyStorage;
 
 	/**
-	 * @param IUser $user
+	 * @param IUserSession $user
 	 * @param Crypt $crypt
 	 * @param ISecureRandom $random
 	 * @param KeyManager $keyManager
 	 * @param IConfig $config
-	 * @param IKeyStorage $keyStorage
+	 * @param IStorage $keyStorage
 	 */
-	public function __construct(IUser $user,
+	public function __construct(IUserSession $user,
 								Crypt $crypt,
 								ISecureRandom $random,
 								KeyManager $keyManager,
 								IConfig $config,
-								IKeyStorage $keyStorage) {
-		$this->user = $user;
+								IStorage $keyStorage) {
+		$this->user = $user && $user->isLoggedIn() ? $user->getUser() : false;
 		$this->crypt = $crypt;
 		$this->random = $random;
 		$this->keyManager = $keyManager;
@@ -97,7 +98,7 @@ class Recovery {
 		if (!$keyManager->recoveryKeyExists()) {
 			$keyPair = $this->crypt->createKeyPair();
 
-			return $this->keyManager->storeKeyPair($password, $keyPair);
+			return $this->keyManager->storeKeyPair($this->user->getUID(), $password, $keyPair);
 		}
 
 		if ($keyManager->checkRecoveryPassword($password)) {
@@ -131,4 +132,45 @@ class Recovery {
 		// No idea new way to do this....
 	}
 
+	/**
+	 * @return bool
+	 */
+	public function recoveryEnabledForUser() {
+		$recoveryMode = $this->config->getUserValue($this->user->getUID(),
+			'encryption',
+			'recoveryEnabled',
+			0);
+
+		return ($recoveryMode === '1');
+	}
+	/**
+	 * @param $enabled
+	 * @return bool
+	 */
+	public function setRecoveryForUser($enabled) {
+		$value = $enabled ? '1' : '0';
+
+		try {
+			$this->config->setUserValue($this->user->getUID(),
+				'encryption',
+				'recoveryEnabled',
+				$value);
+			return true;
+		} catch (PreConditionNotMetException $e) {
+			return false;
+		}
+	}
+
+	/**
+	 * @param $recoveryPassword
+	 */
+	public function recoverUsersFiles($recoveryPassword) {
+		// todo: get system private key here
+//		$this->keyManager->get
+		$privateKey = $this->crypt->decryptPrivateKey($encryptedKey,
+			$recoveryPassword);
+
+		$this->recoverAllFiles('/', $privateKey);
+	}
+
 }
diff --git a/apps/encryption/lib/users/setup.php b/apps/encryption/lib/users/setup.php
index 123d6973be9..662a4b4b6af 100644
--- a/apps/encryption/lib/users/setup.php
+++ b/apps/encryption/lib/users/setup.php
@@ -39,24 +39,24 @@ class Setup extends \OCA\Encryption\Setup {
 	}
 
 	/**
-	 * @param $password
+	 * @param $uid userid
+	 * @param $password user password
 	 * @return bool
 	 */
-	public function setupUser($password) {
-		if ($this->keyManager->ready()) {
-			$this->logger->debug('Encryption Library: User Account ' . $this->user->getUID() . ' Is not ready for encryption; configuration started');
-			return $this->setupServerSide($password);
-		}
+	public function setupUser($uid, $password) {
+		return $this->setupServerSide($uid, $password);
 	}
 
 	/**
-	 * @param $password
+	 * @param $uid userid
+	 * @param $password user password
 	 * @return bool
 	 */
-	private function setupServerSide($password) {
+	public function setupServerSide($uid, $password) {
 		// Check if user already has keys
-		if (!$this->keyManager->userHasKeys($this->user->getUID())) {
-			return $this->keyManager->storeKeyPair($password, $this->crypt->createKeyPair());
+		if (!$this->keyManager->userHasKeys($uid)) {
+			return $this->keyManager->storeKeyPair($uid, $password,
+				$this->crypt->createKeyPair());
 		}
 		return true;
 	}
diff --git a/apps/encryption/lib/util.php b/apps/encryption/lib/util.php
new file mode 100644
index 00000000000..5cc658a3136
--- /dev/null
+++ b/apps/encryption/lib/util.php
@@ -0,0 +1,394 @@
+<?php
+/**
+ * @author Clark Tomlinson  <clark@owncloud.com>
+ * @since 3/17/15, 10:31 AM
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+
+namespace OCA\Encryption;
+
+
+use OC\Files\Filesystem;
+use OC\Files\View;
+use OCA\Encryption\Crypto\Crypt;
+use OCA\Files_Versions\Storage;
+use OCP\App;
+use OCP\IConfig;
+use OCP\ILogger;
+use OCP\IUser;
+use OCP\IUserSession;
+use OCP\PreConditionNotMetException;
+use OCP\Share;
+
+class Util {
+	/**
+	 * @var View
+	 */
+	private $files;
+	/**
+	 * @var Filesystem
+	 */
+	private $filesystem;
+	/**
+	 * @var Crypt
+	 */
+	private $crypt;
+	/**
+	 * @var KeyManager
+	 */
+	private $keyManager;
+	/**
+	 * @var ILogger
+	 */
+	private $logger;
+	/**
+	 * @var bool|IUser
+	 */
+	private $user;
+	/**
+	 * @var IConfig
+	 */
+	private $config;
+
+	/**
+	 * Util constructor.
+	 *
+	 * @param View $files
+	 * @param Filesystem $filesystem
+	 * @param Crypt $crypt
+	 * @param KeyManager $keyManager
+	 * @param ILogger $logger
+	 * @param IUserSession $userSession
+	 * @param IConfig $config
+	 */
+	public function __construct(
+		View $files,
+		Filesystem $filesystem,
+		Crypt $crypt,
+		KeyManager $keyManager,
+		ILogger $logger,
+		IUserSession $userSession,
+		IConfig $config
+	) {
+		$this->files = $files;
+		$this->filesystem = $filesystem;
+		$this->crypt = $crypt;
+		$this->keyManager = $keyManager;
+		$this->logger = $logger;
+		$this->user = $userSession && $userSession->isLoggedIn() ? $userSession->getUser() : false;
+		$this->config = $config;
+	}
+
+	/**
+	 * @param $dirPath
+	 * @param bool $found
+	 * @return array|bool
+	 */
+	private function findEncryptedFiles($dirPath, &$found = false) {
+
+		if ($found === false) {
+			$found = [
+				'plain' => [],
+				'encrypted' => [],
+				'broken' => [],
+			];
+		}
+
+		if ($this->files->is_dir($dirPath) && $handle = $this->files->opendir($dirPath)) {
+			if (is_resource($handle)) {
+				while (($file = readdir($handle) !== false)) {
+					if ($file !== '.' && $file !== '..') {
+
+						// Skip stray part files
+						if ($this->isPartialFilePath($file)) {
+							continue;
+						}
+
+						$filePath = $dirPath . '/' . $this->files->getRelativePath('/' . $file);
+						$relPath = $this->stripUserFilesPath($filePath);
+
+						// If the path is a directory, search its contents
+						if ($this->files->is_dir($filePath)) {
+							// Recurse back
+							$this->findEncryptedFiles($filePath);
+
+							/*
+							 * If the path is a file,
+							 * determine where they got re-enabled :/
+							*/
+						} elseif ($this->files->is_file($filePath)) {
+							$isEncryptedPath = $this->isEncryptedPath($filePath);
+
+							/**
+							 * If the file is encrypted
+							 *
+							 * @note: if the userId is
+							 * empty or not set, file will
+							 * be detected as plain
+							 * @note: this is inefficient;
+							 * scanning every file like this
+							 * will eat server resources :(
+							 * fixMe: xxx find better way
+							 */
+							if ($isEncryptedPath) {
+								$fileKey = $this->keyManager->getFileKey($relPath);
+								$shareKey = $this->keyManager->getShareKey($relPath);
+								// If file is encrypted but now file key is available, throw exception
+								if (!$fileKey || !$shareKey) {
+									$this->logger->error('Encryption library, no keys avilable to decrypt the file: ' . $file);
+									$found['broken'][] = [
+										'name' => $file,
+										'path' => $filePath,
+									];
+								} else {
+									$found['encrypted'][] = [
+										'name' => $file,
+										'path' => $filePath
+									];
+								}
+							} else {
+								$found['plain'][] = [
+									'name' => $file,
+									'path' => $filePath
+								];
+							}
+						}
+					}
+
+				}
+			}
+		}
+
+		return $found;
+	}
+
+	/**
+	 * @param $path
+	 * @return bool
+	 */
+	private function isPartialFilePath($path) {
+		$extension = pathinfo($path, PATHINFO_EXTENSION);
+
+		if ($extension === 'part') {
+			return true;
+		}
+		return false;
+	}
+
+	/**
+	 * @param $filePath
+	 * @return bool|string
+	 */
+	private function stripUserFilesPath($filePath) {
+		$split = $this->splitPath($filePath);
+
+		// It is not a file relative to data/user/files
+		if (count($split) < 4 || $split[2] !== 'files') {
+			return false;
+		}
+
+		$sliced = array_slice($split, 3);
+
+		return implode('/', $sliced);
+
+	}
+
+	/**
+	 * @param $filePath
+	 * @return array
+	 */
+	private function splitPath($filePath) {
+		$normalized = $this->filesystem->normalizePath($filePath);
+
+		return explode('/', $normalized);
+	}
+
+	/**
+	 * @param $filePath
+	 * @return bool
+	 */
+	private function isEncryptedPath($filePath) {
+		$data = '';
+
+		// We only need 24 bytes from the last chunck
+		if ($this->files->file_exists($filePath)) {
+			$handle = $this->files->fopen($filePath, 'r');
+			if (is_resource($handle)) {
+				// Suppress fseek warning, we handle the case that fseek
+				// doesn't work in the else branch
+				if (@fseek($handle, -24, SEEK_END) === 0) {
+					$data = fgets($handle);
+				} else {
+					// if fseek failed on the storage we create a local copy
+					// from the file and read this one
+					fclose($handle);
+					$localFile = $this->files->getLocalFile($filePath);
+					$handle = fopen($localFile, 'r');
+
+					if (is_resource($handle) && fseek($handle,
+							-24,
+							SEEK_END) === 0
+					) {
+						$data = fgets($handle);
+					}
+				}
+				fclose($handle);
+				return $this->crypt->isCatfileContent($data);
+			}
+		}
+	}
+
+	/**
+	 * @return bool
+	 */
+	public function recoveryEnabledForUser() {
+		$recoveryMode = $this->config->getUserValue($this->user->getUID(),
+			'encryption',
+			'recoveryEnabled',
+			0);
+
+		return ($recoveryMode === '1');
+	}
+
+	/**
+	 * @param $enabled
+	 * @return bool
+	 */
+	public function setRecoveryForUser($enabled) {
+		$value = $enabled ? '1' : '0';
+
+		try {
+			$this->config->setUserValue($this->user->getUID(),
+				'encryption',
+				'recoveryEnabled',
+				$value);
+			return true;
+		} catch (PreConditionNotMetException $e) {
+			return false;
+		}
+	}
+
+	/**
+	 * @param $recoveryPassword
+	 */
+	public function recoverUsersFiles($recoveryPassword) {
+		// todo: get system private key here
+//		$this->keyManager->get
+		$privateKey = $this->crypt->decryptPrivateKey($encryptedKey,
+			$recoveryPassword);
+
+		$this->recoverAllFiles('/', $privateKey);
+	}
+
+	/**
+	 * @param string $uid
+	 * @return bool
+	 */
+	public function userHasFiles($uid) {
+		return $this->files->file_exists($uid . '/files');
+	}
+
+	/**
+	 * @param $path
+	 * @param $privateKey
+	 */
+	private function recoverAllFiles($path, $privateKey) {
+		// Todo relocate to storage
+		$dirContent = $this->files->getDirectoryContent($path);
+
+		foreach ($dirContent as $item) {
+			// Get relative path from encryption/keyfiles
+			$filePath = substr($item['path'], strlen('encryption/keys'));
+			if ($this->files->is_dir($this->user->getUID() . '/files' . '/' . $filePath)) {
+				$this->recoverAllFiles($filePath . '/', $privateKey);
+			} else {
+				$this->recoverFile($filePath, $privateKey);
+			}
+		}
+
+	}
+
+	/**
+	 * @param $filePath
+	 * @param $privateKey
+	 */
+	private function recoverFile($filePath, $privateKey) {
+		$sharingEnabled = Share::isEnabled();
+
+		// Find out who, if anyone, is sharing the file
+		if ($sharingEnabled) {
+			$result = Share::getUsersSharingFile($filePath,
+				$this->user->getUID(),
+				true);
+			$userIds = $result['users'];
+			$userIds[] = 'public';
+		} else {
+			$userIds = [
+				$this->user->getUID(),
+				$this->recoveryKeyId
+			];
+		}
+		$filteredUids = $this->filterShareReadyUsers($userIds);
+
+		// Decrypt file key
+		$encKeyFile = $this->keyManager->getFileKey($filePath);
+		$shareKey = $this->keyManager->getShareKey($filePath);
+		$plainKeyFile = $this->crypt->multiKeyDecrypt($encKeyFile,
+			$shareKey,
+			$privateKey);
+
+		// Encrypt the file key again to all users, this time with the new publick keyt for the recovered user
+		$userPublicKeys = $this->keyManager->getPublicKeys($filteredUids['ready']);
+		$multiEncryptionKey = $this->crypt->multiKeyEncrypt($plainKeyFile,
+			$userPublicKeys);
+
+		$this->keyManager->setFileKeys($multiEncryptionKey['data']);
+		$this->keyManager->setShareKeys($multiEncryptionKey['keys']);
+	}
+
+	/**
+	 * @param $userIds
+	 * @return array
+	 */
+	private function filterShareReadyUsers($userIds) {
+		// This array will collect the filtered IDs
+		$readyIds = $unreadyIds = [];
+
+		// Loop though users and create array of UIDs that need new keyfiles
+		foreach ($userIds as $user) {
+			// Check that the user is encryption capable, or is the
+			// public system user (for public shares)
+			if ($this->isUserReady($user)) {
+				// construct array of ready UIDs for keymanager
+				$readyIds[] = $user;
+			} else {
+				// Construct array of unready UIDs for keymanager
+				$unreadyIds[] = $user;
+
+				// Log warning; we cant do necessary setup here
+				// because we don't have the user passphrase
+				$this->logger->warning('Encryption Library ' . $this->user->getUID() . ' is not setup for encryption');
+			}
+		}
+		return [
+			'ready' => $readyIds,
+			'unready' => $unreadyIds
+		];
+	}
+
+}
diff --git a/apps/encryption/settings/settings-admin.php b/apps/encryption/settings/settings-admin.php
index 0f5d56a3734..d3acdfe2ba4 100644
--- a/apps/encryption/settings/settings-admin.php
+++ b/apps/encryption/settings/settings-admin.php
@@ -6,17 +6,17 @@
  * See the COPYING-README file.
  */
 
+use OCA\Encryption\KeyManager;
+
 \OC_Util::checkAdminUser();
 
 $tmpl = new OCP\Template('files_encryption', 'settings-admin');
 
 // Check if an adminRecovery account is enabled for recovering files after lost pwd
-$recoveryAdminEnabled = \OC::$server->getAppConfig()->getValue('files_encryption', 'recoveryAdminEnabled', '0');
-$session = new \OCA\Files_Encryption\Session(new \OC\Files\View('/'));
-$initStatus = $session->getInitialized();
+$recoveryAdminEnabled = \OC::$server->getConfig()->getAppValue('encryption', 'recoveryAdminEnabled', '0');
 
 $tmpl->assign('recoveryEnabled', $recoveryAdminEnabled);
-$tmpl->assign('initStatus', $initStatus);
+$tmpl->assign('initStatus', KeyManager::$cacheFactory->get('initStatus'));
 
 \OCP\Util::addscript('files_encryption', 'settings-admin');
 \OCP\Util::addscript('core', 'multiselect');
diff --git a/apps/encryption/tests/lib/KeyManagerTest.php b/apps/encryption/tests/lib/KeyManagerTest.php
index 260e69a73bf..01c5e1d2d07 100644
--- a/apps/encryption/tests/lib/KeyManagerTest.php
+++ b/apps/encryption/tests/lib/KeyManagerTest.php
@@ -27,33 +27,42 @@ class KeyManagerTest extends TestCase {
 	 */
 	private $dummyKeys;
 
+	/**
+	 *
+	 */
 	public function setUp() {
 		parent::setUp();
-		$keyStorageMock = $this->getMock('OCP\Encryption\IKeyStorage');
-		$cryptMock = $this->getMockBuilder('OCA\Encryption\Crypt')
+		$keyStorageMock = $this->getMock('OCP\Encryption\Keys\IStorage');
+		$keyStorageMock->method('getUserKey')
+			->will($this->returnValue(false));
+		$keyStorageMock->method('setUserKey')
+			->will($this->returnValue(true));
+		$cryptMock = $this->getMockBuilder('OCA\Encryption\Crypto\Crypt')
 			->disableOriginalConstructor()
 			->getMock();
 		$configMock = $this->getMock('OCP\IConfig');
-		$userMock = $this->getMock('OCP\IUser');
-		$userMock->expects($this->once())
+		$userMock = $this->getMock('OCP\IUserSession');
+		$userMock
 			->method('getUID')
 			->will($this->returnValue('admin'));
+		$cacheMock = $this->getMock('OCP\ICacheFactory');
+		$logMock = $this->getMock('OCP\ILogger');
 		$this->userId = 'admin';
-		$this->instance = new KeyManager($keyStorageMock, $cryptMock, $configMock, $userMock);
+		$this->instance = new KeyManager($keyStorageMock, $cryptMock, $configMock, $userMock, $cacheMock, $logMock);
 
 		$this->dummyKeys = ['public' => 'randomweakpublickeyhere',
 			'private' => 'randomweakprivatekeyhere'];
 	}
 
 	/**
-	 * @expectedException OC\Encryption\Exceptions\PrivateKeyMissingException
+	 * @expectedException \OC\Encryption\Exceptions\PrivateKeyMissingException
 	 */
 	public function testGetPrivateKey() {
 		$this->assertFalse($this->instance->getPrivateKey($this->userId));
 	}
 
 	/**
-	 * @expectedException OC\Encryption\Exceptions\PublicKeyMissingException
+	 * @expectedException \OC\Encryption\Exceptions\PublicKeyMissingException
 	 */
 	public function testGetPublicKey() {
 		$this->assertFalse($this->instance->getPublicKey($this->userId));
@@ -73,18 +82,34 @@ class KeyManagerTest extends TestCase {
 		$this->assertFalse($this->instance->checkRecoveryPassword('pass'));
 	}
 
+	/**
+	 *
+	 */
 	public function testSetPublicKey() {
 
 		$this->assertTrue($this->instance->setPublicKey($this->userId, $this->dummyKeys['public']));
 	}
 
+	/**
+	 *
+	 */
 	public function testSetPrivateKey() {
 		$this->assertTrue($this->instance->setPrivateKey($this->userId, $this->dummyKeys['private']));
 	}
 
+	/**
+	 *
+	 */
 	public function testUserHasKeys() {
 		$this->assertFalse($this->instance->userHasKeys($this->userId));
 	}
 
+	/**
+	 *
+	 */
+	public function testInit() {
+		$this->assertFalse($this->instance->init($this->userId, 'pass'));
+	}
+
 
 }
diff --git a/apps/encryption/tests/lib/MigratorTest.php b/apps/encryption/tests/lib/MigratorTest.php
index a9d57b34209..0d30b9865b9 100644
--- a/apps/encryption/tests/lib/MigratorTest.php
+++ b/apps/encryption/tests/lib/MigratorTest.php
@@ -51,8 +51,7 @@ class MigratorTest extends TestCase {
 		parent::setUp();
 
 		$cryptMock = $this->getMockBuilder('OCA\Encryption\Crypto\Crypt')->disableOriginalConstructor()->getMock();
-		$this->instance = new Migrator($this->getMock('OCP\IUser'),
-			$this->getMock('OCP\IConfig'),
+		$this->instance = new Migrator($this->getMock('OCP\IConfig'),
 			$this->getMock('OCP\IUserManager'),
 			$this->getMock('OCP\ILogger'),
 			$cryptMock);
diff --git a/apps/files_encryption/lib/hooks.php b/apps/files_encryption/lib/hooks.php
index 536e512bdb2..4a29ffaaedf 100644
--- a/apps/files_encryption/lib/hooks.php
+++ b/apps/files_encryption/lib/hooks.php
@@ -34,8 +34,234 @@ class Hooks {
 	// file for which we want to delete the keys after the delete operation was successful
 	private static $unmountedFiles = array();
 
+	/**
+	 * Startup encryption backend upon user login
+	 * @note This method should never be called for users using client side encryption
+	 */
+	public static function login($params) {
+
+		if (\OCP\App::isEnabled('files_encryption') === false) {
+			return true;
+		}
+
+
+		$l = new \OC_L10N('files_encryption');
+
+		$view = new \OC\Files\View('/');
+
+		// ensure filesystem is loaded
+		if (!\OC\Files\Filesystem::$loaded) {
+			\OC_Util::setupFS($params['uid']);
+		}
+
+		$privateKey = Keymanager::getPrivateKey($view, $params['uid']);
+
+		// if no private key exists, check server configuration
+		if (!$privateKey) {
+			//check if all requirements are met
+			if (!Helper::checkRequirements() || !Helper::checkConfiguration()) {
+				$error_msg = $l->t("Missing requirements.");
+				$hint = $l->t('Please make sure that OpenSSL together with the PHP extension is enabled and configured properly. For now, the encryption app has been disabled.');
+				\OC_App::disable('files_encryption');
+				\OCP\Util::writeLog('Encryption library', $error_msg . ' ' . $hint, \OCP\Util::ERROR);
+				\OCP\Template::printErrorPage($error_msg, $hint);
+			}
+		}
+
+		$util = new Util($view, $params['uid']);
+
+		// setup user, if user not ready force relogin
+		if (Helper::setupUser($util, $params['password']) === false) {
+			return false;
+		}
+
+		$session = $util->initEncryption($params);
+
+		// Check if first-run file migration has already been performed
+		$ready = false;
+		$migrationStatus = $util->getMigrationStatus();
+		if ($migrationStatus === Util::MIGRATION_OPEN && $session !== false) {
+			$ready = $util->beginMigration();
+		} elseif ($migrationStatus === Util::MIGRATION_IN_PROGRESS) {
+			// refuse login as long as the initial encryption is running
+			sleep(5);
+			\OCP\User::logout();
+			return false;
+		}
+
+		$result = true;
+
+		// If migration not yet done
+		if ($ready) {
+
+			// Encrypt existing user files
+			try {
+				$result = $util->encryptAll('/' . $params['uid'] . '/' . 'files');
+			} catch (\Exception $ex) {
+				\OCP\Util::writeLog('Encryption library', 'Initial encryption failed! Error: ' . $ex->getMessage(), \OCP\Util::FATAL);
+				$result = false;
+			}
+
+			if ($result) {
+				\OC_Log::write(
+						'Encryption library', 'Encryption of existing files belonging to "' . $params['uid'] . '" completed'
+						, \OC_Log::INFO
+					);
+				// Register successful migration in DB
+				$util->finishMigration();
+			} else  {
+				\OCP\Util::writeLog('Encryption library', 'Initial encryption failed!', \OCP\Util::FATAL);
+				$util->resetMigrationStatus();
+				\OCP\User::logout();
+			}
+		}
+
+		return $result;
+	}
+
+	/**
+	 * remove keys from session during logout
+	 */
+	public static function logout() {
+		$session = new Session(new \OC\Files\View());
+		$session->removeKeys();
+	}
+
+	/**
+	 * setup encryption backend upon user created
+	 * @note This method should never be called for users using client side encryption
+	 */
+	public static function postCreateUser($params) {
+
+		if (\OCP\App::isEnabled('files_encryption')) {
+			$view = new \OC\Files\View('/');
+			$util = new Util($view, $params['uid']);
+			Helper::setupUser($util, $params['password']);
+		}
+	}
+
+	/**
+	 * cleanup encryption backend upon user deleted
+	 * @note This method should never be called for users using client side encryption
+	 */
+	public static function postDeleteUser($params) {
+
+		if (\OCP\App::isEnabled('files_encryption')) {
+			Keymanager::deletePublicKey(new \OC\Files\View(), $params['uid']);
+		}
+	}
+
+	/**
+	 * If the password can't be changed within ownCloud, than update the key password in advance.
+	 */
+	public static function preSetPassphrase($params) {
+		if (\OCP\App::isEnabled('files_encryption')) {
+			if ( ! \OC_User::canUserChangePassword($params['uid']) ) {
+				self::setPassphrase($params);
+			}
+		}
+	}
+
+	/**
+	 * Change a user's encryption passphrase
+	 * @param array $params keys: uid, password
+	 */
+	public static function setPassphrase($params) {
+		if (\OCP\App::isEnabled('files_encryption') === false) {
+			return true;
+		}
+
+		// Only attempt to change passphrase if server-side encryption
+		// is in use (client-side encryption does not have access to
+		// the necessary keys)
+		if (Crypt::mode() === 'server') {
+
+			$view = new \OC\Files\View('/');
+			$session = new Session($view);
+
+			// Get existing decrypted private key
+			$privateKey = $session->getPrivateKey();
+
+			if ($params['uid'] === \OCP\User::getUser() && $privateKey) {
+
+				// Encrypt private key with new user pwd as passphrase
+				$encryptedPrivateKey = Crypt::symmetricEncryptFileContent($privateKey, $params['password'], Helper::getCipher());
+
+				// Save private key
+				if ($encryptedPrivateKey) {
+					Keymanager::setPrivateKey($encryptedPrivateKey, \OCP\User::getUser());
+				} else {
+					\OCP\Util::writeLog('files_encryption', 'Could not update users encryption password', \OCP\Util::ERROR);
+				}
+
+				// NOTE: Session does not need to be updated as the
+				// private key has not changed, only the passphrase
+				// used to decrypt it has changed
 
 
+			} else { // admin changed the password for a different user, create new keys and reencrypt file keys
+
+				$user = $params['uid'];
+				$util = new Util($view, $user);
+				$recoveryPassword = isset($params['recoveryPassword']) ? $params['recoveryPassword'] : null;
+
+				// we generate new keys if...
+				// ...we have a recovery password and the user enabled the recovery key
+				// ...encryption was activated for the first time (no keys exists)
+				// ...the user doesn't have any files
+				if (($util->recoveryEnabledForUser() && $recoveryPassword)
+						|| !$util->userKeysExists()
+						|| !$view->file_exists($user . '/files')) {
+
+					// backup old keys
+					$util->backupAllKeys('recovery');
+
+					$newUserPassword = $params['password'];
+
+					// make sure that the users home is mounted
+					\OC\Files\Filesystem::initMountPoints($user);
+
+					$keypair = Crypt::createKeypair();
+
+					// Disable encryption proxy to prevent recursive calls
+					$proxyStatus = \OC_FileProxy::$enabled;
+					\OC_FileProxy::$enabled = false;
+
+					// Save public key
+					Keymanager::setPublicKey($keypair['publicKey'], $user);
+
+					// Encrypt private key with new password
+					$encryptedKey = Crypt::symmetricEncryptFileContent($keypair['privateKey'], $newUserPassword, Helper::getCipher());
+					if ($encryptedKey) {
+						Keymanager::setPrivateKey($encryptedKey, $user);
+
+						if ($recoveryPassword) { // if recovery key is set we can re-encrypt the key files
+							$util = new Util($view, $user);
+							$util->recoverUsersFiles($recoveryPassword);
+						}
+					} else {
+						\OCP\Util::writeLog('files_encryption', 'Could not update users encryption password', \OCP\Util::ERROR);
+					}
+
+					\OC_FileProxy::$enabled = $proxyStatus;
+				}
+			}
+		}
+	}
+
+	/**
+	 * after password reset we create a new key pair for the user
+	 *
+	 * @param array $params
+	 */
+	public static function postPasswordReset($params) {
+		$uid = $params['uid'];
+		$password = $params['password'];
+
+		$util = new Util(new \OC\Files\View(), $uid);
+		$util->replaceUserKeys($password);
+	}
+
 	/*
 	 * check if files can be encrypted to every user.
 	 */
diff --git a/lib/private/server.php b/lib/private/server.php
index 8c5169f229e..6a2e45aa59d 100644
--- a/lib/private/server.php
+++ b/lib/private/server.php
@@ -702,7 +702,7 @@ class Server extends SimpleContainer implements IServerContainer {
 	 *
 	 * @return \OCP\Security\ISecureRandom
 	 */
-	function getSecureRandom() {
+function getSecureRandom() {
 		return $this->query('SecureRandom');
 	}
 
-- 
cgit v1.2.3