Initial commit

This commit is contained in:
Clark Tomlinson 2015-02-24 13:05:19 -05:00 committed by Thomas Müller
parent 63e7fe608a
commit 39733c8da1
37 changed files with 2630 additions and 253 deletions

View File

@ -0,0 +1,33 @@
<?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/>
*
*/
use OCA\Encryption\AppInfo\Encryption;
if (!OC::$CLI) {
$di = \OC::$server;
$app = new Encryption('encryption',
[],
$di->getEncryptionManager(),
$di->getConfig());
$app->boot();
}

View File

@ -0,0 +1,182 @@
<?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 OCA\Encryption\Crypto\Crypt;
use OCA\Encryption\HookManager;
use OCA\Encryption\Hooks\AppHooks;
use OCA\Encryption\Hooks\FileSystemHooks;
use OCA\Encryption\Hooks\ShareHooks;
use OCA\Encryption\Hooks\UserHooks;
use OCA\Encryption\KeyManager;
use OCA\Encryption\Migrator;
use OCA\Encryption\Recovery;
use OCA\Encryption\Users\Setup;
use OCP\App;
use OCP\AppFramework\IAppContainer;
use OCP\Encryption\IManager;
use OCP\IConfig;
class Encryption extends \OCP\AppFramework\App {
/**
* @var IManager
*/
private $encryptionManager;
/**
* @var IConfig
*/
private $config;
/**
* @param $appName
* @param array $urlParams
* @param IManager $encryptionManager
* @param IConfig $config
*/
public function __construct($appName, $urlParams = array(), IManager $encryptionManager, IConfig $config) {
parent::__construct($appName, $urlParams);
$this->encryptionManager = $encryptionManager;
$this->config = $config;
}
/**
*
*/
public function boot() {
$this->registerServices();
$this->registerHooks();
$this->registerEncryptionModule();
$this->registerSettings();
}
/**
*
*/
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'),
$container->query('Migrator'),
$server->getUserSession()),
// new ShareHooks(),
// new FileSystemHooks(),
// new AppHooks()
]);
$hookManager->fireHooks();
} else {
// Logout user if we are in maintenance to force re-login
$this->getContainer()->getServer()->getUserSession()->logout();
}
}
/**
*
*/
public function registerEncryptionModule() {
// $this->encryptionManager->registerEncryptionModule(new \OCA\Encryption\Crypto\Encryption());
}
/**
*
*/
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(),
$c->query('Crypt'),
$server->getConfig(),
$server->getUserSession());
});
$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());
});
$container->registerService('UserSetup',
function (IAppContainer $c) {
$server = $c->getServer();
return new Setup($server->getLogger(),
$server->getUserSession(),
$c->query('Crypt'),
$c->query('KeyManager'));
});
$container->registerService('Migrator',
function (IAppContainer $c) {
$server = $c->getServer();
return new Migrator($server->getUserSession(),
$server->getConfig(),
$server->getUserManager(),
$server->getLogger(),
$c->query('Crypt'));
});
}
/**
*
*/
public function registerSettings() {
// script('encryption', 'encryption');
// script('encryption', 'detect-migration');
// Register settings scripts
App::registerAdmin('encryption', 'settings/settings-admin');
App::registerPersonal('encryption', 'settings/settings-personal');
}
}

View File

@ -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>

View File

@ -0,0 +1,39 @@
<?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/>
*
*/
use OCP\AppFramework\App;
(new App('encryption'))->registerRoutes($this, array('routes' => array(
[
'name' => 'recovery#adminRecovery',
'url' => '/ajax/adminRecovery',
'verb' => 'POST'
],
[
'name' => 'recovery#userRecovery',
'url' => '/ajax/userRecovery',
'verb' => 'POST'
]
)));

View File

@ -0,0 +1,106 @@
<?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 Symfony\Component\HttpFoundation\JsonResponse;
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 = $this->l->t('Missing recovery key password');
return new JsonResponse(['data' => ['message' => $errorMessage]], 500);
}
if (empty($confirmPassword)) {
$errorMessage = $this->l->t('Please repeat the recovery key password');
return new JsonResponse(['data' => ['message' => $errorMessage]], 500);
}
if ($recoveryPassword !== $confirmPassword) {
$errorMessage = $this->l->t('Repeated recovery key password does not match the provided recovery key password');
return new JsonResponse(['data' => ['message' => $errorMessage]], 500);
}
// Enable recoveryAdmin
$recoveryKeyId = $this->config->getAppValue('encryption', 'recoveryKeyId');
if (isset($adminEnableRecovery) && $adminEnableRecovery === '1') {
if ($this->recovery->enableAdminRecovery($recoveryKeyId, $recoveryPassword)) {
return new JsonResponse(['data' => array('message' => $this->l->t('Recovery key successfully enabled'))]);
}
return new JsonResponse(['data' => array('message' => $this->l->t('Could not enable recovery key. Please check your recovery key password!'))]);
} elseif (isset($adminEnableRecovery) && $adminEnableRecovery === '0') {
if ($this->recovery->disableAdminRecovery($recoveryKeyId, $recoveryPassword)) {
return new JsonResponse(['data' => array('message' => $this->l->t('Recovery key successfully disabled'))]);
}
return new JsonResponse(['data' => array('message' => $this->l->t('Could not disable recovery key. Please check your recovery key password!'))]);
}
}
public function userRecovery($userEnableRecovery) {
if (isset($userEnableRecovery) && ($userEnableRecovery === '0' || $userEnableRecovery === '1')) {
$userId = $this->user->getUID();
if ($userEnableRecovery === '1') {
// Todo xxx figure out if we need keyid's here or what.
return $this->recovery->addRecoveryKeys();
}
// Todo xxx see :98
return $this->recovery->removeRecoveryKeys();
}
}
}

View File

@ -0,0 +1,37 @@
<?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 OCA\Encryption\Hooks\Contracts\IHook;
use OCP\Util;
class AppHooks implements IHook {
/**
* Connects Hooks
*
* @return null
*/
public function addHooks() {
Util::connectHook('OC_App', 'pre_disable', 'OCA\Encryption\Hooks', 'preDisable');
Util::connectHook('OC_App', 'post_disable', 'OCA\Encryption\Hooks', 'postEnable');
}
}

View File

@ -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();
}

View File

@ -0,0 +1,46 @@
<?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 OCA\Encryption\Hooks\Contracts\IHook;
use OCP\Util;
class FileSystemHooks implements IHook {
/**
* Connects Hooks
*
* @return null
*/
public function addHooks() {
Util::connectHook('OC_Filesystem', 'rename', 'OCA\Encryption\Hooks', 'preRename');
Util::connectHook('OC_Filesystem', 'post_rename', 'OCA\Encryption\Hooks', 'postRenameOrCopy');
Util::connectHook('OC_Filesystem', 'copy', 'OCA\Encryption\Hooks', 'preCopy');
Util::connectHook('OC_Filesystem', 'post_copy', 'OCA\Encryption\Hooks', 'postRenameOrCopy');
Util::connectHook('OC_Filesystem', 'post_delete', 'OCA\Encryption\Hooks', 'postDelete');
Util::connectHook('OC_Filesystem', 'delete', 'OCA\Encryption\Hooks', 'preDelete');
Util::connectHook('\OC\Core\LostPassword\Controller\LostController', 'post_passwordReset', 'OCA\Encryption\Hooks', 'postPasswordReset');
Util::connectHook('OC_Filesystem', 'post_umount', 'OCA\Encryption\Hooks', 'postUnmount');
Util::connectHook('OC_Filesystem', 'umount', 'OCA\Encryption\Hooks', 'preUnmount');
}
}

View File

@ -0,0 +1,40 @@
<?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 OCA\Encryption\Hooks\Contracts\IHook;
use OCP\Util;
class ShareHooks implements IHook {
/**
* Connects Hooks
*
* @return null
*/
public function addHooks() {
Util::connectHook('OCP\Share', 'pre_shared', 'OCA\Encryption\Hooks', 'preShared');
Util::connectHook('OCP\Share', 'post_shared', 'OCA\Encryption\Hooks', 'postShared');
Util::connectHook('OCP\Share', 'post_unshare', 'OCA\Encryption\Hooks', 'postUnshare');
}
}

View File

@ -0,0 +1,304 @@
<?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 OCA\Encryption\Hooks\Contracts\IHook;
use OCA\Encryption\KeyManager;
use OCA\Encryption\Migrator;
use OCA\Encryption\RequirementsChecker;
use OCA\Encryption\Users\Setup;
use OCP\App;
use OCP\ILogger;
use OCP\IUserSession;
use OCP\Util;
use Test\User;
class UserHooks implements IHook {
/**
* @var KeyManager
*/
private $keyManager;
/**
* @var ILogger
*/
private $logger;
/**
* @var Setup
*/
private $userSetup;
/**
* @var Migrator
*/
private $migrator;
/**
* @var IUserSession
*/
private $user;
/**
* UserHooks constructor.
*
* @param KeyManager $keyManager
* @param ILogger $logger
* @param Setup $userSetup
* @param Migrator $migrator
* @param IUserSession $user
*/
public function __construct(
KeyManager $keyManager, ILogger $logger, Setup $userSetup, Migrator $migrator, IUserSession $user) {
$this->keyManager = $keyManager;
$this->logger = $logger;
$this->userSetup = $userSetup;
$this->migrator = $migrator;
$this->user = $user;
}
/**
* Connects Hooks
*
* @return null
*/
public function addHooks() {
Util::connectHook('OC_User', 'post_login', $this, 'login');
Util::connectHook('OC_User', 'logout', $this, 'logout');
Util::connectHook('OC_User', 'post_setPassword', $this, 'setPassphrase');
Util::connectHook('OC_User', 'pre_setPassword', $this, 'preSetPassphrase');
Util::connectHook('OC_User', 'post_createUser', $this, 'postCreateUser');
Util::connectHook('OC_User', 'post_deleteUser', $this, 'postDeleteUser');
}
/**
* Startup encryption backend upon user login
*
* @note This method should never be called for users using client side encryption
*/
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['password'])) {
return false;
}
$cache = $this->keyManager->init();
// Check if first-run file migration has already been performed
$ready = false;
$migrationStatus = $this->migrator->getStatus($params['uid']);
if ($migrationStatus === Migrator::$migrationOpen && $cache !== false) {
$ready = $this->migrator->beginMigration();
} elseif ($migrationStatus === Migrator::$migrationInProgress) {
// refuse login as long as the initial encryption is running
sleep(5);
$this->user->logout();
return false;
}
$result = true;
// If migration not yet done
if ($ready) {
// Encrypt existing user files
try {
$result = $util->encryptAll('/' . $params['uid'] . '/' . 'files');
} catch (\Exception $ex) {
\OCP\Util::writeLog('Encryption library', 'Initial encryption failed! Error: ' . $ex->getMessage(), \OCP\Util::FATAL);
$result = false;
}
if ($result) {
\OC_Log::write(
'Encryption library', 'Encryption of existing files belonging to "' . $params['uid'] . '" completed'
, \OC_Log::INFO
);
// Register successful migration in DB
$util->finishMigration();
} else {
\OCP\Util::writeLog('Encryption library', 'Initial encryption failed!', \OCP\Util::FATAL);
$util->resetMigrationStatus();
\OCP\User::logout();
}
}
return $result;
}
/**
* remove keys from session during logout
*/
public function logout() {
$session = new Session(new \OC\Files\View());
$session->removeKeys();
}
/**
* setup encryption backend upon user created
*
* @note This method should never be called for users using client side encryption
*/
public function postCreateUser($params) {
if (App::isEnabled('files_encryption')) {
$view = new \OC\Files\View('/');
$util = new Util($view, $params['uid']);
Helper::setupUser($util, $params['password']);
}
}
/**
* cleanup encryption backend upon user deleted
*
* @note This method should never be called for users using client side encryption
*/
public function postDeleteUser($params) {
if (App::isEnabled('files_encryption')) {
Keymanager::deletePublicKey(new \OC\Files\View(), $params['uid']);
}
}
/**
* If the password can't be changed within ownCloud, than update the key password in advance.
*/
public function preSetPassphrase($params) {
if (App::isEnabled('files_encryption')) {
if (!\OC_User::canUserChangePassword($params['uid'])) {
self::setPassphrase($params);
}
}
}
/**
* Change a user's encryption passphrase
*
* @param array $params keys: uid, password
*/
public function setPassphrase($params) {
if (App::isEnabled('files_encryption') === false) {
return true;
}
// Only attempt to change passphrase if server-side encryption
// is in use (client-side encryption does not have access to
// the necessary keys)
if (Crypt::mode() === 'server') {
$view = new \OC\Files\View('/');
$session = new Session($view);
// Get existing decrypted private key
$privateKey = $session->getPrivateKey();
if ($params['uid'] === \OCP\User::getUser() && $privateKey) {
// Encrypt private key with new user pwd as passphrase
$encryptedPrivateKey = Crypt::symmetricEncryptFileContent($privateKey, $params['password'], Helper::getCipher());
// Save private key
if ($encryptedPrivateKey) {
Keymanager::setPrivateKey($encryptedPrivateKey, \OCP\User::getUser());
} else {
\OCP\Util::writeLog('files_encryption', 'Could not update users encryption password', \OCP\Util::ERROR);
}
// NOTE: Session does not need to be updated as the
// private key has not changed, only the passphrase
// used to decrypt it has changed
} else { // admin changed the password for a different user, create new keys and reencrypt file keys
$user = $params['uid'];
$util = new Util($view, $user);
$recoveryPassword = isset($params['recoveryPassword']) ? $params['recoveryPassword'] : null;
// we generate new keys if...
// ...we have a recovery password and the user enabled the recovery key
// ...encryption was activated for the first time (no keys exists)
// ...the user doesn't have any files
if (($util->recoveryEnabledForUser() && $recoveryPassword)
|| !$util->userKeysExists()
|| !$view->file_exists($user . '/files')
) {
// backup old keys
$util->backupAllKeys('recovery');
$newUserPassword = $params['password'];
// make sure that the users home is mounted
\OC\Files\Filesystem::initMountPoints($user);
$keypair = Crypt::createKeypair();
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
// Save public key
Keymanager::setPublicKey($keypair['publicKey'], $user);
// Encrypt private key with new password
$encryptedKey = Crypt::symmetricEncryptFileContent($keypair['privateKey'], $newUserPassword, Helper::getCipher());
if ($encryptedKey) {
Keymanager::setPrivateKey($encryptedKey, $user);
if ($recoveryPassword) { // if recovery key is set we can re-encrypt the key files
$util = new Util($view, $user);
$util->recoverUsersFiles($recoveryPassword);
}
} else {
\OCP\Util::writeLog('files_encryption', 'Could not update users encryption password', \OCP\Util::ERROR);
}
\OC_FileProxy::$enabled = $proxyStatus;
}
}
}
}
/**
* after password reset we create a new key pair for the user
*
* @param array $params
*/
public function postPasswordReset($params) {
$uid = $params['uid'];
$password = $params['password'];
$util = new Util(new \OC\Files\View(), $uid);
$util->replaceUserKeys($password);
}
}

View File

@ -0,0 +1,116 @@
<?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 OCP\Encryption\IEncryptionModule;
class Encryption extends Crypt implements IEncryptionModule {
/**
* @return string defining the technical unique id
*/
public function getId() {
// TODO: Implement getId() method.
}
/**
* In comparison to getKey() this function returns a human readable (maybe translated) name
*
* @return string
*/
public function getDisplayName() {
// TODO: Implement getDisplayName() method.
}
/**
* 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 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, $header, $accessList) {
// TODO: Implement begin() method.
}
/**
* 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) {
// TODO: Implement end() method.
}
/**
* encrypt data
*
* @param string $data you want to encrypt
* @return mixed encrypted data
*/
public function encrypt($data) {
// Todo: xxx Update Signature and usages
$this->symmetricEncryptFileContent($data);
}
/**
* decrypt data
*
* @param string $data you want to decrypt
* @param string $user decrypt as user (null for public access)
* @return mixed decrypted data
*/
public function decrypt($data, $user) {
// Todo: xxx Update Usages?
$this->symmetricDecryptFileContent($data, $user);
}
/**
* update encrypted file, e.g. give additional users access to the file
*
* @param string $path path to the file which should be updated
* @param array $accessList who has access to the file contains the key 'users' and 'public'
* @return boolean
*/
public function update($path, $accessList) {
// TODO: Implement update() method.
}
/**
* should the file be encrypted or not
*
* @param string $path
* @return boolean
*/
public function shouldEncrypt($path) {
// TODO: Implement shouldEncrypt() method.
}
/**
* calculate unencrypted size
*
* @param string $path to file
* @return integer unencrypted size
*/
public function calculateUnencryptedSize($path) {
// TODO: Implement calculateUnencryptedSize() method.
}
}

View File

@ -0,0 +1,355 @@
<?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 OC\Encryption\Exceptions\GenericEncryptionException;
use OCP\IConfig;
use OCP\ILogger;
use OCP\IUser;
use OCP\IUserSession;
class Crypt {
const ENCRYPTION_UKNOWN_ERROR = -1;
const ENCRYPTION_NOT_INIALIZED_ERROR = 1;
const ENCRYPTIION_PRIVATE_KEY_NOT_VALID_ERROR = 2;
const ENCRYPTION_NO_SHARE_KEY_FOUND = 3;
const BLOCKSIZE = 8192;
const DEFAULT_CIPHER = 'AES-256-CFB';
const HEADERSTART = 'HBEGIN';
const HEADEREND = '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;
}
/**
* @param null $user
* @return string
*/
public function mode($user = null) {
return 'server';
}
/**
*
*/
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.' . $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 $plainContent
* @param $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();
try {
$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;
} catch (EncryptionFailedException $e) {
$message = 'Could not encrypt file content (code: ' . $e->getCode() . '): ';
$this->logger->error('files_encryption' . $message . $e->getMessage(), ['app' => 'encryption']);
return false;
}
}
/**
* @param $plainContent
* @param $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 $encryptedContent
* @param $iv
* @return string
*/
private function concatIV($encryptedContent, $iv) {
return $encryptedContent . '00iv00' . $iv;
}
/**
* @param $data
* @return string
*/
private function addPadding($data) {
return $data . 'xx';
}
/**
* @param $recoveryKey
* @param $password
* @return bool|string
*/
public function decryptPrivateKey($recoveryKey, $password) {
$header = $this->parseHeader($recoveryKey);
$cipher = $this->getCipher($header);
// If we found a header we need to remove it from the key we want to decrypt
if (!empty($header)) {
$recoveryKey = substr($recoveryKey, strpos($recoveryKey, self::HEADEREND) + strlen(self::HEADERSTART));
}
$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 bool|string
* @throws DecryptionFailedException
*/
public function symmetricDecryptFileContent($keyFileContents, $passphrase = '', $cipher = self::DEFAULT_CIPHER) {
// Remove Padding
$noPadding = $this->removePadding($keyFileContents);
$catFile = $this->splitIv($noPadding);
$plainContent = $this->decrypt($catFile['encrypted'], $catFile['iv'], $passphrase, $cipher);
if ($plainContent) {
return $plainContent;
}
return false;
}
/**
* @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');
}
}
/**
* @param $data
* @return array
*/
private function parseHeader($data) {
$result = [];
if (substr($data, 0, strlen(self::HEADERSTART)) === self::HEADERSTART) {
$endAt = strpos($data, self::HEADEREND);
$header = substr($data, 0, $endAt + strlen(self::HEADEREND));
// +1 not to start with an ':' which would result in empty element at the beginning
$exploded = explode(':', substr($header, strlen(self::HEADERSTART) + 1));
$element = array_shift($exploded);
while ($element != self::HEADEREND) {
$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');
}
}

View File

@ -0,0 +1,64 @@
<?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;
return true;
}
}
$this->hookInstances[] = $instances;
return true;
}
/**
*
*/
public function fireHooks() {
foreach ($this->hookInstances as $instance) {
/**
* @var $instance IHook
*/
$instance->addHooks();
}
}
}

View File

@ -0,0 +1,217 @@
<?php
/**
* @author Clark Tomlinson <clark@owncloud.com>
* @since 2/19/15, 1:20 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;
use OC\Encryption\Exceptions\PrivateKeyMissingException;
use OC\Encryption\Exceptions\PublicKeyMissingException;
use OCA\Encryption\Crypto\Crypt;
use OCP\Encryption\IKeyStorage;
use OCP\IConfig;
use OCP\IUser;
use OCP\IUserSession;
class KeyManager {
/**
* @var IKeyStorage
*/
private $keyStorage;
/**
* @var Crypt
*/
private $crypt;
/**
* @var string
*/
private $recoveryKeyId;
/**
* @var string
*/
private $publicShareKeyId;
/**
* @var string UserID
*/
private $keyId;
/**
* @var string
*/
private $publicKeyId = '.public';
/**
* @var string
*/
private $privateKeyId = '.private';
/**
* @var IConfig
*/
private $config;
/**
* @param IKeyStorage $keyStorage
* @param Crypt $crypt
* @param IConfig $config
* @param IUserSession $userSession
*/
public function __construct(IKeyStorage $keyStorage, Crypt $crypt, IConfig $config, IUserSession $userSession) {
$this->keyStorage = $keyStorage;
$this->crypt = $crypt;
$this->config = $config;
$this->recoveryKeyId = $this->config->getAppValue('encryption', 'recoveryKeyId');
$this->publicShareKeyId = $this->config->getAppValue('encryption', 'publicShareKeyId');
$this->keyId = $userSession && $userSession->isLoggedIn() ? $userSession->getUser()->getUID() : false;
}
/**
* @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();
}
/**
* @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();
}
/**
* @return bool
*/
public function recoveryKeyExists() {
return (strlen($this->keyStorage->getSystemUserKey($this->recoveryKeyId)) !== 0);
}
/**
* @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 $password
* @return bool
*/
public function checkRecoveryPassword($password) {
$recoveryKey = $this->keyStorage->getSystemUserKey($this->recoveryKeyId);
$decryptedRecoveryKey = $this->crypt->decryptPrivateKey($recoveryKey, $password);
if ($decryptedRecoveryKey) {
return true;
}
return false;
}
/**
* @param $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);
}
/**
* @param $password
* @param $keyPair
* @return bool
*/
public function storeKeyPair($password, $keyPair) {
// Save Public Key
$this->setPublicKey($this->keyId, $keyPair['publicKey']);
$encryptedKey = $this->crypt->symmetricEncryptFileContent($keyPair['privateKey'], $password);
if ($encryptedKey) {
$this->setPrivateKey($this->keyId, $encryptedKey);
$this->config->setAppValue('encryption', 'recoveryAdminEnabled', 1);
return true;
}
return false;
}
/**
* @return bool
*/
public function ready() {
return $this->keyStorage->ready();
}
/**
* @return \OCP\ICache
* @throws PrivateKeyMissingException
*/
public function init() {
try {
$privateKey = $this->getPrivateKey($this->keyId);
} catch (PrivateKeyMissingException $e) {
return false;
}
$cache = \OC::$server->getMemCacheFactory();
$cacheInstance = $cache->create('Encryption');
$cacheInstance->set('privateKey', $privateKey);
return $cacheInstance;
}
}

View File

@ -0,0 +1,123 @@
<?php
/**
* @author Clark Tomlinson <fallen013@gmail.com>
* @since 3/9/15, 2:44 PM
* @link http:/www.clarkt.com
* @copyright Clark Tomlinson © 2015
*
*/
namespace OCA\Encryption;
use OCA\Encryption\Crypto\Crypt;
use OCP\IConfig;
use OCP\ILogger;
use OCP\IUserManager;
use OCP\IUserSession;
use OCP\PreConditionNotMetException;
class Migrator {
/**
* @var bool
*/
private $status = false;
/**
* @var IUserManager
*/
private $user;
/**
* @var IConfig
*/
private $config;
/**
* @var string
*/
public static $migrationOpen = '0';
/**
* @var string
*/
public static $migrationInProgress = '-1';
/**
* @var string
*/
public static $migrationComplete = '1';
/**
* @var IUserManager
*/
private $userManager;
/**
* @var ILogger
*/
private $log;
/**
* @var Crypt
*/
private $crypt;
/**
* Migrator constructor.
*
* @param IUserSession $userSession
* @param IConfig $config
* @param IUserManager $userManager
* @param ILogger $log
* @param Crypt $crypt
*/
public function __construct(IUserSession $userSession, IConfig $config, IUserManager $userManager, ILogger $log, Crypt $crypt) {
$this->user = $userSession && $userSession->isLoggedIn() ? $userSession->getUser() : false;
$this->config = $config;
$this->userManager = $userManager;
$this->log = $log;
$this->crypt = $crypt;
}
/**
* @param $userId
* @return bool|string
*/
public function getStatus($userId) {
if ($this->userManager->userExists($userId)) {
$this->status = $this->config->getUserValue($userId, 'encryption', 'migrationStatus', false);
if (!$this->status) {
$this->config->setUserValue($userId, 'encryption', 'migrationStatus', self::$migrationOpen);
$this->status = self::$migrationOpen;
}
}
return $this->status;
}
/**
* @return bool
*/
public function beginMigration() {
$status = $this->setMigrationStatus(self::$migrationInProgress, self::$migrationOpen);
if ($status) {
$this->log->info('Encryption Library Start migration to encrypt for ' . $this->user->getUID());
return $status;
}
$this->log->warning('Encryption Library Could not activate migration for ' . $this->user->getUID() . '. Probably another process already started the inital encryption');
return $status;
}
/**
* @param $status
* @param bool $preCondition
* @return bool
*/
private function setMigrationStatus($status, $preCondition = false) {
// Convert to string if preCondition is set
$preCondition = ($preCondition === false) ? false : (string)$preCondition;
try {
$this->config->setUserValue($this->user->getUID(), 'encryption', 'migrationStatus', (string)$status, $preCondition);
return true;
} catch (PreConditionNotMetException $e) {
return false;
}
}
}

View File

@ -0,0 +1,134 @@
<?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 OC\Files\View;
use OCA\Encryption\Crypto\Crypt;
use OCP\Encryption\IKeyStorage;
use OCP\IConfig;
use OCP\IUser;
use OCP\Security\ISecureRandom;
class Recovery {
/**
* @var null|IUser
*/
protected $user;
/**
* @var Crypt
*/
protected $crypt;
/**
* @var ISecureRandom
*/
private $random;
/**
* @var KeyManager
*/
private $keyManager;
/**
* @var IConfig
*/
private $config;
/**
* @var IEncryptionKeyStorage
*/
private $keyStorage;
/**
* @param IUser $user
* @param Crypt $crypt
* @param ISecureRandom $random
* @param KeyManager $keyManager
* @param IConfig $config
* @param IKeyStorage $keyStorage
*/
public function __construct(IUser $user,
Crypt $crypt,
ISecureRandom $random,
KeyManager $keyManager,
IConfig $config,
IKeyStorage $keyStorage) {
$this->user = $user;
$this->crypt = $crypt;
$this->random = $random;
$this->keyManager = $keyManager;
$this->config = $config;
$this->keyStorage = $keyStorage;
}
/**
* @param $recoveryKeyId
* @param $password
* @return bool
*/
public function enableAdminRecovery($recoveryKeyId, $password) {
$appConfig = $this->config;
if ($recoveryKeyId === null) {
$recoveryKeyId = $this->random->getLowStrengthGenerator();
$appConfig->setAppValue('encryption', 'recoveryKeyId', $recoveryKeyId);
}
$keyManager = $this->keyManager;
if (!$keyManager->recoveryKeyExists()) {
$keyPair = $this->crypt->createKeyPair();
return $this->keyManager->storeKeyPair($password, $keyPair);
}
if ($keyManager->checkRecoveryPassword($password)) {
$appConfig->setAppValue('encryption', 'recoveryAdminEnabled', 1);
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;
}
public function addRecoveryKeys($keyId) {
// No idea new way to do this....
}
public function removeRecoveryKeys() {
// No idea new way to do this....
}
}

View File

@ -0,0 +1,38 @@
<?php
/**
* @author Clark Tomlinson <fallen013@gmail.com>
* @since 3/6/15, 11:30 AM
* @link http:/www.clarkt.com
* @copyright Clark Tomlinson © 2015
*
*/
namespace OCA\Encryption;
use OCP\ILogger;
use OCP\IUser;
use OCP\IUserSession;
class Setup {
/**
* @var ILogger
*/
protected $logger;
/**
* @var IUser
*/
protected $user;
/**
* Setup constructor.
*
* @param ILogger $logger
* @param IUserSession $userSession
*/
public function __construct(ILogger $logger, IUserSession $userSession) {
$this->logger = $logger;
$this->user = $userSession && $userSession->isLoggedIn() ? $userSession->getUser()->getUID() : false;
}
}

View File

@ -0,0 +1,63 @@
<?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 extends \OCA\Encryption\Setup {
/**
* @var Crypt
*/
private $crypt;
/**
* @var KeyManager
*/
private $keyManager;
/**
* @param ILogger $logger
* @param IUserSession $userSession
* @param Crypt $crypt
* @param KeyManager $keyManager
*/
public function __construct(ILogger $logger, IUserSession $userSession, Crypt $crypt, KeyManager $keyManager) {
parent::__construct($logger, $userSession);
$this->crypt = $crypt;
$this->keyManager = $keyManager;
}
/**
* @param $password
* @return bool
*/
public function setupUser($password) {
if ($this->keyManager->ready()) {
$this->logger->debug('Encryption Library: User Account ' . $this->user->getUID() . ' Is not ready for encryption; configuration started');
return $this->setupServerSide($password);
}
}
/**
* @param $password
* @return bool
*/
private function setupServerSide($password) {
// Check if user already has keys
if (!$this->keyManager->userHasKeys($this->user->getUID())) {
return $this->keyManager->storeKeyPair($password, $this->crypt->createKeyPair());
}
return true;
}
}

View File

@ -0,0 +1,24 @@
<?php
/**
* Copyright (c) 2011 Robin Appelman <icewind@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('files_encryption', 'settings-admin');
// Check if an adminRecovery account is enabled for recovering files after lost pwd
$recoveryAdminEnabled = \OC::$server->getAppConfig()->getValue('files_encryption', 'recoveryAdminEnabled', '0');
$session = new \OCA\Files_Encryption\Session(new \OC\Files\View('/'));
$initStatus = $session->getInitialized();
$tmpl->assign('recoveryEnabled', $recoveryAdminEnabled);
$tmpl->assign('initStatus', $initStatus);
\OCP\Util::addscript('files_encryption', 'settings-admin');
\OCP\Util::addscript('core', 'multiselect');
return $tmpl->fetchPage();

View File

@ -0,0 +1,41 @@
<?php
/**
* 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.
*/
// Add CSS stylesheet
\OC_Util::addStyle('files_encryption', 'settings-personal');
$tmpl = new OCP\Template('files_encryption', 'settings-personal');
$user = \OCP\USER::getUser();
$view = new \OC\Files\View('/');
$util = new \OCA\Files_Encryption\Util($view, $user);
$session = new \OCA\Files_Encryption\Session($view);
$privateKeySet = $session->getPrivateKey() !== false;
// did we tried to initialize the keys for this session?
$initialized = $session->getInitialized();
$recoveryAdminEnabled = \OC::$server->getAppConfig()->getValue('files_encryption', 'recoveryAdminEnabled');
$recoveryEnabledForUser = $util->recoveryEnabledForUser();
$result = false;
if ($recoveryAdminEnabled || !$privateKeySet) {
\OCP\Util::addscript('files_encryption', 'settings-personal');
$tmpl->assign('recoveryEnabled', $recoveryAdminEnabled);
$tmpl->assign('recoveryEnabledForUser', $recoveryEnabledForUser);
$tmpl->assign('privateKeySet', $privateKeySet);
$tmpl->assign('initialized', $initialized);
$result = $tmpl->fetchPage();
}
return $result;

View File

@ -0,0 +1,90 @@
<?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
*/
private $userId;
/**
* @var
*/
private $dummyKeys;
public function setUp() {
parent::setUp();
$keyStorageMock = $this->getMock('OCP\Encryption\IKeyStorage');
$cryptMock = $this->getMockBuilder('OCA\Encryption\Crypt')
->disableOriginalConstructor()
->getMock();
$configMock = $this->getMock('OCP\IConfig');
$userMock = $this->getMock('OCP\IUser');
$userMock->expects($this->once())
->method('getUID')
->will($this->returnValue('admin'));
$this->userId = 'admin';
$this->instance = new KeyManager($keyStorageMock, $cryptMock, $configMock, $userMock);
$this->dummyKeys = ['public' => 'randomweakpublickeyhere',
'private' => 'randomweakprivatekeyhere'];
}
/**
* @expectedException OC\Encryption\Exceptions\PrivateKeyMissingException
*/
public function testGetPrivateKey() {
$this->assertFalse($this->instance->getPrivateKey($this->userId));
}
/**
* @expectedException OC\Encryption\Exceptions\PublicKeyMissingException
*/
public function testGetPublicKey() {
$this->assertFalse($this->instance->getPublicKey($this->userId));
}
/**
*
*/
public function testRecoveryKeyExists() {
$this->assertFalse($this->instance->recoveryKeyExists());
}
/**
*
*/
public function testCheckRecoveryKeyPassword() {
$this->assertFalse($this->instance->checkRecoveryPassword('pass'));
}
public function testSetPublicKey() {
$this->assertTrue($this->instance->setPublicKey($this->userId, $this->dummyKeys['public']));
}
public function testSetPrivateKey() {
$this->assertTrue($this->instance->setPrivateKey($this->userId, $this->dummyKeys['private']));
}
public function testUserHasKeys() {
$this->assertFalse($this->instance->userHasKeys($this->userId));
}
}

View File

@ -0,0 +1,62 @@
<?php
/**
* @author Clark Tomlinson <fallen013@gmail.com>
* @since 3/9/15, 2:56 PM
* @link http:/www.clarkt.com
* @copyright Clark Tomlinson © 2015
*
*/
namespace OCA\Encryption\Tests;
use OCA\Encryption\Migrator;
use Test\TestCase;
class MigratorTest extends TestCase {
/**
* @var Migrator
*/
private $instance;
/**
*
*/
public function testGetStatus() {
$this->assertFalse($this->instance->getStatus('admin'));
}
/**
*
*/
public function testBeginMigration() {
$this->assertTrue($this->instance->beginMigration());
}
/**
*
*/
public function testSetMigrationStatus() {
$this->assertTrue(\Test_Helper::invokePrivate($this->instance,
'setMigrationStatus',
['0', '-1'])
);
}
/**
*
*/
protected function setUp() {
parent::setUp();
$cryptMock = $this->getMockBuilder('OCA\Encryption\Crypto\Crypt')->disableOriginalConstructor()->getMock();
$this->instance = new Migrator($this->getMock('OCP\IUser'),
$this->getMock('OCP\IConfig'),
$this->getMock('OCP\IUserManager'),
$this->getMock('OCP\ILogger'),
$cryptMock);
}
}

View File

@ -0,0 +1,51 @@
<?php
/**
* @author Clark Tomlinson <fallen013@gmail.com>
* @since 3/6/15, 10:36 AM
* @link http:/www.clarkt.com
* @copyright Clark Tomlinson © 2015
*
*/
namespace OCA\Encryption\Tests;
use OCA\Encryption\RequirementsChecker;
use Test\TestCase;
class RequirementsCheckerTest extends TestCase {
/**
* @var RequirementsChecker
*/
private $instance;
/**
*
*/
protected function setUp() {
parent::setUp();
$log = $this->getMock('OCP\ILogger');
$crypt = $this->getMockBuilder('OCA\Encryption\Crypt')
->disableOriginalConstructor()
->getMock();
$crypt
->method('getOpenSSLPkey')
->will($this->returnValue(true));
$this->instance = new RequirementsChecker($crypt, $log);
}
/**
*
*/
public function testCanCheckConfigration() {
$this->assertTrue($this->instance->checkConfiguration());
}
/**
*
*/
public function testCanCheckRequiredExtensions() {
$this->assertTrue($this->instance->checkExtensions());
}
}

View File

@ -34,233 +34,7 @@ class Hooks {
// file for which we want to delete the keys after the delete operation was successful
private static $unmountedFiles = array();
/**
* Startup encryption backend upon user login
* @note This method should never be called for users using client side encryption
*/
public static function login($params) {
if (\OCP\App::isEnabled('files_encryption') === false) {
return true;
}
$l = new \OC_L10N('files_encryption');
$view = new \OC\Files\View('/');
// ensure filesystem is loaded
if (!\OC\Files\Filesystem::$loaded) {
\OC_Util::setupFS($params['uid']);
}
$privateKey = Keymanager::getPrivateKey($view, $params['uid']);
// if no private key exists, check server configuration
if (!$privateKey) {
//check if all requirements are met
if (!Helper::checkRequirements() || !Helper::checkConfiguration()) {
$error_msg = $l->t("Missing requirements.");
$hint = $l->t('Please make sure that OpenSSL together with the PHP extension is enabled and configured properly. For now, the encryption app has been disabled.');
\OC_App::disable('files_encryption');
\OCP\Util::writeLog('Encryption library', $error_msg . ' ' . $hint, \OCP\Util::ERROR);
\OCP\Template::printErrorPage($error_msg, $hint);
}
}
$util = new Util($view, $params['uid']);
// setup user, if user not ready force relogin
if (Helper::setupUser($util, $params['password']) === false) {
return false;
}
$session = $util->initEncryption($params);
// Check if first-run file migration has already been performed
$ready = false;
$migrationStatus = $util->getMigrationStatus();
if ($migrationStatus === Util::MIGRATION_OPEN && $session !== false) {
$ready = $util->beginMigration();
} elseif ($migrationStatus === Util::MIGRATION_IN_PROGRESS) {
// refuse login as long as the initial encryption is running
sleep(5);
\OCP\User::logout();
return false;
}
$result = true;
// If migration not yet done
if ($ready) {
// Encrypt existing user files
try {
$result = $util->encryptAll('/' . $params['uid'] . '/' . 'files');
} catch (\Exception $ex) {
\OCP\Util::writeLog('Encryption library', 'Initial encryption failed! Error: ' . $ex->getMessage(), \OCP\Util::FATAL);
$result = false;
}
if ($result) {
\OC_Log::write(
'Encryption library', 'Encryption of existing files belonging to "' . $params['uid'] . '" completed'
, \OC_Log::INFO
);
// Register successful migration in DB
$util->finishMigration();
} else {
\OCP\Util::writeLog('Encryption library', 'Initial encryption failed!', \OCP\Util::FATAL);
$util->resetMigrationStatus();
\OCP\User::logout();
}
}
return $result;
}
/**
* remove keys from session during logout
*/
public static function logout() {
$session = new Session(new \OC\Files\View());
$session->removeKeys();
}
/**
* setup encryption backend upon user created
* @note This method should never be called for users using client side encryption
*/
public static function postCreateUser($params) {
if (\OCP\App::isEnabled('files_encryption')) {
$view = new \OC\Files\View('/');
$util = new Util($view, $params['uid']);
Helper::setupUser($util, $params['password']);
}
}
/**
* cleanup encryption backend upon user deleted
* @note This method should never be called for users using client side encryption
*/
public static function postDeleteUser($params) {
if (\OCP\App::isEnabled('files_encryption')) {
Keymanager::deletePublicKey(new \OC\Files\View(), $params['uid']);
}
}
/**
* If the password can't be changed within ownCloud, than update the key password in advance.
*/
public static function preSetPassphrase($params) {
if (\OCP\App::isEnabled('files_encryption')) {
if ( ! \OC_User::canUserChangePassword($params['uid']) ) {
self::setPassphrase($params);
}
}
}
/**
* Change a user's encryption passphrase
* @param array $params keys: uid, password
*/
public static function setPassphrase($params) {
if (\OCP\App::isEnabled('files_encryption') === false) {
return true;
}
// Only attempt to change passphrase if server-side encryption
// is in use (client-side encryption does not have access to
// the necessary keys)
if (Crypt::mode() === 'server') {
$view = new \OC\Files\View('/');
$session = new Session($view);
// Get existing decrypted private key
$privateKey = $session->getPrivateKey();
if ($params['uid'] === \OCP\User::getUser() && $privateKey) {
// Encrypt private key with new user pwd as passphrase
$encryptedPrivateKey = Crypt::symmetricEncryptFileContent($privateKey, $params['password'], Helper::getCipher());
// Save private key
if ($encryptedPrivateKey) {
Keymanager::setPrivateKey($encryptedPrivateKey, \OCP\User::getUser());
} else {
\OCP\Util::writeLog('files_encryption', 'Could not update users encryption password', \OCP\Util::ERROR);
}
// NOTE: Session does not need to be updated as the
// private key has not changed, only the passphrase
// used to decrypt it has changed
} else { // admin changed the password for a different user, create new keys and reencrypt file keys
$user = $params['uid'];
$util = new Util($view, $user);
$recoveryPassword = isset($params['recoveryPassword']) ? $params['recoveryPassword'] : null;
// we generate new keys if...
// ...we have a recovery password and the user enabled the recovery key
// ...encryption was activated for the first time (no keys exists)
// ...the user doesn't have any files
if (($util->recoveryEnabledForUser() && $recoveryPassword)
|| !$util->userKeysExists()
|| !$view->file_exists($user . '/files')) {
// backup old keys
$util->backupAllKeys('recovery');
$newUserPassword = $params['password'];
// make sure that the users home is mounted
\OC\Files\Filesystem::initMountPoints($user);
$keypair = Crypt::createKeypair();
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
// Save public key
Keymanager::setPublicKey($keypair['publicKey'], $user);
// Encrypt private key with new password
$encryptedKey = Crypt::symmetricEncryptFileContent($keypair['privateKey'], $newUserPassword, Helper::getCipher());
if ($encryptedKey) {
Keymanager::setPrivateKey($encryptedKey, $user);
if ($recoveryPassword) { // if recovery key is set we can re-encrypt the key files
$util = new Util($view, $user);
$util->recoverUsersFiles($recoveryPassword);
}
} else {
\OCP\Util::writeLog('files_encryption', 'Could not update users encryption password', \OCP\Util::ERROR);
}
\OC_FileProxy::$enabled = $proxyStatus;
}
}
}
}
/**
* after password reset we create a new key pair for the user
*
* @param array $params
*/
public static function postPasswordReset($params) {
$uid = $params['uid'];
$password = $params['password'];
$util = new Util(new \OC\Files\View(), $uid);
$util->replaceUserKeys($password);
}
/*
* check if files can be encrypted to every user.

View File

@ -0,0 +1,28 @@
<?php
/**
* @author Clark Tomlinson <clark@owncloud.com>
* @since 2/25/15, 9:38 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 OC\Encryption\Exceptions;
class DecryptionFailedException extends GenericEncryptionException {
}

View File

@ -0,0 +1,28 @@
<?php
/**
* @author Clark Tomlinson <clark@owncloud.com>
* @since 2/25/15, 9:38 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 OC\Encryption\Exceptions;
class EmptyEncryptionDataException extends GenericEncryptionException{
}

View File

@ -0,0 +1,28 @@
<?php
/**
* @author Clark Tomlinson <clark@owncloud.com>
* @since 2/25/15, 9:37 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 OC\Encryption\Exceptions;
class EncryptionFailedException extends GenericEncryptionException{
}

View File

@ -0,0 +1,28 @@
<?php
/**
* @author Clark Tomlinson <clark@owncloud.com>
* @since 2/25/15, 9:35 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 OC\Encryption\Exceptions;
class EncryptionHeaderToLargeException extends GenericEncryptionException {
}

View File

@ -0,0 +1,27 @@
<?php
/**
* @author Clark Tomlinson <clark@owncloud.com>
* @since 2/25/15, 9:30 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 OC\Encryption\Exceptions;
class GenericEncryptionException extends \Exception {
}

View File

@ -0,0 +1,28 @@
<?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 OC\Encryption\Exceptions;
class PrivateKeyMissingException extends GenericEncryptionException{
}

View File

@ -0,0 +1,28 @@
<?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 OC\Encryption\Exceptions;
class PublicKeyMissingException extends GenericEncryptionException {
}

View File

@ -0,0 +1,28 @@
<?php
/**
* @author Clark Tomlinson <clark@owncloud.com>
* @since 2/25/15, 9:35 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 OC\Encryption\Exceptions;
interface UnexpectedBlockSize {
}

View File

@ -0,0 +1,28 @@
<?php
/**
* @author Clark Tomlinson <clark@owncloud.com>
* @since 2/25/15, 9:34 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 OC\Encryption\Exceptions;
class UnexpectedEndOfEncryptionHeaderException extends GenericEncryptionException {
}

View File

@ -0,0 +1,28 @@
<?php
/**
* @author Clark Tomlinson <clark@owncloud.com>
* @since 2/25/15, 9:36 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 OC\Encryption\Exceptions;
class UnknownCipherException extends GenericEncryptionException{
}

View File

@ -23,25 +23,29 @@
namespace OC\Encryption;
use OC\Encryption\Util;
use OC\Files\View;
use OCA\Files_Encryption\Exception\EncryptionException;
class KeyStorage implements \OCP\Encryption\IKeyStorage {
/** @var \OC\Files\View */
/** @var View */
private $view;
/** @var \OC\Encryption\Util */
/** @var Util */
private $util;
// base dir where all the file related keys are stored
private static $keys_base_dir = '/files_encryption/keys/';
private static $encryption_base_dir = '/files_encryption';
private static $key_cache = array(); // cache keys
private $keyCache = array();
/**
* @param \OC\Files\View $view
* @param \OC\Encryption\Util $util
* @param View $view
* @param Util $util
*/
public function __construct(\OC\Files\View $view, \OC\Encryption\Util $util) {
public function __construct(View $view, Util $util) {
$this->view = $view;
$this->util = $util;
}
@ -50,14 +54,13 @@ class KeyStorage implements \OCP\Encryption\IKeyStorage {
* get user specific key
*
* @param string $uid ID if the user for whom we want the key
* @param string $keyid id of the key
* @param string $keyId id of the key
*
* @return mixed key
*/
public function getUserKey($uid, $keyid) {
$path = '/' . $uid . self::$encryption_base_dir . '/' . $uid . '.' . $keyid;
public function getUserKey($uid, $keyId) {
$path = '/' . $uid . self::$encryption_base_dir . '/' . $uid . '.' . $keyId;
return $this->getKey($path);
}
/**
@ -135,8 +138,8 @@ class KeyStorage implements \OCP\Encryption\IKeyStorage {
$key = '';
if (isset(self::$key_cache[$path])) {
$key = self::$key_cache[$path];
if (isset($this->keyCache[$path])) {
$key = $this->keyCache[$path];
} else {
/** @var \OCP\Files\Storage $storage */
@ -144,7 +147,7 @@ class KeyStorage implements \OCP\Encryption\IKeyStorage {
if ($storage->file_exists($internalPath)) {
$key = $storage->file_get_contents($internalPath);
self::$key_cache[$path] = $key;
$this->keyCache[$path] = $key;
}
}
@ -168,7 +171,7 @@ class KeyStorage implements \OCP\Encryption\IKeyStorage {
$result = $storage->file_put_contents($internalPath, $key);
if (is_int($result) && $result > 0) {
self::$key_cache[$path] = $key;
$this->keyCache[$path] = $key;
return true;
}
@ -180,11 +183,16 @@ class KeyStorage implements \OCP\Encryption\IKeyStorage {
*
* @param string $path path to the file, relative to the users file directory
* @return string
* @throws EncryptionException
* @internal param string $keyId
*/
private function getFileKeyDir($path) {
//
// TODO: NO DEPRICATED API !!!
//
if ($this->view->is_dir('/' . \OCP\User::getUser() . '/' . $path)) {
throw new Exception\EncryptionException('file was expected but directoy was given', Exception\EncryptionException::GENERIC);
throw new EncryptionException('file was expected but directory was given', EncryptionException::GENERIC);
}
list($owner, $filename) = $this->util->getUidAndFilename($path);
@ -220,4 +228,23 @@ class KeyStorage implements \OCP\Encryption\IKeyStorage {
}
}
/**
* Check if encryption system is ready to begin encrypting
* all the things
*
* @return bool
*/
public function ready() {
$paths = [
self::$encryption_base_dir,
self::$keys_base_dir
];
foreach ($paths as $path) {
if (!$this->view->file_exists($path)) {
return false;
}
}
return true;
}
}

View File

@ -29,59 +29,65 @@ interface IKeyStorage {
* get user specific key
*
* @param string $uid ID if the user for whom we want the key
* @param string $keyid id of the key
* @param string $keyId id of the key
*
* @return mixed key
*/
public function getUserKey($uid, $keyid);
public function getUserKey($uid, $keyId);
/**
* get file specific key
*
* @param string $path path to file
* @param string $keyid id of the key
* @param string $keyId id of the key
*
* @return mixed key
*/
public function getFileKey($path, $keyid);
public function getFileKey($path, $keyId);
/**
* get system-wide encryption keys not related to a specific user,
* e.g something like a key for public link shares
*
* @param string $keyid id of the key
* @param string $keyId id of the key
*
* @return mixed key
*/
public function getSystemUserKey($uid, $keyid);
public function getSystemUserKey($keyId);
/**
* set user specific key
*
* @param string $uid ID if the user for whom we want the key
* @param string $keyid id of the key
* @param string $keyId id of the key
* @param mixed $key
*/
public function setUserKey($uid, $keyid, $key);
public function setUserKey($uid, $keyId, $key);
/**
* set file specific key
*
* @param string $path path to file
* @param string $keyid id of the key
* @param string $keyId id of the key
* @param mixed $key
*/
public function setFileKey($path, $keyid, $key);
public function setFileKey($path, $keyId, $key);
/**
* set system-wide encryption keys not related to a specific user,
* e.g something like a key for public link shares
*
* @param string $keyid id of the key
* @param string $keyId id of the key
* @param mixed $key
*
* @return mixed key
*/
public function setSystemUserKey($uid, $keyid, $key);
public function setSystemUserKey($keyId, $key);
/**
* Return if encryption is setup and ready encrypt things
*
* @return bool
*/
public function ready();
}

View File

@ -111,4 +111,62 @@ class ManagerTest extends TestCase {
$en0 = $m->getEncryptionModule(0);
$this->assertEquals(0, $en0->getId());
}
/**
* @expectedException \OC\Encryption\Exceptions\ModuleAlreadyExistsException
* @expectedExceptionMessage At the moment it is not allowed to register more than one encryption module
*/
public function testModuleRegistration() {
$config = $this->getMock('\OCP\IConfig');
$config->expects($this->any())->method('getSystemValue')->willReturn(true);
$em = $this->getMock('\OCP\Encryption\IEncryptionModule');
$em->expects($this->any())->method('getId')->willReturn(0);
$em->expects($this->any())->method('getDisplayName')->willReturn('TestDummyModule0');
$m = new Manager($config);
$m->registerEncryptionModule($em);
$this->assertTrue($m->isEnabled());
$m->registerEncryptionModule($em);
}
public function testModuleUnRegistration() {
$config = $this->getMock('\OCP\IConfig');
$config->expects($this->any())->method('getSystemValue')->willReturn(true);
$em = $this->getMock('\OCP\Encryption\IEncryptionModule');
$em->expects($this->any())->method('getId')->willReturn(0);
$em->expects($this->any())->method('getDisplayName')->willReturn('TestDummyModule0');
$m = new Manager($config);
$m->registerEncryptionModule($em);
$this->assertTrue($m->isEnabled());
$m->unregisterEncryptionModule($em);
$this->assertFalse($m->isEnabled());
}
/**
* @expectedException \OC\Encryption\Exceptions\ModuleDoesNotExistsException
* @expectedExceptionMessage Module with id: unknown does not exists.
*/
public function testGetEncryptionModuleUnknown() {
$config = $this->getMock('\OCP\IConfig');
$config->expects($this->any())->method('getSystemValue')->willReturn(true);
$em = $this->getMock('\OCP\Encryption\IEncryptionModule');
$em->expects($this->any())->method('getId')->willReturn(0);
$em->expects($this->any())->method('getDisplayName')->willReturn('TestDummyModule0');
$m = new Manager($config);
$m->registerEncryptionModule($em);
$this->assertTrue($m->isEnabled());
$m->getEncryptionModule('unknown');
}
public function testGetEncryptionModule() {
$config = $this->getMock('\OCP\IConfig');
$config->expects($this->any())->method('getSystemValue')->willReturn(true);
$em = $this->getMock('\OCP\Encryption\IEncryptionModule');
$em->expects($this->any())->method('getId')->willReturn(0);
$em->expects($this->any())->method('getDisplayName')->willReturn('TestDummyModule0');
$m = new Manager($config);
$m->registerEncryptionModule($em);
$this->assertTrue($m->isEnabled());
$en0 = $m->getEncryptionModule(0);
$this->assertEquals(0, $en0->getId());
}
}