summaryrefslogtreecommitdiffstats
path: root/apps/encryption
diff options
context:
space:
mode:
authorThomas Müller <thomas.mueller@tmit.eu>2015-04-07 16:46:45 +0200
committerThomas Müller <thomas.mueller@tmit.eu>2015-04-07 16:46:45 +0200
commit1fbf5d86df7ba4001ca826d9dfb8fad073924fde (patch)
tree9260b35011fabbbf69747419282d193fa7a9089c /apps/encryption
parent2182ae0d278f466e7f117b03bf4ebca0e6e9fe9b (diff)
parent2d2cb09715554926945de29b80f033905a219abd (diff)
downloadnextcloud-server-1fbf5d86df7ba4001ca826d9dfb8fad073924fde.tar.gz
nextcloud-server-1fbf5d86df7ba4001ca826d9dfb8fad073924fde.zip
Merge pull request #14472 from owncloud/feature/wipencryptionapp
encryption 2.0 app
Diffstat (limited to 'apps/encryption')
-rw-r--r--apps/encryption/appinfo/app.php27
-rw-r--r--apps/encryption/appinfo/application.php190
-rw-r--r--apps/encryption/appinfo/info.xml36
-rw-r--r--apps/encryption/appinfo/routes.php44
-rw-r--r--apps/encryption/controller/recoverycontroller.php160
-rw-r--r--apps/encryption/css/settings-personal.css10
-rw-r--r--apps/encryption/hooks/contracts/ihook.php32
-rw-r--r--apps/encryption/hooks/userhooks.php286
-rw-r--r--apps/encryption/img/app.svg51
-rw-r--r--apps/encryption/js/detect-migration.js33
-rw-r--r--apps/encryption/js/encryption.js16
-rw-r--r--apps/encryption/js/settings-admin.js55
-rw-r--r--apps/encryption/js/settings-personal.js62
-rw-r--r--apps/encryption/l10n/.gitkeep0
-rw-r--r--apps/encryption/lib/crypto/crypt.php457
-rw-r--r--apps/encryption/lib/crypto/encryption.php328
-rw-r--r--apps/encryption/lib/exceptions/multikeydecryptexception.php9
-rw-r--r--apps/encryption/lib/exceptions/multikeyencryptexception.php9
-rw-r--r--apps/encryption/lib/exceptions/privatekeymissingexception.php38
-rw-r--r--apps/encryption/lib/exceptions/publickeymissingexception.php20
-rw-r--r--apps/encryption/lib/hookmanager.php66
-rw-r--r--apps/encryption/lib/keymanager.php511
-rw-r--r--apps/encryption/lib/recovery.php316
-rw-r--r--apps/encryption/lib/session.php114
-rw-r--r--apps/encryption/lib/users/setup.php72
-rw-r--r--apps/encryption/lib/util.php117
-rw-r--r--apps/encryption/settings/settings-admin.php21
-rw-r--r--apps/encryption/settings/settings-personal.php58
-rw-r--r--apps/encryption/templates/settings-admin.php73
-rw-r--r--apps/encryption/templates/settings-personal.php72
-rw-r--r--apps/encryption/tests/lib/HookManagerTest.php74
-rw-r--r--apps/encryption/tests/lib/KeyManagerTest.php286
-rw-r--r--apps/encryption/tests/lib/RecoveryTest.php265
-rw-r--r--apps/encryption/tests/lib/SessionTest.php140
-rw-r--r--apps/encryption/tests/lib/UtilTest.php128
-rw-r--r--apps/encryption/tests/lib/crypto/encryptionTest.php78
-rw-r--r--apps/encryption/tests/lib/users/SetupTest.php81
37 files changed, 4335 insertions, 0 deletions
diff --git a/apps/encryption/appinfo/app.php b/apps/encryption/appinfo/app.php
new file mode 100644
index 00000000000..240a1726715
--- /dev/null
+++ b/apps/encryption/appinfo/app.php
@@ -0,0 +1,27 @@
+<?php
+/**
+ * @author Clark Tomlinson <clark@owncloud.com>
+ * @since 2/19/15, 9:52 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\AppInfo;
+
+$app = new Application();
+$app->registerEncryptionModule();
+$app->registerHooks();
+$app->registerSettings();
diff --git a/apps/encryption/appinfo/application.php b/apps/encryption/appinfo/application.php
new file mode 100644
index 00000000000..0d1bd0d6bed
--- /dev/null
+++ b/apps/encryption/appinfo/application.php
@@ -0,0 +1,190 @@
+<?php
+/**
+ * @author Clark Tomlinson <clark@owncloud.com>
+ * @since 3/11/15, 11:03 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\AppInfo;
+
+
+use OC\Files\Filesystem;
+use OC\Files\View;
+use OCA\Encryption\Crypto\Crypt;
+use OCA\Encryption\HookManager;
+use OCA\Encryption\Hooks\UserHooks;
+use OCA\Encryption\KeyManager;
+use OCA\Encryption\Recovery;
+use OCA\Encryption\Users\Setup;
+use OCA\Encryption\Util;
+use OCP\App;
+use OCP\AppFramework\IAppContainer;
+use OCP\Encryption\IManager;
+use OCP\IConfig;
+
+
+class Application extends \OCP\AppFramework\App {
+ /**
+ * @var IManager
+ */
+ private $encryptionManager;
+ /**
+ * @var IConfig
+ */
+ private $config;
+
+ /**
+ * @param $appName
+ * @param array $urlParams
+ */
+ public function __construct($urlParams = array()) {
+ parent::__construct('encryption', $urlParams);
+ $this->encryptionManager = \OC::$server->getEncryptionManager();
+ $this->config = \OC::$server->getConfig();
+ $this->registerServices();
+ }
+
+ /**
+ *
+ */
+ public function registerHooks() {
+ if (!$this->config->getSystemValue('maintenance', false)) {
+
+ $container = $this->getContainer();
+ $server = $container->getServer();
+ // Register our hooks and fire them.
+ $hookManager = new HookManager();
+
+ $hookManager->registerHook([
+ new UserHooks($container->query('KeyManager'),
+ $server->getLogger(),
+ $container->query('UserSetup'),
+ $server->getUserSession(),
+ $container->query('Util'),
+ new \OCA\Encryption\Session($server->getSession()),
+ $container->query('Crypt'),
+ $container->query('Recovery'))
+ ]);
+
+ $hookManager->fireHooks();
+
+ } else {
+ // Logout user if we are in maintenance to force re-login
+ $this->getContainer()->getServer()->getUserSession()->logout();
+ }
+ }
+
+ /**
+ *
+ */
+ public function registerEncryptionModule() {
+ $container = $this->getContainer();
+ $container->registerService('EncryptionModule', function (IAppContainer $c) {
+ return new \OCA\Encryption\Crypto\Encryption(
+ $c->query('Crypt'),
+ $c->query('KeyManager'),
+ $c->query('Util'));
+ });
+ $module = $container->query('EncryptionModule');
+ $this->encryptionManager->registerEncryptionModule($module);
+ }
+
+ /**
+ *
+ */
+ public function registerServices() {
+ $container = $this->getContainer();
+
+ $container->registerService('Crypt',
+ function (IAppContainer $c) {
+ $server = $c->getServer();
+ return new Crypt($server->getLogger(),
+ $server->getUserSession(),
+ $server->getConfig());
+ });
+
+ $container->registerService('KeyManager',
+ function (IAppContainer $c) {
+ $server = $c->getServer();
+
+ return new KeyManager($server->getEncryptionKeyStorage(\OCA\Encryption\Crypto\Encryption::ID),
+ $c->query('Crypt'),
+ $server->getConfig(),
+ $server->getUserSession(),
+ new \OCA\Encryption\Session($server->getSession()),
+ $server->getLogger(),
+ $c->query('Util')
+ );
+ });
+
+
+ $container->registerService('Recovery',
+ function (IAppContainer $c) {
+ $server = $c->getServer();
+
+ return new Recovery(
+ $server->getUserSession(),
+ $c->query('Crypt'),
+ $server->getSecureRandom(),
+ $c->query('KeyManager'),
+ $server->getConfig(),
+ $server->getEncryptionKeyStorage(\OCA\Encryption\Crypto\Encryption::ID),
+ $server->getEncryptionFilesHelper(),
+ new \OC\Files\View());
+ });
+
+ $container->registerService('RecoveryController', function (IAppContainer $c) {
+ $server = $c->getServer();
+ return new \OCA\Encryption\Controller\RecoveryController(
+ $c->getAppName(),
+ $server->getRequest(),
+ $server->getConfig(),
+ $server->getL10N($c->getAppName()),
+ $c->query('Recovery'));
+ });
+
+ $container->registerService('UserSetup',
+ function (IAppContainer $c) {
+ $server = $c->getServer();
+ return new Setup($server->getLogger(),
+ $server->getUserSession(),
+ $c->query('Crypt'),
+ $c->query('KeyManager'));
+ });
+
+ $container->registerService('Util',
+ function (IAppContainer $c) {
+ $server = $c->getServer();
+
+ return new Util(
+ new View(),
+ $c->query('Crypt'),
+ $server->getLogger(),
+ $server->getUserSession(),
+ $server->getConfig());
+ });
+
+ }
+
+ /**
+ *
+ */
+ public function registerSettings() {
+ // Register settings scripts
+ App::registerAdmin('encryption', 'settings/settings-admin');
+ App::registerPersonal('encryption', 'settings/settings-personal');
+ }
+}
diff --git a/apps/encryption/appinfo/info.xml b/apps/encryption/appinfo/info.xml
new file mode 100644
index 00000000000..e4a7d790e9c
--- /dev/null
+++ b/apps/encryption/appinfo/info.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0"?>
+<info>
+ <id>encryption</id>
+ <description>
+ This application encrypts all files accessed by ownCloud at rest,
+ wherever they are stored. As an example, with this application
+ enabled, external cloud based Amazon S3 storage will be encrypted,
+ protecting this data on storage outside of the control of the Admin.
+ When this application is enabled for the first time, all files are
+ encrypted as users log in and are prompted for their password. The
+ recommended recovery key option enables recovery of files in case
+ the key is lost.
+ Note that this app encrypts all files that are touched by ownCloud,
+ so external storage providers and applications such as SharePoint
+ will see new files encrypted when they are accessed. Encryption is
+ based on AES 128 or 256 bit keys. More information is available in
+ the Encryption documentation
+ </description>
+<name>Encryption</name>
+ <license>AGPL</license>
+ <author>Bjoern Schiessle, Clark Tomlinson</author>
+ <requiremin>8</requiremin>
+ <shipped>true</shipped>
+ <documentation>
+ <user>user-encryption</user>
+ <admin>admin-encryption</admin>
+ </documentation>
+ <rememberlogin>false</rememberlogin>
+ <types>
+ <filesystem/>
+ </types>
+ <dependencies>
+ <lib>openssl</lib>
+ </dependencies>
+
+</info>
diff --git a/apps/encryption/appinfo/routes.php b/apps/encryption/appinfo/routes.php
new file mode 100644
index 00000000000..d4867f5fdaa
--- /dev/null
+++ b/apps/encryption/appinfo/routes.php
@@ -0,0 +1,44 @@
+<?php
+/**
+ * @author Clark Tomlinson <clark@owncloud.com>
+ * @since 2/19/15, 11:22 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\AppInfo;
+
+(new Application())->registerRoutes($this, array('routes' => array(
+
+ [
+ 'name' => 'Recovery#adminRecovery',
+ 'url' => '/ajax/adminRecovery',
+ 'verb' => 'POST'
+ ],
+ [
+ 'name' => 'Recovery#changeRecoveryPassword',
+ 'url' => '/ajax/changeRecoveryPassword',
+ 'verb' => 'POST'
+ ],
+ [
+ 'name' => 'Recovery#userSetRecovery',
+ 'url' => '/ajax/userSetRecovery',
+ 'verb' => 'POST'
+ ]
+
+
+)));
diff --git a/apps/encryption/controller/recoverycontroller.php b/apps/encryption/controller/recoverycontroller.php
new file mode 100644
index 00000000000..da55d81f63a
--- /dev/null
+++ b/apps/encryption/controller/recoverycontroller.php
@@ -0,0 +1,160 @@
+<?php
+/**
+ * @author Clark Tomlinson <clark@owncloud.com>
+ * @since 2/19/15, 11:25 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\Controller;
+
+
+use OCA\Encryption\Recovery;
+use OCP\AppFramework\Controller;
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\IRequest;
+use OCP\JSON;
+use OCP\AppFramework\Http\DataResponse;
+
+class RecoveryController extends Controller {
+ /**
+ * @var IConfig
+ */
+ private $config;
+ /**
+ * @var IL10N
+ */
+ private $l;
+ /**
+ * @var Recovery
+ */
+ private $recovery;
+
+ /**
+ * @param string $AppName
+ * @param IRequest $request
+ * @param IConfig $config
+ * @param IL10N $l10n
+ * @param Recovery $recovery
+ */
+ public function __construct($AppName, IRequest $request, IConfig $config, IL10N $l10n, Recovery $recovery) {
+ parent::__construct($AppName, $request);
+ $this->config = $config;
+ $this->l = $l10n;
+ $this->recovery = $recovery;
+ }
+
+ public function adminRecovery($recoveryPassword, $confirmPassword, $adminEnableRecovery) {
+ // Check if both passwords are the same
+ if (empty($recoveryPassword)) {
+ $errorMessage = (string) $this->l->t('Missing recovery key password');
+ return new DataResponse(['data' => ['message' => $errorMessage]], 500);
+ }
+
+ if (empty($confirmPassword)) {
+ $errorMessage = (string) $this->l->t('Please repeat the recovery key password');
+ return new DataResponse(['data' => ['message' => $errorMessage]], 500);
+ }
+
+ if ($recoveryPassword !== $confirmPassword) {
+ $errorMessage = (string) $this->l->t('Repeated recovery key password does not match the provided recovery key password');
+ return new DataResponse(['data' => ['message' => $errorMessage]], 500);
+ }
+
+ if (isset($adminEnableRecovery) && $adminEnableRecovery === '1') {
+ if ($this->recovery->enableAdminRecovery($recoveryPassword)) {
+ return new DataResponse(['status' =>'success', 'data' => array('message' => (string) $this->l->t('Recovery key successfully enabled'))]);
+ }
+ return new DataResponse(['data' => array('message' => (string) $this->l->t('Could not enable recovery key. Please check your recovery key password!'))]);
+ } elseif (isset($adminEnableRecovery) && $adminEnableRecovery === '0') {
+ if ($this->recovery->disableAdminRecovery($recoveryPassword)) {
+ return new DataResponse(['data' => array('message' => (string) $this->l->t('Recovery key successfully disabled'))]);
+ }
+ return new DataResponse(['data' => array('message' => (string) $this->l->t('Could not disable recovery key. Please check your recovery key password!'))]);
+ }
+ }
+
+ public function changeRecoveryPassword($newPassword, $oldPassword, $confirmPassword) {
+ //check if both passwords are the same
+ if (empty($oldPassword)) {
+ $errorMessage = (string) $this->l->t('Please provide the old recovery password');
+ return new DataResponse(array('data' => array('message' => $errorMessage)));
+ }
+
+ if (empty($newPassword)) {
+ $errorMessage = (string) $this->l->t('Please provide a new recovery password');
+ return new DataResponse (array('data' => array('message' => $errorMessage)));
+ }
+
+ if (empty($confirmPassword)) {
+ $errorMessage = (string) $this->l->t('Please repeat the new recovery password');
+ return new DataResponse(array('data' => array('message' => $errorMessage)));
+ }
+
+ if ($newPassword !== $confirmPassword) {
+ $errorMessage = (string) $this->l->t('Repeated recovery key password does not match the provided recovery key password');
+ return new DataResponse(array('data' => array('message' => $errorMessage)));
+ }
+
+ $result = $this->recovery->changeRecoveryKeyPassword($newPassword, $oldPassword);
+
+ if ($result) {
+ return new DataResponse(
+ array(
+ 'status' => 'success' ,
+ 'data' => array(
+ 'message' => (string) $this->l->t('Password successfully changed.'))
+ )
+ );
+ } else {
+ return new DataResponse(
+ array(
+ 'data' => array
+ ('message' => (string) $this->l->t('Could not change the password. Maybe the old password was not correct.'))
+ )
+ );
+ }
+ }
+
+ /**
+ * @NoAdminRequired
+ */
+ public function userSetRecovery($userEnableRecovery) {
+ if ($userEnableRecovery === '0' || $userEnableRecovery === '1') {
+
+ $result = $this->recovery->setRecoveryForUser($userEnableRecovery);
+
+ if ($result) {
+ return new DataResponse(
+ array(
+ 'status' => 'success',
+ 'data' => array(
+ 'message' => (string) $this->l->t('Recovery Key enabled'))
+ )
+ );
+ } else {
+ return new DataResponse(
+ array(
+ 'data' => array
+ ('message' => (string) $this->l->t('Could not enable the recovery key, please try again or contact your administrator'))
+ )
+ );
+ }
+ }
+ }
+
+}
diff --git a/apps/encryption/css/settings-personal.css b/apps/encryption/css/settings-personal.css
new file mode 100644
index 00000000000..8eb5bedcb06
--- /dev/null
+++ b/apps/encryption/css/settings-personal.css
@@ -0,0 +1,10 @@
+/* Copyright (c) 2013, Sam Tuke, <samtuke@owncloud.com>
+ This file is licensed under the Affero General Public License version 3 or later.
+ See the COPYING-README file. */
+
+#encryptAllError
+, #encryptAllSuccess
+, #recoveryEnabledError
+, #recoveryEnabledSuccess {
+ display: none;
+}
diff --git a/apps/encryption/hooks/contracts/ihook.php b/apps/encryption/hooks/contracts/ihook.php
new file mode 100644
index 00000000000..2cc01fd7c9b
--- /dev/null
+++ b/apps/encryption/hooks/contracts/ihook.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * @author Clark Tomlinson <clark@owncloud.com>
+ * @since 2/19/15, 10:03 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\Hooks\Contracts;
+
+
+interface IHook {
+ /**
+ * Connects Hooks
+ *
+ * @return null
+ */
+ public function addHooks();
+}
diff --git a/apps/encryption/hooks/userhooks.php b/apps/encryption/hooks/userhooks.php
new file mode 100644
index 00000000000..1ec0950d941
--- /dev/null
+++ b/apps/encryption/hooks/userhooks.php
@@ -0,0 +1,286 @@
+<?php
+/**
+ * @author Clark Tomlinson <clark@owncloud.com>
+ * @since 2/19/15, 10:02 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\Hooks;
+
+
+use OCP\Util as OCUtil;
+use OCA\Encryption\Hooks\Contracts\IHook;
+use OCA\Encryption\KeyManager;
+use OCA\Encryption\Crypto\Crypt;
+use OCA\Encryption\Users\Setup;
+use OCP\App;
+use OCP\ILogger;
+use OCP\IUserSession;
+use OCA\Encryption\Util;
+use OCA\Encryption\Session;
+use OCA\Encryption\Recovery;
+
+class UserHooks implements IHook {
+ /**
+ * @var KeyManager
+ */
+ private $keyManager;
+ /**
+ * @var ILogger
+ */
+ private $logger;
+ /**
+ * @var Setup
+ */
+ private $userSetup;
+ /**
+ * @var IUserSession
+ */
+ private $user;
+ /**
+ * @var Util
+ */
+ private $util;
+ /**
+ * @var Session
+ */
+ private $session;
+ /**
+ * @var Recovery
+ */
+ private $recovery;
+ /**
+ * @var Crypt
+ */
+ private $crypt;
+
+ /**
+ * UserHooks constructor.
+ *
+ * @param KeyManager $keyManager
+ * @param ILogger $logger
+ * @param Setup $userSetup
+ * @param IUserSession $user
+ * @param Util $util
+ * @param Session $session
+ * @param Crypt $crypt
+ * @param Recovery $recovery
+ */
+ public function __construct(KeyManager $keyManager,
+ ILogger $logger,
+ Setup $userSetup,
+ IUserSession $user,
+ Util $util,
+ Session $session,
+ Crypt $crypt,
+ Recovery $recovery) {
+
+ $this->keyManager = $keyManager;
+ $this->logger = $logger;
+ $this->userSetup = $userSetup;
+ $this->user = $user;
+ $this->util = $util;
+ $this->session = $session;
+ $this->recovery = $recovery;
+ $this->crypt = $crypt;
+ }
+
+ /**
+ * Connects Hooks
+ *
+ * @return null
+ */
+ public function addHooks() {
+ 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');
+ }
+
+
+ /**
+ * 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) {
+
+ if (!App::isEnabled('encryption')) {
+ return true;
+ }
+
+ // ensure filesystem is loaded
+ // Todo: update?
+ if (!\OC\Files\Filesystem::$loaded) {
+ \OC_Util::setupFS($params['uid']);
+ }
+
+ // setup user, if user not ready force relogin
+ if (!$this->userSetup->setupUser($params['uid'], $params['password'])) {
+ return false;
+ }
+
+ $this->keyManager->init($params['uid'], $params['password']);
+ }
+
+ /**
+ * remove keys from session during logout
+ */
+ public function logout() {
+ $this->session->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('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('encryption')) {
+ $this->keyManager->deletePublicKey($params['uid']);
+ }
+ }
+
+ /**
+ * If the password can't be changed within ownCloud, than update the key password in advance.
+ *
+ * @param array $params : uid, password
+ * @return bool
+ */
+ public function preSetPassphrase($params) {
+ if (App::isEnabled('encryption')) {
+
+ if (!$this->user->getUser()->canChangePassword()) {
+ $this->setPassphrase($params);
+ }
+ }
+ }
+
+ /**
+ * Change a user's encryption passphrase
+ *
+ * @param array $params keys: uid, password
+ * @return bool
+ */
+ public function setPassphrase($params) {
+
+ // Get existing decrypted private key
+ $privateKey = $this->session->getPrivateKey();
+
+ if ($params['uid'] === $this->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->keyManager->setPrivateKey($this->user->getUser()->getUID(),
+ $encryptedPrivateKey);
+ } else {
+ $this->logger->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 (
+ ($this->recovery->isRecoveryEnabledForUser($user) && $recoveryPassword)
+ || !$this->keyManager->userHasKeys($user)
+ || !$this->util->userHasFiles($user)
+ ) {
+
+ // backup old keys
+ //$this->backupAllKeys('recovery');
+
+ $newUserPassword = $params['password'];
+
+ $keyPair = $this->crypt->createKeyPair();
+
+ // Save public key
+ $this->keyManager->setPublicKey($user, $keyPair['publicKey']);
+
+ // Encrypt private key with new password
+ $encryptedKey = $this->crypt->symmetricEncryptFileContent($keyPair['privateKey'],
+ $newUserPassword);
+
+ if ($encryptedKey) {
+ $this->keyManager->setPrivateKey($user, $encryptedKey);
+
+ if ($recoveryPassword) { // if recovery key is set we can re-encrypt the key files
+ $this->recovery->recoverUsersFiles($recoveryPassword, $user);
+ }
+ } else {
+ $this->logger->error('Encryption Could not update users encryption password');
+ }
+ }
+ }
+ }
+
+
+
+ /**
+ * after password reset we create a new key pair for the user
+ *
+ * @param array $params
+ */
+ public function postPasswordReset($params) {
+ $password = $params['password'];
+
+ $this->keyManager->replaceUserKeys($params['uid']);
+ $this->userSetup->setupServerSide($params['uid'], $password);
+ }
+}
diff --git a/apps/encryption/img/app.svg b/apps/encryption/img/app.svg
new file mode 100644
index 00000000000..1157c71c66e
--- /dev/null
+++ b/apps/encryption/img/app.svg
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xml:space="preserve"
+ height="16px"
+ width="16px"
+ version="1.1"
+ y="0px"
+ x="0px"
+ viewBox="0 0 71 100"
+ id="svg2"
+ inkscape:version="0.48.5 r10040"
+ sodipodi:docname="app.svg"><metadata
+ id="metadata10"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
+ id="defs8" /><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1920"
+ inkscape:window-height="1014"
+ id="namedview6"
+ showgrid="false"
+ inkscape:zoom="14.75"
+ inkscape:cx="-21.423729"
+ inkscape:cy="8"
+ inkscape:window-x="0"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg2" /><path
+ d="m8 1c-2.2091 0-4 1.7909-4 4v2h-1v7h10v-7h-1v-2c0-2.2091-1.791-4-4-4zm0 2c1.1046 0 2 0.89543 2 2v2h-4v-2c0-1.1046 0.8954-2 2-2z"
+ transform="matrix(6.25,0,0,6.25,-14.5,0)"
+ id="path4"
+ style="fill:#ffffff;fill-opacity:1" /><path
+ style="fill:none"
+ d="m 3.0644068,10.508475 0,-3.4576275 0.4655371,0 0.465537,0 0.049537,-1.2033899 C 4.1094633,4.2818838 4.1578923,4.0112428 4.4962182,3.3259708 4.7075644,2.8978935 4.9002217,2.6327599 5.2605792,2.2740624 6.7855365,0.75613022 8.9920507,0.69157582 10.623172,2.1171729 c 0.384104,0.3357058 0.882069,1.0763131 1.054177,1.5678422 0.147302,0.4206856 0.262873,1.6086448 0.266436,2.7387137 l 0.002,0.6271187 0.508474,0 0.508475,0 0,3.4576275 0,3.457627 -4.9491527,0 -4.9491525,0 0,-3.457627 z M 10.065882,6.3559322 c -0.02012,-0.3822034 -0.04774,-0.7076271 -0.0614,-0.7231639 -0.013653,-0.015537 -0.024824,0.281921 -0.024824,0.661017 l 0,0.6892655 -1.9630041,0 -1.963004,0 -0.023717,-0.4576271 -0.023717,-0.4576271 -0.013279,0.4915254 -0.013279,0.4915255 2.0613978,0 2.0613972,0 -0.03657,-0.6949153 0,0 z M 6.5396275,3.7118644 C 6.648082,3.5720339 6.7197092,3.4576271 6.6987988,3.4576271 c -0.062956,0 -0.5835446,0.6841947 -0.5835446,0.7669359 0,0.042237 0.051116,0.00136 0.1135916,-0.090834 0.062475,-0.092195 0.2023271,-0.2820343 0.3107817,-0.4218648 z M 9.7498983,4.0169492 C 9.6961899,3.9144068 9.5352369,3.723769 9.392225,3.5933098 L 9.1322034,3.356111 9.3784249,3.6272081 c 0.1354218,0.1491033 0.2814105,0.3397411 0.3244192,0.4236394 0.043009,0.083898 0.093162,0.1525423 0.1114515,0.1525423 0.01829,0 -0.010689,-0.083898 -0.064397,-0.1864406 l 0,0 z M 7.3032896,3.1315382 C 7.2704731,3.0987216 6.877102,3.3089557 6.8306315,3.3841466 6.8091904,3.4188389 6.911918,3.3813452 7.0589148,3.300827 7.2059117,3.2203088 7.3158803,3.1441289 7.3032896,3.1315382 l 0,0 z"
+ id="path3007"
+ inkscape:connector-curvature="0"
+ transform="matrix(6.25,0,0,6.25,-14.5,0)" /></svg> \ No newline at end of file
diff --git a/apps/encryption/js/detect-migration.js b/apps/encryption/js/detect-migration.js
new file mode 100644
index 00000000000..f5627edf4e4
--- /dev/null
+++ b/apps/encryption/js/detect-migration.js
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2013
+ * Bjoern Schiessle <schiessle@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+
+$(document).ready(function(){
+ $('form[name="login"]').on('submit', function() {
+ var user = $('#user').val();
+ var password = $('#password').val();
+ $.ajax({
+ type: 'POST',
+ url: OC.linkTo('files_encryption', 'ajax/getMigrationStatus.php'),
+ dataType: 'json',
+ data: {user: user, password: password},
+ async: false,
+ success: function(response) {
+ if (response.data.migrationStatus === OC.Encryption.MIGRATION_OPEN) {
+ var message = t('files_encryption', 'Initial encryption started... This can take some time. Please wait.');
+ $('#messageText').text(message);
+ $('#message').removeClass('hidden').addClass('update');
+ } else if (response.data.migrationStatus === OC.Encryption.MIGRATION_IN_PROGRESS) {
+ var message = t('files_encryption', 'Initial encryption running... Please try again later.');
+ $('#messageText').text(message);
+ $('#message').removeClass('hidden').addClass('update');
+ }
+ }
+ });
+ });
+
+});
diff --git a/apps/encryption/js/encryption.js b/apps/encryption/js/encryption.js
new file mode 100644
index 00000000000..d2d1c3a1fc5
--- /dev/null
+++ b/apps/encryption/js/encryption.js
@@ -0,0 +1,16 @@
+/**
+ * Copyright (c) 2014
+ * Bjoern Schiessle <schiessle@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+/**
+ * @namespace
+ * @memberOf OC
+ */
+OC.Encryption={
+ MIGRATION_OPEN:0,
+ MIGRATION_COMPLETED:1,
+ MIGRATION_IN_PROGRESS:-1,
+};
diff --git a/apps/encryption/js/settings-admin.js b/apps/encryption/js/settings-admin.js
new file mode 100644
index 00000000000..36765adf3e4
--- /dev/null
+++ b/apps/encryption/js/settings-admin.js
@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) 2013
+ * Sam Tuke <samtuke@owncloud.com>
+ * Robin Appelman <icewind1991@gmail.com>
+ * Bjoern Schiessle <schiessle@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+$(document).ready(function(){
+
+ $( 'input:radio[name="adminEnableRecovery"]' ).change(
+ function() {
+ var recoveryStatus = $( this ).val();
+ var oldStatus = (1+parseInt(recoveryStatus)) % 2;
+ var recoveryPassword = $( '#encryptionRecoveryPassword' ).val();
+ var confirmPassword = $( '#repeatEncryptionRecoveryPassword' ).val();
+ OC.msg.startSaving('#encryptionSetRecoveryKey .msg');
+ $.post(
+ OC.generateUrl('/apps/encryption/ajax/adminRecovery')
+ , { adminEnableRecovery: recoveryStatus, recoveryPassword: recoveryPassword, confirmPassword: confirmPassword }
+ , function( result ) {
+ OC.msg.finishedSaving('#encryptionSetRecoveryKey .msg', result);
+ if (result.status === "error") {
+ $('input:radio[name="adminEnableRecovery"][value="'+oldStatus.toString()+'"]').attr("checked", "true");
+ } else {
+ if (recoveryStatus === "0") {
+ $('p[name="changeRecoveryPasswordBlock"]').addClass("hidden");
+ } else {
+ $('input:password[name="changeRecoveryPassword"]').val("");
+ $('p[name="changeRecoveryPasswordBlock"]').removeClass("hidden");
+ }
+ }
+ }
+ );
+ }
+ );
+
+ // change recovery password
+
+ $('button:button[name="submitChangeRecoveryKey"]').click(function() {
+ var oldRecoveryPassword = $('#oldEncryptionRecoveryPassword').val();
+ var newRecoveryPassword = $('#newEncryptionRecoveryPassword').val();
+ var confirmNewPassword = $('#repeatedNewEncryptionRecoveryPassword').val();
+ OC.msg.startSaving('#encryptionChangeRecoveryKey .msg');
+ $.post(
+ OC.generateUrl('/apps/encryption/ajax/changeRecoveryPassword')
+ , { oldPassword: oldRecoveryPassword, newPassword: newRecoveryPassword, confirmPassword: confirmNewPassword }
+ , function( data ) {
+ OC.msg.finishedSaving('#encryptionChangeRecoveryKey .msg', data);
+ }
+ );
+ });
+
+});
diff --git a/apps/encryption/js/settings-personal.js b/apps/encryption/js/settings-personal.js
new file mode 100644
index 00000000000..dcfbba4ecde
--- /dev/null
+++ b/apps/encryption/js/settings-personal.js
@@ -0,0 +1,62 @@
+/**
+ * Copyright (c) 2013, Sam Tuke <samtuke@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+function updatePrivateKeyPasswd() {
+ var oldPrivateKeyPassword = $('input:password[id="oldPrivateKeyPassword"]').val();
+ var newPrivateKeyPassword = $('input:password[id="newPrivateKeyPassword"]').val();
+ OC.msg.startSaving('#encryption .msg');
+ $.post(
+ OC.generateUrl('/apps/encryption/ajax/updatePrivateKeyPassword')
+ , { oldPassword: oldPrivateKeyPassword, newPassword: newPrivateKeyPassword }
+ , function( data ) {
+ if (data.status === "error") {
+ OC.msg.finishedSaving('#encryption .msg', data);
+ } else {
+ OC.msg.finishedSaving('#encryption .msg', data);
+ }
+ }
+ );
+}
+
+$(document).ready(function(){
+
+ // Trigger ajax on recoveryAdmin status change
+ $( 'input:radio[name="userEnableRecovery"]' ).change(
+ function() {
+ var recoveryStatus = $( this ).val();
+ OC.msg.startAction('#userEnableRecovery .msg', 'Updating recovery keys. This can take some time...');
+ $.post(
+ OC.generateUrl('/apps/encryption/ajax/userSetRecovery')
+ , { userEnableRecovery: recoveryStatus }
+ , function( data ) {
+ OC.msg.finishedAction('#userEnableRecovery .msg', data);
+ }
+ );
+ // Ensure page is not reloaded on form submit
+ return false;
+ }
+ );
+
+ // update private key password
+
+ $('input:password[name="changePrivateKeyPassword"]').keyup(function(event) {
+ var oldPrivateKeyPassword = $('input:password[id="oldPrivateKeyPassword"]').val();
+ var newPrivateKeyPassword = $('input:password[id="newPrivateKeyPassword"]').val();
+ if (newPrivateKeyPassword !== '' && oldPrivateKeyPassword !== '' ) {
+ $('button:button[name="submitChangePrivateKeyPassword"]').removeAttr("disabled");
+ if(event.which === 13) {
+ updatePrivateKeyPasswd();
+ }
+ } else {
+ $('button:button[name="submitChangePrivateKeyPassword"]').attr("disabled", "true");
+ }
+ });
+
+ $('button:button[name="submitChangePrivateKeyPassword"]').click(function() {
+ updatePrivateKeyPasswd();
+ });
+
+});
diff --git a/apps/encryption/l10n/.gitkeep b/apps/encryption/l10n/.gitkeep
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/apps/encryption/l10n/.gitkeep
diff --git a/apps/encryption/lib/crypto/crypt.php b/apps/encryption/lib/crypto/crypt.php
new file mode 100644
index 00000000000..c0b737a3daa
--- /dev/null
+++ b/apps/encryption/lib/crypto/crypt.php
@@ -0,0 +1,457 @@
+<?php
+/**
+ * @author Clark Tomlinson <clark@owncloud.com>
+ * @since 2/19/15, 1:42 PM
+ * @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\Crypto;
+
+
+use OC\Encryption\Exceptions\DecryptionFailedException;
+use OC\Encryption\Exceptions\EncryptionFailedException;
+use OCA\Encryption\Exceptions\MultiKeyDecryptException;
+use OCA\Encryption\Exceptions\MultiKeyEncryptException;
+use OCP\Encryption\Exceptions\GenericEncryptionException;
+use OCP\IConfig;
+use OCP\ILogger;
+use OCP\IUser;
+use OCP\IUserSession;
+
+class Crypt {
+
+ const DEFAULT_CIPHER = 'AES-256-CFB';
+
+ const HEADER_START = 'HBEGIN';
+ const HEADER_END = 'HEND';
+ /**
+ * @var ILogger
+ */
+ private $logger;
+ /**
+ * @var IUser
+ */
+ private $user;
+ /**
+ * @var IConfig
+ */
+ private $config;
+
+ /**
+ * @param ILogger $logger
+ * @param IUserSession $userSession
+ * @param IConfig $config
+ */
+ public function __construct(ILogger $logger, IUserSession $userSession, IConfig $config) {
+ $this->logger = $logger;
+ $this->user = $userSession && $userSession->isLoggedIn() ? $userSession->getUser() : false;
+ $this->config = $config;
+ }
+
+ /**
+ * @return array|bool
+ */
+ public function createKeyPair() {
+
+ $log = $this->logger;
+ $res = $this->getOpenSSLPKey();
+
+ if (!$res) {
+ $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']);
+ }
+ } elseif (openssl_pkey_export($res,
+ $privateKey,
+ null,
+ $this->getOpenSSLConfig())) {
+ $keyDetails = openssl_pkey_get_details($res);
+ $publicKey = $keyDetails['key'];
+
+ return [
+ 'publicKey' => $publicKey,
+ 'privateKey' => $privateKey
+ ];
+ }
+ $log->error('Encryption library couldn\'t export users private key, please check your servers openSSL configuration.' . $this->user->getUID(),
+ ['app' => 'encryption']);
+ if (openssl_error_string()) {
+ $log->error('Encryption Library:' . openssl_error_string(),
+ ['app' => 'encryption']);
+ }
+
+ return false;
+ }
+
+ /**
+ * @return resource
+ */
+ public function getOpenSSLPKey() {
+ $config = $this->getOpenSSLConfig();
+ return openssl_pkey_new($config);
+ }
+
+ /**
+ * @return array
+ */
+ private function getOpenSSLConfig() {
+ $config = ['private_key_bits' => 4096];
+ $config = array_merge(\OC::$server->getConfig()->getSystemValue('openssl',
+ []),
+ $config);
+ return $config;
+ }
+
+ /**
+ * @param string $plainContent
+ * @param string $passPhrase
+ * @return bool|string
+ * @throws GenericEncryptionException
+ */
+ public function symmetricEncryptFileContent($plainContent, $passPhrase) {
+
+ if (!$plainContent) {
+ $this->logger->error('Encryption Library, symmetrical encryption failed no content given',
+ ['app' => 'encryption']);
+ return false;
+ }
+
+ $iv = $this->generateIv();
+
+ $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);
+
+ return $padded;
+ }
+
+ /**
+ * @param string $plainContent
+ * @param string $iv
+ * @param string $passPhrase
+ * @param string $cipher
+ * @return string
+ * @throws EncryptionFailedException
+ */
+ private function encrypt($plainContent, $iv, $passPhrase = '', $cipher = self::DEFAULT_CIPHER) {
+ $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']);
+ throw new EncryptionFailedException($error);
+ }
+
+ return $encryptedContent;
+ }
+
+ /**
+ * @return mixed|string
+ */
+ 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']);
+ $cipher = self::DEFAULT_CIPHER;
+ }
+
+ return $cipher;
+ }
+
+ /**
+ * @param string $encryptedContent
+ * @param string $iv
+ * @return string
+ */
+ private function concatIV($encryptedContent, $iv) {
+ return $encryptedContent . '00iv00' . $iv;
+ }
+
+ /**
+ * @param $data
+ * @return string
+ */
+ private function addPadding($data) {
+ return $data . 'xx';
+ }
+
+ /**
+ * @param string $recoveryKey
+ * @param string $password
+ * @return bool|string
+ */
+ public function decryptPrivateKey($recoveryKey, $password) {
+
+ $header = $this->parseHeader($recoveryKey);
+ $cipher = $this->getCipher();
+
+ // 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::HEADER_END) + strlen(self::HEADER_START));
+ }
+
+ $plainKey = $this->symmetricDecryptFileContent($recoveryKey,
+ $password,
+ $cipher);
+
+ // Check if this is a valid private key
+ $res = openssl_get_privatekey($plainKey);
+ if (is_resource($res)) {
+ $sslInfo = openssl_pkey_get_details($res);
+ if (!isset($sslInfo['key'])) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+
+ return $plainKey;
+ }
+
+ /**
+ * @param $keyFileContents
+ * @param string $passPhrase
+ * @param string $cipher
+ * @return string
+ * @throws DecryptionFailedException
+ */
+ public function symmetricDecryptFileContent($keyFileContents, $passPhrase = '', $cipher = self::DEFAULT_CIPHER) {
+ // Remove Padding
+ $noPadding = $this->removePadding($keyFileContents);
+
+ $catFile = $this->splitIv($noPadding);
+
+ return $this->decrypt($catFile['encrypted'],
+ $catFile['iv'],
+ $passPhrase,
+ $cipher);
+ }
+
+ /**
+ * @param $padded
+ * @return bool|string
+ */
+ private function removePadding($padded) {
+ if (substr($padded, -2) === 'xx') {
+ return substr($padded, 0, -2);
+ }
+ return false;
+ }
+
+ /**
+ * @param $catFile
+ * @return array
+ */
+ private function splitIv($catFile) {
+ // Fetch encryption metadata from end of file
+ $meta = substr($catFile, -22);
+
+ // Fetch IV from end of file
+ $iv = substr($meta, -16);
+
+ // Remove IV and IV Identifier text to expose encrypted content
+
+ $encrypted = substr($catFile, 0, -22);
+
+ return [
+ 'encrypted' => $encrypted,
+ 'iv' => $iv
+ ];
+ }
+
+ /**
+ * @param $encryptedContent
+ * @param $iv
+ * @param string $passPhrase
+ * @param string $cipher
+ * @return string
+ * @throws DecryptionFailedException
+ */
+ private function decrypt($encryptedContent, $iv, $passPhrase = '', $cipher = self::DEFAULT_CIPHER) {
+ $plainContent = openssl_decrypt($encryptedContent,
+ $cipher,
+ $passPhrase,
+ false,
+ $iv);
+
+ if ($plainContent) {
+ return $plainContent;
+ } else {
+ throw new DecryptionFailedException('Encryption library: Decryption (symmetric) of content failed: ' . openssl_error_string());
+ }
+ }
+
+ /**
+ * @param $data
+ * @return array
+ */
+ private function parseHeader($data) {
+ $result = [];
+
+ if (substr($data, 0, strlen(self::HEADER_START)) === self::HEADER_START) {
+ $endAt = strpos($data, self::HEADER_END);
+ $header = substr($data, 0, $endAt + strlen(self::HEADER_END));
+
+ // +1 not to start with an ':' which would result in empty element at the beginning
+ $exploded = explode(':',
+ substr($header, strlen(self::HEADER_START) + 1));
+
+ $element = array_shift($exploded);
+
+ while ($element != self::HEADER_END) {
+ $result[$element] = array_shift($exploded);
+ $element = array_shift($exploded);
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * @return string
+ * @throws GenericEncryptionException
+ */
+ private function generateIv() {
+ $random = openssl_random_pseudo_bytes(12, $strong);
+ 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']);
+ }
+
+ /*
+ * We encode the iv purely for string manipulation
+ * purposes -it gets decoded before use
+ */
+ return base64_encode($random);
+ }
+ // If we ever get here we've failed anyway no need for an else
+ throw new GenericEncryptionException('Generating IV Failed');
+ }
+
+ /**
+ * Generate a pseudo random 256-bit ASCII key, used as file key
+ * @return string
+ */
+ public static function generateFileKey() {
+ // Generate key
+ $key = base64_encode(openssl_random_pseudo_bytes(32, $strong));
+ if (!$key || !$strong) {
+ // If OpenSSL indicates randomness is insecure, log error
+ throw new \Exception('Encryption library, Insecure symmetric key was generated using openssl_random_pseudo_bytes()');
+ }
+
+ return $key;
+ }
+
+ /**
+ * 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:' . openssl_error_string());
+ }
+ }
+
+ /**
+ * @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 corresponding key id
+ 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/crypto/encryption.php b/apps/encryption/lib/crypto/encryption.php
new file mode 100644
index 00000000000..7c633b7411f
--- /dev/null
+++ b/apps/encryption/lib/crypto/encryption.php
@@ -0,0 +1,328 @@
+<?php
+/**
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ * @since 3/6/15, 2:28 PM
+ * @link http:/www.clarkt.com
+ * @copyright Clark Tomlinson © 2015
+ *
+ */
+
+namespace OCA\Encryption\Crypto;
+
+
+use OCA\Encryption\Util;
+use OCP\Encryption\IEncryptionModule;
+use OCA\Encryption\KeyManager;
+
+class Encryption implements IEncryptionModule {
+
+ const ID = 'OC_DEFAULT_MODULE';
+
+ /**
+ * @var Crypt
+ */
+ private $crypt;
+
+ /** @var string */
+ private $cipher;
+
+ /** @var string */
+ private $path;
+
+ /** @var string */
+ private $user;
+
+ /** @var string */
+ private $fileKey;
+
+ /** @var string */
+ private $writeCache;
+
+ /** @var KeyManager */
+ private $keyManager;
+
+ /** @var array */
+ private $accessList;
+
+ /** @var boolean */
+ private $isWriteOperation;
+
+ /** @var Util */
+ private $util;
+
+ /**
+ *
+ * @param \OCA\Encryption\Crypto\Crypt $crypt
+ * @param KeyManager $keyManager
+ * @param Util $util
+ */
+ public function __construct(Crypt $crypt, KeyManager $keyManager, Util $util) {
+ $this->crypt = $crypt;
+ $this->keyManager = $keyManager;
+ $this->util = $util;
+ }
+
+ /**
+ * @return string defining the technical unique id
+ */
+ public function getId() {
+ return self::ID;
+ }
+
+ /**
+ * In comparison to getKey() this function returns a human readable (maybe translated) name
+ *
+ * @return string
+ */
+ public function getDisplayName() {
+ return 'ownCloud Default Encryption';
+ }
+
+ /**
+ * start receiving chunks from a file. This is the place where you can
+ * perform some initial step before starting encrypting/decrypting the
+ * chunks
+ *
+ * @param string $path to the file
+ * @param string $user who read/write the file
+ * @param array $header contains the header data read from the file
+ * @param array $accessList who has access to the file contains the key 'users' and 'public'
+ *
+ * @return array $header contain data as key-value pairs which should be
+ * written to the header, in case of a write operation
+ * or if no additional data is needed return a empty array
+ */
+ public function begin($path, $user, $header, $accessList) {
+
+ if (isset($header['cipher'])) {
+ $this->cipher = $header['cipher'];
+ } else {
+ $this->cipher = $this->crypt->getCipher();
+ }
+
+ $this->path = $this->getPathToRealFile($path);
+ $this->accessList = $accessList;
+ $this->user = $user;
+ $this->writeCache = '';
+ $this->isWriteOperation = false;
+
+ $this->fileKey = $this->keyManager->getFileKey($this->path, $this->user);
+
+ return array('cipher' => $this->cipher);
+ }
+
+ /**
+ * last chunk received. This is the place where you can perform some final
+ * operation and return some remaining data if something is left in your
+ * buffer.
+ *
+ * @param string $path to the file
+ * @return string remained data which should be written to the file in case
+ * of a write operation
+ */
+ public function end($path) {
+ $result = '';
+ if ($this->isWriteOperation) {
+ if (!empty($this->writeCache)) {
+ $result = $this->crypt->symmetricEncryptFileContent($this->writeCache, $this->fileKey);
+ $this->writeCache = '';
+ }
+ $publicKeys = array();
+ foreach ($this->accessList['users'] as $uid) {
+ $publicKeys[$uid] = $this->keyManager->getPublicKey($uid);
+ }
+
+ $publicKeys = $this->keyManager->addSystemKeys($this->accessList, $publicKeys);
+
+ $encryptedKeyfiles = $this->crypt->multiKeyEncrypt($this->fileKey, $publicKeys);
+ $this->keyManager->setAllFileKeys($this->path, $encryptedKeyfiles);
+ }
+ return $result;
+ }
+
+ /**
+ * encrypt data
+ *
+ * @param string $data you want to encrypt
+ * @return mixed encrypted data
+ */
+ public function encrypt($data) {
+ $this->isWriteOperation = true;
+ if (empty($this->fileKey)) {
+ $this->fileKey = $this->crypt->generateFileKey();
+ }
+
+ // If extra data is left over from the last round, make sure it
+ // is integrated into the next 6126 / 8192 block
+ if ($this->writeCache) {
+
+ // Concat writeCache to start of $data
+ $data = $this->writeCache . $data;
+
+ // Clear the write cache, ready for reuse - it has been
+ // flushed and its old contents processed
+ $this->writeCache = '';
+
+ }
+
+ $encrypted = '';
+ // While there still remains some data to be processed & written
+ while (strlen($data) > 0) {
+
+ // Remaining length for this iteration, not of the
+ // entire file (may be greater than 8192 bytes)
+ $remainingLength = strlen($data);
+
+ // If data remaining to be written is less than the
+ // size of 1 6126 byte block
+ if ($remainingLength < 6126) {
+
+ // Set writeCache to contents of $data
+ // The writeCache will be carried over to the
+ // next write round, and added to the start of
+ // $data to ensure that written blocks are
+ // always the correct length. If there is still
+ // data in writeCache after the writing round
+ // has finished, then the data will be written
+ // to disk by $this->flush().
+ $this->writeCache = $data;
+
+ // Clear $data ready for next round
+ $data = '';
+
+ } else {
+
+ // Read the chunk from the start of $data
+ $chunk = substr($data, 0, 6126);
+
+ $encrypted .= $this->crypt->symmetricEncryptFileContent($chunk, $this->fileKey);
+
+ // Remove the chunk we just processed from
+ // $data, leaving only unprocessed data in $data
+ // var, for handling on the next round
+ $data = substr($data, 6126);
+
+ }
+
+ }
+
+ return $encrypted;
+ }
+
+ /**
+ * decrypt data
+ *
+ * @param string $data you want to decrypt
+ * @return mixed decrypted data
+ */
+ public function decrypt($data) {
+ $result = '';
+ if (!empty($data)) {
+ $result = $this->crypt->symmetricDecryptFileContent($data, $this->fileKey);
+ }
+ return $result;
+ }
+
+ /**
+ * update encrypted file, e.g. give additional users access to the file
+ *
+ * @param string $path path to the file which should be updated
+ * @param string $uid of the user who performs the operation
+ * @param array $accessList who has access to the file contains the key 'users' and 'public'
+ * @return boolean
+ */
+ public function update($path, $uid, $accessList) {
+ $fileKey = $this->keyManager->getFileKey($path, $uid);
+ $publicKeys = array();
+ foreach ($accessList['users'] as $user) {
+ $publicKeys[$user] = $this->keyManager->getPublicKey($user);
+ }
+
+ $publicKeys = $this->keyManager->addSystemKeys($accessList, $publicKeys);
+
+ $encryptedFileKey = $this->crypt->multiKeyEncrypt($fileKey, $publicKeys);
+
+ $this->keyManager->deleteAllFileKeys($path);
+
+ $this->keyManager->setAllFileKeys($path, $encryptedFileKey);
+
+ return true;
+ }
+
+ /**
+ * add system keys such as the public share key and the recovery key
+ *
+ * @param array $accessList
+ * @param array $publicKeys
+ * @return array
+ */
+ public function addSystemKeys(array $accessList, array $publicKeys) {
+ if (!empty($accessList['public'])) {
+ $publicKeys[$this->keyManager->getPublicShareKeyId()] = $this->keyManager->getPublicShareKey();
+ }
+
+ if ($this->keyManager->recoveryKeyExists() &&
+ $this->util->recoveryEnabled($this->user)) {
+
+ $publicKeys[$this->keyManager->getRecoveryKeyId()] = $this->keyManager->getRecoveryKey();
+ }
+
+
+ return $publicKeys;
+ }
+
+
+ /**
+ * should the file be encrypted or not
+ *
+ * @param string $path
+ * @return boolean
+ */
+ public function shouldEncrypt($path) {
+ $parts = explode('/', $path);
+ if (count($parts) < 3) {
+ return false;
+ }
+
+ if ($parts[2] == 'files') {
+ return true;
+ }
+ if ($parts[2] == 'files_versions') {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * calculate unencrypted size
+ *
+ * @param string $path to file
+ * @return integer unencrypted size
+ */
+ 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() {
+ return 6126;
+ }
+
+ protected function getPathToRealFile($path) {
+ $realPath = $path;
+ $parts = explode('/', $path);
+ if ($parts[2] === 'files_versions') {
+ $realPath = '/' . $parts[1] . '/files/' . implode('/', array_slice($parts, 3));
+ $length = strrpos($realPath, '.');
+ $realPath = substr($realPath, 0, $length);
+ }
+
+ return $realPath;
+ }
+}
diff --git a/apps/encryption/lib/exceptions/multikeydecryptexception.php b/apps/encryption/lib/exceptions/multikeydecryptexception.php
new file mode 100644
index 00000000000..1466d35eda3
--- /dev/null
+++ b/apps/encryption/lib/exceptions/multikeydecryptexception.php
@@ -0,0 +1,9 @@
+<?php
+
+namespace OCA\Encryption\Exceptions;
+
+use OCP\Encryption\Exceptions\GenericEncryptionException;
+
+class MultiKeyDecryptException extends GenericEncryptionException {
+
+}
diff --git a/apps/encryption/lib/exceptions/multikeyencryptexception.php b/apps/encryption/lib/exceptions/multikeyencryptexception.php
new file mode 100644
index 00000000000..daf528e2cf7
--- /dev/null
+++ b/apps/encryption/lib/exceptions/multikeyencryptexception.php
@@ -0,0 +1,9 @@
+<?php
+
+namespace OCA\Encryption\Exceptions;
+
+use OCP\Encryption\Exceptions\GenericEncryptionException;
+
+class MultiKeyEncryptException extends GenericEncryptionException {
+
+}
diff --git a/apps/encryption/lib/exceptions/privatekeymissingexception.php b/apps/encryption/lib/exceptions/privatekeymissingexception.php
new file mode 100644
index 00000000000..50d75870b20
--- /dev/null
+++ b/apps/encryption/lib/exceptions/privatekeymissingexception.php
@@ -0,0 +1,38 @@
+<?php
+ /**
+ * @author Clark Tomlinson <clark@owncloud.com>
+ * @since 2/25/15, 9:39 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\Exceptions;
+
+use OCP\Encryption\Exceptions\GenericEncryptionException;
+
+class PrivateKeyMissingException extends GenericEncryptionException {
+
+ /**
+ * @param string $userId
+ */
+ public function __construct($userId) {
+ if(empty($userId)) {
+ $userId = "<no-user-id-given>";
+ }
+ parent::__construct("Private Key missing for user: $userId");
+ }
+
+}
diff --git a/apps/encryption/lib/exceptions/publickeymissingexception.php b/apps/encryption/lib/exceptions/publickeymissingexception.php
new file mode 100644
index 00000000000..9638c28e427
--- /dev/null
+++ b/apps/encryption/lib/exceptions/publickeymissingexception.php
@@ -0,0 +1,20 @@
+<?php
+
+
+namespace OCA\Encryption\Exceptions;
+
+use OCP\Encryption\Exceptions\GenericEncryptionException;
+
+class PublicKeyMissingException extends GenericEncryptionException {
+
+ /**
+ * @param string $userId
+ */
+ public function __construct($userId) {
+ if(empty($userId)) {
+ $userId = "<no-user-id-given>";
+ }
+ parent::__construct("Public Key missing for user: $userId");
+ }
+
+}
diff --git a/apps/encryption/lib/hookmanager.php b/apps/encryption/lib/hookmanager.php
new file mode 100644
index 00000000000..19ee142a622
--- /dev/null
+++ b/apps/encryption/lib/hookmanager.php
@@ -0,0 +1,66 @@
+<?php
+/**
+ * @author Clark Tomlinson <clark@owncloud.com>
+ * @since 2/19/15, 10:13 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 OCA\Encryption\Hooks\Contracts\IHook;
+
+class HookManager {
+
+ private $hookInstances = [];
+
+ /**
+ * @param array|IHook $instances
+ * - This accepts either a single instance of IHook or an array of instances of IHook
+ * @return bool
+ */
+ public function registerHook($instances) {
+ if (is_array($instances)) {
+ foreach ($instances as $instance) {
+ if (!$instance instanceof IHook) {
+ return false;
+ }
+ $this->hookInstances[] = $instance;
+ }
+
+ } elseif ($instances instanceof IHook) {
+ $this->hookInstances[] = $instances;
+ }
+ return true;
+ }
+
+ /**
+ *
+ */
+ 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
new file mode 100644
index 00000000000..1f71a891e81
--- /dev/null
+++ b/apps/encryption/lib/keymanager.php
@@ -0,0 +1,511 @@
+<?php
+
+namespace OCA\Encryption;
+
+use OC\Encryption\Exceptions\DecryptionFailedException;
+use OCA\Encryption\Exceptions\PrivateKeyMissingException;
+use OCA\Encryption\Exceptions\PublicKeyMissingException;
+use OCA\Encryption\Crypto\Crypt;
+use OCP\Encryption\Keys\IStorage;
+use OCP\IConfig;
+use OCP\ILogger;
+use OCP\IUserSession;
+
+class KeyManager {
+
+ /**
+ * @var Session
+ */
+ protected $session;
+ /**
+ * @var IStorage
+ */
+ private $keyStorage;
+ /**
+ * @var Crypt
+ */
+ private $crypt;
+ /**
+ * @var string
+ */
+ private $recoveryKeyId;
+ /**
+ * @var string
+ */
+ private $publicShareKeyId;
+ /**
+ * @var string UserID
+ */
+ private $keyId;
+ /**
+ * @var string
+ */
+ private $publicKeyId = 'publicKey';
+ /**
+ * @var string
+ */
+ private $privateKeyId = 'privateKey';
+
+ /**
+ * @var string
+ */
+ private $shareKeyId = 'shareKey';
+
+ /**
+ * @var string
+ */
+ private $fileKeyId = 'fileKey';
+ /**
+ * @var IConfig
+ */
+ private $config;
+ /**
+ * @var ILogger
+ */
+ private $log;
+ /**
+ * @var Util
+ */
+ private $util;
+
+ /**
+ * @param IStorage $keyStorage
+ * @param Crypt $crypt
+ * @param IConfig $config
+ * @param IUserSession $userSession
+ * @param Session $session
+ * @param ILogger $log
+ * @param Util $util
+ */
+ public function __construct(
+ IStorage $keyStorage,
+ Crypt $crypt,
+ IConfig $config,
+ IUserSession $userSession,
+ Session $session,
+ ILogger $log,
+ Util $util
+ ) {
+
+ $this->util = $util;
+ $this->session = $session;
+ $this->keyStorage = $keyStorage;
+ $this->crypt = $crypt;
+ $this->config = $config;
+ $this->log = $log;
+
+ $this->recoveryKeyId = $this->config->getAppValue('encryption',
+ 'recoveryKeyId');
+ if (empty($this->recoveryKeyId)) {
+ $this->recoveryKeyId = 'recoveryKey_' . substr(md5(time()), 0, 8);
+ $this->config->setAppValue('encryption',
+ 'recoveryKeyId',
+ $this->recoveryKeyId);
+ }
+
+ $this->publicShareKeyId = $this->config->getAppValue('encryption',
+ 'publicShareKeyId');
+ if (empty($this->publicShareKeyId)) {
+ $this->publicShareKeyId = 'pubShare_' . substr(md5(time()), 0, 8);
+ $this->config->setAppValue('encryption', 'publicShareKeyId', $this->publicShareKeyId);
+ }
+
+ $shareKey = $this->getPublicShareKey();
+ if (empty($shareKey)) {
+ $keyPair = $this->crypt->createKeyPair();
+
+ // Save public key
+ $this->keyStorage->setSystemUserKey(
+ $this->publicShareKeyId . '.publicKey', $keyPair['publicKey']);
+
+ // Encrypt private key empty passphrase
+ $encryptedKey = $this->crypt->symmetricEncryptFileContent($keyPair['privateKey'], '');
+ $this->keyStorage->setSystemUserKey($this->publicShareKeyId . '.privateKey', $encryptedKey);
+ }
+
+ $this->keyId = $userSession && $userSession->isLoggedIn() ? $userSession->getUser()->getUID() : false;
+ $this->log = $log;
+ }
+
+ /**
+ * @return bool
+ */
+ public function recoveryKeyExists() {
+ $key = $this->getRecoveryKey();
+ return (!empty($key));
+ }
+
+ /**
+ * get recovery key
+ *
+ * @return string
+ */
+ public function getRecoveryKey() {
+ return $this->keyStorage->getSystemUserKey($this->recoveryKeyId . '.publicKey');
+ }
+
+ /**
+ * get recovery key ID
+ *
+ * @return string
+ */
+ public function getRecoveryKeyId() {
+ return $this->recoveryKeyId;
+ }
+
+ /**
+ * @param $password
+ * @return bool
+ */
+ public function checkRecoveryPassword($password) {
+ $recoveryKey = $this->keyStorage->getSystemUserKey($this->recoveryKeyId . '.privateKey');
+ $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);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @param string $password
+ * @param array $keyPair
+ * @return bool
+ */
+ public function setRecoveryKey($password, $keyPair) {
+ // Save Public Key
+ $this->keyStorage->setSystemUserKey($this->getRecoveryKeyId(). '.publicKey', $keyPair['publicKey']);
+
+ $encryptedKey = $this->crypt->symmetricEncryptFileContent($keyPair['privateKey'],
+ $password);
+
+ if ($encryptedKey) {
+ $this->setSystemPrivateKey($this->getRecoveryKeyId(), $encryptedKey);
+ 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);
+ }
+
+ /**
+ * write file key to key storage
+ *
+ * @param string $path
+ * @param string $key
+ * @return boolean
+ */
+ public function setFileKey($path, $key) {
+ return $this->keyStorage->setFileKey($path, $this->fileKeyId, $key);
+ }
+
+ /**
+ * set all file keys (the file key and the corresponding share keys)
+ *
+ * @param string $path
+ * @param array $keys
+ */
+ public function setAllFileKeys($path, $keys) {
+ $this->setFileKey($path, $keys['data']);
+ foreach ($keys['keys'] as $uid => $keyFile) {
+ $this->setShareKey($path, $uid, $keyFile);
+ }
+ }
+
+ /**
+ * write share key to the key storage
+ *
+ * @param string $path
+ * @param string $uid
+ * @param string $key
+ * @return boolean
+ */
+ public function setShareKey($path, $uid, $key) {
+ $keyId = $uid . '.' . $this->shareKeyId;
+ return $this->keyStorage->setFileKey($path, $keyId, $key);
+ }
+
+ /**
+ * Decrypt private key and store it
+ *
+ * @param string $uid userid
+ * @param string $passPhrase users password
+ * @return boolean
+ */
+ 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;
+ }
+
+ $this->session->setPrivateKey($privateKey);
+ $this->session->setStatus(Session::INIT_SUCCESSFUL);
+
+ return true;
+ }
+
+ /**
+ * @param $userId
+ * @return mixed
+ * @throws PrivateKeyMissingException
+ */
+ public function getPrivateKey($userId) {
+ $privateKey = $this->keyStorage->getUserKey($userId,
+ $this->privateKeyId);
+
+ if (strlen($privateKey) !== 0) {
+ return $privateKey;
+ }
+ throw new PrivateKeyMissingException($userId);
+ }
+
+ /**
+ * @param $path
+ * @param $uid
+ * @return string
+ */
+ public function getFileKey($path, $uid) {
+ $encryptedFileKey = $this->keyStorage->getFileKey($path, $this->fileKeyId);
+
+ if (is_null($uid)) {
+ $uid = $this->getPublicShareKeyId();
+ $shareKey = $this->getShareKey($path, $uid);
+ $privateKey = $this->keyStorage->getSystemUserKey($this->publicShareKeyId . '.privateKey');
+ $privateKey = $this->crypt->symmetricDecryptFileContent($privateKey);
+ } else {
+ $shareKey = $this->getShareKey($path, $uid);
+ $privateKey = $this->session->getPrivateKey();
+ }
+
+ if ($encryptedFileKey && $shareKey && $privateKey) {
+ return $this->crypt->multiKeyDecrypt($encryptedFileKey,
+ $shareKey,
+ $privateKey);
+ }
+
+ return '';
+ }
+
+ /**
+ * get the encrypted file key
+ *
+ * @param $path
+ * @return string
+ */
+ public function getEncryptedFileKey($path) {
+ $encryptedFileKey = $this->keyStorage->getFileKey($path,
+ $this->fileKeyId);
+
+ return $encryptedFileKey;
+ }
+
+ /**
+ * delete share key
+ *
+ * @param string $path
+ * @param string $keyId
+ * @return boolean
+ */
+ public function deleteShareKey($path, $keyId) {
+ return $this->keyStorage->deleteFileKey($path, $keyId . '.' . $this->shareKeyId);
+ }
+
+
+ /**
+ * @param $path
+ * @param $uid
+ * @return mixed
+ */
+ public function getShareKey($path, $uid) {
+ $keyId = $uid . '.' . $this->shareKeyId;
+ return $this->keyStorage->getFileKey($path, $keyId);
+ }
+
+ /**
+ * @param $userId
+ * @return bool
+ */
+ public function userHasKeys($userId) {
+ try {
+ $this->getPrivateKey($userId);
+ $this->getPublicKey($userId);
+ } catch (PrivateKeyMissingException $e) {
+ return false;
+ } catch (PublicKeyMissingException $e) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * @param $userId
+ * @return mixed
+ * @throws PublicKeyMissingException
+ */
+ public function getPublicKey($userId) {
+ $publicKey = $this->keyStorage->getUserKey($userId, $this->publicKeyId);
+
+ if (strlen($publicKey) !== 0) {
+ return $publicKey;
+ }
+ throw new PublicKeyMissingException($userId);
+ }
+
+ public function getPublicShareKeyId() {
+ return $this->publicShareKeyId;
+ }
+
+ /**
+ * get public key for public link shares
+ *
+ * @return string
+ */
+ public function getPublicShareKey() {
+ return $this->keyStorage->getSystemUserKey($this->publicShareKeyId . '.publicKey');
+ }
+
+ /**
+ * @param $purpose
+ * @param bool $timestamp
+ * @param bool $includeUserKeys
+ */
+ public function backupAllKeys($purpose, $timestamp = true, $includeUserKeys = true) {
+// $backupDir = $this->keyStorage->;
+ }
+
+ /**
+ * @param string $uid
+ */
+ public function replaceUserKeys($uid) {
+ $this->backupAllKeys('password_reset');
+ $this->deletePublicKey($uid);
+ $this->deletePrivateKey($uid);
+ }
+
+ /**
+ * @param $uid
+ * @return bool
+ */
+ public function deletePublicKey($uid) {
+ return $this->keyStorage->deleteUserKey($uid, $this->publicKeyId);
+ }
+
+ /**
+ * @param $uid
+ * @return bool
+ */
+ private function deletePrivateKey($uid) {
+ return $this->keyStorage->deleteUserKey($uid, $this->privateKeyId);
+ }
+
+ public function deleteAllFileKeys($path) {
+ return $this->keyStorage->deleteAllFileKeys($path);
+ }
+
+ /**
+ * @param array $userIds
+ * @return array
+ * @throws PublicKeyMissingException
+ */
+ public function getPublicKeys(array $userIds) {
+ $keys = [];
+
+ foreach ($userIds as $userId) {
+ try {
+ $keys[$userId] = $this->getPublicKey($userId);
+ } catch (PublicKeyMissingException $e) {
+ continue;
+ }
+ }
+
+ return $keys;
+
+ }
+
+ /**
+ * @param string $keyId
+ * @return string returns openssl key
+ */
+ public function getSystemPrivateKey($keyId) {
+ return $this->keyStorage->getSystemUserKey($keyId . '.' . $this->privateKeyId);
+ }
+
+ /**
+ * @param string $keyId
+ * @param string $key
+ * @return string returns openssl key
+ */
+ public function setSystemPrivateKey($keyId, $key) {
+ return $this->keyStorage->setSystemUserKey($keyId . '.' . $this->privateKeyId, $key);
+ }
+
+ /**
+ * add system keys such as the public share key and the recovery key
+ *
+ * @param array $accessList
+ * @param array $publicKeys
+ * @return array
+ * @throws PublicKeyMissingException
+ */
+ public function addSystemKeys(array $accessList, array $publicKeys) {
+ if (!empty($accessList['public'])) {
+ $publicShareKey = $this->getPublicShareKey();
+ if (empty($publicShareKey)) {
+ throw new PublicKeyMissingException($this->getPublicShareKeyId());
+ }
+ $publicKeys[$this->getPublicShareKeyId()] = $publicShareKey;
+ }
+
+ if ($this->recoveryKeyExists() &&
+ $this->util->isRecoveryEnabledForUser()) {
+
+ $publicKeys[$this->getRecoveryKeyId()] = $this->getRecoveryKey();
+ }
+
+ return $publicKeys;
+ }
+}
diff --git a/apps/encryption/lib/recovery.php b/apps/encryption/lib/recovery.php
new file mode 100644
index 00000000000..34acdd0a6e3
--- /dev/null
+++ b/apps/encryption/lib/recovery.php
@@ -0,0 +1,316 @@
+<?php
+/**
+ * @author Clark Tomlinson <clark@owncloud.com>
+ * @since 2/19/15, 11:45 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 OCA\Encryption\Crypto\Crypt;
+use OCP\Encryption\Keys\IStorage;
+use OCP\IConfig;
+use OCP\IUser;
+use OCP\IUserSession;
+use OCP\PreConditionNotMetException;
+use OCP\Security\ISecureRandom;
+use OC\Files\View;
+use OCP\Encryption\IFile;
+
+class Recovery {
+
+
+ /**
+ * @var null|IUser
+ */
+ protected $user;
+ /**
+ * @var Crypt
+ */
+ protected $crypt;
+ /**
+ * @var ISecureRandom
+ */
+ private $random;
+ /**
+ * @var KeyManager
+ */
+ private $keyManager;
+ /**
+ * @var IConfig
+ */
+ private $config;
+ /**
+ * @var IStorage
+ */
+ private $keyStorage;
+ /**
+ * @var View
+ */
+ private $view;
+ /**
+ * @var IFile
+ */
+ private $file;
+ /**
+ * @var string
+ */
+ private $recoveryKeyId;
+
+ /**
+ * @param IUserSession $user
+ * @param Crypt $crypt
+ * @param ISecureRandom $random
+ * @param KeyManager $keyManager
+ * @param IConfig $config
+ * @param IStorage $keyStorage
+ * @param IFile $file
+ * @param View $view
+ */
+ public function __construct(IUserSession $user,
+ Crypt $crypt,
+ ISecureRandom $random,
+ KeyManager $keyManager,
+ IConfig $config,
+ IStorage $keyStorage,
+ IFile $file,
+ View $view) {
+ $this->user = ($user && $user->isLoggedIn()) ? $user->getUser() : false;
+ $this->crypt = $crypt;
+ $this->random = $random;
+ $this->keyManager = $keyManager;
+ $this->config = $config;
+ $this->keyStorage = $keyStorage;
+ $this->view = $view;
+ $this->file = $file;
+ }
+
+ /**
+ * @param $recoveryKeyId
+ * @param $password
+ * @return bool
+ */
+ public function enableAdminRecovery($password) {
+ $appConfig = $this->config;
+ $keyManager = $this->keyManager;
+
+ if (!$keyManager->recoveryKeyExists()) {
+ $keyPair = $this->crypt->createKeyPair();
+
+ $this->keyManager->setRecoveryKey($password, $keyPair);
+ }
+
+ if ($keyManager->checkRecoveryPassword($password)) {
+ $appConfig->setAppValue('encryption', 'recoveryAdminEnabled', 1);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * change recovery key id
+ *
+ * @param string $newPassword
+ * @param string $oldPassword
+ */
+ public function changeRecoveryKeyPassword($newPassword, $oldPassword) {
+ $recoveryKey = $this->keyManager->getSystemPrivateKey($this->keyManager->getRecoveryKeyId());
+ $decryptedRecoveryKey = $this->crypt->decryptPrivateKey($recoveryKey, $oldPassword);
+ $encryptedRecoveryKey = $this->crypt->symmetricEncryptFileContent($decryptedRecoveryKey, $newPassword);
+ if ($encryptedRecoveryKey) {
+ $this->keyManager->setSystemPrivateKey($this->keyManager->getRecoveryKeyId(), $encryptedRecoveryKey);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @param $recoveryPassword
+ * @return bool
+ */
+ public function disableAdminRecovery($recoveryPassword) {
+ $keyManager = $this->keyManager;
+
+ if ($keyManager->checkRecoveryPassword($recoveryPassword)) {
+ // Set recoveryAdmin as disabled
+ $this->config->setAppValue('encryption', 'recoveryAdminEnabled', 0);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * check if recovery is enabled for user
+ *
+ * @param string $user if no user is given we check the current logged-in user
+ *
+ * @return bool
+ */
+ public function isRecoveryEnabledForUser($user = '') {
+ $uid = empty($user) ? $this->user->getUID() : $user;
+ $recoveryMode = $this->config->getUserValue($uid,
+ 'encryption',
+ 'recoveryEnabled',
+ 0);
+
+ return ($recoveryMode === '1');
+ }
+
+ /**
+ * check if recovery is key is enabled by the administrator
+ *
+ * @return bool
+ */
+ public function isRecoveryKeyEnabled() {
+ $enabled = $this->config->getAppValue('encryption', 'recoveryAdminEnabled', 0);
+
+ return ($enabled === '1');
+ }
+
+ /**
+ * @param string $value
+ * @return bool
+ */
+ public function setRecoveryForUser($value) {
+
+ try {
+ $this->config->setUserValue($this->user->getUID(),
+ 'encryption',
+ 'recoveryEnabled',
+ $value);
+
+ if ($value === '1') {
+ $this->addRecoveryKeys('/' . $this->user->getUID() . '/files/');
+ } else {
+ $this->removeRecoveryKeys('/' . $this->user->getUID() . '/files/');
+ }
+
+ return true;
+ } catch (PreConditionNotMetException $e) {
+ return false;
+ }
+ }
+
+ /**
+ * add recovery key to all encrypted files
+ */
+ private function addRecoveryKeys($path) {
+ $dirContent = $this->view->getDirectoryContent($path);
+ foreach ($dirContent as $item) {
+ $filePath = $item->getPath();
+ if ($item['type'] === 'dir') {
+ $this->addRecoveryKeys($filePath . '/');
+ } else {
+ $fileKey = $this->keyManager->getFileKey($filePath, $this->user->getUID());
+ if (!empty($fileKey)) {
+ $accessList = $this->file->getAccessList($filePath);
+ $publicKeys = array();
+ foreach ($accessList['users'] as $uid) {
+ $publicKeys[$uid] = $this->keyManager->getPublicKey($uid);
+ }
+
+ $publicKeys = $this->keyManager->addSystemKeys($accessList, $publicKeys);
+
+ $encryptedKeyfiles = $this->crypt->multiKeyEncrypt($fileKey, $publicKeys);
+ $this->keyManager->setAllFileKeys($filePath, $encryptedKeyfiles);
+ }
+ }
+ }
+ }
+
+ /**
+ * remove recovery key to all encrypted files
+ */
+ private function removeRecoveryKeys($path) {
+ $dirContent = $this->view->getDirectoryContent($path);
+ foreach ($dirContent as $item) {
+ $filePath = $item->getPath();
+ if ($item['type'] === 'dir') {
+ $this->removeRecoveryKeys($filePath . '/');
+ } else {
+ $this->keyManager->deleteShareKey($filePath, $this->keyManager->getRecoveryKeyId());
+ }
+ }
+ }
+
+ /**
+ * recover users files with the recovery key
+ *
+ * @param string $recoveryPassword
+ * @param string $user
+ */
+ public function recoverUsersFiles($recoveryPassword, $user) {
+ $encryptedKey = $this->keyManager->getSystemPrivateKey($this->keyManager->getRecoveryKeyId());
+
+ $privateKey = $this->crypt->decryptPrivateKey($encryptedKey,
+ $recoveryPassword);
+
+ $this->recoverAllFiles('/' . $user . '/files/', $privateKey);
+ }
+
+ /**
+ * @param $path
+ * @param $privateKey
+ */
+ private function recoverAllFiles($path, $privateKey) {
+ $dirContent = $this->view->getDirectoryContent($path);
+
+ foreach ($dirContent as $item) {
+ // Get relative path from encryption/keyfiles
+ $filePath = $item->getPath();
+ if ($this->view->is_dir($filePath)) {
+ $this->recoverAllFiles($filePath . '/', $privateKey);
+ } else {
+ $this->recoverFile($filePath, $privateKey);
+ }
+ }
+
+ }
+
+ /**
+ * @param string $path
+ * @param string $privateKey
+ */
+ private function recoverFile($path, $privateKey) {
+ $encryptedFileKey = $this->keyManager->getEncryptedFileKey($path);
+ $shareKey = $this->keyManager->getShareKey($path, $this->keyManager->getRecoveryKeyId());
+
+ if ($encryptedFileKey && $shareKey && $privateKey) {
+ $fileKey = $this->crypt->multiKeyDecrypt($encryptedFileKey,
+ $shareKey,
+ $privateKey);
+ }
+
+ if (!empty($fileKey)) {
+ $accessList = $this->file->getAccessList($path);
+ $publicKeys = array();
+ foreach ($accessList['users'] as $uid) {
+ $publicKeys[$uid] = $this->keyManager->getPublicKey($uid);
+ }
+
+ $publicKeys = $this->keyManager->addSystemKeys($accessList, $publicKeys);
+
+ $encryptedKeyfiles = $this->crypt->multiKeyEncrypt($fileKey, $publicKeys);
+ $this->keyManager->setAllFileKeys($path, $encryptedKeyfiles);
+ }
+
+ }
+
+
+}
diff --git a/apps/encryption/lib/session.php b/apps/encryption/lib/session.php
new file mode 100644
index 00000000000..e705611fa6e
--- /dev/null
+++ b/apps/encryption/lib/session.php
@@ -0,0 +1,114 @@
+<?php
+
+/**
+ * ownCloud
+ *
+ * @copyright (C) 2015 ownCloud, Inc.
+ *
+ * @author Bjoern Schiessle <schiessle@owncloud.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace OCA\Encryption;
+
+use \OCP\ISession;
+
+class Session {
+
+ /** @var ISession */
+ protected $session;
+
+ const NOT_INITIALIZED = '0';
+ const INIT_EXECUTED = '1';
+ const INIT_SUCCESSFUL = '2';
+
+ public function __construct(ISession $session) {
+ $this->session = $session;
+ }
+
+ /**
+ * Sets status of encryption app
+ *
+ * @param string $status INIT_SUCCESSFUL, INIT_EXECUTED, NOT_INITIALIZED
+ */
+ public function setStatus($status) {
+ $this->session->set('encryptionInitialized', $status);
+ }
+
+ /**
+ * Gets status if we already tried to initialize the encryption app
+ *
+ * @return string init status INIT_SUCCESSFUL, INIT_EXECUTED, NOT_INITIALIZED
+ */
+ public function getStatus() {
+ $status = $this->session->get('encryptionInitialized');
+ if (is_null($status)) {
+ $status = self::NOT_INITIALIZED;
+ }
+
+ return $status;
+ }
+
+ /**
+ * Gets user or public share private key from session
+ *
+ * @return string $privateKey The user's plaintext private key
+ * @throws Exceptions\PrivateKeyMissingException
+ */
+ public function getPrivateKey() {
+ $key = $this->session->get('privateKey');
+ if (is_null($key)) {
+ throw new Exceptions\PrivateKeyMissingException('please try to log-out and log-in again', 0);
+ }
+ return $key;
+ }
+
+ /**
+ * check if private key is set
+ *
+ * @return boolean
+ */
+ public function isPrivateKeySet() {
+ $key = $this->session->get('privateKey');
+ if (is_null($key)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Sets user private key to session
+ *
+ * @param string $key users private key
+ *
+ * @note this should only be set on login
+ */
+ public function setPrivateKey($key) {
+ $this->session->set('privateKey', $key);
+ }
+
+
+ /**
+ * remove keys from session
+ */
+ public function clear() {
+ $this->session->remove('publicSharePrivateKey');
+ $this->session->remove('privateKey');
+ $this->session->remove('encryptionInitialized');
+
+ }
+
+}
diff --git a/apps/encryption/lib/users/setup.php b/apps/encryption/lib/users/setup.php
new file mode 100644
index 00000000000..e80bf6003e6
--- /dev/null
+++ b/apps/encryption/lib/users/setup.php
@@ -0,0 +1,72 @@
+<?php
+/**
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ * @since 3/6/15, 11:36 AM
+ * @link http:/www.clarkt.com
+ * @copyright Clark Tomlinson © 2015
+ *
+ */
+
+namespace OCA\Encryption\Users;
+
+
+use OCA\Encryption\Crypto\Crypt;
+use OCA\Encryption\KeyManager;
+use OCP\ILogger;
+use OCP\IUserSession;
+
+class Setup {
+ /**
+ * @var Crypt
+ */
+ private $crypt;
+ /**
+ * @var KeyManager
+ */
+ private $keyManager;
+ /**
+ * @var ILogger
+ */
+ private $logger;
+ /**
+ * @var bool|string
+ */
+ private $user;
+
+
+ /**
+ * @param ILogger $logger
+ * @param IUserSession $userSession
+ * @param Crypt $crypt
+ * @param KeyManager $keyManager
+ */
+ public function __construct(ILogger $logger, IUserSession $userSession, Crypt $crypt, KeyManager $keyManager) {
+ $this->logger = $logger;
+ $this->user = $userSession && $userSession->isLoggedIn() ? $userSession->getUser()->getUID() : false;
+ $this->crypt = $crypt;
+ $this->keyManager = $keyManager;
+ }
+
+ /**
+ * @param $uid userid
+ * @param $password user password
+ * @return bool
+ */
+ public function setupUser($uid, $password) {
+ return $this->setupServerSide($uid, $password);
+ }
+
+ /**
+ * @param $uid userid
+ * @param $password user password
+ * @return bool
+ */
+ public function setupServerSide($uid, $password) {
+ // Check if user already has keys
+ 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..6b6b8b6b38c
--- /dev/null
+++ b/apps/encryption/lib/util.php
@@ -0,0 +1,117 @@
+<?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\View;
+use OCA\Encryption\Crypto\Crypt;
+use OCP\IConfig;
+use OCP\ILogger;
+use OCP\IUser;
+use OCP\IUserSession;
+use OCP\PreConditionNotMetException;
+
+class Util {
+ /**
+ * @var View
+ */
+ private $files;
+ /**
+ * @var Crypt
+ */
+ private $crypt;
+ /**
+ * @var ILogger
+ */
+ private $logger;
+ /**
+ * @var bool|IUser
+ */
+ private $user;
+ /**
+ * @var IConfig
+ */
+ private $config;
+
+ /**
+ * Util constructor.
+ *
+ * @param View $files
+ * @param Crypt $crypt
+ * @param ILogger $logger
+ * @param IUserSession $userSession
+ * @param IConfig $config
+ */
+ public function __construct(View $files,
+ Crypt $crypt,
+ ILogger $logger,
+ IUserSession $userSession,
+ IConfig $config
+ ) {
+ $this->files = $files;
+ $this->crypt = $crypt;
+ $this->logger = $logger;
+ $this->user = $userSession && $userSession->isLoggedIn() ? $userSession->getUser() : false;
+ $this->config = $config;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isRecoveryEnabledForUser() {
+ $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 string $uid
+ * @return bool
+ */
+ public function userHasFiles($uid) {
+ return $this->files->file_exists($uid . '/files');
+ }
+
+
+}
diff --git a/apps/encryption/settings/settings-admin.php b/apps/encryption/settings/settings-admin.php
new file mode 100644
index 00000000000..36e9c532bbd
--- /dev/null
+++ b/apps/encryption/settings/settings-admin.php
@@ -0,0 +1,21 @@
+<?php
+/**
+ * Copyright (c) 2015 Clark Tomlinson <clark@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+\OC_Util::checkAdminUser();
+
+$tmpl = new OCP\Template('encryption', 'settings-admin');
+
+// Check if an adminRecovery account is enabled for recovering files after lost pwd
+$recoveryAdminEnabled = \OC::$server->getConfig()->getAppValue('encryption', 'recoveryAdminEnabled', '0');
+$session = new \OCA\Encryption\Session(\OC::$server->getSession());
+
+
+$tmpl->assign('recoveryEnabled', $recoveryAdminEnabled);
+$tmpl->assign('initStatus', $session->getStatus());
+
+return $tmpl->fetchPage();
diff --git a/apps/encryption/settings/settings-personal.php b/apps/encryption/settings/settings-personal.php
new file mode 100644
index 00000000000..ec3d30f457d
--- /dev/null
+++ b/apps/encryption/settings/settings-personal.php
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Copyright (c) 2015 Clark Tomlinson <clark@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+$session = new \OCA\Encryption\Session(\OC::$server->getSession());
+$userSession = \OC::$server->getUserSession();
+
+$template = new OCP\Template('encryption', 'settings-personal');
+$crypt = new \OCA\Encryption\Crypto\Crypt(
+ \OC::$server->getLogger(),
+ $userSession,
+ \OC::$server->getConfig());
+
+$util = new \OCA\Encryption\Util(
+ new \OC\Files\View(),
+ $crypt,
+ \OC::$server->getLogger(),
+ $userSession,
+ \OC::$server->getConfig());
+
+$keyManager = new \OCA\Encryption\KeyManager(
+ \OC::$server->getEncryptionKeyStorage(\OCA\Encryption\Crypto\Encryption::ID),
+ $crypt,
+ \OC::$server->getConfig(),
+ $userSession,
+ $session,
+ \OC::$server->getLogger(), $util);
+
+$user = $userSession->getUser()->getUID();
+
+$view = new \OC\Files\View('/');
+
+
+
+$privateKeySet = $session->isPrivateKeySet();
+// did we tried to initialize the keys for this session?
+$initialized = $session->getStatus();
+
+$recoveryAdminEnabled = \OC::$server->getConfig()->getAppValue('encryption', 'recoveryAdminEnabled');
+$recoveryEnabledForUser = $util->isRecoveryEnabledForUser();
+
+$result = false;
+
+if ($recoveryAdminEnabled || !$privateKeySet) {
+ $template->assign('recoveryEnabled', $recoveryAdminEnabled);
+ $template->assign('recoveryEnabledForUser', $recoveryEnabledForUser);
+ $template->assign('privateKeySet', $privateKeySet);
+ $template->assign('initialized', $initialized);
+
+ $result = $template->fetchPage();
+}
+
+return $result;
+
diff --git a/apps/encryption/templates/settings-admin.php b/apps/encryption/templates/settings-admin.php
new file mode 100644
index 00000000000..b64e75512e7
--- /dev/null
+++ b/apps/encryption/templates/settings-admin.php
@@ -0,0 +1,73 @@
+<?php
+/** @var array $_ */
+/** @var OC_L10N $l */
+script('encryption', 'settings-admin');
+script('core', 'multiselect');
+?>
+<form id="encryption" class="section">
+ <h2><?php p($l->t('ownCloud basic encryption module')); ?></h2>
+
+ <?php if(!$_["initStatus"]): ?>
+ <?php p($l->t("Encryption App is enabled but your keys are not initialized, please log-out and log-in again")); ?>
+ <?php else: ?>
+ <p id="encryptionSetRecoveryKey">
+ <?php p($l->t("Enable recovery key (allow to recover users files in case of password loss):")); ?>
+ <span class="msg"></span>
+ <br/>
+ <br/>
+ <input type="password" name="encryptionRecoveryPassword" id="encryptionRecoveryPassword"/>
+ <label for="recoveryPassword"><?php p($l->t("Recovery key password")); ?></label>
+ <br/>
+ <input type="password" name="encryptionRecoveryPassword" id="repeatEncryptionRecoveryPassword"/>
+ <label for="repeatEncryptionRecoveryPassword"><?php p($l->t("Repeat Recovery key password")); ?></label>
+ <br/>
+ <input
+ type='radio'
+ id='adminEnableRecovery'
+ name='adminEnableRecovery'
+ value='1'
+ <?php echo($_["recoveryEnabled"] === '1' ? 'checked="checked"' : ''); ?> />
+ <label for="adminEnableRecovery"><?php p($l->t("Enabled")); ?></label>
+ <br/>
+
+ <input
+ type='radio'
+ id='adminDisableRecovery'
+ name='adminEnableRecovery'
+ value='0'
+ <?php echo($_["recoveryEnabled"] === '0' ? 'checked="checked"' : ''); ?> />
+ <label for="adminDisableRecovery"><?php p($l->t("Disabled")); ?></label>
+ </p>
+ <br/><br/>
+
+ <p name="changeRecoveryPasswordBlock" id="encryptionChangeRecoveryKey" <?php if ($_['recoveryEnabled'] === '0') print_unescaped('class="hidden"');?>>
+ <strong><?php p($l->t("Change recovery key password:")); ?></strong>
+ <span class="msg"></span>
+ <br/><br/>
+ <input
+ type="password"
+ name="changeRecoveryPassword"
+ id="oldEncryptionRecoveryPassword" />
+ <label for="oldEncryptionRecoveryPassword"><?php p($l->t("Old Recovery key password")); ?></label>
+ <br/>
+ <br/>
+ <input
+ type="password"
+ name="changeRecoveryPassword"
+ id="newEncryptionRecoveryPassword" />
+ <label for="newEncryptionRecoveryPassword"><?php p($l->t("New Recovery key password")); ?></label>
+ <br/>
+ <input
+ type="password"
+ name="changeRecoveryPassword"
+ id="repeatedNewEncryptionRecoveryPassword" />
+ <label for="repeatEncryptionRecoveryPassword"><?php p($l->t("Repeat New Recovery key password")); ?></label>
+ <br/>
+ <button
+ type="button"
+ name="submitChangeRecoveryKey">
+ <?php p($l->t("Change Password")); ?>
+ </button>
+ </p>
+ <?php endif; ?>
+</form>
diff --git a/apps/encryption/templates/settings-personal.php b/apps/encryption/templates/settings-personal.php
new file mode 100644
index 00000000000..6b8821ca8a8
--- /dev/null
+++ b/apps/encryption/templates/settings-personal.php
@@ -0,0 +1,72 @@
+<?php
+ /** @var array $_ */
+ /** @var OC_L10N $l */
+script('encryption', 'settings-personal');
+script('core', 'multiselect');
+?>
+<form id="encryption" class="section">
+ <h2><?php p($l->t('ownCloud basic encryption module')); ?></h2>
+
+ <?php if ($_["initialized"] === \OCA\Encryption\Session::NOT_INITIALIZED ): ?>
+
+ <?php p($l->t("Encryption App is enabled but your keys are not initialized, please log-out and log-in again")); ?>
+
+ <?php elseif ( $_["initialized"] === \OCA\Encryption\Session::INIT_EXECUTED ): ?>
+ <p>
+ <a name="changePKPasswd" />
+ <label for="changePrivateKeyPasswd">
+ <em><?php p( $l->t( "Your private key password no longer matches your log-in password." ) ); ?></em>
+ </label>
+ <br />
+ <?php p( $l->t( "Set your old private key password to your current log-in password:" ) ); ?>
+ <?php if ( $_["recoveryEnabledForUser"] ):
+ p( $l->t( " If you don't remember your old password you can ask your administrator to recover your files." ) );
+ endif; ?>
+ <br />
+ <input
+ type="password"
+ name="changePrivateKeyPassword"
+ id="oldPrivateKeyPassword" />
+ <label for="oldPrivateKeyPassword"><?php p($l->t( "Old log-in password" )); ?></label>
+ <br />
+ <input
+ type="password"
+ name="changePrivateKeyPassword"
+ id="newPrivateKeyPassword" />
+ <label for="newRecoveryPassword"><?php p($l->t( "Current log-in password" )); ?></label>
+ <br />
+ <button
+ type="button"
+ name="submitChangePrivateKeyPassword"
+ disabled><?php p($l->t( "Update Private Key Password" )); ?>
+ </button>
+ <span class="msg"></span>
+ </p>
+
+ <?php elseif ( $_["recoveryEnabled"] && $_["privateKeySet"] && $_["initialized"] === \OCA\Encryption\Session::INIT_SUCCESSFUL ): ?>
+ <br />
+ <p id="userEnableRecovery">
+ <label for="userEnableRecovery"><?php p( $l->t( "Enable password recovery:" ) ); ?></label>
+ <span class="msg"></span>
+ <br />
+ <em><?php p( $l->t( "Enabling this option will allow you to reobtain access to your encrypted files in case of password loss" ) ); ?></em>
+ <br />
+ <input
+ type='radio'
+ id='userEnableRecovery'
+ name='userEnableRecovery'
+ value='1'
+ <?php echo ( $_["recoveryEnabledForUser"] ? 'checked="checked"' : '' ); ?> />
+ <label for="userEnableRecovery"><?php p( $l->t( "Enabled" ) ); ?></label>
+ <br />
+
+ <input
+ type='radio'
+ id='userDisableRecovery'
+ name='userEnableRecovery'
+ value='0'
+ <?php echo ( $_["recoveryEnabledForUser"] === false ? 'checked="checked"' : '' ); ?> />
+ <label for="userDisableRecovery"><?php p( $l->t( "Disabled" ) ); ?></label>
+ </p>
+ <?php endif; ?>
+</form>
diff --git a/apps/encryption/tests/lib/HookManagerTest.php b/apps/encryption/tests/lib/HookManagerTest.php
new file mode 100644
index 00000000000..3c360ff3504
--- /dev/null
+++ b/apps/encryption/tests/lib/HookManagerTest.php
@@ -0,0 +1,74 @@
+<?php
+/**
+ * @author Clark Tomlinson <clark@owncloud.com>
+ * @since 3/31/15, 1:54 PM
+ * @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\Tests;
+
+
+use OCA\Encryption\HookManager;
+use Test\TestCase;
+
+class HookManagerTest extends TestCase {
+
+ /**
+ * @var HookManager
+ */
+ private static $instance;
+
+ /**
+ *
+ */
+ public function testRegisterHookWithArray() {
+ self::$instance->registerHook([
+ $this->getMockBuilder('OCA\Encryption\Hooks\Contracts\IHook')->disableOriginalConstructor()->getMock(),
+ $this->getMockBuilder('OCA\Encryption\Hooks\Contracts\IHook')->disableOriginalConstructor()->getMock(),
+ $this->getMock('NotIHook')
+ ]);
+
+ $hookInstances = \Test_Helper::invokePrivate(self::$instance, 'hookInstances');
+ // Make sure our type checking works
+ $this->assertCount(2, $hookInstances);
+ }
+
+
+ /**
+ *
+ */
+ public static function setUpBeforeClass() {
+ parent::setUpBeforeClass();
+ // have to make instance static to preserve data between tests
+ self::$instance = new HookManager();
+
+ }
+
+ /**
+ *
+ */
+ public function testRegisterHooksWithInstance() {
+ $mock = $this->getMockBuilder('OCA\Encryption\Hooks\Contracts\IHook')->disableOriginalConstructor()->getMock();
+ self::$instance->registerHook($mock);
+
+ $hookInstances = \Test_Helper::invokePrivate(self::$instance, 'hookInstances');
+ $this->assertCount(3, $hookInstances);
+
+ }
+
+}
diff --git a/apps/encryption/tests/lib/KeyManagerTest.php b/apps/encryption/tests/lib/KeyManagerTest.php
new file mode 100644
index 00000000000..d12578bb8d2
--- /dev/null
+++ b/apps/encryption/tests/lib/KeyManagerTest.php
@@ -0,0 +1,286 @@
+<?php
+/**
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ * @since 3/5/15, 10:53 AM
+ * @link http:/www.clarkt.com
+ * @copyright Clark Tomlinson © 2015
+ *
+ */
+
+namespace OCA\Encryption\Tests;
+
+
+use OCA\Encryption\KeyManager;
+use Test\TestCase;
+
+class KeyManagerTest extends TestCase {
+ /**
+ * @var KeyManager
+ */
+ private $instance;
+ /**
+ * @var string
+ */
+ private $userId;
+
+ /** @var string */
+ private $systemKeyId;
+
+ /** @var \PHPUnit_Framework_MockObject_MockObject */
+ private $keyStorageMock;
+
+ /** @var \PHPUnit_Framework_MockObject_MockObject */
+ private $cryptMock;
+
+ /** @var \PHPUnit_Framework_MockObject_MockObject */
+ private $userMock;
+
+ /** @var \PHPUnit_Framework_MockObject_MockObject */
+ private $sessionMock;
+
+ /** @var \PHPUnit_Framework_MockObject_MockObject */
+ private $logMock;
+
+ /** @var \PHPUnit_Framework_MockObject_MockObject */
+ private $utilMock;
+
+ /** @var \PHPUnit_Framework_MockObject_MockObject */
+ private $configMock;
+
+ public function setUp() {
+ parent::setUp();
+ $this->userId = 'user1';
+ $this->systemKeyId = 'systemKeyId';
+ $this->keyStorageMock = $this->getMock('OCP\Encryption\Keys\IStorage');
+ $this->cryptMock = $this->getMockBuilder('OCA\Encryption\Crypto\Crypt')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->configMock = $this->getMock('OCP\IConfig');
+ $this->configMock->expects($this->any())
+ ->method('getAppValue')
+ ->willReturn($this->systemKeyId);
+ $this->userMock = $this->getMock('OCP\IUserSession');
+ $this->sessionMock = $this->getMockBuilder('OCA\Encryption\Session')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->logMock = $this->getMock('OCP\ILogger');
+ $this->utilMock = $this->getMockBuilder('OCA\Encryption\Util')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->instance = new KeyManager(
+ $this->keyStorageMock,
+ $this->cryptMock,
+ $this->configMock,
+ $this->userMock,
+ $this->sessionMock,
+ $this->logMock,
+ $this->utilMock);
+ }
+
+ public function testDeleteShareKey() {
+ $this->keyStorageMock->expects($this->any())
+ ->method('deleteFileKey')
+ ->with($this->equalTo('/path'), $this->equalTo('keyId.shareKey'))
+ ->willReturn(true);
+
+ $this->assertTrue(
+ $this->instance->deleteShareKey('/path', 'keyId')
+ );
+ }
+
+ public function testGetPrivateKey() {
+ $this->keyStorageMock->expects($this->any())
+ ->method('getUserKey')
+ ->with($this->equalTo($this->userId), $this->equalTo('privateKey'))
+ ->willReturn('privateKey');
+
+
+ $this->assertSame('privateKey',
+ $this->instance->getPrivateKey($this->userId)
+ );
+ }
+
+ public function testGetPublicKey() {
+ $this->keyStorageMock->expects($this->any())
+ ->method('getUserKey')
+ ->with($this->equalTo($this->userId), $this->equalTo('publicKey'))
+ ->willReturn('publicKey');
+
+
+ $this->assertSame('publicKey',
+ $this->instance->getPublicKey($this->userId)
+ );
+ }
+
+ public function testRecoveryKeyExists() {
+ $this->keyStorageMock->expects($this->any())
+ ->method('getSystemUserKey')
+ ->with($this->equalTo($this->systemKeyId . '.publicKey'))
+ ->willReturn('recoveryKey');
+
+
+ $this->assertTrue($this->instance->recoveryKeyExists());
+ }
+
+ public function testCheckRecoveryKeyPassword() {
+ $this->keyStorageMock->expects($this->any())
+ ->method('getSystemUserKey')
+ ->with($this->equalTo($this->systemKeyId . '.privateKey'))
+ ->willReturn('recoveryKey');
+ $this->cryptMock->expects($this->any())
+ ->method('decryptPrivateKey')
+ ->with($this->equalTo('recoveryKey'), $this->equalTo('pass'))
+ ->willReturn('decryptedRecoveryKey');
+
+ $this->assertTrue($this->instance->checkRecoveryPassword('pass'));
+ }
+
+ public function testSetPublicKey() {
+ $this->keyStorageMock->expects($this->any())
+ ->method('setUserKey')
+ ->with(
+ $this->equalTo($this->userId),
+ $this->equalTo('publicKey'),
+ $this->equalTo('key'))
+ ->willReturn(true);
+
+
+ $this->assertTrue(
+ $this->instance->setPublicKey($this->userId, 'key')
+ );
+ }
+
+ public function testSetPrivateKey() {
+ $this->keyStorageMock->expects($this->any())
+ ->method('setUserKey')
+ ->with(
+ $this->equalTo($this->userId),
+ $this->equalTo('privateKey'),
+ $this->equalTo('key'))
+ ->willReturn(true);
+
+
+ $this->assertTrue(
+ $this->instance->setPrivateKey($this->userId, 'key')
+ );
+ }
+
+ public function testUserHasKeys() {
+ $this->keyStorageMock->expects($this->exactly(2))
+ ->method('getUserKey')
+ ->with($this->equalTo($this->userId), $this->anything())
+ ->willReturn('key');
+
+
+ $this->assertTrue(
+ $this->instance->userHasKeys($this->userId)
+ );
+ }
+
+ public function testInit() {
+ $this->keyStorageMock->expects($this->any())
+ ->method('getUserKey')
+ ->with($this->equalTo($this->userId), $this->equalTo('privateKey'))
+ ->willReturn('privateKey');
+ $this->cryptMock->expects($this->any())
+ ->method('decryptPrivateKey')
+ ->with($this->equalTo('privateKey'), $this->equalTo('pass'))
+ ->willReturn('decryptedPrivateKey');
+
+
+ $this->assertTrue(
+ $this->instance->init($this->userId, 'pass')
+ );
+
+ }
+
+ public function testSetRecoveryKey() {
+ $this->keyStorageMock->expects($this->exactly(2))
+ ->method('setSystemUserKey')
+ ->willReturn(true);
+ $this->cryptMock->expects($this->any())
+ ->method('symmetricEncryptFileContent')
+ ->with($this->equalTo('privateKey'), $this->equalTo('pass'))
+ ->willReturn('decryptedPrivateKey');
+
+
+ $this->assertTrue(
+ $this->instance->setRecoveryKey('pass',
+ array('publicKey' => 'publicKey', 'privateKey' => 'privateKey'))
+ );
+ }
+
+ public function testSetSystemPrivateKey() {
+ $this->keyStorageMock->expects($this->exactly(1))
+ ->method('setSystemUserKey')
+ ->with($this->equalTo('keyId.privateKey'), $this->equalTo('key'))
+ ->willReturn(true);
+
+
+ $this->assertTrue(
+ $this->instance->setSystemPrivateKey('keyId', 'key')
+ );
+ }
+
+ public function testGetSystemPrivateKey() {
+ $this->keyStorageMock->expects($this->exactly(1))
+ ->method('getSystemUserKey')
+ ->with($this->equalTo('keyId.privateKey'))
+ ->willReturn('systemPrivateKey');
+
+
+ $this->assertSame('systemPrivateKey',
+ $this->instance->getSystemPrivateKey('keyId')
+ );
+ }
+
+ public function testGetEncryptedFileKey() {
+ $this->keyStorageMock->expects($this->once())
+ ->method('getFileKey')
+ ->with('/', 'fileKey')
+ ->willReturn(true);
+
+ $this->assertTrue($this->instance->getEncryptedFileKey('/'));
+ }
+
+ public function testGetFileKey() {
+ $this->keyStorageMock->expects($this->exactly(4))
+ ->method('getFileKey')
+ ->willReturn(true);
+
+ $this->keyStorageMock->expects($this->once())
+ ->method('getSystemUserKey')
+ ->willReturn(true);
+
+ $this->cryptMock->expects($this->once())
+ ->method('symmetricDecryptFileContent')
+ ->willReturn(true);
+
+ $this->cryptMock->expects($this->once())
+ ->method('multiKeyDecrypt')
+ ->willReturn(true);
+
+ $this->assertTrue($this->instance->getFileKey('/', null));
+ $this->assertEmpty($this->instance->getFileKey('/', $this->userId));
+ }
+
+ public function testDeletePrivateKey() {
+ $this->keyStorageMock->expects($this->once())
+ ->method('deleteUserKey')
+ ->with('user1', 'privateKey')
+ ->willReturn(true);
+
+ $this->assertTrue(\Test_Helper::invokePrivate($this->instance,
+ 'deletePrivateKey',
+ [$this->userId]));
+ }
+
+ public function testDeleteAllFileKeys() {
+ $this->keyStorageMock->expects($this->once())
+ ->method('deleteAllFileKeys')
+ ->willReturn(true);
+
+ $this->assertTrue($this->instance->deleteAllFileKeys('/'));
+ }
+}
diff --git a/apps/encryption/tests/lib/RecoveryTest.php b/apps/encryption/tests/lib/RecoveryTest.php
new file mode 100644
index 00000000000..701762b56d6
--- /dev/null
+++ b/apps/encryption/tests/lib/RecoveryTest.php
@@ -0,0 +1,265 @@
+<?php
+/**
+ * @author Clark Tomlinson <clark@owncloud.com>
+ * @since 4/3/15, 9:57 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\Tests;
+
+
+use OCA\Encryption\Recovery;
+use Test\TestCase;
+
+class RecoveryTest extends TestCase {
+ private static $tempStorage = [];
+ /**
+ * @var \PHPUnit_Framework_MockObject_MockObject
+ */
+ private $fileMock;
+ /**
+ * @var \PHPUnit_Framework_MockObject_MockObject
+ */
+ private $viewMock;
+ /**
+ * @var \PHPUnit_Framework_MockObject_MockObject
+ */
+ private $userSessionMock;
+ /**
+ * @var \PHPUnit_Framework_MockObject_MockObject
+ */
+ private $keyManagerMock;
+ /**
+ * @var \PHPUnit_Framework_MockObject_MockObject
+ */
+ private $configMock;
+ /**
+ * @var \PHPUnit_Framework_MockObject_MockObject
+ */
+ private $cryptMock;
+ /**
+ * @var Recovery
+ */
+ private $instance;
+
+ public function testEnableAdminRecovery() {
+ $this->keyManagerMock->expects($this->exactly(2))
+ ->method('recoveryKeyExists')
+ ->willReturnOnConsecutiveCalls(false, true);
+
+ $this->cryptMock->expects($this->once())
+ ->method('createKeyPair')
+ ->willReturn(true);
+
+ $this->keyManagerMock->expects($this->once())
+ ->method('setRecoveryKey')
+ ->willReturn(false);
+
+ $this->keyManagerMock->expects($this->exactly(2))
+ ->method('checkRecoveryPassword')
+ ->willReturnOnConsecutiveCalls(true, false);
+
+ $this->assertTrue($this->instance->enableAdminRecovery('password'));
+ $this->assertArrayHasKey('recoveryAdminEnabled', self::$tempStorage);
+ $this->assertEquals(1, self::$tempStorage['recoveryAdminEnabled']);
+
+ $this->assertFalse($this->instance->enableAdminRecovery('password'));
+ }
+
+ public function testChangeRecoveryKeyPassword() {
+ $this->assertFalse($this->instance->changeRecoveryKeyPassword('password',
+ 'passwordOld'));
+
+ $this->keyManagerMock->expects($this->once())
+ ->method('getSystemPrivateKey');
+
+ $this->cryptMock->expects($this->once())
+ ->method('decryptPrivateKey');
+
+ $this->cryptMock->expects($this->once())
+ ->method('symmetricEncryptFileContent')
+ ->willReturn(true);
+
+ $this->assertTrue($this->instance->changeRecoveryKeyPassword('password',
+ 'passwordOld'));
+ }
+
+ public function testDisableAdminRecovery() {
+
+ $this->keyManagerMock->expects($this->exactly(2))
+ ->method('checkRecoveryPassword')
+ ->willReturnOnConsecutiveCalls(true, false);
+
+ $this->assertArrayHasKey('recoveryAdminEnabled', self::$tempStorage);
+ $this->assertTrue($this->instance->disableAdminRecovery('password'));
+ $this->assertEquals(0, self::$tempStorage['recoveryAdminEnabled']);
+
+ $this->assertFalse($this->instance->disableAdminRecovery('password'));
+ }
+
+ public function testIsRecoveryEnabledForUser() {
+
+ $this->configMock->expects($this->exactly(2))
+ ->method('getUserValue')
+ ->willReturnOnConsecutiveCalls('1', '0');
+
+ $this->assertTrue($this->instance->isRecoveryEnabledForUser());
+ $this->assertFalse($this->instance->isRecoveryEnabledForUser('admin'));
+ }
+
+ public function testIsRecoveryKeyEnabled() {
+ $this->assertFalse($this->instance->isRecoveryKeyEnabled());
+ self::$tempStorage['recoveryAdminEnabled'] = '1';
+ $this->assertTrue($this->instance->isRecoveryKeyEnabled());
+ }
+
+ public function testSetRecoveryFolderForUser() {
+ $this->viewMock->expects($this->exactly(2))
+ ->method('getDirectoryContent')
+ ->willReturn([]);
+ $this->assertTrue($this->instance->setRecoveryForUser(0));
+ $this->assertTrue($this->instance->setRecoveryForUser('1'));
+ }
+
+ public function testRecoverUserFiles() {
+ $this->viewMock->expects($this->once())
+ ->method('getDirectoryContent')
+ ->willReturn([]);
+
+ $this->cryptMock->expects($this->once())
+ ->method('decryptPrivateKey');
+ $this->assertNull($this->instance->recoverUsersFiles('password',
+ 'admin'));
+ }
+
+ public function testRecoverFile() {
+ $this->keyManagerMock->expects($this->once())
+ ->method('getEncryptedFileKey')
+ ->willReturn(true);
+
+ $this->keyManagerMock->expects($this->once())
+ ->method('getShareKey')
+ ->willReturn(true);
+
+ $this->cryptMock->expects($this->once())
+ ->method('multiKeyDecrypt')
+ ->willReturn(true);
+
+ $this->fileMock->expects($this->once())
+ ->method('getAccessList')
+ ->willReturn(['users' => ['admin']]);
+
+ $this->keyManagerMock->expects($this->once())
+ ->method('getPublicKey')
+ ->willReturn('publicKey');
+
+ $this->keyManagerMock->expects($this->once())
+ ->method('addSystemKeys')
+ ->willReturn(['admin' => 'publicKey']);
+
+
+ $this->cryptMock->expects($this->once())
+ ->method('multiKeyEncrypt');
+
+ $this->keyManagerMock->expects($this->once())
+ ->method('setAllFileKeys');
+
+ $this->assertNull(\Test_Helper::invokePrivate($this->instance,
+ 'recoverFile',
+ ['/', 'testkey']));
+ }
+
+ protected function setUp() {
+ parent::setUp();
+
+
+ $this->userSessionMock = $this->getMockBuilder('OCP\IUserSession')
+ ->disableOriginalConstructor()
+ ->setMethods([
+ 'isLoggedIn',
+ 'getUID',
+ 'login',
+ 'logout',
+ 'setUser',
+ 'getUser'
+ ])
+ ->getMock();
+
+ $this->userSessionMock->expects($this->any())->method('getUID')->will($this->returnValue('admin'));
+
+ $this->userSessionMock->expects($this->any())
+ ->method($this->anything())
+ ->will($this->returnSelf());
+
+ $this->cryptMock = $this->getMockBuilder('OCA\Encryption\Crypto\Crypt')->disableOriginalConstructor()->getMock();
+ $randomMock = $this->getMock('OCP\Security\ISecureRandom');
+ $this->keyManagerMock = $this->getMockBuilder('OCA\Encryption\KeyManager')->disableOriginalConstructor()->getMock();
+ $this->configMock = $this->getMock('OCP\IConfig');
+ $keyStorageMock = $this->getMock('OCP\Encryption\Keys\IStorage');
+ $this->fileMock = $this->getMock('OCP\Encryption\IFile');
+ $this->viewMock = $this->getMock('OC\Files\View');
+
+ $this->configMock->expects($this->any())
+ ->method('setAppValue')
+ ->will($this->returnCallback([$this, 'setValueTester']));
+
+ $this->configMock->expects($this->any())
+ ->method('getAppValue')
+ ->will($this->returnCallback([$this, 'getValueTester']));
+
+ $this->instance = new Recovery($this->userSessionMock,
+ $this->cryptMock,
+ $randomMock,
+ $this->keyManagerMock,
+ $this->configMock,
+ $keyStorageMock,
+ $this->fileMock,
+ $this->viewMock);
+ }
+
+
+ /**
+ * @param $app
+ * @param $key
+ * @param $value
+ */
+ public function setValueTester($app, $key, $value) {
+ self::$tempStorage[$key] = $value;
+ }
+
+ /**
+ * @param $key
+ */
+ public function removeValueTester($key) {
+ unset(self::$tempStorage[$key]);
+ }
+
+ /**
+ * @param $app
+ * @param $key
+ * @return mixed
+ */
+ public function getValueTester($app, $key) {
+ if (!empty(self::$tempStorage[$key])) {
+ return self::$tempStorage[$key];
+ }
+ return null;
+ }
+
+
+}
diff --git a/apps/encryption/tests/lib/SessionTest.php b/apps/encryption/tests/lib/SessionTest.php
new file mode 100644
index 00000000000..f7e026808f0
--- /dev/null
+++ b/apps/encryption/tests/lib/SessionTest.php
@@ -0,0 +1,140 @@
+<?php
+/**
+ * @author Clark Tomlinson <clark@owncloud.com>
+ * @since 3/31/15, 10:19 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\Tests;
+
+
+use OCA\Encryption\Session;
+use Test\TestCase;
+
+class SessionTest extends TestCase {
+ private static $tempStorage = [];
+ /**
+ * @var Session
+ */
+ private $instance;
+ private $sessionMock;
+
+ /**
+ * @expectedException \OCA\Encryption\Exceptions\PrivateKeyMissingException
+ * @expectedExceptionMessage Private Key missing for user: please try to log-out and log-in again
+ */
+ public function testThatGetPrivateKeyThrowsExceptionWhenNotSet() {
+ $this->instance->getPrivateKey();
+ }
+
+ /**
+ * @depends testThatGetPrivateKeyThrowsExceptionWhenNotSet
+ */
+ public function testSetAndGetPrivateKey() {
+ $this->instance->setPrivateKey('dummyPrivateKey');
+ $this->assertEquals('dummyPrivateKey', $this->instance->getPrivateKey());
+
+ }
+
+ /**
+ * @depends testSetAndGetPrivateKey
+ */
+ public function testIsPrivateKeySet() {
+ $this->assertTrue($this->instance->isPrivateKeySet());
+
+ unset(self::$tempStorage['privateKey']);
+ $this->assertFalse($this->instance->isPrivateKeySet());
+
+ // Set private key back so we can test clear method
+ self::$tempStorage['privateKey'] = 'dummyPrivateKey';
+ }
+
+ /**
+ *
+ */
+ public function testSetAndGetStatusWillSetAndReturn() {
+ // Check if get status will return 0 if it has not been set before
+ $this->assertEquals(0, $this->instance->getStatus());
+
+ $this->instance->setStatus(Session::NOT_INITIALIZED);
+ $this->assertEquals(0, $this->instance->getStatus());
+
+ $this->instance->setStatus(Session::INIT_EXECUTED);
+ $this->assertEquals(1, $this->instance->getStatus());
+
+ $this->instance->setStatus(Session::INIT_SUCCESSFUL);
+ $this->assertEquals(2, $this->instance->getStatus());
+ }
+
+ /**
+ * @param $key
+ * @param $value
+ */
+ public function setValueTester($key, $value) {
+ self::$tempStorage[$key] = $value;
+ }
+
+ /**
+ * @param $key
+ */
+ public function removeValueTester($key) {
+ unset(self::$tempStorage[$key]);
+ }
+
+ /**
+ * @param $key
+ * @return mixed
+ */
+ public function getValueTester($key) {
+ if (!empty(self::$tempStorage[$key])) {
+ return self::$tempStorage[$key];
+ }
+ return null;
+ }
+
+ /**
+ *
+ */
+ public function testClearWillRemoveValues() {
+ $this->instance->clear();
+ $this->assertEmpty(self::$tempStorage);
+ }
+
+ /**
+ *
+ */
+ protected function setUp() {
+ parent::setUp();
+ $this->sessionMock = $this->getMock('OCP\ISession');
+
+ $this->sessionMock->expects($this->any())
+ ->method('set')
+ ->will($this->returnCallback([$this, "setValueTester"]));
+
+ $this->sessionMock->expects($this->any())
+ ->method('get')
+ ->will($this->returnCallback([$this, "getValueTester"]));
+
+ $this->sessionMock->expects($this->any())
+ ->method('remove')
+ ->will($this->returnCallback([$this, "removeValueTester"]));
+
+
+ $this->instance = new Session($this->sessionMock);
+ }
+}
diff --git a/apps/encryption/tests/lib/UtilTest.php b/apps/encryption/tests/lib/UtilTest.php
new file mode 100644
index 00000000000..fa87a629f2f
--- /dev/null
+++ b/apps/encryption/tests/lib/UtilTest.php
@@ -0,0 +1,128 @@
+<?php
+/**
+ * @author Clark Tomlinson <clark@owncloud.com>
+ * @since 3/31/15, 3:49 PM
+ * @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\Tests;
+
+
+use OCA\Encryption\Util;
+use Test\TestCase;
+
+class UtilTest extends TestCase {
+ private static $tempStorage = [];
+ private $configMock;
+ private $filesMock;
+ /**
+ * @var Util
+ */
+ private $instance;
+
+ public function testSetRecoveryForUser() {
+ $this->instance->setRecoveryForUser('1');
+ $this->assertArrayHasKey('recoveryEnabled', self::$tempStorage);
+ }
+
+ /**
+ *
+ */
+ public function testIsRecoveryEnabledForUser() {
+ $this->assertTrue($this->instance->isRecoveryEnabledForUser());
+
+ // Assert recovery will return default value if not set
+ unset(self::$tempStorage['recoveryEnabled']);
+ $this->assertEquals(0, $this->instance->isRecoveryEnabledForUser());
+ }
+
+ public function testUserHasFiles() {
+ $this->filesMock->expects($this->once())
+ ->method('file_exists')
+ ->will($this->returnValue(true));
+
+ $this->assertTrue($this->instance->userHasFiles('admin'));
+ }
+
+ protected function setUp() {
+ parent::setUp();
+ $this->filesMock = $this->getMock('OC\Files\View');
+
+ $cryptMock = $this->getMockBuilder('OCA\Encryption\Crypto\Crypt')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $loggerMock = $this->getMock('OCP\ILogger');
+ $userSessionMock = $this->getMockBuilder('OCP\IUserSession')
+ ->disableOriginalConstructor()
+ ->setMethods([
+ 'isLoggedIn',
+ 'getUID',
+ 'login',
+ 'logout',
+ 'setUser',
+ 'getUser'
+ ])
+ ->getMock();
+
+ $userSessionMock->method('isLoggedIn')->will($this->returnValue(true));
+
+ $userSessionMock->method('getUID')->will($this->returnValue('admin'));
+
+ $userSessionMock->expects($this->any())
+ ->method($this->anything())
+ ->will($this->returnSelf());
+
+
+ $this->configMock = $configMock = $this->getMock('OCP\IConfig');
+
+ $this->configMock->expects($this->any())
+ ->method('getUserValue')
+ ->will($this->returnCallback([$this, 'getValueTester']));
+
+ $this->configMock->expects($this->any())
+ ->method('setUserValue')
+ ->will($this->returnCallback([$this, 'setValueTester']));
+
+ $this->instance = new Util($this->filesMock, $cryptMock, $loggerMock, $userSessionMock, $configMock);
+ }
+
+ /**
+ * @param $userId
+ * @param $app
+ * @param $key
+ * @param $value
+ */
+ public function setValueTester($userId, $app, $key, $value) {
+ self::$tempStorage[$key] = $value;
+ }
+
+ /**
+ * @param $userId
+ * @param $app
+ * @param $key
+ * @param $default
+ * @return mixed
+ */
+ public function getValueTester($userId, $app, $key, $default) {
+ if (!empty(self::$tempStorage[$key])) {
+ return self::$tempStorage[$key];
+ }
+ return $default ?: null;
+ }
+
+}
diff --git a/apps/encryption/tests/lib/crypto/encryptionTest.php b/apps/encryption/tests/lib/crypto/encryptionTest.php
new file mode 100644
index 00000000000..52a322463a9
--- /dev/null
+++ b/apps/encryption/tests/lib/crypto/encryptionTest.php
@@ -0,0 +1,78 @@
+<?php
+
+/**
+ * ownCloud
+ *
+ * @copyright (C) 2015 ownCloud, Inc.
+ *
+ * @author Bjoern Schiessle <schiessle@owncloud.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace OCA\Encryption\Tests\Crypto;
+
+use Test\TestCase;
+use OCA\Encryption\Crypto\Encryption;
+
+class EncryptionTest extends TestCase {
+
+ /** @var Encryption */
+ private $instance;
+
+ /** @var \PHPUnit_Framework_MockObject_MockObject */
+ private $keyManagerMock;
+
+ /** @var \PHPUnit_Framework_MockObject_MockObject */
+ private $cryptMock;
+
+ /** @var \PHPUnit_Framework_MockObject_MockObject */
+ private $utilMock;
+
+ public function setUp() {
+ parent::setUp();
+
+ $this->cryptMock = $this->getMockBuilder('OCA\Encryption\Crypto\Crypt')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->utilMock = $this->getMockBuilder('OCA\Encryption\Util')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->keyManagerMock = $this->getMockBuilder('OCA\Encryption\KeyManager')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->instance = new Encryption($this->cryptMock, $this->keyManagerMock, $this->utilMock);
+ }
+
+ /**
+ * @dataProvider dataProviderForTestGetPathToRealFile
+ */
+ public function testGetPathToRealFile($path, $expected) {
+ $this->assertSame($expected,
+ \Test_Helper::invokePrivate($this->instance, 'getPathToRealFile', array($path))
+ );
+ }
+
+ public function dataProviderForTestGetPathToRealFile() {
+ return array(
+ array('/user/files/foo/bar.txt', '/user/files/foo/bar.txt'),
+ array('/user/files/foo.txt', '/user/files/foo.txt'),
+ array('/user/files_versions/foo.txt.v543534', '/user/files/foo.txt'),
+ array('/user/files_versions/foo/bar.txt.v5454', '/user/files/foo/bar.txt'),
+ );
+ }
+
+
+} \ No newline at end of file
diff --git a/apps/encryption/tests/lib/users/SetupTest.php b/apps/encryption/tests/lib/users/SetupTest.php
new file mode 100644
index 00000000000..6a66df3674b
--- /dev/null
+++ b/apps/encryption/tests/lib/users/SetupTest.php
@@ -0,0 +1,81 @@
+<?php
+/**
+ * @author Clark Tomlinson <clark@owncloud.com>
+ * @since 4/6/15, 11:50 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\Tests\Users;
+
+
+use OCA\Encryption\Users\Setup;
+use Test\TestCase;
+
+class SetupTest extends TestCase {
+ /**
+ * @var \PHPUnit_Framework_MockObject_MockObject
+ */
+ private $keyManagerMock;
+ /**
+ * @var \PHPUnit_Framework_MockObject_MockObject
+ */
+ private $cryptMock;
+ /**
+ * @var Setup
+ */
+ private $instance;
+
+ public function testSetupServerSide() {
+ $this->keyManagerMock->expects($this->exactly(2))
+ ->method('userHasKeys')
+ ->with('admin')
+ ->willReturnOnConsecutiveCalls(true, false);
+
+ $this->assertTrue($this->instance->setupServerSide('admin',
+ 'password'));
+
+ $this->keyManagerMock->expects($this->once())
+ ->method('storeKeyPair')
+ ->with('admin', 'password')
+ ->willReturn(false);
+
+ $this->assertFalse($this->instance->setupServerSide('admin',
+ 'password'));
+ }
+
+ protected function setUp() {
+ parent::setUp();
+ $logMock = $this->getMock('OCP\ILogger');
+ $userSessionMock = $this->getMockBuilder('OCP\IUserSession')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->cryptMock = $this->getMockBuilder('OCA\Encryption\Crypto\Crypt')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->keyManagerMock = $this->getMockBuilder('OCA\Encryption\KeyManager')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->instance = new Setup($logMock,
+ $userSessionMock,
+ $this->cryptMock,
+ $this->keyManagerMock);
+ }
+
+}