Initial commit
This commit is contained in:
parent
63e7fe608a
commit
39733c8da1
|
@ -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();
|
||||
}
|
||||
|
|
@ -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');
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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'
|
||||
]
|
||||
|
||||
|
||||
)));
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
}
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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....
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
|
@ -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;
|
||||
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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 {
|
||||
|
||||
}
|
|
@ -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{
|
||||
|
||||
}
|
|
@ -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{
|
||||
|
||||
}
|
|
@ -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 {
|
||||
|
||||
}
|
|
@ -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 {
|
||||
|
||||
}
|
|
@ -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{
|
||||
|
||||
}
|
|
@ -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 {
|
||||
|
||||
}
|
|
@ -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 {
|
||||
|
||||
}
|
|
@ -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 {
|
||||
|
||||
}
|
|
@ -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{
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue